Compare commits

...

10 commits

Author SHA1 Message Date
baa4a148fe Fixed indentation issue 2024-10-01 10:58:47 +02:00
VegOwOtenks
1b3d007ec2 StringView_Partition for One-Off Splits 2024-09-23 18:37:54 +02:00
VegOwOtenks
1a330b3c9b DynamicBuffer rename 2024-09-23 18:37:21 +02:00
VegOwOtenks
a0da4ce1a3 Subprocess with pipe capturing 2024-09-22 22:26:33 +02:00
VegOwOtenks
7a3f1955b9 Removed unused import 2024-09-22 22:26:04 +02:00
VegOwOtenks
840a29a59d Fixed linearsearch (agai) 2024-09-22 22:25:40 +02:00
VegOwOtenks
fe63781996 Binary Tree with working insertions, no duplicates 2024-07-01 16:31:45 +02:00
VegOwOtenks
30f288e5d7 Fixed uninitialised memory lucky bug 2024-06-13 16:55:57 +02:00
VegOwOtenks
0b651b819a Disposal of RegexMatch structures 2024-06-13 16:51:46 +02:00
VegOwOtenks
e6064bd375 Literal matching working again! 2024-06-13 16:50:37 +02:00
22 changed files with 997 additions and 109 deletions

288
src/BinaryTree/BinaryTree.c Normal file
View file

@ -0,0 +1,288 @@
#include "BinaryTree.h"
#include <iso646.h>
#include <stdlib.h>
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;
}

View file

@ -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

View file

@ -0,0 +1 @@
add_library(BinaryTree STATIC BinaryTree.c)

View file

@ -1,18 +1,20 @@
add_subdirectory(./Scratchpad)
add_subdirectory(./allocator-interface)
add_subdirectory(./FixedBuffer) add_subdirectory(./FixedBuffer)
add_subdirectory(./dynamicbuffer) add_subdirectory(./allocator-interface)
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(./arraylist) add_subdirectory(./arraylist)
add_subdirectory(./argumentc)
add_subdirectory(./rand)
add_subdirectory(./siphash) add_subdirectory(./siphash)
add_subdirectory(./pointers)
add_subdirectory(./dynamicarray) 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)

View file

