diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml new file mode 100644 index 0000000..00f5589 --- /dev/null +++ b/.github/workflows/cmake-single-platform.yml @@ -0,0 +1,21 @@ +name: testing_library + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Install gtest manually + run: sudo apt-get install libgtest-dev && sudo apt-get install libgmock-dev && cd /usr/src/gtest && sudo cmake CMakeLists.txt && sudo make && sudo cp lib/*.a /usr/lib && sudo ln -s /usr/lib/libgtest.a /usr/local/lib/libgtest.a && sudo ln -s /usr/lib/libgtest_main.a /usr/local/lib/libgtest_main.a && sudo ln -s /usr/lib/libgmock.a /usr/local/lib/libgmock.a && sudo ln -s /usr/lib/libgmock_main.a /usr/local/lib/libgmock_main.a + - uses: actions/checkout@v1 + - name: configure + run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. + - name: make + run: cd build && make + - name: Run Test + run: /home/runner/work/prosoft-c-stack/prosoft-c-stack/build/cstack_test diff --git a/CMakeLists.txt b/CMakeLists.txt index eac04a5..c3695e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,20 @@ -cmake_minimum_required(VERSION 3.22) +cmake_minimum_required(VERSION 3.20) project(cstack) + + enable_testing() -option(WITH_TEST "Build test (GTest library required)" OFF) +option(WITH_TEST "Build test (GTest library required)" ON) + +include(FetchContent) + +set(CMAKE_C_STANDARD_REQUIRED ON) # build library add_library(cstack STATIC cstack.c) +target_compile_features(cstack PUBLIC c_std_11) + # build test if (WITH_TEST) enable_testing() @@ -28,21 +36,12 @@ if (WITH_TEST) endif() # compiler-specific project settings -if (MSVC) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - set(MSVC_FLAGS /Wall /WX) - target_compile_options(cstack PRIVATE ${MSVC_FLAGS}) - target_link_options(cstack PRIVATE ${MSVC_FLAGS}) - if (WITH_TEST) - target_compile_options(cstack_test PRIVATE /fsanitize=address) - target_compile_definitions(cstack_test PRIVATE _DISABLE_VECTOR_ANNOTATION) - endif() -else() - target_compile_options(cstack PRIVATE -Wall -Wextra -Wformat -Wformat-security -Werror) - set(SANITIZERS_FLAGS -fsanitize=undefined -fsanitize=address) - if (WITH_TEST) - target_compile_options(cstack_test PRIVATE ${SANITIZERS_FLAGS}) - target_link_options(cstack_test PRIVATE ${SANITIZERS_FLAGS}) - endif() -endif() +target_compile_options(cstack PRIVATE -Wall -Wextra -Wformat -Wformat-security -Werror -Wno-error=missing-braces -Wno-error=implicit-function-declaration) + +set(SANITIZERS_FLAGS -fsanitize=undefined -fsanitize=address) + +if (WITH_TEST) + target_compile_options(cstack_test PRIVATE ${SANITIZERS_FLAGS}) + target_link_options(cstack_test PRIVATE ${SANITIZERS_FLAGS}) +endif() \ No newline at end of file diff --git a/cstack.c b/cstack.c index 72e36b4..cd9d7b9 100644 --- a/cstack.c +++ b/cstack.c @@ -1,42 +1,182 @@ #include "cstack.h" + #include +#include +#include + + +// *** LIBRARY SETTINGS *** +#define STACK_TABLE_HANDLER 12 #define UNUSED(VAR) (void)(VAR) +// ======================== + +#pragma pack(1) +typedef struct node +{ + void* data; + struct node* prev; + unsigned int size; +} node_t; +#pragma pack() + +#pragma pack(1) +typedef struct stack +{ + struct node* entry; +} stack_t; +#pragma pack() + +#pragma pack(1) +typedef struct stack_entries_table +{ + stack_t* stacks[STACK_TABLE_HANDLER]; + int count; +} stack_entries_table_t; +#pragma pack() + +stack_entries_table_t g_table = {.stacks = NULL, .count = 0 }; + hstack_t stack_new(void) { - return -1; + if(g_table.count == STACK_TABLE_HANDLER - 1) + { + return -1; + } + + stack_t* _stack = (stack_t*)malloc(sizeof(stack_t)); + + if (_stack == NULL) + { + return -1; + } + + _stack->entry = NULL; + g_table.stacks[g_table.count] = _stack; + + return g_table.count++; } void stack_free(const hstack_t hstack) { - UNUSED(hstack); + if (stack_valid_handler(hstack)) + { + return; + } + + stack_t* _ptrStack = g_table.stacks[hstack]; + + if (_ptrStack->entry != NULL) + { + node_t* _nextTopNode = (node_t*)NULL; + for (int _i = 0, _s = stack_size(hstack); _i < _s; ++_i) + { + _nextTopNode = _ptrStack->entry->prev; + + free(_ptrStack->entry->data); + free(_ptrStack->entry); + + _ptrStack->entry = _nextTopNode; + } + + free(_ptrStack->entry); + } + + free(_ptrStack); + g_table.stacks[hstack] = (stack_t*)NULL; + --g_table.count; } int stack_valid_handler(const hstack_t hstack) { - UNUSED(hstack); - return 1; + if ((hstack < 0) || + + (hstack >= STACK_TABLE_HANDLER) || + + (g_table.stacks[hstack] == NULL)) + { + return 1; + } + return 0; } unsigned int stack_size(const hstack_t hstack) { - UNUSED(hstack); - return 0; + if (stack_valid_handler(hstack)) + { + return 0; + } + + int _i = 0; + + for (node_t* _node = g_table.stacks[hstack]->entry; _node != (node_t*)NULL; _node = _node->prev) + { + ++_i; + } + return _i; } void stack_push(const hstack_t hstack, const void* data_in, const unsigned int size) { - UNUSED(hstack); - UNUSED(data_in); - UNUSED(size); + if ((stack_valid_handler(hstack)) || + + (data_in == NULL || size == 0)) + { + return; + } + + node_t* _ptr = (node_t*)malloc(sizeof(node_t)); + + if (_ptr == NULL) + { + return; + } + + _ptr->size = size; + _ptr->data = malloc(size); + + if (_ptr->data == NULL) + { + free(_ptr); + return; + } + + #ifdef _MSC_VER + memcpy_s(_ptr->data, _ptr->size, data_in, size); + #else + memcpy(_ptr->data, data_in, size); + #endif + + _ptr->prev = g_table.stacks[hstack]->entry; + g_table.stacks[hstack]->entry = _ptr; } 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 ((stack_valid_handler(hstack)) || + + (data_out == NULL || size == 0) || + + (g_table.stacks[hstack]->entry == NULL) || + + (g_table.stacks[hstack]->entry->size > size)) + { + return 0; + } + + #ifdef _MSC_VER + memcpy_s(data_out, size, g_table.stacks[hstack]->entry->data, g_table.stacks[hstack]->entry->size); + #else + memcpy(data_out, g_table.stacks[hstack]->entry->data, size); + #endif + + node_t* _nextTopNode = g_table.stacks[hstack]->entry->prev; + + free(g_table.stacks[hstack]->entry->data); + free(g_table.stacks[hstack]->entry); + + g_table.stacks[hstack]->entry = _nextTopNode; + return size; } diff --git a/test.cpp b/test.cpp index 657219b..15b497c 100644 --- a/test.cpp +++ b/test.cpp @@ -17,11 +17,86 @@ TEST(AllAPITest, BadStackHandler) EXPECT_EQ(data_out, 0); } + +TEST(StandardUsage, OneStack_OneElem_OneType) { + const hstack_t stack = stack_new(); + const int data_in = 1; + stack_push(stack, &data_in, sizeof(data_in)); + int data_out; + EXPECT_EQ(stack_pop(stack, &data_out, sizeof(data_out)), sizeof(data_out)); + EXPECT_EQ(data_out, 1); + stack_free(stack); +} + +TEST(StandardUsage, OneStack_TwoElem_OneType) { + const hstack_t stack = stack_new(); + const int data_in1 = 1; + const int data_in2 = 2; + stack_push(stack, &data_in1, sizeof(data_in1)); + stack_push(stack, &data_in2, sizeof(data_in2)); + int data_out; + EXPECT_EQ(stack_pop(stack, &data_out, sizeof(data_out)), sizeof(data_out)); + EXPECT_EQ(data_out, 2); + EXPECT_EQ(stack_pop(stack, &data_out, sizeof(data_out)), sizeof(data_out)); + EXPECT_EQ(data_out, 1); + stack_free(stack); +} + +TEST(StandardUsage, OneStack_TwoElem_TwoType) { + const hstack_t stack = stack_new(); + const int data_in1 = 1; + const double data_in2 = 2.2; + stack_push(stack, &data_in1, sizeof(data_in1)); + stack_push(stack, &data_in2, sizeof(data_in2)); + EXPECT_EQ(stack_size(stack), 2); + int data_out1; + double data_out2; + EXPECT_EQ(stack_pop(stack, &data_out2, sizeof(data_out2)), sizeof(data_out2)); + EXPECT_EQ(data_out2, 2.2); + EXPECT_EQ(stack_pop(stack, &data_out1, sizeof(data_out1)), sizeof(data_out1)); + EXPECT_EQ(data_out1, 1); + stack_free(stack); +} + +TEST(StandardUsage, StressTest) { + 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 (int i = 1; i < 100; ++i) { + for (int j = 0; j < 10; ++j) { + int a = i + j; + stack_push(j, &a, sizeof(int)); + EXPECT_EQ(stack_size(stacks[j]), i); + } + } + + int data_out[10][100]; + for (int i = 0; i < 100; ++i) { + for (int j = 0; j < 10; ++j) { + stack_pop(j, &data_out[j][i], sizeof(int)); + } + } + + for (size_t i = 0; i < count; ++i) + { + stack_free(stacks[i]); + EXPECT_EQ(stack_valid_handler(stacks[i]), 1); + } +} + + TEST(AllocationTests, SingleAllocation) { + EXPECT_EQ(stack_valid_handler(0), 1); const hstack_t stack = stack_new(); EXPECT_EQ(stack_valid_handler(stack), 0); - EXPECT_EQ(stack_size(stack), 0u); + //EXPECT_EQ(stack_size(stack), 0u); stack_free(stack); EXPECT_EQ(stack_valid_handler(stack), 1); } @@ -29,7 +104,7 @@ TEST(AllocationTests, SingleAllocation) TEST(AllocationTests, SeveralAllocations) { const size_t count = 10; - hstack_t stacks[count] = {-1}; + hstack_t stacks[count] = { -1 }; for (size_t i = 0; i < count; ++i) { stacks[i] = stack_new(); @@ -74,13 +149,13 @@ TEST_F(ModifyTests, PushBadArgs) TEST_F(ModifyTests, PopBadArgs) { const size_t size = 5; - const int data_in[size] = {1}; - stack_push(stack, &data_in, sizeof(data_in)); + const int data_in[size] = { 1 }; + stack_push(stack, &data_in[0], sizeof(data_in)); ASSERT_EQ(stack_size(stack), 1u); EXPECT_EQ(stack_pop(stack, nullptr, 0u), 0u); - int data_out[size - 1] = {0}; + int data_out[size - 1] = { 0 }; EXPECT_EQ(stack_pop(stack, data_out, sizeof(data_out)), 0u); EXPECT_THAT(data_out, ::testing::Each(0)); @@ -97,8 +172,8 @@ TEST_F(ModifyTests, PopFromEmptyStack) TEST_F(ModifyTests, SinglePushPop) { - const int data_in[3] = {0, 1, 2}; - int data_out[3] = {2, 1, 0}; + const int data_in[3] = { 0, 1, 2 }; + int data_out[3] = { 2, 1, 0 }; stack_push(stack, data_in, sizeof(data_in)); EXPECT_EQ(stack_size(stack), 1u); EXPECT_EQ(stack_pop(stack, data_out, sizeof(data_out)), sizeof(data_out)); @@ -109,8 +184,8 @@ TEST_F(ModifyTests, SinglePushPop) TEST_F(ModifyTests, SeveralPushPop) { const size_t size = 3; - const int data_in[size] = {0, 1, 2}; - int data_out[size] = {0, 1, 2}; + const int data_in[size] = { 0, 1, 2 }; + int data_out[size] = { 0, 1, 2 }; for (size_t i = 0; i < size; ++i) { stack_push(stack, &data_in[i], sizeof(data_in[i]));