Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ if(BUILD_TESTS)
tests/random_op.c
tests/realloc_move.c
tests/reg.c
tests/shrink.c
)

add_executable(buffer_set_tests ${TEST_SRCS})
Expand Down
8 changes: 8 additions & 0 deletions include/buffer_set/buffer_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ int buffer_set_verify(
FILE * file
);

/**
* Shrink the buffer capacity of the set if it is underutilized.
*
* If the number of elements in the set is less than one quarter of the
* current buffer capacity, the capacity is reduced by half.
*/
void buffer_set_shrink(buffer_set_t * buffer_set);

void buffer_set_clear(buffer_set_t * buffer_set);
void buffer_set_destroy(buffer_set_t * buffer_set);

Expand Down
87 changes: 87 additions & 0 deletions src/buffer_set.c
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,93 @@ static uint16_t _buffer_set_clear(
return idx;
}

static uint16_t _buffer_set_move_tree(
buffer_set_t * buffer_set,
uint16_t src_idx,
void * src_buffer
) {
uint16_t idx = ++buffer_set->size;
size_t node_size = buffer_set->node_size;
struct node_s * dst_node = _get_node(buffer_set, idx);
struct node_s * src_node = (void*) (((char*)src_buffer) + (src_idx * node_size));

uint16_t left = src_node->left;
if (left != NULL_IDX)
{
left = _buffer_set_move_tree(buffer_set, left, src_buffer);
struct node_s * left_node = _get_node(buffer_set, left);
left_node->parent = idx;
}
dst_node->left = left;

uint16_t right = src_node->right;
if (right != NULL_IDX)
{
right = _buffer_set_move_tree(buffer_set, right, src_buffer);
struct node_s * right_node = _get_node(buffer_set, right);
right_node->parent = idx;
}
dst_node->right = right;

dst_node->balance = src_node->balance;

void (*move)(void*, void*, void*) = buffer_set->move;
if (move == NULL)
{
size_t value_size = (node_size - _round(sizeof(struct node_s)));
memcpy(
_node_get_value(dst_node),
_node_get_value(src_node),
value_size
);
}
else
move(dst_node, src_node, buffer_set->thunk);

return idx;
}

void buffer_set_shrink(buffer_set_t * buffer_set)
{
uint16_t new_capacity = buffer_set->capacity;
while ((buffer_set->size + 1) < (new_capacity / 4))
new_capacity /= 2;

if (new_capacity < MIN_CAPACITY)
{
new_capacity = MIN_CAPACITY;
if (new_capacity >= buffer_set->capacity)
return;
}

void * buffer = malloc(buffer_set->node_size * new_capacity);
if (buffer == NULL)
return;

void * old_buffer = buffer_set->buffer;
buffer_set->buffer = buffer;
buffer_set->capacity = new_capacity;

uint16_t root = buffer_set->root;
if (root != NULL_IDX)
{
buffer_set->size = 0;
root = _buffer_set_move_tree(buffer_set, root, old_buffer);
buffer_set->root = root;
struct node_s * node = _get_node(buffer_set, root);
node->parent = NULL_IDX;
}

free(old_buffer);

buffer_set->free_list = _make_free_list(
buffer,
buffer_set->node_size,
buffer_set->size + 1,
(new_capacity - buffer_set->size - 1)
);
}

void buffer_set_clear(buffer_set_t * buffer_set)
{
const uint16_t root = buffer_set->root;
Expand Down
2 changes: 2 additions & 0 deletions tests/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ int print_debug();
int random_op();
int realloc_move();
int reg();
int shrink();

void run_test(int * failed_tests, const char * name, int (*test_func)())
{
Expand Down Expand Up @@ -72,6 +73,7 @@ int main(int argc, const char * argv[])
RUN_TEST(print_debug);
RUN_TEST(random_op);
RUN_TEST(reg);
RUN_TEST(shrink);

#undef RUN_TEST

Expand Down
74 changes: 74 additions & 0 deletions tests/shrink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* This file is part of BUFFER_SET library.
* Copyright (C) 2020 Sergey Zubarev, info@js-labs.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*/

#include <buffer_set/buffer_set.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"

int shrink()
{
buffer_set_t* buffer_set = buffer_set_create(sizeof(int), 0, &int_cmp, NULL, NULL);
if (buffer_set == NULL)
{
printf("buffer_set_create() failed");
return -1;
}

for (int idx=0; idx<63; idx++)
{
int inserted = 0;
void * ptr = buffer_set_insert(buffer_set, &idx, &inserted);
*((int*)ptr) = idx;
}

for (int idx=0; idx<50; idx++)
buffer_set_erase(buffer_set, &idx);

buffer_set_shrink(buffer_set);

int rc = 0;
buffer_set_iterator_t * it = buffer_set_begin(buffer_set);

for (int idx=50; idx<63; idx++)
{
if (it == buffer_set_end(buffer_set))
{
fprintf(stderr, "unexpected iterator end\n");
rc = -1;
break;
}

int * value = buffer_set_get_at(buffer_set, it);
if (*value != idx)
{
fprintf(stderr, "got %d instead of expected %d\n", *value, idx);
rc = -1;
break;
}

it = buffer_set_iterator_next(buffer_set, it);
}

if (it != buffer_set_end(buffer_set))
{
fprintf(stderr, "iterator unexpectedly not end\n");
rc = -1;
}

buffer_set_destroy(buffer_set);

return rc;
}