@ -133,6 +133,22 @@ StringView StringView_Slice(StringView string, size_t start, size_t end)
return slice; 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) bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim)
{ {
if (source->length == 0) return false; if (source->length == 0) return false;
@ -149,6 +165,23 @@ bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim
return true; 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) StringView StringView_StripLeft(StringView sv, StringView strip)
{ {
while (StringView_StartsWith(sv, strip)) { while (StringView_StartsWith(sv, strip)) {

View file

@ -46,7 +46,9 @@ size_t StringView_FindStringOffset(StringView haystack, StringView needle);
StringView StringView_FindString(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 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_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_StripLeft(StringView sv, StringView strip);
StringView StringView_StripRight(StringView sv, StringView strip); StringView StringView_StripRight(StringView sv, StringView strip);

View file

@ -0,0 +1 @@
add_library(Subprocess STATIC Subprocess.c)

109
src/Subprocess/Subprocess.c Normal file
View file

@ -0,0 +1,109 @@
#include "Subprocess.h"
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
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);
}

View file

@ -0,0 +1,43 @@
#ifndef UTILITIEC_SUBPROCESS_H
#define UTILITIEC_SUBPROCESS_H
#include "../StringView/StringView.h"
#include <sys/types.h>
// 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

View file

@ -257,17 +257,25 @@ size_t DynamicArray_FindFunctionLinear(DynamicArray* array, DynamicArrayLinearFi
size_t mid = bot + (top - bot) / 2; size_t mid = bot + (top - bot) / 2;
int eval = -1; int eval = -1;
while (bot != top) { while (eval != 0 && mid != top && mid < array->reserved) {
void* current = DynamicArray_GetPointer(array, mid); void* current = DynamicArray_GetPointer(array, mid);
eval = function(current, xarg); eval = function(current, xarg);
if (eval > 0) { if (eval == 0) {
bot = mid + 1; bot = top = mid;
} else if (eval < 0) { } else if (eval < 0) {
top = mid - 1; top = mid - 1;
} else { } 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) { if (eval != 0) {

View file

@ -19,12 +19,12 @@
*/ */
#include "dynamicbuffer.h" #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); 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) { if (destination == NULL) {
return EDESTADDRREQ; return EDESTADDRREQ;
@ -34,7 +34,7 @@ int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t init
return EINVAL; return EINVAL;
} }
dynamic_buffer_t local; DynamicBuffer local;
local.allocator = allocator; local.allocator = allocator;
local.capacity = initial_capacity; local.capacity = initial_capacity;
@ -54,7 +54,7 @@ int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t init
return EXIT_SUCCESS; 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) { if (buffer == NULL) {
return EINVAL; return EINVAL;
@ -77,7 +77,7 @@ int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t* buffer, size_t needed_u
return EXIT_SUCCESS; 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) { if (buffer == NULL) {
return EINVAL; return EINVAL;
@ -96,7 +96,7 @@ int DynamicBuffer_EnsureCapacity(dynamic_buffer_t* buffer, size_t minimal_capaci
return EXIT_SUCCESS; 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) { if (buffer == NULL) {
return EINVAL; return EINVAL;
@ -121,7 +121,7 @@ int DynamicBuffer_Resize(dynamic_buffer_t* buffer, size_t new_capacity)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int DynamicBuffer_Prune(dynamic_buffer_t* buffer) int DynamicBuffer_Prune(DynamicBuffer* buffer)
{ {
if (buffer == NULL) { if (buffer == NULL) {
return ENOMEM; return ENOMEM;
@ -130,7 +130,7 @@ int DynamicBuffer_Prune(dynamic_buffer_t* buffer)
return DynamicBuffer_Resize(buffer, buffer->used); return DynamicBuffer_Resize(buffer, buffer->used);
} }
int DynamicBuffer_Reset(dynamic_buffer_t* buffer) int DynamicBuffer_Reset(DynamicBuffer* buffer)
{ {
if (buffer == NULL) { if (buffer == NULL) {
return EINVAL; return EINVAL;
@ -141,7 +141,7 @@ int DynamicBuffer_Reset(dynamic_buffer_t* buffer)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes) int DynamicBuffer_RewindBytes(DynamicBuffer* buffer, size_t bytes)
{ {
if (buffer == NULL) { if (buffer == NULL) {
return EINVAL; return EINVAL;
@ -155,7 +155,7 @@ int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes)
return EXIT_SUCCESS; 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) { if (buffer == NULL) {
return EINVAL; return EINVAL;
@ -180,12 +180,12 @@ int DynamicBuffer_Store(dynamic_buffer_t* buffer, const void* data, size_t data_
return EXIT_SUCCESS; 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; 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) { if (offset >= buffer->used) {
return NULL; return NULL;
@ -194,12 +194,12 @@ void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset)
return (void*) (((char*) buffer->array) + 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); return DynamicBuffer_ReadAt(buffer, block_size * index);
} }
int DynamicBuffer_Destroy(dynamic_buffer_t* buffer) int DynamicBuffer_Destroy(DynamicBuffer* buffer)
{ {
if (buffer == NULL) { if (buffer == NULL) {
return EINVAL; return EINVAL;

View file

@ -27,90 +27,90 @@
#include "../allocator-interface/allocator-interface.h" #include "../allocator-interface/allocator-interface.h"
typedef struct DynamicBuffer { typedef struct DynamicBuffer_s {
void* array; void* array;
size_t capacity; size_t capacity;
size_t used; size_t used;
allocator_t* allocator; allocator_t* allocator;
} dynamic_buffer_t; } DynamicBuffer;
/// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity /// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity
/// @param destination where the buffer will be stored /// @param destination where the buffer will be stored
/// @param initialCapacity what it's initialCapacity will be /// @param initialCapacity what it's initialCapacity will be
/// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity
/// @param destination where the buffer will be stored /// @param destination where the buffer will be stored
/// @param initialCapacity what it's initialCapacity will be /// @param initialCapacity what it's initialCapacity will be
/// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief This function checks that there are at least needed_unused free bytes in the allocated area
/// @param buffer buffer to check /// @param buffer buffer to check
/// @param needed_unused required free array size /// @param needed_unused required free array size
/// @return EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief This function resizes the buffer to minimal_capacity, if necessary
/// @param buffer buffer to check /// @param buffer buffer to check
/// @param minimal_capacity minimal capacity of the buffer array /// @param minimal_capacity minimal capacity of the buffer array
/// @return EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief This function resizes the buffers array via realloc to new_capacity
/// @param buffer buffer to resize /// @param buffer buffer to resize
/// @param new_capacity capacity of the new buffer array /// @param new_capacity capacity of the new buffer array
/// @return EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief This function sets the buffers capacity to what it uses
/// @param buffer buffer to prune /// @param buffer buffer to prune
/// @return EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief Resets the count of used bytes to "clear" the buffer
/// @param buffer buffer to reset /// @param buffer buffer to reset
/// @return EINVAL, EXIT_SUCCESS /// @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 /// @brief Rewind the buffer pointer by bytes bytes, losing the bytes stored
/// @param buffer buffer to rewind /// @param buffer buffer to rewind
/// @param bytes How many bytes the buffer will lose /// @param bytes How many bytes the buffer will lose
/// @return EINVAL, EBOUNDS, EXIT_SUCCESS /// @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 /// @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 buffer buffer in which the data will be stored
/// @param data data to store /// @param data data to store
/// @param data_size how many bytes will be copied from data /// @param data_size how many bytes will be copied from data
/// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS /// @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 /// @brief Calculate how many blocks are currently in the buffer
/// @param buffer buffer to query /// @param buffer buffer to query
/// @param block_size what's the size of a single block /// @param block_size what's the size of a single block
/// @return How many of these block_sizes does the buffer currently hold /// @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 /// @brief Get a pointer reference to the buffer contents at offset
/// @param buffer buffer to query /// @param buffer buffer to query
/// @param offset offset from the buffer start /// @param offset offset from the buffer start
/// @return Pointer to the address in the buffer at offset or NULL if out of Bounds /// @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 /// @brief Get a pointer reference to indexn'th block in buffer
/// @param buffer buffer to query /// @param buffer buffer to query
/// @param block_size size of a single block /// @param block_size size of a single block
/// @param index which block to read /// @param index which block to read
/// @return Pointer to the block at index or NULL if out of bounds /// @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 /// @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 /// @param buffer buffer that shall be destroyed
/// @return EINVAL, EXIT_SUCCESS /// @return EINVAL, EXIT_SUCCESS
int DynamicBuffer_Destroy(dynamic_buffer_t *buffer); int DynamicBuffer_Destroy(DynamicBuffer *buffer);
#endif #endif

View file

@ -8,8 +8,10 @@
typedef struct RegexMatcher_s { typedef struct RegexMatcher_s {
RegexMatchThreadGroup top_group; RegexMatchThreadGroup top_group;
DynamicArray visitor_stack;
size_t depth;
RegexMatchThread finished_threads; RegexMatchThread finished_thread;
} RegexMatcher; } RegexMatcher;
static int RegexMatchThread_New(RegexMatchThread* thread, StringView string, size_t index, Regex* regex) 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,
visitors->reserved - 1 visitors->reserved - 1
); );
RegexMatchThreadGroup* group = (void*) current_visitor->type; RegexMatchThreadGroup* group = current_visitor->as.group;
if (DynamicArray_GetLength(&group->threads) != 0) { if (DynamicArray_GetLength(&group->threads) != 0) {
// Append first sub thread to visitor stack // Append first sub thread to visitor stack
@ -312,6 +314,57 @@ static void RegexMatchThreadGroup_Destroy(DynamicArray* visitors)
return; 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) static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, size_t* depth, RegexMachineStateBase* new_head)
{ {
RegexMatchThread* child; RegexMatchThread* child;
@ -384,6 +437,7 @@ static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, siz
// remove from old group // remove from old group
_RegexMatchThreadGroup_ForgetThread(parent_group, *parent); _RegexMatchThreadGroup_ForgetThread(parent_group, *parent);
parent_visitor->position--;
} }
if (child == NULL) { if (child == NULL) {
@ -396,18 +450,19 @@ static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, siz
} }
child->machine_head = new_head; 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; return EXIT_SUCCESS;
} }
static int _HandleOption(DynamicArray* visitors, size_t* depth) static int _HandleOption(RegexMatcher* matcher)
{ {
struct GroupVisitor* visitor = DynamicArray_GetPointer( struct GroupVisitor* visitor = DynamicArray_GetPointer(
visitors, &matcher->visitor_stack,
visitors->reserved - 1 DynamicArray_GetLength(&matcher->visitor_stack)
); );
RegexMatchThread* thread = (void*) visitor->type; RegexMatchThread* thread = visitor->as.thread;
RegexMachineStateBase* first_head = thread->machine_head; RegexMachineStateBase* first_head = thread->machine_head;
{ {
@ -420,8 +475,8 @@ static int _HandleOption(DynamicArray* visitors, size_t* depth)
int child_code = _NewRegexChild( int child_code = _NewRegexChild(
&thread, &thread,
visitors, &matcher->visitor_stack,
depth, &matcher->depth,
option_head->next option_head->next
); );
if (child_code) { if (child_code) {
@ -435,13 +490,37 @@ static int _HandleOption(DynamicArray* visitors, size_t* depth)
return EXIT_SUCCESS; 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( struct GroupVisitor* visitor = DynamicArray_GetPointer(
visitors, &matcher->visitor_stack,
visitors->reserved - 1 DynamicArray_GetLength(&matcher->visitor_stack) - 1
); );
RegexMatchThread* thread = (void*) visitor->type; RegexMatchThread* thread = visitor->as.thread;
int code; int code;
bool discard; bool discard;
@ -453,7 +532,7 @@ static int _AdvanceThread(DynamicArray* visitors, size_t* depth)
code = _HandleGroup(thread); code = _HandleGroup(thread);
break; break;
case REGEXMACHINESTATETYPE_OPTION: case REGEXMACHINESTATETYPE_OPTION:
code = _HandleOption(visitors, depth); code = _HandleOption(matcher);
break; break;
case REGEXMACHINESTATETYPE_REPEAT: case REGEXMACHINESTATETYPE_REPEAT:
code = _HandleRepeat(thread); code = _HandleRepeat(thread);
@ -467,10 +546,12 @@ static int _AdvanceThread(DynamicArray* visitors, size_t* depth)
} }
if (thread->machine_head == NULL) { 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; return EXIT_SUCCESS;
} }
@ -509,85 +590,132 @@ static void _DestroyThreadHierarchy(DynamicArray* visitors, RegexMatchThreadGrou
return; return;
} }
static int _HandleAdvanceVisitor(DynamicArray* visitor_stack, size_t* depth) static int _AdvanceGroup(RegexMatcher* matcher)
{ {
struct GroupVisitor* current_visitor = DynamicArray_GetPointer( struct GroupVisitor* visitor = DynamicArray_GetPointer(
visitor_stack, &matcher->visitor_stack,
visitor_stack->reserved - 1 DynamicArray_GetLength(&matcher->visitor_stack) - 1
); );
RegexMatchThreadGroup* group = visitor->as.group;
visitor->position += 1;
switch (current_visitor->type) { if (DynamicArray_GetLength(&group->threads) > visitor->position) {
case REGEXMATCHTHREADTYPE_THREAD: struct GroupVisitor* child_visitor;
_AdvanceThread( DynamicArray_AppendEmpty(
visitor_stack, &matcher->visitor_stack,
depth (void**) &child_visitor
); );
break; child_visitor->type = REGEXMATCHTHREADTYPE_THREAD;
case REGEXMATCHTHREADTYPE_GROUP: child_visitor->as.thread = DynamicArray_GetPointer(&group->threads, visitor->position);
break; } 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
);
} 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; 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; struct GroupVisitor* zero_visitor;
DynamicArray_AppendEmpty( DynamicArray_AppendEmpty(
visitor_stack, &matcher->visitor_stack,
(void**) &zero_visitor (void**) &zero_visitor
); );
zero_visitor->type = REGEXMATCHTHREADTYPE_GROUP; 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) { while (DynamicArray_GetLength(&matcher->visitor_stack) != 0) {
int advance_code = _HandleAdvanceVisitor( int advance_code = _HandleAdvanceVisitor(matcher);
visitor_stack,
depth
);
if (advance_code) return advance_code; if (advance_code) return advance_code;
} }
return EXIT_SUCCESS; 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 Regex_MatchHere(Regex* regex, StringView string, size_t start, RegexMatch* match)
{ {
int return_code = EXIT_SUCCESS; int return_code = EXIT_SUCCESS;
// initialize variables etc.... RegexMatcher matcher;
DynamicArray visitor_stack; if (_RegexMatcher_Create(&matcher, regex, string, start)) {
if (DynamicArray_Create( return EXIT_FAILURE;
&visitor_stack,
sizeof(struct GroupVisitor),
16,
regex->machine_memory.allocator)
) {
return ENOMEM;
} }
RegexMatchThreadGroup top_level_group; while (! _HaveFinishedThread(&matcher)
RegexMatchThreadGroup_Create(&top_level_group, 0, regex->machine_memory.allocator); && _HaveThreadsRunning(&matcher)) {
size_t depth = 1; _WalkThreads(&matcher);
{
RegexMatchThread* first_thread;
first_thread = RegexMatchThreadGroup_NewThread(&top_level_group);
first_head->number = 0;
} }
bool haveFinishedThread = false; if (_HaveFinishedThread(&matcher)) {
while (! haveFinishedThread) { *match = matcher.finished_thread.match;
_WalkThreads(&visitor_stack, &top_level_group, &depth); memset(
&matcher.finished_thread.match,
0,
sizeof(matcher.finished_thread.match)
);
RegexMatchThread_Del(&matcher.finished_thread);
} }
defer_tl_group: _DestroyThreadHierarchy(&matcher.visitor_stack, &matcher.top_group);
_DestroyThreadHierarchy(&visitor_stack, &top_level_group); _RegexMatcher_Destroy(&matcher);
defer_stack:
DynamicArray_Destroy(&visitor_stack);
return return_code; return return_code;
} }

View file

@ -43,3 +43,9 @@ RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number)
return DynamicArray_GetPointer(&match->captures, capture_index); return DynamicArray_GetPointer(&match->captures, capture_index);
} }
void RegexMatch_Destroy(RegexMatch* match)
{
DynamicArray_Destroy(&match->captures);
match->match = STRINGVIEW_NONE;
}

View file

@ -26,4 +26,6 @@ typedef struct RegexMatch_s {
bool RegexMatch_HaveNumberedCapture(RegexMatch* match, size_t number); bool RegexMatch_HaveNumberedCapture(RegexMatch* match, size_t number);
RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number); RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number);
void RegexMatch_Destroy(RegexMatch* match);
#endif #endif

View file

@ -4,7 +4,6 @@
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <pthread.h> #include <pthread.h>
#include <signal.h>
int OSThread_Create(os_thread_t* destination, ThreadFunction subroutine, void* arg) int OSThread_Create(os_thread_t* destination, ThreadFunction subroutine, void* arg)
{ {

126
tests/BinaryTree.test.c Normal file
View file

@ -0,0 +1,126 @@
#include <assert.h>
#include <stdlib.h>
#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;
}

View file

@ -36,3 +36,19 @@ target_link_libraries(regex-test
StringView StringView
utf8 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
)

View file

@ -29,8 +29,24 @@ void test_split(void)
return; 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() int main()
{ {
test_split(); test_split();
test_fext();
return 0; return 0;
} }

31
tests/Subprocess.test.c Normal file
View file

@ -0,0 +1,31 @@
#include "../src/Subprocess/Subprocess.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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;
}

View file

@ -31,6 +31,8 @@ void testLiteral(void)
assert(Regex_FirstMatch(&regex, test_string_0, &match) == EXIT_SUCCESS); assert(Regex_FirstMatch(&regex, test_string_0, &match) == EXIT_SUCCESS);
assert(StringView_Equal(match.match, test_string_0)); assert(StringView_Equal(match.match, test_string_0));
RegexMatch_Destroy(&match);
Regex_Destroy(&regex); Regex_Destroy(&regex);
assert(allocator.reserved == 0); assert(allocator.reserved == 0);
Allocator_DestroySystemAllocator(&allocator); Allocator_DestroySystemAllocator(&allocator);
@ -52,6 +54,8 @@ void testBackslash(void)
assert(Regex_FirstMatch(&regex, match_string, &match) == EXIT_SUCCESS); assert(Regex_FirstMatch(&regex, match_string, &match) == EXIT_SUCCESS);
assert(StringView_Equal(match.match, match_string)); assert(StringView_Equal(match.match, match_string));
RegexMatch_Destroy(&match);
Regex_Destroy(&regex); Regex_Destroy(&regex);
assert(allocator.reserved == 0); assert(allocator.reserved == 0);
Allocator_DestroySystemAllocator(&allocator); Allocator_DestroySystemAllocator(&allocator);
@ -75,6 +79,38 @@ void testGroup(void)
assert(RegexMatch_HaveNumberedCapture(&match, 1)); assert(RegexMatch_HaveNumberedCapture(&match, 1));
assert(RegexMatch_HaveNumberedCapture(&match, 2)); assert(RegexMatch_HaveNumberedCapture(&match, 2));
Regex_Destroy(&regex);
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(&regex, &allocator) == EXIT_SUCCESS);
assert(Regex_Compile(&regex, regex_string) == EXIT_SUCCESS);
RegexMatch match;
assert(Regex_FirstMatch(&regex, match_string_0, &match) == EXIT_SUCCESS);
assert(StringView_Equal(match.match, match_string_0));
assert(Regex_FirstMatch(&regex, match_string_1, &match) == EXIT_SUCCESS);
assert(StringView_Equal(match.match, match_string_1));
assert(Regex_FirstMatch(&regex, match_string_2, &match) == EXIT_SUCCESS);
assert(StringView_Equal(match.match, match_string_2));
Regex_Destroy(&regex); Regex_Destroy(&regex);
assert(allocator.reserved == 0); assert(allocator.reserved == 0);
Allocator_DestroySystemAllocator(&allocator); Allocator_DestroySystemAllocator(&allocator);
@ -114,6 +150,7 @@ int main()
testLiteral(); testLiteral();
testBackslash(); testBackslash();
testGroup(); testGroup();
testOption();
testQuantifier(); testQuantifier();
return 0; return 0;
} }