Compare commits
10 commits
a2317836fa
...
baa4a148fe
Author | SHA1 | Date | |
---|---|---|---|
baa4a148fe | |||
|
1b3d007ec2 | ||
|
1a330b3c9b | ||
|
a0da4ce1a3 | ||
|
7a3f1955b9 | ||
|
840a29a59d | ||
|
fe63781996 | ||
|
30f288e5d7 | ||
|
0b651b819a | ||
|
e6064bd375 |
22 changed files with 997 additions and 109 deletions
288
src/BinaryTree/BinaryTree.c
Normal file
288
src/BinaryTree/BinaryTree.c
Normal 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;
|
||||||
|
}
|
40
src/BinaryTree/BinaryTree.h
Normal file
40
src/BinaryTree/BinaryTree.h
Normal 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
|
1
src/BinaryTree/CMakeLists.txt
Normal file
1
src/BinaryTree/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
add_library(BinaryTree STATIC BinaryTree.c)
|
|
@ -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)
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
1
src/Subprocess/CMakeLists.txt
Normal file
1
src/Subprocess/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
add_library(Subprocess STATIC Subprocess.c)
|
109
src/Subprocess/Subprocess.c
Normal file
109
src/Subprocess/Subprocess.c
Normal 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);
|
||||||
|
}
|
43
src/Subprocess/Subprocess.h
Normal file
43
src/Subprocess/Subprocess.h
Normal 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
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
126
tests/BinaryTree.test.c
Normal 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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
|
@ -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
31
tests/Subprocess.test.c
Normal 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;
|
||||||
|
}
|
|
@ -31,6 +31,8 @@ void testLiteral(void)
|
||||||
assert(Regex_FirstMatch(®ex, test_string_0, &match) == EXIT_SUCCESS);
|
assert(Regex_FirstMatch(®ex, 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(®ex);
|
Regex_Destroy(®ex);
|
||||||
assert(allocator.reserved == 0);
|
assert(allocator.reserved == 0);
|
||||||
Allocator_DestroySystemAllocator(&allocator);
|
Allocator_DestroySystemAllocator(&allocator);
|
||||||
|
@ -52,6 +54,8 @@ void testBackslash(void)
|
||||||
assert(Regex_FirstMatch(®ex, match_string, &match) == EXIT_SUCCESS);
|
assert(Regex_FirstMatch(®ex, match_string, &match) == EXIT_SUCCESS);
|
||||||
assert(StringView_Equal(match.match, match_string));
|
assert(StringView_Equal(match.match, match_string));
|
||||||
|
|
||||||
|
RegexMatch_Destroy(&match);
|
||||||
|
|
||||||
Regex_Destroy(®ex);
|
Regex_Destroy(®ex);
|
||||||
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(®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);
|
Regex_Destroy(®ex);
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue