diff --git a/src/BinaryTree/BinaryTree.c b/src/BinaryTree/BinaryTree.c new file mode 100644 index 0000000..b044c97 --- /dev/null +++ b/src/BinaryTree/BinaryTree.c @@ -0,0 +1,288 @@ +#include "BinaryTree.h" +#include +#include + +enum BinaryTreeNodePosition { + BINARYTREENODEPOSITION_SELF, + BINARYTREENODEPOSITION_LEFT, + BINARYTREENODEPOSITION_RIGHT, +}; + +typedef struct BinaryTreeWalker_s { + BinaryTreeNode* node; + enum BinaryTreeNodePosition position; +} BinaryTreeWalker; + +static size_t _TreeNodeSize(BinaryTree* tree) { + return sizeof(BinaryTreeNode) + tree->value_size; +} + +static inline size_t _DepthOf(BinaryTreeNode* node) +{ + return node == NULL ? 0 : node->depth; +} + +static BinaryTreeNode* _CreateNode(BinaryTree* tree, void* value) { + BinaryTreeNode* node = Allocator_Allocate(tree->allocator, _TreeNodeSize(tree)); + if (node != NULL) { + memcpy(node->value, value, tree->value_size); + node->depth = 1; + node->left = NULL; + node-> right = NULL; + } + + return node; +} + +int BinaryTree_Create(BinaryTree* target, BinaryTreeComparator compare, void* xdata, size_t value_size, allocator_t* allocator) { + target->root = NULL; + target->compare = compare; + target->xdata = xdata; + target->allocator = allocator; + target->value_size = value_size; + + int array_code = DynamicArray_Create(&target->walker_stack, sizeof(BinaryTreeWalker), 8, allocator); + if (array_code) return array_code; + + return EXIT_SUCCESS; +} + +static void _UpdateDepth(BinaryTreeNode* node) { + size_t depth_left = 0, depth_right = 0; + + if (node->left != NULL) { + depth_left = node->left->depth; + } + if (node->right != NULL) { + depth_right = node->right->depth; + } + + if (depth_left > depth_right) { + node->depth = depth_left; + } else { + node->depth = depth_right; + } + + node->depth += 1; +} + +static void _UpdateDepths(DynamicArray* parents) { + for (size_t i = parents->reserved - 1; i != SIZE_MAX; i--) { + BinaryTreeWalker* walker = DynamicArray_GetPointer(parents, i); + _UpdateDepth(walker->node); + } +} + +int _Insert(BinaryTree* tree, BinaryTreeNode* node) { + BinaryTreeNode** parent = &tree->root; + tree->walker_stack.reserved = 0; + + if (*parent == NULL) { + *parent = node; + } + + while ((*parent) != node) { + BinaryTreeWalker* walker; + DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &walker); + // Can't be NULL because the size was just increased + + walker->node = *parent; + + int comparison = tree->compare(node->value, (*parent)->value, tree->xdata); + + if (comparison == 0) { + // equal + return EXIT_FAILURE; + } else if (comparison < 0) { + // less than + if ((*parent)->left == NULL) { + (*parent)->left = node; + } + + parent = &(*parent)->left; + walker->position = BINARYTREENODEPOSITION_LEFT; + } else { + // greater than + if ((*parent)->right == NULL) { + (*parent)->right = node; + } + + parent = &(*parent)->right; + walker->position = BINARYTREENODEPOSITION_RIGHT; + } + } + + _UpdateDepths(&tree->walker_stack); + _UpdateDepth(node); + + return EXIT_SUCCESS; +} + +int _GrowParentStack(BinaryTree* tree) +{ + size_t depth = _DepthOf(tree->root); + if (tree->walker_stack.capacity < depth + 1) { + return DynamicArray_Resize(&tree->walker_stack, tree->walker_stack.capacity * 2); + } + + return EXIT_SUCCESS; +} + +// right is heavier, shift it to the left +static void _RotateLeft(BinaryTreeNode** node) +{ + BinaryTreeNode* right = (*node)->right; + + (*node)->right = right->left; + right->left = *node; + *node = right; +} + +// left is heavier, shift it to the right +static void _RotateRight(BinaryTreeNode** node) +{ + BinaryTreeNode* left = (*node)->left; + + (*node)->left = left->right; + left->right = *node; + *node = left; +} + +static void _RebalanceNode(BinaryTreeNode** node) +{ + size_t left_depth = _DepthOf(node[0]->left); + size_t right_depth = _DepthOf(node[0]->right); + + if (left_depth < right_depth && right_depth - left_depth > 1) { + _RotateLeft(node); + _UpdateDepth((*node)->left); // old node + _UpdateDepth(*node); // rotated node + } else if (right_depth < left_depth && left_depth - right_depth > 1) { + _RotateRight(node); + _UpdateDepth((*node)->right); // old node + _UpdateDepth(*node); // rotated node + } +} + +static void _RebalanceRoot(BinaryTree* tree) +{ + _RebalanceNode(&tree->root); +} + +static void _Rebalance(BinaryTree* tree) +{ + for (size_t i = tree->walker_stack.reserved - 1; i + 1 != 0; i--) { + BinaryTreeWalker* walker = DynamicArray_GetPointer( + &tree->walker_stack, + i + ); + BinaryTreeNode** nodep = NULL; + switch (walker->position) { + case BINARYTREENODEPOSITION_LEFT: + nodep = &walker->node->left; + break; + + case BINARYTREENODEPOSITION_RIGHT: + nodep = &walker->node->right; + break; + + case BINARYTREENODEPOSITION_SELF: + // not gonna happen + break; + } + + _RebalanceNode(nodep); + } + + + _RebalanceRoot(tree); +} + +static int _BinaryTree_DestroyNode(BinaryTree* tree, BinaryTreeNode* node) +{ + Allocator_Free(tree->allocator, node, sizeof(*node) + tree->value_size); + + return EXIT_SUCCESS; +} + +int BinaryTree_Insert(BinaryTree* tree, void* value) { + if (tree == NULL) return EDESTADDRREQ; + if (value == NULL) return EINVAL; + + if (_GrowParentStack(tree)) { + return ENOMEM; + } + + BinaryTreeNode* new_node = _CreateNode(tree, value); + if (new_node == NULL) return ENOMEM; + + if (_Insert(tree, new_node)) { + _BinaryTree_DestroyNode(tree, new_node); + return EXIT_FAILURE; + } + _Rebalance(tree); + + return EXIT_SUCCESS; +} + +int BinaryTree_ForEachPostOrder(BinaryTree* tree, BinaryTreeNodeFunction action, void* context) +{ + tree->walker_stack.reserved = 0; + + { + BinaryTreeWalker* root_walker; + DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &root_walker); + + root_walker->node = tree->root; + root_walker->position = BINARYTREENODEPOSITION_LEFT; + } + + while (tree->walker_stack.reserved != 0) { + BinaryTreeWalker* current = DynamicArray_GetPointer(&tree->walker_stack, tree->walker_stack.reserved - 1); + if (current->node == NULL) { + tree->walker_stack.reserved--; // remove last item + continue; + } + + BinaryTreeNode* append = NULL; + + switch (current->position) { + + case BINARYTREENODEPOSITION_SELF: + { + int error = action(context, current->node); + if (error) return ECANCELED; + + tree->walker_stack.reserved--; + break; + } + + case BINARYTREENODEPOSITION_LEFT: + current->position = BINARYTREENODEPOSITION_RIGHT; + append = current->node->left; + break; + + case BINARYTREENODEPOSITION_RIGHT: + current->position = BINARYTREENODEPOSITION_SELF; + append = current->node->right; + break; + + } + + if (append != NULL) { + BinaryTreeWalker* new_walker; + DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &new_walker); + new_walker->node = append; + new_walker->position = BINARYTREENODEPOSITION_LEFT; + } + } + + return EXIT_SUCCESS; +} + +void BinaryTree_Destroy(BinaryTree* tree) +{ + BinaryTree_ForEachPostOrder(tree, (BinaryTreeNodeFunction) _BinaryTree_DestroyNode, tree); + DynamicArray_Destroy(&tree->walker_stack); + return; +} diff --git a/src/BinaryTree/BinaryTree.h b/src/BinaryTree/BinaryTree.h new file mode 100644 index 0000000..72fbe18 --- /dev/null +++ b/src/BinaryTree/BinaryTree.h @@ -0,0 +1,40 @@ +#ifndef UTILITIEC_BINARYTREE_H +#define UTILITIEC_BINARYTREE_H + +#include "../allocator-interface/allocator-interface.h" +#include "../dynamicarray/dynamicarray.h" + +typedef struct BinaryTreeNode_s { + struct BinaryTreeNode_s* left; + struct BinaryTreeNode_s* right; + + size_t depth; + + char value[]; +} BinaryTreeNode; + +typedef int (*BinaryTreeComparator) (void* this, void* other, void* xdata); + +typedef struct BinaryTree_s { + BinaryTreeNode* root; + + DynamicArray walker_stack; + + size_t value_size; + BinaryTreeComparator compare; + void* xdata; + + allocator_t* allocator; +} BinaryTree; + +int BinaryTree_Create(BinaryTree* target, BinaryTreeComparator compare, void* xdata, size_t value_size, allocator_t* allocator); +void BinaryTree_Destroy(BinaryTree* tree); + +int BinaryTree_Insert(BinaryTree* tree, void* value); +int BinaryTree_Remove(BinaryTree* tree, void* value); + +typedef int (*BinaryTreeNodeFunction) (void* context, BinaryTreeNode* node); + +int BinaryTree_ForEachPostOrder(BinaryTree* tree, BinaryTreeNodeFunction action, void* context); + +#endif diff --git a/src/BinaryTree/CMakeLists.txt b/src/BinaryTree/CMakeLists.txt new file mode 100644 index 0000000..4898ac9 --- /dev/null +++ b/src/BinaryTree/CMakeLists.txt @@ -0,0 +1 @@ +add_library(BinaryTree STATIC BinaryTree.c) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30aeedc..6c6e0ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,18 +1,20 @@ -add_subdirectory(./Scratchpad) -add_subdirectory(./allocator-interface) add_subdirectory(./FixedBuffer) -add_subdirectory(./dynamicbuffer) -add_subdirectory(./FreeList) -add_subdirectory(./hashmap) -add_subdirectory(./QuadTree) -add_subdirectory(./threading) -add_subdirectory(./ThreadPool) -add_subdirectory(./regex) -add_subdirectory(./utf8) -add_subdirectory(./StringView) +add_subdirectory(./allocator-interface) add_subdirectory(./arraylist) -add_subdirectory(./argumentc) -add_subdirectory(./rand) add_subdirectory(./siphash) -add_subdirectory(./pointers) add_subdirectory(./dynamicarray) +add_subdirectory(./dynamicbuffer) +add_subdirectory(./hashmap) +add_subdirectory(./rand) +add_subdirectory(./utf8) +add_subdirectory(./pointers) +add_subdirectory(./StringView) +add_subdirectory(./ThreadPool) +add_subdirectory(./threading) +add_subdirectory(./FreeList) +add_subdirectory(./argumentc) +add_subdirectory(./Scratchpad) +add_subdirectory(./regex) +add_subdirectory(./QuadTree) +add_subdirectory(./BinaryTree) +add_subdirectory(./Subprocess) diff --git a/src/StringView/StringView.c b/src/StringView/StringView.c index 20b06ec..95f2e88 100644 --- a/src/StringView/StringView.c +++ b/src/StringView/StringView.c @@ -133,6 +133,22 @@ StringView StringView_Slice(StringView string, size_t start, size_t end) return slice; } +bool StringView_Partition(StringView* left, StringView* right, StringView source, StringView delim) +{ + if (source.length == 0) return false; + + size_t offset = StringView_FindStringOffset(source, delim); + if (offset != SIZE_MAX) { + if (left != NULL) *left = StringView_Slice(source, 0, offset); + if (right != NULL) *right = StringView_Slice(source, offset + delim.length, source.length); + } else { + if (left != NULL) *left = source; + if (right != NULL) *right = STRINGVIEW_NONE; + } + + return offset != SIZE_MAX; +} + bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim) { if (source->length == 0) return false; @@ -149,6 +165,23 @@ bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim return true; } +bool StringView_LastSplit(StringView* dest, StringView* source, StringView delim) +{ + StringView s = *source; + while (s.length != 0 && ! StringView_EndsWith(s, delim)) { + s.length -= 1; + } + + if (s.length == 0) { + return false; + } else { + *dest = StringView_Slice(*source, s.length, source->length); + s.length -= delim.length; + *source = s; + return true; + } +} + StringView StringView_StripLeft(StringView sv, StringView strip) { while (StringView_StartsWith(sv, strip)) { diff --git a/src/StringView/StringView.h b/src/StringView/StringView.h index 8be710c..3c57de9 100644 --- a/src/StringView/StringView.h +++ b/src/StringView/StringView.h @@ -46,7 +46,9 @@ size_t StringView_FindStringOffset(StringView haystack, StringView needle); StringView StringView_FindString(StringView haystack, StringView needle); StringView StringView_Slice(StringView string, size_t start, size_t end); // start and end are offsets +bool StringView_Partition(StringView* left, StringView* right, StringView source, StringView delim); bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim); +bool StringView_LastSplit(StringView* dest, StringView* source, StringView delim); StringView StringView_StripLeft(StringView sv, StringView strip); StringView StringView_StripRight(StringView sv, StringView strip); diff --git a/src/Subprocess/CMakeLists.txt b/src/Subprocess/CMakeLists.txt new file mode 100644 index 0000000..0b775ae --- /dev/null +++ b/src/Subprocess/CMakeLists.txt @@ -0,0 +1 @@ +add_library(Subprocess STATIC Subprocess.c) diff --git a/src/Subprocess/Subprocess.c b/src/Subprocess/Subprocess.c new file mode 100644 index 0000000..d48c96f --- /dev/null +++ b/src/Subprocess/Subprocess.c @@ -0,0 +1,109 @@ +#include "Subprocess.h" +#include +#include +#include +#include + +typedef struct PipePair_s { + int read; + int write; +} PipePair; + +static int _PipePair_Create(PipePair* pair) +{ + int pipefds[2]; + int pipe_call = pipe(pipefds); + + if (pipe_call == 0) { + pair->read = pipefds[0]; + pair->write = pipefds[1]; + } + + return pipe_call; +} + +static void _PipePair_Destroy(PipePair* pair) +{ + close(pair->read); + close(pair->write); +} + +int Subprocess_Create(Subprocess* subprocess, char* file, char** argv) +{ + if (subprocess == NULL) { + return EDESTADDRREQ; + } + if (file == NULL) { + return EINVAL; + } + if (argv == NULL) { + return EINVAL; + } + if (subprocess->in.type != SUBPROCESSREDIRECTIONTYPE_PIPE) { + return ENOTSUP; + } + if (subprocess->out.type != SUBPROCESSREDIRECTIONTYPE_PIPE) { + return ENOTSUP; + } + if (subprocess->err.type != SUBPROCESSREDIRECTIONTYPE_PIPE) { + return ENOTSUP; + } + + // TODO: Implement FILE redirection + + // Create pipe pairs + PipePair in_pair, out_pair, err_pair; + if (_PipePair_Create(&in_pair)) { + return errno; + } + if (_PipePair_Create(&out_pair)) { + _PipePair_Destroy(&in_pair); + return errno; + } + if (_PipePair_Create(&err_pair)) { + _PipePair_Destroy(&in_pair); + _PipePair_Destroy(&out_pair); + return errno; + } + + pid_t fork_result = fork(); + if (fork_result == 0) { + // child + // TODO: Check errors + + close(in_pair.write); + close(out_pair.read); + close(err_pair.read); + dup2(in_pair.read, STDIN_FILENO); + dup2(out_pair.write, STDOUT_FILENO); + dup2(err_pair.write, STDERR_FILENO); + execvp(file, argv); + + } else if (fork_result != -1) { + // parent + + close(in_pair.read); + close(out_pair.write); + close(err_pair.write); + subprocess->in.get.pipefd = in_pair.write; + subprocess->out.get.pipefd = out_pair.read; + subprocess->err.get.pipefd = err_pair.read; + subprocess->child_pid = fork_result; + + } else { + // error + return errno; + } + + return EXIT_SUCCESS; +} + + +void Subprocess_Destroy(Subprocess* subprocess) +{ + waitpid(subprocess->child_pid, NULL, 0); + + if (subprocess->in .type == SUBPROCESSREDIRECTIONTYPE_PIPE) close(subprocess->in.get.pipefd); + if (subprocess->out.type == SUBPROCESSREDIRECTIONTYPE_PIPE) close(subprocess->out.get.pipefd); + if (subprocess->err.type == SUBPROCESSREDIRECTIONTYPE_PIPE) close(subprocess->err.get.pipefd); +} diff --git a/src/Subprocess/Subprocess.h b/src/Subprocess/Subprocess.h new file mode 100644 index 0000000..dac0c43 --- /dev/null +++ b/src/Subprocess/Subprocess.h @@ -0,0 +1,43 @@ +#ifndef UTILITIEC_SUBPROCESS_H +#define UTILITIEC_SUBPROCESS_H + +#include "../StringView/StringView.h" +#include + +// TODO: Implement FILE redirection +enum SubprocessRedirectionType { + SUBPROCESSREDIRECTIONTYPE_PIPE, + SUBPROCESSREDIRECTIONTYPE_FILE, +}; + +union SubprocessRedirectionContent { + int pipefd; + StringView file_name; +}; + +typedef struct SubprocessRedirection_s { + enum SubprocessRedirectionType type; + union SubprocessRedirectionContent get; +} SubprocessRedirection; + +typedef struct Subprocess_t { + pid_t child_pid; + SubprocessRedirection in; + SubprocessRedirection out; + SubprocessRedirection err; +} Subprocess; + + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + + +int Subprocess_Create(Subprocess* subprocess, char* file, char** argv); +void Subprocess_Destroy(Subprocess* subprocess); + + +#else +// TODO: Implement for Windows with a windows testing machine +#error "This module is not implemented for your platform yet" +#endif // platform + +#endif // header diff --git a/src/argumentc/argumentc.c b/src/argumentc/argumentc.c index 4a617fb..0181870 100644 --- a/src/argumentc/argumentc.c +++ b/src/argumentc/argumentc.c @@ -237,7 +237,7 @@ OptionArgument Argumentc_PopLongArgument(Argumentc* argumentc, StringView name) } Option* array_long = DynamicArray_GetPointer(&argumentc->array, index); - Option* array_arg = DynamicArray_GetPointer(&argumentc->array, index + 1); + Option* array_arg = DynamicArray_GetPointer(&argumentc->array, index + 1); if (array_arg == NULL || array_arg->type != OPTIONTYPE_ARGUMENT) { return (OptionArgument) {{OPTIONTYPE_NONE, name}, {OPTIONTYPE_NONE, name}}; diff --git a/src/dynamicarray/dynamicarray.c b/src/dynamicarray/dynamicarray.c index 6a4255a..61f9075 100644 --- a/src/dynamicarray/dynamicarray.c +++ b/src/dynamicarray/dynamicarray.c @@ -257,17 +257,25 @@ size_t DynamicArray_FindFunctionLinear(DynamicArray* array, DynamicArrayLinearFi size_t mid = bot + (top - bot) / 2; int eval = -1; - while (bot != top) { + while (eval != 0 && mid != top && mid < array->reserved) { void* current = DynamicArray_GetPointer(array, mid); eval = function(current, xarg); - if (eval > 0) { - bot = mid + 1; + if (eval == 0) { + bot = top = mid; } else if (eval < 0) { top = mid - 1; } else { - bot = top; + bot = mid + 1; } + // TODO: Is there a bug with overlapping top and bot here? + + mid = bot + (top - bot) / 2; + } + + if (mid == top && mid < array->reserved) { + void* current = DynamicArray_GetPointer(array, mid); + eval = function(current, xarg); } if (eval != 0) { diff --git a/src/dynamicbuffer/dynamicbuffer.c b/src/dynamicbuffer/dynamicbuffer.c index a6e5421..73fb6f1 100644 --- a/src/dynamicbuffer/dynamicbuffer.c +++ b/src/dynamicbuffer/dynamicbuffer.c @@ -19,12 +19,12 @@ */ #include "dynamicbuffer.h" -int DynamicBuffer_Create(dynamic_buffer_t *destination, size_t initial_capacity) +int DynamicBuffer_Create(DynamicBuffer *destination, size_t initial_capacity) { return DynamicBuffer_CreateWithAllocator(destination, initial_capacity, NULL); } -int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t initial_capacity, allocator_t* allocator) +int DynamicBuffer_CreateWithAllocator(DynamicBuffer *destination, size_t initial_capacity, allocator_t* allocator) { if (destination == NULL) { return EDESTADDRREQ; @@ -34,7 +34,7 @@ int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t init return EINVAL; } - dynamic_buffer_t local; + DynamicBuffer local; local.allocator = allocator; local.capacity = initial_capacity; @@ -54,7 +54,7 @@ int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t init return EXIT_SUCCESS; } -int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t* buffer, size_t needed_unused) +int DynamicBuffer_EnsureUnusedCapacity(DynamicBuffer* buffer, size_t needed_unused) { if (buffer == NULL) { return EINVAL; @@ -77,7 +77,7 @@ int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t* buffer, size_t needed_u return EXIT_SUCCESS; } -int DynamicBuffer_EnsureCapacity(dynamic_buffer_t* buffer, size_t minimal_capacity) +int DynamicBuffer_EnsureCapacity(DynamicBuffer* buffer, size_t minimal_capacity) { if (buffer == NULL) { return EINVAL; @@ -96,7 +96,7 @@ int DynamicBuffer_EnsureCapacity(dynamic_buffer_t* buffer, size_t minimal_capaci return EXIT_SUCCESS; } -int DynamicBuffer_Resize(dynamic_buffer_t* buffer, size_t new_capacity) +int DynamicBuffer_Resize(DynamicBuffer* buffer, size_t new_capacity) { if (buffer == NULL) { return EINVAL; @@ -121,7 +121,7 @@ int DynamicBuffer_Resize(dynamic_buffer_t* buffer, size_t new_capacity) return EXIT_SUCCESS; } -int DynamicBuffer_Prune(dynamic_buffer_t* buffer) +int DynamicBuffer_Prune(DynamicBuffer* buffer) { if (buffer == NULL) { return ENOMEM; @@ -130,7 +130,7 @@ int DynamicBuffer_Prune(dynamic_buffer_t* buffer) return DynamicBuffer_Resize(buffer, buffer->used); } -int DynamicBuffer_Reset(dynamic_buffer_t* buffer) +int DynamicBuffer_Reset(DynamicBuffer* buffer) { if (buffer == NULL) { return EINVAL; @@ -141,7 +141,7 @@ int DynamicBuffer_Reset(dynamic_buffer_t* buffer) return EXIT_SUCCESS; } -int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes) +int DynamicBuffer_RewindBytes(DynamicBuffer* buffer, size_t bytes) { if (buffer == NULL) { return EINVAL; @@ -155,7 +155,7 @@ int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes) return EXIT_SUCCESS; } -int DynamicBuffer_Store(dynamic_buffer_t* buffer, const void* data, size_t data_size) +int DynamicBuffer_Store(DynamicBuffer* buffer, const void* data, size_t data_size) { if (buffer == NULL) { return EINVAL; @@ -180,12 +180,12 @@ int DynamicBuffer_Store(dynamic_buffer_t* buffer, const void* data, size_t data_ return EXIT_SUCCESS; } -size_t DynamicBuffer_GetBlockCount(dynamic_buffer_t* buffer, size_t block_size) +size_t DynamicBuffer_GetBlockCount(DynamicBuffer* buffer, size_t block_size) { return buffer->used / block_size; } -void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset) +void* DynamicBuffer_ReadAt(DynamicBuffer* buffer, size_t offset) { if (offset >= buffer->used) { return NULL; @@ -194,12 +194,12 @@ void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset) return (void*) (((char*) buffer->array) + offset); } -void* DynamicBuffer_ReadBlockAt(dynamic_buffer_t* buffer, size_t block_size, size_t index) +void* DynamicBuffer_ReadBlockAt(DynamicBuffer* buffer, size_t block_size, size_t index) { return DynamicBuffer_ReadAt(buffer, block_size * index); } -int DynamicBuffer_Destroy(dynamic_buffer_t* buffer) +int DynamicBuffer_Destroy(DynamicBuffer* buffer) { if (buffer == NULL) { return EINVAL; diff --git a/src/dynamicbuffer/dynamicbuffer.h b/src/dynamicbuffer/dynamicbuffer.h index 1ca304e..6ceef0b 100644 --- a/src/dynamicbuffer/dynamicbuffer.h +++ b/src/dynamicbuffer/dynamicbuffer.h @@ -27,90 +27,90 @@ #include "../allocator-interface/allocator-interface.h" -typedef struct DynamicBuffer { +typedef struct DynamicBuffer_s { void* array; size_t capacity; size_t used; allocator_t* allocator; -} dynamic_buffer_t; +} DynamicBuffer; /// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity /// @param destination where the buffer will be stored /// @param initialCapacity what it's initialCapacity will be /// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_Create(dynamic_buffer_t* destination, size_t initialCapacity); +int DynamicBuffer_Create(DynamicBuffer* destination, size_t initialCapacity); /// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity /// @param destination where the buffer will be stored /// @param initialCapacity what it's initialCapacity will be /// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t* destination, size_t initialCapacity, allocator_t* allocator); +int DynamicBuffer_CreateWithAllocator(DynamicBuffer* destination, size_t initialCapacity, allocator_t* allocator); /// @brief This function checks that there are at least needed_unused free bytes in the allocated area /// @param buffer buffer to check /// @param needed_unused required free array size /// @return EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t *buffer, size_t needed_unused); +int DynamicBuffer_EnsureUnusedCapacity(DynamicBuffer *buffer, size_t needed_unused); /// @brief This function resizes the buffer to minimal_capacity, if necessary /// @param buffer buffer to check /// @param minimal_capacity minimal capacity of the buffer array /// @return EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_EnsureCapacity(dynamic_buffer_t *buffer, size_t minimal_capacity); +int DynamicBuffer_EnsureCapacity(DynamicBuffer *buffer, size_t minimal_capacity); /// @brief This function resizes the buffers array via realloc to new_capacity /// @param buffer buffer to resize /// @param new_capacity capacity of the new buffer array /// @return EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_Resize(dynamic_buffer_t *buffer, size_t new_capacity); +int DynamicBuffer_Resize(DynamicBuffer *buffer, size_t new_capacity); /// @brief This function sets the buffers capacity to what it uses /// @param buffer buffer to prune /// @return EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_Prune(dynamic_buffer_t* buffer); +int DynamicBuffer_Prune(DynamicBuffer* buffer); /// @brief Resets the count of used bytes to "clear" the buffer /// @param buffer buffer to reset /// @return EINVAL, EXIT_SUCCESS -int DynamicBuffer_Reset(dynamic_buffer_t *buffer); +int DynamicBuffer_Reset(DynamicBuffer *buffer); /// @brief Rewind the buffer pointer by bytes bytes, losing the bytes stored /// @param buffer buffer to rewind /// @param bytes How many bytes the buffer will lose /// @return EINVAL, EBOUNDS, EXIT_SUCCESS -int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes); +int DynamicBuffer_RewindBytes(DynamicBuffer* buffer, size_t bytes); /// @brief Stores data[.data_size] at the end of the buffer array and resizes if necessary /// @param buffer buffer in which the data will be stored /// @param data data to store /// @param data_size how many bytes will be copied from data /// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS -int DynamicBuffer_Store(dynamic_buffer_t *buffer, const void *data, size_t data_size); +int DynamicBuffer_Store(DynamicBuffer *buffer, const void *data, size_t data_size); /// @brief Calculate how many blocks are currently in the buffer /// @param buffer buffer to query /// @param block_size what's the size of a single block /// @return How many of these block_sizes does the buffer currently hold -size_t DynamicBuffer_GetBlockCount(dynamic_buffer_t* buffer, size_t block_size); +size_t DynamicBuffer_GetBlockCount(DynamicBuffer* buffer, size_t block_size); /// @brief Get a pointer reference to the buffer contents at offset /// @param buffer buffer to query /// @param offset offset from the buffer start /// @return Pointer to the address in the buffer at offset or NULL if out of Bounds -void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset); +void* DynamicBuffer_ReadAt(DynamicBuffer* buffer, size_t offset); /// @brief Get a pointer reference to indexn'th block in buffer /// @param buffer buffer to query /// @param block_size size of a single block /// @param index which block to read /// @return Pointer to the block at index or NULL if out of bounds -void* DynamicBuffer_ReadBlockAt(dynamic_buffer_t* buffer, size_t block_size, size_t index); +void* DynamicBuffer_ReadBlockAt(DynamicBuffer* buffer, size_t block_size, size_t index); /// @brief Destroys the dynamic buffer and releases all resources held, the struct will not hold anything it did before /// @param buffer buffer that shall be destroyed /// @return EINVAL, EXIT_SUCCESS -int DynamicBuffer_Destroy(dynamic_buffer_t *buffer); +int DynamicBuffer_Destroy(DynamicBuffer *buffer); #endif diff --git a/src/regex/match.c b/src/regex/match.c index 18622df..ffa3e62 100644 --- a/src/regex/match.c +++ b/src/regex/match.c @@ -8,8 +8,10 @@ typedef struct RegexMatcher_s { RegexMatchThreadGroup top_group; + DynamicArray visitor_stack; + size_t depth; - RegexMatchThread finished_threads; + RegexMatchThread finished_thread; } RegexMatcher; static int RegexMatchThread_New(RegexMatchThread* thread, StringView string, size_t index, Regex* regex) @@ -275,7 +277,7 @@ static void RegexMatchThreadGroup_Destroy(DynamicArray* visitors) visitors, visitors->reserved - 1 ); - RegexMatchThreadGroup* group = (void*) current_visitor->type; + RegexMatchThreadGroup* group = current_visitor->as.group; if (DynamicArray_GetLength(&group->threads) != 0) { // Append first sub thread to visitor stack @@ -312,6 +314,57 @@ static void RegexMatchThreadGroup_Destroy(DynamicArray* visitors) return; } +static int _RegexMatcher_Create(RegexMatcher* matcher, Regex* regex, StringView string, size_t start) +{ + int group_code = RegexMatchThreadGroup_Create( + &matcher->top_group, + 0, + regex->machine_memory.allocator + ); + if (group_code) { + return group_code; + } + + int stack_code = DynamicArray_Create( + &matcher->visitor_stack, + sizeof(struct GroupVisitor), + 16, + regex->machine_memory.allocator + ); + if (stack_code) { + RegexMatchThreadGroup_Destroy2(&matcher->top_group); + return stack_code; + } + + RegexMatchThread* root_thread = _RegexMatchThreadGroup_NewThread( + &matcher->top_group + ); + if (root_thread == NULL) { + RegexMatchThreadGroup_Destroy2(&matcher->top_group); + DynamicArray_Destroy(&matcher->visitor_stack); + return ENOMEM; + } + if (RegexMatchThread_New(root_thread, string, start, regex)) { + RegexMatchThreadGroup_Destroy2(&matcher->top_group); + DynamicArray_Destroy(&matcher->visitor_stack); + return ENOMEM; + } + root_thread->number = 0; + + matcher->depth = 1; + + memset(&matcher->finished_thread, 0, sizeof(matcher->finished_thread)); + + return EXIT_SUCCESS; +} + +static void _RegexMatcher_Destroy(RegexMatcher* matcher) +{ + RegexMatchThreadGroup_Destroy2(&matcher->top_group); + DynamicArray_Destroy(&matcher->visitor_stack); + memset(matcher, 0, sizeof(*matcher)); +} + static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, size_t* depth, RegexMachineStateBase* new_head) { RegexMatchThread* child; @@ -384,6 +437,7 @@ static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, siz // remove from old group _RegexMatchThreadGroup_ForgetThread(parent_group, *parent); + parent_visitor->position--; } if (child == NULL) { @@ -396,18 +450,19 @@ static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, siz } child->machine_head = new_head; - child->number = parent->number + DynamicArray_GetLength(&parent_group.threads) - 1; + child->number = (*parent)->number + + DynamicArray_GetLength(&parent_group->threads) - 1; return EXIT_SUCCESS; } -static int _HandleOption(DynamicArray* visitors, size_t* depth) +static int _HandleOption(RegexMatcher* matcher) { struct GroupVisitor* visitor = DynamicArray_GetPointer( - visitors, - visitors->reserved - 1 + &matcher->visitor_stack, + DynamicArray_GetLength(&matcher->visitor_stack) ); - RegexMatchThread* thread = (void*) visitor->type; + RegexMatchThread* thread = visitor->as.thread; RegexMachineStateBase* first_head = thread->machine_head; { @@ -420,8 +475,8 @@ static int _HandleOption(DynamicArray* visitors, size_t* depth) int child_code = _NewRegexChild( &thread, - visitors, - depth, + &matcher->visitor_stack, + &matcher->depth, option_head->next ); if (child_code) { @@ -435,13 +490,37 @@ static int _HandleOption(DynamicArray* visitors, size_t* depth) return EXIT_SUCCESS; } -static int _AdvanceThread(DynamicArray* visitors, size_t* depth) +static void _TryFinishThread(RegexMatcher* matcher, RegexMatchThread* thread) +{ + struct GroupVisitor* parent_visitor = DynamicArray_GetPointer( + &matcher->visitor_stack, + DynamicArray_GetLength(&matcher->visitor_stack) - 2 + ); + RegexMatchThreadGroup* parent_group = parent_visitor->as.group; + size_t thread_index = DynamicArray_FindFunctionLinear( + &parent_group->threads, + (DynamicArrayLinearFindFunction) _FindThreadLinear, + thread + ); + + if (thread_index == 0 + && DynamicArray_GetLength(&matcher->visitor_stack) == 2) { + // Only the first thread in the top-most group is finishable + // All other threads have a lower priority, this is because + // of the order they have to be matched in. + matcher->finished_thread = *thread; + _RegexMatchThreadGroup_ForgetThread(parent_group, thread); + matcher->visitor_stack.reserved = 0; + } +} + +static int _AdvanceThread(RegexMatcher* matcher) { struct GroupVisitor* visitor = DynamicArray_GetPointer( - visitors, - visitors->reserved - 1 + &matcher->visitor_stack, + DynamicArray_GetLength(&matcher->visitor_stack) - 1 ); - RegexMatchThread* thread = (void*) visitor->type; + RegexMatchThread* thread = visitor->as.thread; int code; bool discard; @@ -453,7 +532,7 @@ static int _AdvanceThread(DynamicArray* visitors, size_t* depth) code = _HandleGroup(thread); break; case REGEXMACHINESTATETYPE_OPTION: - code = _HandleOption(visitors, depth); + code = _HandleOption(matcher); break; case REGEXMACHINESTATETYPE_REPEAT: code = _HandleRepeat(thread); @@ -467,10 +546,12 @@ static int _AdvanceThread(DynamicArray* visitors, size_t* depth) } if (thread->machine_head == NULL) { - // Match done - + _TryFinishThread(matcher, thread); } + // Remove from visitor stack + DynamicArray_Remove(&matcher->visitor_stack, matcher->visitor_stack.reserved - 1); + return EXIT_SUCCESS; } @@ -509,85 +590,132 @@ static void _DestroyThreadHierarchy(DynamicArray* visitors, RegexMatchThreadGrou return; } -static int _HandleAdvanceVisitor(DynamicArray* visitor_stack, size_t* depth) +static int _AdvanceGroup(RegexMatcher* matcher) { - struct GroupVisitor* current_visitor = DynamicArray_GetPointer( - visitor_stack, - visitor_stack->reserved - 1 + struct GroupVisitor* visitor = DynamicArray_GetPointer( + &matcher->visitor_stack, + DynamicArray_GetLength(&matcher->visitor_stack) - 1 ); + RegexMatchThreadGroup* group = visitor->as.group; + visitor->position += 1; - switch (current_visitor->type) { - case REGEXMATCHTHREADTYPE_THREAD: - _AdvanceThread( - visitor_stack, - depth + if (DynamicArray_GetLength(&group->threads) > visitor->position) { + struct GroupVisitor* child_visitor; + DynamicArray_AppendEmpty( + &matcher->visitor_stack, + (void**) &child_visitor + ); + child_visitor->type = REGEXMATCHTHREADTYPE_THREAD; + child_visitor->as.thread = DynamicArray_GetPointer(&group->threads, visitor->position); + } else { + // calculate position in subgroup array + unsigned int position = visitor->position + - DynamicArray_GetLength(&group->threads); + + if (DynamicArray_GetLength(&group->subgroups) == position) { + // Remove this group + DynamicArray_Remove( + &matcher->visitor_stack, + matcher->visitor_stack.reserved - 1 ); - break; - case REGEXMATCHTHREADTYPE_GROUP: - break; + } else { + // Add subgroup + struct GroupVisitor* sgroup_visitor; + DynamicArray_AppendEmpty( + &matcher->visitor_stack, + (void**) &sgroup_visitor + ); + sgroup_visitor->type = REGEXMATCHTHREADTYPE_GROUP; + sgroup_visitor->position = -1; + sgroup_visitor->as.group = DynamicArray_GetPointer( + &group->subgroups, + position + ); + } } return EXIT_SUCCESS; } -static int _WalkThreads(DynamicArray* visitor_stack, RegexMatchThreadGroup* group, size_t* depth) +static int _HandleAdvanceVisitor(RegexMatcher* matcher) +{ + struct GroupVisitor* current_visitor = DynamicArray_GetPointer( + &matcher->visitor_stack, + DynamicArray_GetLength(&matcher->visitor_stack) - 1 + ); + + int advance_code = EXIT_SUCCESS; + switch (current_visitor->type) { + case REGEXMATCHTHREADTYPE_THREAD: + advance_code = _AdvanceThread(matcher); + break; + case REGEXMATCHTHREADTYPE_GROUP: + advance_code = _AdvanceGroup(matcher); + break; + } + + return advance_code; +} + +static int _WalkThreads(RegexMatcher* matcher) { { struct GroupVisitor* zero_visitor; DynamicArray_AppendEmpty( - visitor_stack, + &matcher->visitor_stack, (void**) &zero_visitor ); zero_visitor->type = REGEXMATCHTHREADTYPE_GROUP; - zero_visitor->as.group = group; + zero_visitor->as.group = &matcher->top_group; + zero_visitor->position = -1; } - while (DynamicArray_GetLength(visitor_stack) != 0) { - int advance_code = _HandleAdvanceVisitor( - visitor_stack, - depth - ); + while (DynamicArray_GetLength(&matcher->visitor_stack) != 0) { + int advance_code = _HandleAdvanceVisitor(matcher); if (advance_code) return advance_code; } return EXIT_SUCCESS; } +static bool _HaveFinishedThread(RegexMatcher* matcher) +{ + return matcher->finished_thread.regex != NULL; +} + +static bool _HaveThreadsRunning(RegexMatcher* matcher) +{ + return matcher->top_group.subgroups.reserved != 0 + || matcher->top_group.threads.reserved != 0; +} + int Regex_MatchHere(Regex* regex, StringView string, size_t start, RegexMatch* match) { int return_code = EXIT_SUCCESS; - // initialize variables etc.... - DynamicArray visitor_stack; - if (DynamicArray_Create( - &visitor_stack, - sizeof(struct GroupVisitor), - 16, - regex->machine_memory.allocator) - ) { - return ENOMEM; + RegexMatcher matcher; + if (_RegexMatcher_Create(&matcher, regex, string, start)) { + return EXIT_FAILURE; } - RegexMatchThreadGroup top_level_group; - RegexMatchThreadGroup_Create(&top_level_group, 0, regex->machine_memory.allocator); - size_t depth = 1; - - { - RegexMatchThread* first_thread; - first_thread = RegexMatchThreadGroup_NewThread(&top_level_group); - first_head->number = 0; + while (! _HaveFinishedThread(&matcher) + && _HaveThreadsRunning(&matcher)) { + _WalkThreads(&matcher); } - bool haveFinishedThread = false; - while (! haveFinishedThread) { - _WalkThreads(&visitor_stack, &top_level_group, &depth); + if (_HaveFinishedThread(&matcher)) { + *match = matcher.finished_thread.match; + memset( + &matcher.finished_thread.match, + 0, + sizeof(matcher.finished_thread.match) + ); + RegexMatchThread_Del(&matcher.finished_thread); } -defer_tl_group: - _DestroyThreadHierarchy(&visitor_stack, &top_level_group); -defer_stack: - DynamicArray_Destroy(&visitor_stack); + _DestroyThreadHierarchy(&matcher.visitor_stack, &matcher.top_group); + _RegexMatcher_Destroy(&matcher); return return_code; } diff --git a/src/regex/match_struct.c b/src/regex/match_struct.c index 3d3024f..649490c 100644 --- a/src/regex/match_struct.c +++ b/src/regex/match_struct.c @@ -43,3 +43,9 @@ RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number) return DynamicArray_GetPointer(&match->captures, capture_index); } + +void RegexMatch_Destroy(RegexMatch* match) +{ + DynamicArray_Destroy(&match->captures); + match->match = STRINGVIEW_NONE; +} diff --git a/src/regex/match_struct.h b/src/regex/match_struct.h index de03ebb..7d1ec92 100644 --- a/src/regex/match_struct.h +++ b/src/regex/match_struct.h @@ -26,4 +26,6 @@ typedef struct RegexMatch_s { bool RegexMatch_HaveNumberedCapture(RegexMatch* match, size_t number); RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number); +void RegexMatch_Destroy(RegexMatch* match); + #endif diff --git a/src/threading/os_thread.c b/src/threading/os_thread.c index 1b3a8b7..51b38d5 100644 --- a/src/threading/os_thread.c +++ b/src/threading/os_thread.c @@ -4,7 +4,6 @@ #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #include -#include int OSThread_Create(os_thread_t* destination, ThreadFunction subroutine, void* arg) { diff --git a/tests/BinaryTree.test.c b/tests/BinaryTree.test.c new file mode 100644 index 0000000..0356153 --- /dev/null +++ b/tests/BinaryTree.test.c @@ -0,0 +1,126 @@ +#include +#include + +#include "../src/BinaryTree/BinaryTree.h" + +int doublecomparator(double* this, double* other, void* xdata) +{ + assert(xdata == (void*) 0xDEADBEEF); + if (*this > *other) { + return 1; + } else if (*this < *other) { + return -1; + } + + return 0; +} + +void testLifetime(void) +{ + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + BinaryTree tree; + + assert(EXIT_SUCCESS == BinaryTree_Create( + &tree, + (BinaryTreeComparator) doublecomparator, + (void*) 0xDEADBEEF, + sizeof(double), + &allocator) + ); + BinaryTree_Destroy(&tree); + + assert(allocator.reserved == 0); + + return; +} + +void testInsert(void) +{ + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + BinaryTree tree; + + assert(EXIT_SUCCESS == BinaryTree_Create( + &tree, + (BinaryTreeComparator) doublecomparator, + (void*) 0xDEADBEEF, + sizeof(double), + &allocator) + ); + + double value = 5; + + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + assert(tree.root != NULL); + + value = 3; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 2; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 4; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + + value = 7; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 6; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 8; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + + BinaryTree_Destroy(&tree); + assert(allocator.reserved == 0); + + return; +} + +void testEqual(void) +{ + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + BinaryTree tree; + + assert(EXIT_SUCCESS == BinaryTree_Create( + &tree, + (BinaryTreeComparator) doublecomparator, + (void*) 0xDEADBEEF, + sizeof(double), + &allocator) + ); + + double value = 5; + + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + assert(tree.root != NULL); + + value = 3; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 2; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 4; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 5; + assert(EXIT_FAILURE == BinaryTree_Insert(&tree, &value)); + + BinaryTree_Destroy(&tree); + assert(allocator.reserved == 0); + + return; +} + +int main() +{ + testLifetime(); + testInsert(); + testEqual(); + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 70dd5f3..95df5c2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,3 +36,19 @@ target_link_libraries(regex-test StringView utf8 ) + +add_executable(BinaryTree-test BinaryTree.test.c) +target_link_libraries(BinaryTree-test + BinaryTree + dynamicarray + pointers + allocator-interface +) + +add_executable(Subprocess-test Subprocess.test.c) +target_link_libraries(Subprocess-test + Subprocess + threading + pointers + allocator-interface +) diff --git a/tests/StringView.test.c b/tests/StringView.test.c index 3fd93b7..9f0a4c0 100644 --- a/tests/StringView.test.c +++ b/tests/StringView.test.c @@ -29,8 +29,24 @@ void test_split(void) return; } +void test_fext(void) +{ + const char* filepath = "database.sqlite.old"; + + StringView sourceSV = StringView_FromString(filepath); + StringView delim = StringView_FromString("."); + StringView split; + + assert(StringView_LastSplit(&split, &sourceSV, delim)); + assert(StringView_Equal(split, StringView_FromString("old"))); + assert(StringView_Equal(sourceSV, StringView_FromString("database.sqlite"))); + + return; +} + int main() { test_split(); + test_fext(); return 0; } diff --git a/tests/Subprocess.test.c b/tests/Subprocess.test.c new file mode 100644 index 0000000..7f55360 --- /dev/null +++ b/tests/Subprocess.test.c @@ -0,0 +1,31 @@ +#include "../src/Subprocess/Subprocess.h" +#include +#include +#include +#include +#include + +char read_buffer[128]; + +void testEcho() +{ + Subprocess sub; + sub.in .type = SUBPROCESSREDIRECTIONTYPE_PIPE; + sub.out.type = SUBPROCESSREDIRECTIONTYPE_PIPE; + sub.err.type = SUBPROCESSREDIRECTIONTYPE_PIPE; + + char* argv[] = { "echo", "Hello World!", NULL }; + + assert(EXIT_SUCCESS == Subprocess_Create(&sub, "/usr/bin/echo", argv)); + ssize_t read_size = read(sub.out.get.pipefd, read_buffer, 128); + assert(read_size == 13); + assert(strcmp(read_buffer, "Hello World!\n") == 0); + assert(read(sub.out.get.pipefd, read_buffer, 128) == 0); + Subprocess_Destroy(&sub); +} + +int main() +{ + testEcho(); + return 0; +} diff --git a/tests/regex.test.c b/tests/regex.test.c index b830f38..091d80a 100644 --- a/tests/regex.test.c +++ b/tests/regex.test.c @@ -31,6 +31,8 @@ void testLiteral(void) assert(Regex_FirstMatch(®ex, test_string_0, &match) == EXIT_SUCCESS); assert(StringView_Equal(match.match, test_string_0)); + RegexMatch_Destroy(&match); + Regex_Destroy(®ex); assert(allocator.reserved == 0); Allocator_DestroySystemAllocator(&allocator); @@ -52,6 +54,8 @@ void testBackslash(void) assert(Regex_FirstMatch(®ex, match_string, &match) == EXIT_SUCCESS); assert(StringView_Equal(match.match, match_string)); + RegexMatch_Destroy(&match); + Regex_Destroy(®ex); assert(allocator.reserved == 0); Allocator_DestroySystemAllocator(&allocator); @@ -75,6 +79,38 @@ void testGroup(void) assert(RegexMatch_HaveNumberedCapture(&match, 1)); assert(RegexMatch_HaveNumberedCapture(&match, 2)); + Regex_Destroy(®ex); + RegexMatch_Destroy(&match); + assert(allocator.reserved == 0); + Allocator_DestroySystemAllocator(&allocator); + return; +} + +void testOption(void) +{ + StringView regex_string = StringView_FromString("abc|def|"); + StringView match_string_0 = StringView_FromString("abc"); + StringView match_string_1 = StringView_FromString("def"); + StringView match_string_2 = StringView_FromString(""); + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + + Regex regex; + assert(Regex_Create(®ex, &allocator) == EXIT_SUCCESS); + + assert(Regex_Compile(®ex, regex_string) == EXIT_SUCCESS); + + RegexMatch match; + + assert(Regex_FirstMatch(®ex, match_string_0, &match) == EXIT_SUCCESS); + assert(StringView_Equal(match.match, match_string_0)); + + assert(Regex_FirstMatch(®ex, match_string_1, &match) == EXIT_SUCCESS); + assert(StringView_Equal(match.match, match_string_1)); + + assert(Regex_FirstMatch(®ex, match_string_2, &match) == EXIT_SUCCESS); + assert(StringView_Equal(match.match, match_string_2)); + Regex_Destroy(®ex); assert(allocator.reserved == 0); Allocator_DestroySystemAllocator(&allocator); @@ -114,6 +150,7 @@ int main() testLiteral(); testBackslash(); testGroup(); + testOption(); testQuantifier(); return 0; }