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(./dynamicbuffer)
|
||||
add_subdirectory(./FreeList)
|
||||
add_subdirectory(./hashmap)
|
||||
add_subdirectory(./QuadTree)
|
||||
add_subdirectory(./threading)
|
||||
add_subdirectory(./ThreadPool)
|
||||
add_subdirectory(./regex)
|
||||
add_subdirectory(./utf8)
|
||||
add_subdirectory(./StringView)
|
||||
add_subdirectory(./allocator-interface)
|
||||
add_subdirectory(./arraylist)
|
||||
add_subdirectory(./argumentc)
|
||||
add_subdirectory(./rand)
|
||||
add_subdirectory(./siphash)
|
||||
add_subdirectory(./pointers)
|
||||
add_subdirectory(./dynamicarray)
|
||||
add_subdirectory(./dynamicbuffer)
|
||||
add_subdirectory(./hashmap)
|
||||
add_subdirectory(./rand)
|
||||
add_subdirectory(./utf8)
|
||||
add_subdirectory(./pointers)
|
||||
add_subdirectory(./StringView)
|
||||
add_subdirectory(./ThreadPool)
|
||||
add_subdirectory(./threading)
|
||||
add_subdirectory(./FreeList)
|
||||
add_subdirectory(./argumentc)
|
||||
add_subdirectory(./Scratchpad)
|
||||
add_subdirectory(./regex)
|
||||
add_subdirectory(./QuadTree)
|
||||
add_subdirectory(./BinaryTree)
|
||||
add_subdirectory(./Subprocess)
|
||||
|
|
|
@ -133,6 +133,22 @@ StringView StringView_Slice(StringView string, size_t start, size_t end)
|
|||
return slice;
|
||||
}
|
||||
|
||||
bool StringView_Partition(StringView* left, StringView* right, StringView source, StringView delim)
|
||||
{
|
||||
if (source.length == 0) return false;
|
||||
|
||||
size_t offset = StringView_FindStringOffset(source, delim);
|
||||
if (offset != SIZE_MAX) {
|
||||
if (left != NULL) *left = StringView_Slice(source, 0, offset);
|
||||
if (right != NULL) *right = StringView_Slice(source, offset + delim.length, source.length);
|
||||
} else {
|
||||
if (left != NULL) *left = source;
|
||||
if (right != NULL) *right = STRINGVIEW_NONE;
|
||||
}
|
||||
|
||||
return offset != SIZE_MAX;
|
||||
}
|
||||
|
||||
bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim)
|
||||
{
|
||||
if (source->length == 0) return false;
|
||||
|
@ -149,6 +165,23 @@ bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim
|
|||
return true;
|
||||
}
|
||||
|
||||
bool StringView_LastSplit(StringView* dest, StringView* source, StringView delim)
|
||||
{
|
||||
StringView s = *source;
|
||||
while (s.length != 0 && ! StringView_EndsWith(s, delim)) {
|
||||
s.length -= 1;
|
||||
}
|
||||
|
||||
if (s.length == 0) {
|
||||
return false;
|
||||
} else {
|
||||
*dest = StringView_Slice(*source, s.length, source->length);
|
||||
s.length -= delim.length;
|
||||
*source = s;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
StringView StringView_StripLeft(StringView sv, StringView strip)
|
||||
{
|
||||
while (StringView_StartsWith(sv, strip)) {
|
||||
|
|
|
@ -46,7 +46,9 @@ size_t StringView_FindStringOffset(StringView haystack, StringView needle);
|
|||
StringView StringView_FindString(StringView haystack, StringView needle);
|
||||
StringView StringView_Slice(StringView string, size_t start, size_t end); // start and end are offsets
|
||||
|
||||
bool StringView_Partition(StringView* left, StringView* right, StringView source, StringView delim);
|
||||
bool StringView_NextSplit(StringView* dest, StringView* source, StringView delim);
|
||||
bool StringView_LastSplit(StringView* dest, StringView* source, StringView delim);
|
||||
|
||||
StringView StringView_StripLeft(StringView sv, StringView strip);
|
||||
StringView StringView_StripRight(StringView sv, StringView strip);
|
||||
|
|
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
|
|
@ -237,7 +237,7 @@ OptionArgument Argumentc_PopLongArgument(Argumentc* argumentc, StringView name)
|
|||
}
|
||||
|
||||
Option* array_long = DynamicArray_GetPointer(&argumentc->array, index);
|
||||
Option* array_arg = DynamicArray_GetPointer(&argumentc->array, index + 1);
|
||||
Option* array_arg = DynamicArray_GetPointer(&argumentc->array, index + 1);
|
||||
|
||||
if (array_arg == NULL || array_arg->type != OPTIONTYPE_ARGUMENT) {
|
||||
return (OptionArgument) {{OPTIONTYPE_NONE, name}, {OPTIONTYPE_NONE, name}};
|
||||
|
|
|
@ -257,17 +257,25 @@ size_t DynamicArray_FindFunctionLinear(DynamicArray* array, DynamicArrayLinearFi
|
|||
size_t mid = bot + (top - bot) / 2;
|
||||
int eval = -1;
|
||||
|
||||
while (bot != top) {
|
||||
while (eval != 0 && mid != top && mid < array->reserved) {
|
||||
void* current = DynamicArray_GetPointer(array, mid);
|
||||
eval = function(current, xarg);
|
||||
|
||||
if (eval > 0) {
|
||||
bot = mid + 1;
|
||||
if (eval == 0) {
|
||||
bot = top = mid;
|
||||
} else if (eval < 0) {
|
||||
top = mid - 1;
|
||||
} else {
|
||||
bot = top;
|
||||
bot = mid + 1;
|
||||
}
|
||||
// TODO: Is there a bug with overlapping top and bot here?
|
||||
|
||||
mid = bot + (top - bot) / 2;
|
||||
}
|
||||
|
||||
if (mid == top && mid < array->reserved) {
|
||||
void* current = DynamicArray_GetPointer(array, mid);
|
||||
eval = function(current, xarg);
|
||||
}
|
||||
|
||||
if (eval != 0) {
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
*/
|
||||
#include "dynamicbuffer.h"
|
||||
|
||||
int DynamicBuffer_Create(dynamic_buffer_t *destination, size_t initial_capacity)
|
||||
int DynamicBuffer_Create(DynamicBuffer *destination, size_t initial_capacity)
|
||||
{
|
||||
return DynamicBuffer_CreateWithAllocator(destination, initial_capacity, NULL);
|
||||
}
|
||||
|
||||
int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t initial_capacity, allocator_t* allocator)
|
||||
int DynamicBuffer_CreateWithAllocator(DynamicBuffer *destination, size_t initial_capacity, allocator_t* allocator)
|
||||
{
|
||||
if (destination == NULL) {
|
||||
return EDESTADDRREQ;
|
||||
|
@ -34,7 +34,7 @@ int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t init
|
|||
return EINVAL;
|
||||
}
|
||||
|
||||
dynamic_buffer_t local;
|
||||
DynamicBuffer local;
|
||||
|
||||
local.allocator = allocator;
|
||||
local.capacity = initial_capacity;
|
||||
|
@ -54,7 +54,7 @@ int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t *destination, size_t init
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t* buffer, size_t needed_unused)
|
||||
int DynamicBuffer_EnsureUnusedCapacity(DynamicBuffer* buffer, size_t needed_unused)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
@ -77,7 +77,7 @@ int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t* buffer, size_t needed_u
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int DynamicBuffer_EnsureCapacity(dynamic_buffer_t* buffer, size_t minimal_capacity)
|
||||
int DynamicBuffer_EnsureCapacity(DynamicBuffer* buffer, size_t minimal_capacity)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
@ -96,7 +96,7 @@ int DynamicBuffer_EnsureCapacity(dynamic_buffer_t* buffer, size_t minimal_capaci
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int DynamicBuffer_Resize(dynamic_buffer_t* buffer, size_t new_capacity)
|
||||
int DynamicBuffer_Resize(DynamicBuffer* buffer, size_t new_capacity)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
@ -121,7 +121,7 @@ int DynamicBuffer_Resize(dynamic_buffer_t* buffer, size_t new_capacity)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int DynamicBuffer_Prune(dynamic_buffer_t* buffer)
|
||||
int DynamicBuffer_Prune(DynamicBuffer* buffer)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return ENOMEM;
|
||||
|
@ -130,7 +130,7 @@ int DynamicBuffer_Prune(dynamic_buffer_t* buffer)
|
|||
return DynamicBuffer_Resize(buffer, buffer->used);
|
||||
}
|
||||
|
||||
int DynamicBuffer_Reset(dynamic_buffer_t* buffer)
|
||||
int DynamicBuffer_Reset(DynamicBuffer* buffer)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
@ -141,7 +141,7 @@ int DynamicBuffer_Reset(dynamic_buffer_t* buffer)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes)
|
||||
int DynamicBuffer_RewindBytes(DynamicBuffer* buffer, size_t bytes)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
@ -155,7 +155,7 @@ int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int DynamicBuffer_Store(dynamic_buffer_t* buffer, const void* data, size_t data_size)
|
||||
int DynamicBuffer_Store(DynamicBuffer* buffer, const void* data, size_t data_size)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
@ -180,12 +180,12 @@ int DynamicBuffer_Store(dynamic_buffer_t* buffer, const void* data, size_t data_
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
size_t DynamicBuffer_GetBlockCount(dynamic_buffer_t* buffer, size_t block_size)
|
||||
size_t DynamicBuffer_GetBlockCount(DynamicBuffer* buffer, size_t block_size)
|
||||
{
|
||||
return buffer->used / block_size;
|
||||
}
|
||||
|
||||
void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset)
|
||||
void* DynamicBuffer_ReadAt(DynamicBuffer* buffer, size_t offset)
|
||||
{
|
||||
if (offset >= buffer->used) {
|
||||
return NULL;
|
||||
|
@ -194,12 +194,12 @@ void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset)
|
|||
return (void*) (((char*) buffer->array) + offset);
|
||||
}
|
||||
|
||||
void* DynamicBuffer_ReadBlockAt(dynamic_buffer_t* buffer, size_t block_size, size_t index)
|
||||
void* DynamicBuffer_ReadBlockAt(DynamicBuffer* buffer, size_t block_size, size_t index)
|
||||
{
|
||||
return DynamicBuffer_ReadAt(buffer, block_size * index);
|
||||
}
|
||||
|
||||
int DynamicBuffer_Destroy(dynamic_buffer_t* buffer)
|
||||
int DynamicBuffer_Destroy(DynamicBuffer* buffer)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
return EINVAL;
|
||||
|
|
|
@ -27,90 +27,90 @@
|
|||
|
||||
#include "../allocator-interface/allocator-interface.h"
|
||||
|
||||
typedef struct DynamicBuffer {
|
||||
typedef struct DynamicBuffer_s {
|
||||
void* array;
|
||||
size_t capacity;
|
||||
size_t used;
|
||||
|
||||
allocator_t* allocator;
|
||||
} dynamic_buffer_t;
|
||||
} DynamicBuffer;
|
||||
|
||||
/// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity
|
||||
/// @param destination where the buffer will be stored
|
||||
/// @param initialCapacity what it's initialCapacity will be
|
||||
/// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_Create(dynamic_buffer_t* destination, size_t initialCapacity);
|
||||
int DynamicBuffer_Create(DynamicBuffer* destination, size_t initialCapacity);
|
||||
|
||||
|
||||
/// @brief Create a new Dynamic Buffer at destination with initialCapacity initialCapacity
|
||||
/// @param destination where the buffer will be stored
|
||||
/// @param initialCapacity what it's initialCapacity will be
|
||||
/// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_CreateWithAllocator(dynamic_buffer_t* destination, size_t initialCapacity, allocator_t* allocator);
|
||||
int DynamicBuffer_CreateWithAllocator(DynamicBuffer* destination, size_t initialCapacity, allocator_t* allocator);
|
||||
|
||||
/// @brief This function checks that there are at least needed_unused free bytes in the allocated area
|
||||
/// @param buffer buffer to check
|
||||
/// @param needed_unused required free array size
|
||||
/// @return EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_EnsureUnusedCapacity(dynamic_buffer_t *buffer, size_t needed_unused);
|
||||
int DynamicBuffer_EnsureUnusedCapacity(DynamicBuffer *buffer, size_t needed_unused);
|
||||
|
||||
/// @brief This function resizes the buffer to minimal_capacity, if necessary
|
||||
/// @param buffer buffer to check
|
||||
/// @param minimal_capacity minimal capacity of the buffer array
|
||||
/// @return EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_EnsureCapacity(dynamic_buffer_t *buffer, size_t minimal_capacity);
|
||||
int DynamicBuffer_EnsureCapacity(DynamicBuffer *buffer, size_t minimal_capacity);
|
||||
|
||||
/// @brief This function resizes the buffers array via realloc to new_capacity
|
||||
/// @param buffer buffer to resize
|
||||
/// @param new_capacity capacity of the new buffer array
|
||||
/// @return EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_Resize(dynamic_buffer_t *buffer, size_t new_capacity);
|
||||
int DynamicBuffer_Resize(DynamicBuffer *buffer, size_t new_capacity);
|
||||
|
||||
/// @brief This function sets the buffers capacity to what it uses
|
||||
/// @param buffer buffer to prune
|
||||
/// @return EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_Prune(dynamic_buffer_t* buffer);
|
||||
int DynamicBuffer_Prune(DynamicBuffer* buffer);
|
||||
|
||||
/// @brief Resets the count of used bytes to "clear" the buffer
|
||||
/// @param buffer buffer to reset
|
||||
/// @return EINVAL, EXIT_SUCCESS
|
||||
int DynamicBuffer_Reset(dynamic_buffer_t *buffer);
|
||||
int DynamicBuffer_Reset(DynamicBuffer *buffer);
|
||||
|
||||
/// @brief Rewind the buffer pointer by bytes bytes, losing the bytes stored
|
||||
/// @param buffer buffer to rewind
|
||||
/// @param bytes How many bytes the buffer will lose
|
||||
/// @return EINVAL, EBOUNDS, EXIT_SUCCESS
|
||||
int DynamicBuffer_RewindBytes(dynamic_buffer_t* buffer, size_t bytes);
|
||||
int DynamicBuffer_RewindBytes(DynamicBuffer* buffer, size_t bytes);
|
||||
|
||||
/// @brief Stores data[.data_size] at the end of the buffer array and resizes if necessary
|
||||
/// @param buffer buffer in which the data will be stored
|
||||
/// @param data data to store
|
||||
/// @param data_size how many bytes will be copied from data
|
||||
/// @return EINVAL, EINVAL, ENOMEM, EXIT_SUCCESS
|
||||
int DynamicBuffer_Store(dynamic_buffer_t *buffer, const void *data, size_t data_size);
|
||||
int DynamicBuffer_Store(DynamicBuffer *buffer, const void *data, size_t data_size);
|
||||
|
||||
/// @brief Calculate how many blocks are currently in the buffer
|
||||
/// @param buffer buffer to query
|
||||
/// @param block_size what's the size of a single block
|
||||
/// @return How many of these block_sizes does the buffer currently hold
|
||||
size_t DynamicBuffer_GetBlockCount(dynamic_buffer_t* buffer, size_t block_size);
|
||||
size_t DynamicBuffer_GetBlockCount(DynamicBuffer* buffer, size_t block_size);
|
||||
|
||||
/// @brief Get a pointer reference to the buffer contents at offset
|
||||
/// @param buffer buffer to query
|
||||
/// @param offset offset from the buffer start
|
||||
/// @return Pointer to the address in the buffer at offset or NULL if out of Bounds
|
||||
void* DynamicBuffer_ReadAt(dynamic_buffer_t* buffer, size_t offset);
|
||||
void* DynamicBuffer_ReadAt(DynamicBuffer* buffer, size_t offset);
|
||||
|
||||
/// @brief Get a pointer reference to indexn'th block in buffer
|
||||
/// @param buffer buffer to query
|
||||
/// @param block_size size of a single block
|
||||
/// @param index which block to read
|
||||
/// @return Pointer to the block at index or NULL if out of bounds
|
||||
void* DynamicBuffer_ReadBlockAt(dynamic_buffer_t* buffer, size_t block_size, size_t index);
|
||||
void* DynamicBuffer_ReadBlockAt(DynamicBuffer* buffer, size_t block_size, size_t index);
|
||||
|
||||
/// @brief Destroys the dynamic buffer and releases all resources held, the struct will not hold anything it did before
|
||||
/// @param buffer buffer that shall be destroyed
|
||||
/// @return EINVAL, EXIT_SUCCESS
|
||||
int DynamicBuffer_Destroy(dynamic_buffer_t *buffer);
|
||||
int DynamicBuffer_Destroy(DynamicBuffer *buffer);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
typedef struct RegexMatcher_s {
|
||||
RegexMatchThreadGroup top_group;
|
||||
DynamicArray visitor_stack;
|
||||
size_t depth;
|
||||
|
||||
RegexMatchThread finished_threads;
|
||||
RegexMatchThread finished_thread;
|
||||
} RegexMatcher;
|
||||
|
||||
static int RegexMatchThread_New(RegexMatchThread* thread, StringView string, size_t index, Regex* regex)
|
||||
|
@ -275,7 +277,7 @@ static void RegexMatchThreadGroup_Destroy(DynamicArray* visitors)
|
|||
visitors,
|
||||
visitors->reserved - 1
|
||||
);
|
||||
RegexMatchThreadGroup* group = (void*) current_visitor->type;
|
||||
RegexMatchThreadGroup* group = current_visitor->as.group;
|
||||
|
||||
if (DynamicArray_GetLength(&group->threads) != 0) {
|
||||
// Append first sub thread to visitor stack
|
||||
|
@ -312,6 +314,57 @@ static void RegexMatchThreadGroup_Destroy(DynamicArray* visitors)
|
|||
return;
|
||||
}
|
||||
|
||||
static int _RegexMatcher_Create(RegexMatcher* matcher, Regex* regex, StringView string, size_t start)
|
||||
{
|
||||
int group_code = RegexMatchThreadGroup_Create(
|
||||
&matcher->top_group,
|
||||
0,
|
||||
regex->machine_memory.allocator
|
||||
);
|
||||
if (group_code) {
|
||||
return group_code;
|
||||
}
|
||||
|
||||
int stack_code = DynamicArray_Create(
|
||||
&matcher->visitor_stack,
|
||||
sizeof(struct GroupVisitor),
|
||||
16,
|
||||
regex->machine_memory.allocator
|
||||
);
|
||||
if (stack_code) {
|
||||
RegexMatchThreadGroup_Destroy2(&matcher->top_group);
|
||||
return stack_code;
|
||||
}
|
||||
|
||||
RegexMatchThread* root_thread = _RegexMatchThreadGroup_NewThread(
|
||||
&matcher->top_group
|
||||
);
|
||||
if (root_thread == NULL) {
|
||||
RegexMatchThreadGroup_Destroy2(&matcher->top_group);
|
||||
DynamicArray_Destroy(&matcher->visitor_stack);
|
||||
return ENOMEM;
|
||||
}
|
||||
if (RegexMatchThread_New(root_thread, string, start, regex)) {
|
||||
RegexMatchThreadGroup_Destroy2(&matcher->top_group);
|
||||
DynamicArray_Destroy(&matcher->visitor_stack);
|
||||
return ENOMEM;
|
||||
}
|
||||
root_thread->number = 0;
|
||||
|
||||
matcher->depth = 1;
|
||||
|
||||
memset(&matcher->finished_thread, 0, sizeof(matcher->finished_thread));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void _RegexMatcher_Destroy(RegexMatcher* matcher)
|
||||
{
|
||||
RegexMatchThreadGroup_Destroy2(&matcher->top_group);
|
||||
DynamicArray_Destroy(&matcher->visitor_stack);
|
||||
memset(matcher, 0, sizeof(*matcher));
|
||||
}
|
||||
|
||||
static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, size_t* depth, RegexMachineStateBase* new_head)
|
||||
{
|
||||
RegexMatchThread* child;
|
||||
|
@ -384,6 +437,7 @@ static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, siz
|
|||
|
||||
// remove from old group
|
||||
_RegexMatchThreadGroup_ForgetThread(parent_group, *parent);
|
||||
parent_visitor->position--;
|
||||
}
|
||||
|
||||
if (child == NULL) {
|
||||
|
@ -396,18 +450,19 @@ static int _NewRegexChild(RegexMatchThread** parent, DynamicArray* visitors, siz
|
|||
}
|
||||
|
||||
child->machine_head = new_head;
|
||||
child->number = parent->number + DynamicArray_GetLength(&parent_group.threads) - 1;
|
||||
child->number = (*parent)->number
|
||||
+ DynamicArray_GetLength(&parent_group->threads) - 1;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int _HandleOption(DynamicArray* visitors, size_t* depth)
|
||||
static int _HandleOption(RegexMatcher* matcher)
|
||||
{
|
||||
struct GroupVisitor* visitor = DynamicArray_GetPointer(
|
||||
visitors,
|
||||
visitors->reserved - 1
|
||||
&matcher->visitor_stack,
|
||||
DynamicArray_GetLength(&matcher->visitor_stack)
|
||||
);
|
||||
RegexMatchThread* thread = (void*) visitor->type;
|
||||
RegexMatchThread* thread = visitor->as.thread;
|
||||
|
||||
RegexMachineStateBase* first_head = thread->machine_head;
|
||||
{
|
||||
|
@ -420,8 +475,8 @@ static int _HandleOption(DynamicArray* visitors, size_t* depth)
|
|||
|
||||
int child_code = _NewRegexChild(
|
||||
&thread,
|
||||
visitors,
|
||||
depth,
|
||||
&matcher->visitor_stack,
|
||||
&matcher->depth,
|
||||
option_head->next
|
||||
);
|
||||
if (child_code) {
|
||||
|
@ -435,13 +490,37 @@ static int _HandleOption(DynamicArray* visitors, size_t* depth)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int _AdvanceThread(DynamicArray* visitors, size_t* depth)
|
||||
static void _TryFinishThread(RegexMatcher* matcher, RegexMatchThread* thread)
|
||||
{
|
||||
struct GroupVisitor* parent_visitor = DynamicArray_GetPointer(
|
||||
&matcher->visitor_stack,
|
||||
DynamicArray_GetLength(&matcher->visitor_stack) - 2
|
||||
);
|
||||
RegexMatchThreadGroup* parent_group = parent_visitor->as.group;
|
||||
size_t thread_index = DynamicArray_FindFunctionLinear(
|
||||
&parent_group->threads,
|
||||
(DynamicArrayLinearFindFunction) _FindThreadLinear,
|
||||
thread
|
||||
);
|
||||
|
||||
if (thread_index == 0
|
||||
&& DynamicArray_GetLength(&matcher->visitor_stack) == 2) {
|
||||
// Only the first thread in the top-most group is finishable
|
||||
// All other threads have a lower priority, this is because
|
||||
// of the order they have to be matched in.
|
||||
matcher->finished_thread = *thread;
|
||||
_RegexMatchThreadGroup_ForgetThread(parent_group, thread);
|
||||
matcher->visitor_stack.reserved = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int _AdvanceThread(RegexMatcher* matcher)
|
||||
{
|
||||
struct GroupVisitor* visitor = DynamicArray_GetPointer(
|
||||
visitors,
|
||||
visitors->reserved - 1
|
||||
&matcher->visitor_stack,
|
||||
DynamicArray_GetLength(&matcher->visitor_stack) - 1
|
||||
);
|
||||
RegexMatchThread* thread = (void*) visitor->type;
|
||||
RegexMatchThread* thread = visitor->as.thread;
|
||||
int code;
|
||||
bool discard;
|
||||
|
||||
|
@ -453,7 +532,7 @@ static int _AdvanceThread(DynamicArray* visitors, size_t* depth)
|
|||
code = _HandleGroup(thread);
|
||||
break;
|
||||
case REGEXMACHINESTATETYPE_OPTION:
|
||||
code = _HandleOption(visitors, depth);
|
||||
code = _HandleOption(matcher);
|
||||
break;
|
||||
case REGEXMACHINESTATETYPE_REPEAT:
|
||||
code = _HandleRepeat(thread);
|
||||
|
@ -467,10 +546,12 @@ static int _AdvanceThread(DynamicArray* visitors, size_t* depth)
|
|||
}
|
||||
|
||||
if (thread->machine_head == NULL) {
|
||||
// Match done
|
||||
|
||||
_TryFinishThread(matcher, thread);
|
||||
}
|
||||
|
||||
// Remove from visitor stack
|
||||
DynamicArray_Remove(&matcher->visitor_stack, matcher->visitor_stack.reserved - 1);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -509,85 +590,132 @@ static void _DestroyThreadHierarchy(DynamicArray* visitors, RegexMatchThreadGrou
|
|||
return;
|
||||
}
|
||||
|
||||
static int _HandleAdvanceVisitor(DynamicArray* visitor_stack, size_t* depth)
|
||||
static int _AdvanceGroup(RegexMatcher* matcher)
|
||||
{
|
||||
struct GroupVisitor* current_visitor = DynamicArray_GetPointer(
|
||||
visitor_stack,
|
||||
visitor_stack->reserved - 1
|
||||
struct GroupVisitor* visitor = DynamicArray_GetPointer(
|
||||
&matcher->visitor_stack,
|
||||
DynamicArray_GetLength(&matcher->visitor_stack) - 1
|
||||
);
|
||||
RegexMatchThreadGroup* group = visitor->as.group;
|
||||
visitor->position += 1;
|
||||
|
||||
switch (current_visitor->type) {
|
||||
case REGEXMATCHTHREADTYPE_THREAD:
|
||||
_AdvanceThread(
|
||||
visitor_stack,
|
||||
depth
|
||||
if (DynamicArray_GetLength(&group->threads) > visitor->position) {
|
||||
struct GroupVisitor* child_visitor;
|
||||
DynamicArray_AppendEmpty(
|
||||
&matcher->visitor_stack,
|
||||
(void**) &child_visitor
|
||||
);
|
||||
child_visitor->type = REGEXMATCHTHREADTYPE_THREAD;
|
||||
child_visitor->as.thread = DynamicArray_GetPointer(&group->threads, visitor->position);
|
||||
} else {
|
||||
// calculate position in subgroup array
|
||||
unsigned int position = visitor->position
|
||||
- DynamicArray_GetLength(&group->threads);
|
||||
|
||||
if (DynamicArray_GetLength(&group->subgroups) == position) {
|
||||
// Remove this group
|
||||
DynamicArray_Remove(
|
||||
&matcher->visitor_stack,
|
||||
matcher->visitor_stack.reserved - 1
|
||||
);
|
||||
break;
|
||||
case REGEXMATCHTHREADTYPE_GROUP:
|
||||
break;
|
||||
} else {
|
||||
// Add subgroup
|
||||
struct GroupVisitor* sgroup_visitor;
|
||||
DynamicArray_AppendEmpty(
|
||||
&matcher->visitor_stack,
|
||||
(void**) &sgroup_visitor
|
||||
);
|
||||
sgroup_visitor->type = REGEXMATCHTHREADTYPE_GROUP;
|
||||
sgroup_visitor->position = -1;
|
||||
sgroup_visitor->as.group = DynamicArray_GetPointer(
|
||||
&group->subgroups,
|
||||
position
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int _WalkThreads(DynamicArray* visitor_stack, RegexMatchThreadGroup* group, size_t* depth)
|
||||
static int _HandleAdvanceVisitor(RegexMatcher* matcher)
|
||||
{
|
||||
struct GroupVisitor* current_visitor = DynamicArray_GetPointer(
|
||||
&matcher->visitor_stack,
|
||||
DynamicArray_GetLength(&matcher->visitor_stack) - 1
|
||||
);
|
||||
|
||||
int advance_code = EXIT_SUCCESS;
|
||||
switch (current_visitor->type) {
|
||||
case REGEXMATCHTHREADTYPE_THREAD:
|
||||
advance_code = _AdvanceThread(matcher);
|
||||
break;
|
||||
case REGEXMATCHTHREADTYPE_GROUP:
|
||||
advance_code = _AdvanceGroup(matcher);
|
||||
break;
|
||||
}
|
||||
|
||||
return advance_code;
|
||||
}
|
||||
|
||||
static int _WalkThreads(RegexMatcher* matcher)
|
||||
{
|
||||
{
|
||||
|
||||
struct GroupVisitor* zero_visitor;
|
||||
DynamicArray_AppendEmpty(
|
||||
visitor_stack,
|
||||
&matcher->visitor_stack,
|
||||
(void**) &zero_visitor
|
||||
);
|
||||
zero_visitor->type = REGEXMATCHTHREADTYPE_GROUP;
|
||||
zero_visitor->as.group = group;
|
||||
zero_visitor->as.group = &matcher->top_group;
|
||||
zero_visitor->position = -1;
|
||||
|
||||
}
|
||||
|
||||
while (DynamicArray_GetLength(visitor_stack) != 0) {
|
||||
int advance_code = _HandleAdvanceVisitor(
|
||||
visitor_stack,
|
||||
depth
|
||||
);
|
||||
while (DynamicArray_GetLength(&matcher->visitor_stack) != 0) {
|
||||
int advance_code = _HandleAdvanceVisitor(matcher);
|
||||
if (advance_code) return advance_code;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static bool _HaveFinishedThread(RegexMatcher* matcher)
|
||||
{
|
||||
return matcher->finished_thread.regex != NULL;
|
||||
}
|
||||
|
||||
static bool _HaveThreadsRunning(RegexMatcher* matcher)
|
||||
{
|
||||
return matcher->top_group.subgroups.reserved != 0
|
||||
|| matcher->top_group.threads.reserved != 0;
|
||||
}
|
||||
|
||||
int Regex_MatchHere(Regex* regex, StringView string, size_t start, RegexMatch* match)
|
||||
{
|
||||
int return_code = EXIT_SUCCESS;
|
||||
|
||||
// initialize variables etc....
|
||||
DynamicArray visitor_stack;
|
||||
if (DynamicArray_Create(
|
||||
&visitor_stack,
|
||||
sizeof(struct GroupVisitor),
|
||||
16,
|
||||
regex->machine_memory.allocator)
|
||||
) {
|
||||
return ENOMEM;
|
||||
RegexMatcher matcher;
|
||||
if (_RegexMatcher_Create(&matcher, regex, string, start)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
RegexMatchThreadGroup top_level_group;
|
||||
RegexMatchThreadGroup_Create(&top_level_group, 0, regex->machine_memory.allocator);
|
||||
size_t depth = 1;
|
||||
|
||||
{
|
||||
RegexMatchThread* first_thread;
|
||||
first_thread = RegexMatchThreadGroup_NewThread(&top_level_group);
|
||||
first_head->number = 0;
|
||||
while (! _HaveFinishedThread(&matcher)
|
||||
&& _HaveThreadsRunning(&matcher)) {
|
||||
_WalkThreads(&matcher);
|
||||
}
|
||||
|
||||
bool haveFinishedThread = false;
|
||||
while (! haveFinishedThread) {
|
||||
_WalkThreads(&visitor_stack, &top_level_group, &depth);
|
||||
if (_HaveFinishedThread(&matcher)) {
|
||||
*match = matcher.finished_thread.match;
|
||||
memset(
|
||||
&matcher.finished_thread.match,
|
||||
0,
|
||||
sizeof(matcher.finished_thread.match)
|
||||
);
|
||||
RegexMatchThread_Del(&matcher.finished_thread);
|
||||
}
|
||||
|
||||
defer_tl_group:
|
||||
_DestroyThreadHierarchy(&visitor_stack, &top_level_group);
|
||||
defer_stack:
|
||||
DynamicArray_Destroy(&visitor_stack);
|
||||
_DestroyThreadHierarchy(&matcher.visitor_stack, &matcher.top_group);
|
||||
_RegexMatcher_Destroy(&matcher);
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
|
|
@ -43,3 +43,9 @@ RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number)
|
|||
|
||||
return DynamicArray_GetPointer(&match->captures, capture_index);
|
||||
}
|
||||
|
||||
void RegexMatch_Destroy(RegexMatch* match)
|
||||
{
|
||||
DynamicArray_Destroy(&match->captures);
|
||||
match->match = STRINGVIEW_NONE;
|
||||
}
|
||||
|
|
|
@ -26,4 +26,6 @@ typedef struct RegexMatch_s {
|
|||
bool RegexMatch_HaveNumberedCapture(RegexMatch* match, size_t number);
|
||||
RegexCapture* RegexMatch_GetNumberedCapture(RegexMatch* match, size_t number);
|
||||
|
||||
void RegexMatch_Destroy(RegexMatch* match);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
void test_fext(void)
|
||||
{
|
||||
const char* filepath = "database.sqlite.old";
|
||||
|
||||
StringView sourceSV = StringView_FromString(filepath);
|
||||
StringView delim = StringView_FromString(".");
|
||||
StringView split;
|
||||
|
||||
assert(StringView_LastSplit(&split, &sourceSV, delim));
|
||||
assert(StringView_Equal(split, StringView_FromString("old")));
|
||||
assert(StringView_Equal(sourceSV, StringView_FromString("database.sqlite")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_split();
|
||||
test_fext();
|
||||
return 0;
|
||||
}
|
||||
|
|
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(StringView_Equal(match.match, test_string_0));
|
||||
|
||||
RegexMatch_Destroy(&match);
|
||||
|
||||
Regex_Destroy(®ex);
|
||||
assert(allocator.reserved == 0);
|
||||
Allocator_DestroySystemAllocator(&allocator);
|
||||
|
@ -52,6 +54,8 @@ void testBackslash(void)
|
|||
assert(Regex_FirstMatch(®ex, match_string, &match) == EXIT_SUCCESS);
|
||||
assert(StringView_Equal(match.match, match_string));
|
||||
|
||||
RegexMatch_Destroy(&match);
|
||||
|
||||
Regex_Destroy(®ex);
|
||||
assert(allocator.reserved == 0);
|
||||
Allocator_DestroySystemAllocator(&allocator);
|
||||
|
@ -75,6 +79,38 @@ void testGroup(void)
|
|||
assert(RegexMatch_HaveNumberedCapture(&match, 1));
|
||||
assert(RegexMatch_HaveNumberedCapture(&match, 2));
|
||||
|
||||
Regex_Destroy(®ex);
|
||||
RegexMatch_Destroy(&match);
|
||||
assert(allocator.reserved == 0);
|
||||
Allocator_DestroySystemAllocator(&allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
void testOption(void)
|
||||
{
|
||||
StringView regex_string = StringView_FromString("abc|def|");
|
||||
StringView match_string_0 = StringView_FromString("abc");
|
||||
StringView match_string_1 = StringView_FromString("def");
|
||||
StringView match_string_2 = StringView_FromString("");
|
||||
allocator_t allocator;
|
||||
Allocator_CreateSystemAllocator(&allocator);
|
||||
|
||||
Regex regex;
|
||||
assert(Regex_Create(®ex, &allocator) == EXIT_SUCCESS);
|
||||
|
||||
assert(Regex_Compile(®ex, regex_string) == EXIT_SUCCESS);
|
||||
|
||||
RegexMatch match;
|
||||
|
||||
assert(Regex_FirstMatch(®ex, match_string_0, &match) == EXIT_SUCCESS);
|
||||
assert(StringView_Equal(match.match, match_string_0));
|
||||
|
||||
assert(Regex_FirstMatch(®ex, match_string_1, &match) == EXIT_SUCCESS);
|
||||
assert(StringView_Equal(match.match, match_string_1));
|
||||
|
||||
assert(Regex_FirstMatch(®ex, match_string_2, &match) == EXIT_SUCCESS);
|
||||
assert(StringView_Equal(match.match, match_string_2));
|
||||
|
||||
Regex_Destroy(®ex);
|
||||
assert(allocator.reserved == 0);
|
||||
Allocator_DestroySystemAllocator(&allocator);
|
||||
|
@ -114,6 +150,7 @@ int main()
|
|||
testLiteral();
|
||||
testBackslash();
|
||||
testGroup();
|
||||
testOption();
|
||||
testQuantifier();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue