Skip to content
Open
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
193 changes: 177 additions & 16 deletions cstack.c
Original file line number Diff line number Diff line change
@@ -1,42 +1,203 @@
#include "cstack.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>

#define UNUSED(VAR) (void)(VAR)
#define RESULT_OK 0
#define RESULT_NOT_OK 1
#define INVALID_HANDLER -1
#define INITIAL_SIZE 10

typedef struct node
{
struct node *prev;
unsigned int size;
char data[0];
} node;

typedef struct stack
{
node *top;
unsigned int size;
} stack;

typedef struct stack_entry
{
int valid;
stack stack;
} stack_entry;

typedef struct stack_entries_table
{
hstack_t size;
stack_entry *entries;
} stack_entries_table;

stack_entries_table stacks = {0u, NULL};

int allocate_table(void)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Внутренним функциям библиотеки, предназначенным для использования только из этой единицы трансляции, советую обеспечить internal linkage. Тогда у пользователя не будет проблем, если он захочет определить собственную функцию allocate_table или get_free_index.

Пример без internal linkage:

$ cat main.c 
int main() {}

void foo() {}

$ cat foo.c 
int foo() {}

$ cc main.c foo.c
/usr/bin/ld: /tmp/ccrXtvnH.o: in function `foo':
foo.c:(.text+0x0): multiple definition of `foo'; /tmp/ccSdwxT5.o:main.c:(.text+0xb): first defined here
collect2: error: ld returned 1 exit status

Пример с internal linkage:

$ cat foo.c 
static int foo() {}

$ cc main.c foo.c

{
assert(!stacks.entries);
stacks.entries = calloc(INITIAL_SIZE, sizeof(stack_entry));
if (stacks.entries)
stacks.size = INITIAL_SIZE;
else
{
stacks.entries = calloc(1, sizeof(stack_entry));
if (stacks.entries)
stacks.size = 1;
else
return RESULT_NOT_OK;
}
return RESULT_OK;
}

void reallocate_table(void)
{
assert(stacks.entries);
hstack_t new_size = stacks.size * 2;
stack_entry *new_array = calloc(new_size, sizeof(stack_entry));

if (!new_array)
{
new_size = stacks.size + 1;
new_array = calloc(new_size, sizeof(stack_entry));
}

if (new_array)
{
memcpy(new_array, stacks.entries, stacks.size * sizeof(stack_entry));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В C11 появилась более безопасная версия memcpy: memcpy_s.

free(stacks.entries);
stacks.entries = new_array;
stacks.size = new_size;
}
}

void deallocate_table(void)
{
free(stacks.entries);
stacks.entries = NULL;
stacks.size = 0;
}

int is_table_empty(void)
{
for (hstack_t index = 0; index < stacks.size; ++index)
{
if (stacks.entries[index].valid)
return RESULT_NOT_OK;
}
return RESULT_OK;
}

hstack_t get_free_index(void)
{
hstack_t index = 0;
for (; index < stacks.size && stacks.entries[index].valid; ++index)
;
return index;
}

hstack_t stack_new(void)
{
return -1;
if (!stacks.entries)
if (allocate_table() != RESULT_OK)
return INVALID_HANDLER;

hstack_t index = get_free_index();

if (index >= stacks.size)
reallocate_table();

if (index < stacks.size)
{
stacks.entries[index].valid = 1;
return index;
}
return INVALID_HANDLER;
}

void stack_free(const hstack_t hstack)
{
UNUSED(hstack);
if (stack_valid_handler(hstack) != RESULT_OK)
return;

stack *stack = &stacks.entries[hstack].stack;
node *cur_node = stack->top;

stacks.entries[hstack].valid = 0;
stack->top = NULL;
stack->size = 0;

node *prev_node;
while (cur_node)
{
prev_node = cur_node->prev;
free(cur_node);
cur_node = prev_node;
}

if (is_table_empty() == RESULT_OK)
deallocate_table();
}

int stack_valid_handler(const hstack_t hstack)
{
UNUSED(hstack);
return 1;
if (hstack >= 0 && hstack < stacks.size && stacks.entries[hstack].valid)
return RESULT_OK;
return RESULT_NOT_OK;
}

unsigned int stack_size(const hstack_t hstack)
{
UNUSED(hstack);
if (stack_valid_handler(hstack) == RESULT_OK)
return stacks.entries[hstack].stack.size;
return 0;
}

