mirror of
https://github.com/bkthomps/Containers.git
synced 2025-11-16 04:24:47 +00:00
Change deque block size (#74)
The block size for deque now scales based on the element size. This means that the byte count in each block remains the same, but with bigger elements, fewer elements may be stored in each block (but still the same number of bytes). There is an exception for elements that are very large, and a minimum element count exists for the block in that case.
This commit is contained in:
89
src/deque.c
89
src/deque.c
@@ -24,12 +24,14 @@
|
||||
#include <errno.h>
|
||||
#include "include/deque.h"
|
||||
|
||||
static const size_t BLOCK_SIZE = 1024;
|
||||
static const size_t MAX_BLOCK_BYTE_SIZE = 4096;
|
||||
static const size_t MIN_BLOCK_ELEMENT_SIZE = 16;
|
||||
static const size_t INITIAL_BLOCK_COUNT = 8;
|
||||
static const double RESIZE_RATIO = 1.5;
|
||||
|
||||
struct internal_deque {
|
||||
size_t data_size;
|
||||
size_t block_size;
|
||||
size_t start_index;
|
||||
size_t end_index;
|
||||
size_t block_count;
|
||||
@@ -57,7 +59,11 @@ deque deque_init(const size_t data_size)
|
||||
return NULL;
|
||||
}
|
||||
init->data_size = data_size;
|
||||
init->start_index = BLOCK_SIZE * INITIAL_BLOCK_COUNT / 2;
|
||||
init->block_size = MAX_BLOCK_BYTE_SIZE / init->data_size;
|
||||
if (init->block_size < MIN_BLOCK_ELEMENT_SIZE) {
|
||||
init->block_size = MIN_BLOCK_ELEMENT_SIZE;
|
||||
}
|
||||
init->start_index = init->block_size * INITIAL_BLOCK_COUNT / 2;
|
||||
init->end_index = init->start_index;
|
||||
init->block_count = INITIAL_BLOCK_COUNT;
|
||||
init->data = calloc(init->block_count, sizeof(char *));
|
||||
@@ -65,13 +71,14 @@ deque deque_init(const size_t data_size)
|
||||
free(init);
|
||||
return NULL;
|
||||
}
|
||||
block = malloc(BLOCK_SIZE * init->data_size);
|
||||
block = malloc(init->block_size * init->data_size);
|
||||
if (!block) {
|
||||
free(init->data);
|
||||
free(init);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(init->data + init->start_index / BLOCK_SIZE, &block, sizeof(char *));
|
||||
memcpy(init->data + init->start_index / init->block_size, &block,
|
||||
sizeof(char *));
|
||||
return init;
|
||||
}
|
||||
|
||||
@@ -113,9 +120,9 @@ int deque_is_empty(deque me)
|
||||
int deque_trim(deque me)
|
||||
{
|
||||
size_t i;
|
||||
const size_t start_block_index = me->start_index / BLOCK_SIZE;
|
||||
const size_t start_block_index = me->start_index / me->block_size;
|
||||
const size_t end_block_index = deque_is_empty(me) ? start_block_index :
|
||||
(me->end_index - 1) / BLOCK_SIZE;
|
||||
(me->end_index - 1) / me->block_size;
|
||||
const size_t updated_block_count = end_block_index - start_block_index + 1;
|
||||
char **updated_data = malloc(updated_block_count * sizeof(char *));
|
||||
if (!updated_data) {
|
||||
@@ -134,8 +141,8 @@ int deque_trim(deque me)
|
||||
free(block);
|
||||
}
|
||||
free(me->data);
|
||||
me->start_index -= start_block_index * BLOCK_SIZE;
|
||||
me->end_index -= start_block_index * BLOCK_SIZE;
|
||||
me->start_index -= start_block_index * me->block_size;
|
||||
me->end_index -= start_block_index * me->block_size;
|
||||
me->block_count = updated_block_count;
|
||||
me->data = updated_data;
|
||||
return 0;
|
||||
@@ -157,13 +164,13 @@ void deque_copy_to_array(void *const arr, deque me)
|
||||
char *block;
|
||||
size_t i;
|
||||
size_t size_offset;
|
||||
const size_t start_block_index = me->start_index / BLOCK_SIZE;
|
||||
const size_t start_inner_index = me->start_index % BLOCK_SIZE;
|
||||
const size_t end_block_index = (me->end_index - 1) / BLOCK_SIZE;
|
||||
const size_t end_inner_index = (me->end_index - 1) % BLOCK_SIZE;
|
||||
const size_t start_block_index = me->start_index / me->block_size;
|
||||
const size_t start_inner_index = me->start_index % me->block_size;
|
||||
const size_t end_block_index = (me->end_index - 1) / me->block_size;
|
||||
const size_t end_inner_index = (me->end_index - 1) % me->block_size;
|
||||
const size_t first_block_length =
|
||||
BLOCK_SIZE - start_inner_index < deque_size(me)
|
||||
? BLOCK_SIZE - start_inner_index : deque_size(me);
|
||||
me->block_size - start_inner_index < deque_size(me)
|
||||
? me->block_size - start_inner_index : deque_size(me);
|
||||
if (deque_is_empty(me)) {
|
||||
return;
|
||||
}
|
||||
@@ -172,8 +179,9 @@ void deque_copy_to_array(void *const arr, deque me)
|
||||
memcpy(arr, block + start_inner_index * me->data_size, size_offset);
|
||||
for (i = start_block_index + 1; i < end_block_index; i++) {
|
||||
memcpy(&block, me->data + i, sizeof(char *));
|
||||
memcpy((char *) arr + size_offset, block, BLOCK_SIZE * me->data_size);
|
||||
size_offset += BLOCK_SIZE * me->data_size;
|
||||
memcpy((char *) arr + size_offset, block,
|
||||
me->block_size * me->data_size);
|
||||
size_offset += me->block_size * me->data_size;
|
||||
}
|
||||
if (end_block_index != start_block_index) {
|
||||
memcpy(&block, me->data + end_block_index, sizeof(char *));
|
||||
@@ -209,17 +217,18 @@ int deque_push_front(deque me, void *const data)
|
||||
memset(temp, 0, added_blocks * sizeof(char *));
|
||||
me->data = temp;
|
||||
me->block_count += added_blocks;
|
||||
me->start_index += added_blocks * BLOCK_SIZE;
|
||||
me->end_index += added_blocks * BLOCK_SIZE;
|
||||
me->start_index += added_blocks * me->block_size;
|
||||
me->end_index += added_blocks * me->block_size;
|
||||
}
|
||||
if (me->start_index % BLOCK_SIZE == 0) {
|
||||
if (me->start_index % me->block_size == 0) {
|
||||
char *block;
|
||||
const size_t previous_block_index = me->start_index / BLOCK_SIZE - 1;
|
||||
const size_t previous_block_index =
|
||||
me->start_index / me->block_size - 1;
|
||||
memcpy(&block, me->data + previous_block_index, sizeof(char *));
|
||||
if (block) {
|
||||
goto copy_element;
|
||||
}
|
||||
block = malloc(BLOCK_SIZE * me->data_size);
|
||||
block = malloc(me->block_size * me->data_size);
|
||||
if (!block) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -229,8 +238,8 @@ int deque_push_front(deque me, void *const data)
|
||||
me->start_index--;
|
||||
{
|
||||
char *block;
|
||||
const size_t block_index = me->start_index / BLOCK_SIZE;
|
||||
const size_t inner_index = me->start_index % BLOCK_SIZE;
|
||||
const size_t block_index = me->start_index / me->block_size;
|
||||
const size_t inner_index = me->start_index % me->block_size;
|
||||
memcpy(&block, me->data + block_index, sizeof(char *));
|
||||
memcpy(block + inner_index * me->data_size, data, me->data_size);
|
||||
}
|
||||
@@ -252,7 +261,7 @@ int deque_push_front(deque me, void *const data)
|
||||
*/
|
||||
int deque_push_back(deque me, void *const data)
|
||||
{
|
||||
if (me->end_index == me->block_count * BLOCK_SIZE) {
|
||||
if (me->end_index == me->block_count * me->block_size) {
|
||||
const size_t updated_block_count =
|
||||
(size_t) (me->block_count * RESIZE_RATIO) + 1;
|
||||
const size_t added_blocks = updated_block_count - me->block_count;
|
||||
@@ -264,14 +273,14 @@ int deque_push_back(deque me, void *const data)
|
||||
me->data = temp;
|
||||
me->block_count += added_blocks;
|
||||
}
|
||||
if (me->end_index % BLOCK_SIZE == 0) {
|
||||
if (me->end_index % me->block_size == 0) {
|
||||
char *block;
|
||||
const size_t tentative_block_index = me->end_index / BLOCK_SIZE;
|
||||
const size_t tentative_block_index = me->end_index / me->block_size;
|
||||
memcpy(&block, me->data + tentative_block_index, sizeof(char *));
|
||||
if (block) {
|
||||
goto copy_element;
|
||||
}
|
||||
block = malloc(BLOCK_SIZE * me->data_size);
|
||||
block = malloc(me->block_size * me->data_size);
|
||||
if (!block) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -280,8 +289,8 @@ int deque_push_back(deque me, void *const data)
|
||||
copy_element:
|
||||
{
|
||||
char *block;
|
||||
const size_t block_index = me->end_index / BLOCK_SIZE;
|
||||
const size_t inner_index = me->end_index % BLOCK_SIZE;
|
||||
const size_t block_index = me->end_index / me->block_size;
|
||||
const size_t inner_index = me->end_index % me->block_size;
|
||||
memcpy(&block, me->data + block_index, sizeof(char *));
|
||||
memcpy(block + inner_index * me->data_size, data, me->data_size);
|
||||
}
|
||||
@@ -306,8 +315,8 @@ int deque_push_back(deque me, void *const data)
|
||||
int deque_pop_front(void *const data, deque me)
|
||||
{
|
||||
char *block;
|
||||
const size_t block_index = me->start_index / BLOCK_SIZE;
|
||||
const size_t inner_index = me->start_index % BLOCK_SIZE;
|
||||
const size_t block_index = me->start_index / me->block_size;
|
||||
const size_t inner_index = me->start_index % me->block_size;
|
||||
if (deque_is_empty(me)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -334,8 +343,8 @@ int deque_pop_front(void *const data, deque me)
|
||||
int deque_pop_back(void *const data, deque me)
|
||||
{
|
||||
char *block;
|
||||
const size_t block_index = (me->end_index - 1) / BLOCK_SIZE;
|
||||
const size_t inner_index = (me->end_index - 1) % BLOCK_SIZE;
|
||||
const size_t block_index = (me->end_index - 1) / me->block_size;
|
||||
const size_t inner_index = (me->end_index - 1) % me->block_size;
|
||||
if (deque_is_empty(me)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -380,8 +389,8 @@ int deque_set_first(deque me, void *const data)
|
||||
int deque_set_at(deque me, size_t index, void *const data)
|
||||
{
|
||||
char *block;
|
||||
const size_t block_index = (index + me->start_index) / BLOCK_SIZE;
|
||||
const size_t inner_index = (index + me->start_index) % BLOCK_SIZE;
|
||||
const size_t block_index = (index + me->start_index) / me->block_size;
|
||||
const size_t inner_index = (index + me->start_index) % me->block_size;
|
||||
if (index >= deque_size(me)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -443,8 +452,8 @@ int deque_get_first(void *const data, deque me)
|
||||
int deque_get_at(void *const data, deque me, size_t index)
|
||||
{
|
||||
char *block;
|
||||
const size_t block_index = (index + me->start_index) / BLOCK_SIZE;
|
||||
const size_t inner_index = (index + me->start_index) % BLOCK_SIZE;
|
||||
const size_t block_index = (index + me->start_index) / me->block_size;
|
||||
const size_t inner_index = (index + me->start_index) % me->block_size;
|
||||
if (index >= deque_size(me)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -487,7 +496,7 @@ int deque_clear(deque me)
|
||||
if (!updated_data) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
updated_block = malloc(BLOCK_SIZE * me->data_size);
|
||||
updated_block = malloc(me->block_size * me->data_size);
|
||||
if (!updated_block) {
|
||||
free(updated_data);
|
||||
return -ENOMEM;
|
||||
@@ -498,11 +507,11 @@ int deque_clear(deque me)
|
||||
free(block);
|
||||
}
|
||||
free(me->data);
|
||||
me->start_index = BLOCK_SIZE * INITIAL_BLOCK_COUNT / 2;
|
||||
me->start_index = me->block_size * INITIAL_BLOCK_COUNT / 2;
|
||||
me->end_index = me->start_index;
|
||||
me->block_count = INITIAL_BLOCK_COUNT;
|
||||
me->data = updated_data;
|
||||
memcpy(me->data + me->start_index / BLOCK_SIZE, &updated_block,
|
||||
memcpy(me->data + me->start_index / me->block_size, &updated_block,
|
||||
sizeof(char *));
|
||||
return 0;
|
||||
}
|
||||
|
||||
25
tst/deque.c
25
tst/deque.c
@@ -241,6 +241,28 @@ static void test_array_copy(void)
|
||||
deque_destroy(me);
|
||||
}
|
||||
|
||||
struct large_data {
|
||||
double data[100];
|
||||
};
|
||||
|
||||
static void test_large_elements(void)
|
||||
{
|
||||
int i;
|
||||
struct large_data data = {0};
|
||||
struct large_data get = {0};
|
||||
deque me = deque_init(sizeof(struct large_data));
|
||||
for (i = 0; i < 100; i++) {
|
||||
data.data[i] = i + 0.5;
|
||||
}
|
||||
deque_push_back(me, &data);
|
||||
deque_pop_back(&get, me);
|
||||
for (i = 0; i < 100; i++) {
|
||||
assert(data.data[i] > i + 0.4);
|
||||
assert(data.data[i] < i + 0.6);
|
||||
}
|
||||
deque_destroy(me);
|
||||
}
|
||||
|
||||
#if STUB_MALLOC
|
||||
static void test_init_out_of_memory(void)
|
||||
{
|
||||
@@ -405,7 +427,10 @@ void test_deque(void)
|
||||
test_trim();
|
||||
test_stress();
|
||||
test_array_copy();
|
||||
test_large_elements();
|
||||
#if STUB_MALLOC
|
||||
/* These OOM tests rely on this being true. */
|
||||
assert(sizeof(int) == 4);
|
||||
test_init_out_of_memory();
|
||||
test_trim_out_of_memory();
|
||||
test_push_front_out_of_memory();
|
||||
|
||||
Reference in New Issue
Block a user