void stack_push(const hstack_t hstack, const void* data_in, const unsigned int size)
void stack_push(const hstack_t hstack, const void *data_in, const unsigned int size)
{
UNUSED(hstack);
UNUSED(data_in);
UNUSED(size);
if (!data_in || !size || stack_valid_handler(hstack) != RESULT_OK)
return;

stack *stack = &stacks.entries[hstack].stack;
if (stack->size == UINT_MAX)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кажется, это избыточная проверка. Вместо этого можно в качестве типа поля size использовать size_t. В этом случае память у вас закончится намного раньше, чем переполнится поле size.

return;

node *new_node = malloc(sizeof(node) + size);
if (!new_node)
return;

new_node->prev = stack->top;
new_node->size = size;
memcpy(new_node->data, data_in, size);

stack->top = new_node;
stack->size += 1;
}

unsigned int stack_pop(const hstack_t hstack, void* data_out, const unsigned int size)
unsigned int stack_pop(const hstack_t hstack, void *data_out, const unsigned int size)
{
UNUSED(hstack);
UNUSED(data_out);
UNUSED(size);
return 0;
}
if (!data_out || !size || stack_valid_handler(hstack) != RESULT_OK)
return 0;

stack *stack = &stacks.entries[hstack].stack;
node *cur_node = stack->top;

if (!cur_node)
return 0;
assert(stack->size > 0);

unsigned int data_size = cur_node->size;
if (data_size > size)
return 0;

stack->top = cur_node->prev;
stack->size -= 1;

memcpy(data_out, cur_node->data, data_size);
free(cur_node);
return data_size;
}
97 changes: 97 additions & 0 deletions test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,100 @@ TEST_F(ModifyTests, SeveralPushPop)
EXPECT_THAT(data_out, ::testing::ElementsAre(2, 1, 0));
}

TEST(CustomTests, NewPushFreePop)
{
const size_t count = 10;
hstack_t stacks[count] = {-1};
for (size_t i = 0; i < count; ++i)
{
stacks[i] = stack_new();
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 0u);
int data_out = 1;
EXPECT_EQ(stack_pop(stacks[i], &data_out, sizeof(data_out)), 0u);
EXPECT_EQ(data_out, 1);
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 0u);
}
for (size_t i = 0; i < count; ++i)
{
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 0u);
const int data_in = 1;
stack_push(stacks[i], &data_in, sizeof(data_in));
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 1u);
}
for (size_t i = 1; i < count; ++i)
{
stack_free(stacks[i]);
EXPECT_EQ(stack_valid_handler(stacks[i]), 1);
}
for (size_t i = 1; i < count; ++i)
{
stacks[i] = stack_new();
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 0u);
int data_out = 0;
EXPECT_EQ(stack_pop(stacks[i], &data_out, sizeof(data_out)), 0u);
EXPECT_EQ(data_out, 0);
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 0u);
}
for (size_t i = 0; i < count; ++i)
{
stack_free(stacks[i]);
EXPECT_EQ(stack_valid_handler(stacks[i]), 1);
}
}

TEST(CustomTests, FreeAfterFree)
{
const size_t count = 10;
hstack_t stacks[count] = {-1};
for (size_t i = 0; i < count; ++i)
{
stacks[i] = stack_new();
EXPECT_EQ(stack_valid_handler(stacks[i]), 0);
EXPECT_EQ(stack_size(stacks[i]), 0u);
}
for (size_t i = 1; i < count; ++i)
{
stack_free(stacks[i]);
EXPECT_EQ(stack_valid_handler(stacks[i]), 1);
}
for (size_t i = 0; i < count; ++i)
{
stack_free(stacks[i]);
EXPECT_EQ(stack_valid_handler(stacks[i]), 1);
}
for (size_t i = 0; i < count; ++i)
{
stack_free(stacks[i]);
EXPECT_EQ(stack_valid_handler(stacks[i]), 1);
}
}

TEST(CustomTests, BigSizeStack)
{
const char example = 'a';
const size_t limit = 100000;
hstack_t stack = stack_new();
EXPECT_EQ(stack_valid_handler(stack), 0);
for (unsigned int i = 0; i < limit; ++i)
{
char c = example;
stack_push(stack, &c, sizeof(c));
EXPECT_EQ(stack_size(stack), i + 1);
}
for (unsigned int i = 0; i < limit; ++i)
{
char c = 0;
unsigned int size = stack_pop(stack, &c, sizeof(c));
EXPECT_EQ(size, sizeof(c));
EXPECT_EQ(c, example);
EXPECT_EQ(stack_size(stack), limit - (i + 1));
}
stack_free(stack);
EXPECT_EQ(stack_valid_handler(stack), 1);
}