Compare commits

...

4 Commits

Author SHA1 Message Date
2a121caf42 Created main makefile - Need to add clean recipe
`make modules debug|release MODULES...` Builds the provided list of lua modules in release/debug
`make release`
`make debug` Builds a debug build of the lbs program
`make clean` TODO
2025-11-25 00:04:24 +11:00
3f829f7a58 Nuked existing codebase 2025-11-25 00:01:47 +11:00
2bcbc62bf8 Why do I need to merge this?! 2025-03-10 20:59:05 +11:00
b4ed4aa4c0 Fixed issue where first process is null 2025-03-10 20:57:05 +11:00
18 changed files with 72 additions and 2466 deletions

View File

@@ -1,12 +0,0 @@
#pragma once
#include <lua.hpp>
#include <luajit.h>
#include <lauxlib.h>
#include <thread>
#include <vector>
#include <map>
#include <string>
#include <set>
// Any function marked with this means that the exposed lua API is not sandboxed
#define LUA_UNSAFE

View File

@@ -1,9 +0,0 @@
#pragma once
#include "includes.h"
int luaopen_datetime(lua_State* L);
int lua_datetime_unix_time(lua_State* L);
int lua_datetime_tostring(lua_State* L);
int lua_datetime_tounix(lua_State* L);

View File

@@ -1,101 +0,0 @@
#pragma once
#include "includes.h"
#include "dynarray.hpp"
#include <set>
#include <string>
#include <map>
struct dependency_tree
{
typedef uint32_t ref_t;
typedef ref_t state_t;
typedef ref_t state_cmp_t;
enum STATE : state_t
{
FULFILLED = 0,
FAILED = 1,
WAITING = 2,
WORKING = 3
};
enum STATE_CMP : state_cmp_t
{
CMP_FULFILED = 1,
CMP_FAILED = 2,
CMP_WAITING = 4,
CMP_WORKING = 8
};
struct node
{
node(const node&) = delete;
node& operator =(const node&) = delete;
node(node&& other) noexcept;
node& operator =(node&& other) noexcept;
node();
// What depends on this node
std::set<ref_t> m_dependents = {};
// Name of the node
const char* m_name = nullptr;
// How many other nodes does this node depend on
ref_t m_num_pending_dependencies = 0;
// A user-controlled u30
ref_t m_userdata : (sizeof(ref_t)-2) = -1;
// State of the node
state_t m_state : 2;
~node();
};
~dependency_tree();
// A list that maps a nodes name to its index
std::map<std::string, ref_t> m_node_name_mapping;
// A list of all nodes in the dependency tree
dynarray<node> m_nodes;
node& operator[](const ref_t idx) noexcept;
const node& operator[](const ref_t idx) const noexcept;
dynarray<ref_t> get_leaf_nodes(const bool ignore_fulfilled=false) const;
dynarray<const char*> get_leaf_node_names(const bool ignore_fulfilled=false) const;
dynarray<ref_t> filter_nodes(const state_cmp_t state_cmp, const ref_t num_dependencies_gt, const bool invert_num_deps) const;
void set_node_state(const ref_t node, const state_t new_state);
void set_node_state(const char* node, const state_t new_state);
ref_t add_node(const char* node_name, const dynarray<ref_t>& dependents);
ref_t add_node(const char* node_name, const dynarray<const char*>& dependents);
ref_t add_node(const char* node_name, const std::initializer_list<ref_t>& dependents);
ref_t add_node(const char* node_name, const std::initializer_list<const char*>& dependents);
ref_t add_node(const char* node_name);
void add_node_dependency(const ref_t node, const ref_t dependency);
void add_node_dependency(const ref_t node, const char* dependency);
void add_node_dependency(const char* node, const ref_t dependency);
void add_node_dependency(const char* node, const char* dependency);
void add_node_dependencies(const ref_t node, const std::initializer_list<ref_t>& dependencies);
void add_node_dependencies(const ref_t node, const dynarray<ref_t>& dependencies);
void add_node_dependencies(const ref_t node, const std::initializer_list<const char*>& dependencies);
void add_node_dependencies(const ref_t node, const dynarray<const char*>& dependencies);
};
int luaopen_dependency_tree(lua_State* L);
int lua_dependency_create(lua_State* L);
int lua_dependency_destroy(lua_State* L);
int lua_dependency_add_node(lua_State* L);
int lua_dependency_add_dependency(lua_State* L);
int lua_dependency_remove_dependency(lua_State* L);
int lua_dependency_add_dependencies(lua_State* L);
int lua_dependency_remove_dependencies(lua_State* L);
int lua_dependency_get_leafs(lua_State* L);
int lua_dependency_match_nodes(lua_State* L);
int lua_dependency_get_node_state(lua_State* L);
int lua_dependency_set_node_state(lua_State* L);
int lua_dependency_get_node_userval(lua_State* L);
int lua_dependency_set_node_userval(lua_State* L);
int lua_dependency_get_node_name(lua_State* L);
int lua_dependency_get_node(lua_State* L);
int lua_dependency_index(lua_State* L);

View File

@@ -1,64 +0,0 @@
#pragma once
#include "includes.h"
// Provides variables;
// fs.work_dir: string
int luaopen_filesystem(lua_State* L);
// x = list_dir(path)
int lua_fs_list_dir(lua_State* L);
// x = list_all_dirs(path)
int lua_fs_list_alldirs(lua_State* L);
int lua_fs_foreach_dir(lua_State* L);
int lua_fs_forall_dir(lua_State* L);
int lua_fs_foreach_dir_next(lua_State* L);
int lua_fs_forall_dir_next(lua_State* L);
int lua_fs_foreach_dir_dtor(lua_State* L);
int lua_fs_forall_dir_dtor(lua_State* L);
// Returns true when a is newer than b. If b does not exist it returns true. If a does not exist it returns false
// boolean | table<boolean> function fs.is_newer(path_a: string | number, path_b: string | table[string])
int lua_fs_is_newer(lua_State* L);
// table<string> | nil function fs.filter_newer(path_a: string | number, path_b: string | table[string])
int lua_fs_filter_newer(lua_State* L);
// number | nil function fs.last_modified(path: string)
int lua_fs_last_modified(lua_State* L);
// boolean function fs.exists(path: string)
int lua_fs_exists(lua_State* L);
// boolean function fs.is_dir(path: string)
int lua_fs_is_dir(lua_State* L);
// boolean function fs.is_file(path: string)
int lua_fs_is_file(lua_State* L);
// boolean function create_dir(path: string)
int lua_fs_create_dir(lua_State* L);
// userdata<file_hdl> | nil function fs.open_file(path: string)
int lua_fs_open_file(lua_State* L);
// nil function fs.close_file(userdata<file_hdl>)
// file dtor
int lua_fs_close_file(lua_State* L);
// string | nil function fs.read_file(userdata<file_hdl>, num_bytes: number)
int lua_fs_read_file(lua_State* L);
// nil function fs.seek(userdata<file_hdl>, num_bytes: number, [opt] boolean: seek_backward=false)
int lua_fs_seek_file(lua_State* L);
// nil function fs.getpos(userdata<file_hdl>)
int lua_fs_getpos_file(lua_State* L);
// nil function fs.write(userdata<file_hdl>, string)
int lua_fs_write_file(lua_State* L);
// nil function fs.append(userdata<file_hdl>, string)
int lua_fs_append_file(lua_State* L);
// string function fs.relative(string base, string path)
int lua_fs_relative(lua_State* L);
// string function fs.absolute(string path)
int lua_fs_absolute(lua_State* L);
// string function fs.file_name(string path)
int lua_fs_file_name(lua_State* L);
// string function fs.file_extension(string path)
int lua_fs_file_extension(lua_State* L);
// string function fs.file_directory(string path)
int lua_fs_file_directory(lua_State* L);

View File

View File

@@ -1,48 +0,0 @@
// A lua module that provides multithreadding utilities to lua
#pragma once
#include "lua_stack.h"
// All functions are sandboxed
int luaopen_parallel_safe(lua_State* L);
// Some functions are not sandboxed
int luaopen_parallel_unsafe(lua_State* L);
int lua_parallel_create_mutex(lua_State* L);
int lua_parallel_destroy_mutex(lua_State* L);
int lua_parallel_lock_mutex(lua_State* L);
int lua_parallel_unlock_mutex(lua_State* L);
int lua_parallel_create_promise(lua_State* L);
int lua_parallel_destroy_promise(lua_State* L);
int lua_parallel_await_promise(lua_State* L);
int lua_parallel_fullfilled_promise(lua_State* L);
int lua_parallel_fullfilled_all_promises(lua_State* L);
struct lua_parallel_thread
{
// A list of all messages recieved from other threads
lua_stack stack;
// Coroutine lua state pointer
lua_State* L;
// underlying thread object
std::jthread thread;
};
// userdata<thread> function parallel.create_thread(function fn, ...)
int lua_parallel_create_thread(lua_State* L);
// N/A
int lua_parallel_destroy_thread(lua_State* L);
// nil function parallel.join(userdata<thread> thread)
int lua_parallel_join_thread(lua_State* L);
// nil function parallel.request_stop(userdata<thread> thread)
int lua_parallel_request_stop(lua_State* L);
// boolean function parallel.stop_requested()
int lua_parallel_stop_requested(lua_State* L);
// nil function parallel.sendmsg(userdata<thread> thread, ...)
int lua_parallel_sendmsg_thread(lua_State* L);
// table function parallel.recvmsg(userdata<thread>[] | nil filter)
int lua_parallel_recvmsg_thread(lua_State* L);
// number function parallel.recvsize(userdata<thread>[] | nil filter)
int lua_parallel_recvsize_thread(lua_State* L);

View File

@@ -1,35 +0,0 @@
#pragma once
#include "lua_parallel.h"
// Platform-related code
// Provided variables;
// platform.os: string
// platform.arch: string
// platform.call_convention: string
// platform.isa_exts: table<string, boolean>
int luaopen_platform(lua_State* L);
// Returns the exit code of the command as well as a string of its stdout
// int, string, string function platform.exec(cmd: string, args: array<string>)
int lua_platform_exec(lua_State* L);
// Executes a command asynchronosly, returning a promise to the functions return values
// promise<int>, promise<string>, promise<string> function platform.exec_async(cmd: string, args: array<string>)
int lua_platform_exec_async(lua_State* L);
// Executes a command asynchronosly, calling a callback on its completion with its error code and
// overall stdout and another callback on each token read from its stdout
// function platform.exec_async_cb(cmd: string,
// function on_complete(cmd: string, exit_code: int, overall_output: string),
// function on_stdout(cmd: string, overall_output: string, delta_output: string)
// function on_stderr(cmd: string, overall_output: string, delta_output: string)
// )
int lua_platform_exec_async_cb(lua_State* L);
// Executes the same command up to N times in parallel, iterating through the args provided until all arguments have been used
// array[promise<int>, promise<string>, promise<string>] function platform.exec_parallel_async(cmd: string, N: int, args: array<array<string>>)
int lua_platform_exec_parallel_async(lua_State* L);
// array<int>, array<string>, array<string> function platform.exec_parallel(cmd: string, N: int, args: array<array<string>>)
int lua_platform_exec_parallel(lua_State* L);

View File

@@ -1,43 +0,0 @@
#pragma once
#include "includes.h"
struct lua_stack
{
typedef uint8_t type_t;
enum TYPES : type_t
{
NIL,
NUMBER,
BOOLEAN,
INTEGER,
LSTRING,
STRING,
USERDATA,
THREAD,
CFUNCTION,
CCLOSURE,
TABLE
};
void push_nil (void);
void push_boolean (const bool val);
void push_integer (const lua_Integer val);
void push_number (const lua_Number val);
void push_lstring (const char* beg, const char* end);
void push_lstring (const std::string::iterator& beg, const std::string::iterator& end);
void push_lstring (const char* val, const size_t slen);
void push_string (const char* val);
void push_string (const std::string& val);
void push_lightuserdata (void* p);
void push_thread (lua_State* L);
void push_cfunction (const lua_CFunction f);
void push_cclosure (const lua_CFunction fn, const int num_upvalues);
void push_table (lua_State* L, const int tbl_idx);
void value_type(const size_t idx);
uint8_t* m_data;
uint8_t* m_types;
size_t m_size;
size_t m_capacity;
};

View File

@@ -1,8 +0,0 @@
#pragma once
#include <stdio.h>
#if defined (WIN32) || defined (_WIN32)
FILE* popen(const char* cmd, const char* mode) { return _popen(cmd, mode); }
int pclose(FILE* stream) { return _pclose(stream); };
#else
#endif

View File

@@ -1,47 +0,0 @@
#include <malloc.h>
#include <string>
class simple_string
{
public:
simple_string(const simple_string& src);
simple_string& operator =(const simple_string& src);
simple_string(simple_string&& src) noexcept;
simple_string& operator =(simple_string&& src) noexcept;
simple_string(const char* beg, const char* end);
simple_string(const char* str, const size_t slen);
simple_string(const char* str);
simple_string(const size_t initial_capacity, const char fill_value='\0');
simple_string(const std::string& str);
simple_string();
~simple_string(void);
simple_string& append(const char* other);
simple_string& append(const char* beg, const char* end);
simple_string& append(const char* other, const size_t slen);
simple_string& append(const std::string& other);
simple_string& append(const simple_string& other);
simple_string& append(const char x);
void resize(const size_t new_size);
void expand(const size_t extra_capacity);
void reserve(const size_t new_capacity);
const char* c_str(void) const noexcept;
char* data(void) noexcept;
size_t size(void) const noexcept;
char* begin(void) noexcept;
char* end(void) noexcept;
const char* cbegin(void) const noexcept;
const char* cend(void) const noexcept;
char& operator [](const size_t idx) noexcept;
const char& operator [](const size_t idx) const noexcept;
private:
char* m_data = nullptr;
size_t m_size = 0;
size_t m_capacity = 0;
};

130
makefile
View File

@@ -1,72 +1,70 @@
CXX := clang++ CC=clang
CC := clang CFLAGS = -Wall -Wextra
INCLUDES := include src thirdparty/luajit/src thirdparty/uniproc/include thirdparty/libgit2/include ifeq ($(OS),Windows_NT)
LIBRARY_DIRECTORIES := thirdparty/luajit/src thirdparty/libgit2 CFLAGS += -D WIN32
LINKER_INPUTS := lua51 luajit ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
CFLAGS += -D AMD64
else
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
CFLAGS += -D AMD64
endif
ifeq ($(PROCESSOR_ARCHITECTURE),x86)
CFLAGS += -D IA32
endif
endif
DEBUG_DEFINES := RELEASE_TARGET=bin/lbs.exe
DEFINES := DEBUG_TARGET=bin/lbs-dbg.exe
CXX_FLAGS := -std=c11 -std=c++20 -g -O0
CC_FLAGS := -std=c11 -g -O0
CXX_DEBUG_FLAGS :=
LNK_FLAGS := -g -O0
LNK_DEBUG_FLAGS :=
LUAJUT_BUILD_CMD := call msvcbuild.bat
.PHONY: all clean
all: build_deps link_lbs
# From https://stackoverflow.com/a/18258352/8617429
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
define \n
endef
ifeq ($(OS), Windows_NT)
PLATFORM_SEPERATOR := &
touch = type nul > $(1)
rm = rmdir /S /Q $(1) > nul || (exit 0)
RM = del
mkdir = mkdir $(subst /,\,$(1)) > nul 2>&1 || (exit 0)
OUTPUT_NAME := lbs.exe
else else
PLATFORM_SEPERATOR := && UNAME_S := $(shell uname -s)
touch = touch $(1) ifeq ($(UNAME_S),Linux)
rm = rm -rf $(1) CFLAGS += -D LINUX
RM = rm -f endif
mkdir = mkdir -p $(1) ifeq ($(UNAME_S),Darwin)
OUTPUT_NAME := lbs CFLAGS += -D OSX
endif
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
CFLAGS += -D AMD64
endif
ifneq ($(filter %86,$(UNAME_P)),)
CFLAGS += -D IA32
endif
ifneq ($(filter arm%,$(UNAME_P)),)
CFLAGS += -D ARM
endif
RELEASE_TARGET=bin/lbs
DEBUG_TARGET=bin/lbs-dbg
endif endif
# If the first argument is "modules"...
ifeq (modules,$(firstword $(MAKECMDGOALS)))
# use the rest as arguments for "run"
MODULE_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
# ...and turn them into do-nothing targets
$(eval $(MODULE_ARGS):;@:)
endif
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
SRC_FILES=$(call rwildcard, src, *.c)
OBJ_FILES=$(addprefix obj/, $(SRC_FILES:.c=.o))
modules:
cd modules && $(MAKE) $(MODULE_ARGS)
obj/src/%.o: src/%.c
$(CC) $(CFLAGS) -c $(@:obj/%.o=%.c) -o $@
release: $(RELEASE_TARGET)
$(RELEASE_TARGET): $(OBJ_FILES)
$(CC) $(LFLAGS) $^ -o $@
debug: $(DEBUG_TARGET)
$(DEBUG_TARGET): $(OBJ_FILES)
$(CC) $(LFLAGS) $^ -o $@
clean: clean:
$(call RM,$(call rwildcard, obj,*.o) build/$(OUTPUT_NAME)) echo TODO
SOURCE_FILES := $(call rwildcard,src,*.cpp,*.hpp) $(call rwildcard,thirdparty/uniproc/src,*.c)
OBJECT_FILES := $(foreach d,$(addsuffix .o,$(SOURCE_FILES)), obj/$(d))
obj/thirdparty/uniproc/src/%.c.o: thirdparty/uniproc/src/%.c
$(CC) -c $(CC_FLAGS) $(foreach inc,$(INCLUDES),-I$(inc)) $(foreach def,$(DEFINES),-D$(def)) $< -o $@
obj/src/%.cpp.o: src/%.cpp
$(CXX) -c $(CXX_FLAGS) $(foreach inc,$(INCLUDES),-I$(inc)) $(foreach def,$(DEFINES),-D$(def)) $< -o $@
link_lbs: $(OBJECT_FILES)
$(CXX) $(LNK_FLAGS) $(LNK_FLAGS) $(foreach lib,$(LIBRARY_DIRECTORIES),-L$(lib)) $(foreach lnk,$(LINKER_INPUTS),-l$(lnk)) $^ -o build/$(OUTPUT_NAME)
build_deps: .make/thirdparty_luajit .make/thirdparty_uniproc
.make/thirdparty_luajit:
git submodule update --init --recursive --remote
cd thirdparty/luajit/src $(PLATFORM_SEPERATOR) $(LUAJUT_BUILD_CMD)
$(call touch,".make/thirdparty_luajit")
.make/thirdparty_uniproc:
git submodule update --init --recursive --remote
$(call touch,".make/thirdparty_uniproc")
.make/thirdparty_libgit2:
git submodule update --init --recursive --remote
cd thirdparty/libgit2 && mkdir build
cd thirdparty/libgit2/build && cmake .. && cmake --build .
$(call touch,".make/thirdparty_libgit2")

View File

@@ -1,365 +0,0 @@
#pragma once
#include <utility>
#include <string.h>
template <typename T>
class dynarray
{
public:
dynarray(const dynarray& src);
dynarray& operator =(const dynarray& src);
dynarray(dynarray&& other) noexcept;
dynarray& operator =(dynarray&& other) noexcept;
dynarray(void);
dynarray(const std::initializer_list<T>& src);
dynarray(const size_t initial_capacity);
~dynarray(void);
T& operator [](const size_t idx) noexcept;
const T& operator [](const size_t idx) const noexcept;
T* begin(void) noexcept;
const T* begin(void) const noexcept;
const T* cbegin(void) const noexcept;
T* end(void) noexcept;
const T* end(void) const noexcept;
const T* cend(void) const noexcept;
T* c_str(void) const noexcept;
T* c_array(void) const noexcept;
T* data(void) const noexcept;
size_t size(void) const noexcept;
size_t capacity(void) const noexcept;
bool is_empty(void) const noexcept;
bool is_nullptr(void) const noexcept;
void push_back(const T& val);
void push_back(T&& val);
void push_back(const std::initializer_list<T>& vals);
void push_back(const dynarray<T>& vals);
void push_back(const T* vals, const size_t num_vals);
T&& pop_back(void) noexcept;
T& front(void) noexcept;
const T& front(void) const noexcept;
T& back(void) noexcept;
const T& back(void) const noexcept;
void resize(const size_t new_size);
void expand(const size_t extra_capacity);
void reserve(const size_t new_capacity);
void shrink_to_fit(void);
private:
T* m_data = nullptr;
size_t m_capacity = 0;
size_t m_size = 0;
};
template<typename T>
inline dynarray<T>::dynarray(const dynarray& src)
{
if (m_data == src.m_data) return;
m_size = src.m_size;
m_capacity = src.m_capacity;
m_data = (T*)malloc(src.m_size*sizeof(T));
if (src.m_data != nullptr)
for (size_t i = 0; i < m_size; ++i)
m_data[i] = src.m_data[i];
}
template<typename T>
inline dynarray<T>& dynarray<T>::operator=(const dynarray& src)
{
if (m_data == src.m_data) return;
if (m_data != nullptr) delete[] m_data;
m_data = nullptr;
m_size = src.m_size;
m_capacity = src.m_capacity;
m_data = (T*)malloc(src.m_size*sizeof(T));
if (src.m_data != nullptr)
for (size_t i = 0; i < m_size; ++i)
m_data[i] = src.m_data[i];
return *this;
}
template<typename T>
inline dynarray<T>::dynarray(dynarray&& other) noexcept
{
if (this == &other) return;
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_size = 0;
other.m_capacity = 0;
other.m_data = nullptr;
}
template<typename T>
inline dynarray<T>& dynarray<T>::operator=(dynarray&& other) noexcept
{
if (this == &other) return *this;
if (m_data != nullptr) delete[] m_data;
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_size = 0;
other.m_capacity = 0;
other.m_data = nullptr;
return *this;
}
template<typename T>
inline dynarray<T>::dynarray(void)
{
m_data = nullptr;
m_size = 0;
m_capacity = 0;
}
template<typename T>
inline dynarray<T>::dynarray(const std::initializer_list<T>& src)
{
m_data = (T*)malloc(src.size()*sizeof(T));
m_size = src.size();
m_capacity = src.size();
if (std::is_trivially_copyable<T>::value)
memcpy(m_data, src.begin(), sizeof(T) * m_size);
else
for (size_t i = 0; i < src.size(); ++i)
m_data[i++] = src.begin()[i];
}
template<typename T>
inline dynarray<T>::dynarray(const size_t initial_capacity)
{
m_data = (initial_capacity == 0) ? nullptr : (T*)malloc(initial_capacity*sizeof(T));
m_size = 0;
m_capacity = initial_capacity;
}
template<typename T>
inline dynarray<T>::~dynarray(void)
{
if (m_data != nullptr)
{
if (!std::is_trivially_destructible<T>::value)
{
for (size_t i = 0; i < m_size; ++i)
m_data[i].~T();
}
free(m_data);
m_data = nullptr;
}
}
template<typename T>
inline T& dynarray<T>::operator[](const size_t idx) noexcept
{ return m_data[idx]; }
template<typename T>
inline const T& dynarray<T>::operator[](const size_t idx) const noexcept
{ return m_data[idx]; }
template<typename T>
inline T* dynarray<T>::begin(void) noexcept
{ return m_data; }
template<typename T>
inline const T* dynarray<T>::begin(void) const noexcept
{ return m_data; }
template<typename T>
inline const T* dynarray<T>::cbegin(void) const noexcept
{ return m_data; }
template<typename T>
inline T* dynarray<T>::end(void) noexcept
{ return m_data == nullptr ? nullptr : (m_data + m_size); }
template<typename T>
inline const T* dynarray<T>::end(void) const noexcept
{ return m_data == nullptr ? nullptr : (m_data + m_size); }
template<typename T>
inline const T* dynarray<T>::cend(void) const noexcept
{ return m_data == nullptr ? nullptr : (m_data + m_size); }
template<typename T>
inline T* dynarray<T>::c_str(void) const noexcept
{ return m_data; }
template<typename T>
inline T* dynarray<T>::c_array(void) const noexcept
{ return m_data; }
template<typename T>
inline T* dynarray<T>::data(void) const noexcept
{ return m_data; }
template<typename T>
inline size_t dynarray<T>::size(void) const noexcept
{ return m_size; }
template<typename T>
inline size_t dynarray<T>::capacity(void) const noexcept
{ return m_capacity; }
template<typename T>
inline bool dynarray<T>::is_empty(void) const noexcept
{ return m_size == 0; }
template<typename T>
inline bool dynarray<T>::is_nullptr(void) const noexcept
{
return m_data == nullptr || m_capacity == 0;
}
template<typename T>
inline void dynarray<T>::push_back(const T& val)
{
// Double capacity if we can not fit an extra value
if (m_size + 1 > m_capacity) this->expand(m_capacity == 0 ? 32 : m_capacity);
m_data[m_size++] = val;
}
template<typename T>
inline void dynarray<T>::push_back(T&& val)
{
// Double capacity if we can not fit an extra value
if (m_size + 1 > m_capacity) this->expand(m_capacity == 0 ? 32 : m_capacity);
new (m_data + m_size) T(std::move(val));
m_size++;
}
template<typename T>
inline void dynarray<T>::push_back(const std::initializer_list<T>& vals)
{
const size_t prev_size = m_size;
this->resize(m_size + vals.size());
if (std::is_trivially_copyable<T>::value)
memcpy(m_data + prev_size, vals.begin(), sizeof(T) * vals.size());
else
for (size_t i = 0; i < vals.size(); ++i)
m_data[prev_size + i] = vals.begin()[i];
}
template<typename T>
inline void dynarray<T>::push_back(const dynarray<T>& vals)
{
const size_t prev_size = m_size;
this->resize(m_size + vals.m_size);
if (std::is_trivially_copyable<T>::value)
memcpy(m_data + prev_size, vals.m_data, sizeof(T) * vals.m_size);
else
for (size_t i = 0; i < vals.m_size; ++i)
m_data[prev_size + i] = vals.m_data[i];
}
template<typename T>
inline void dynarray<T>::push_back(const T* vals, const size_t num_vals)
{
const size_t prev_size = m_size;
this->resize(m_size + num_vals);
if (std::is_trivially_copyable<T>::value)
memcpy(m_data + prev_size, vals, sizeof(T) * num_vals);
else
for (size_t i = 0; i < num_vals; ++i)
m_data[prev_size + i] = vals[i];
}
template<typename T>
inline T&& dynarray<T>::pop_back(void) noexcept
{
m_size--;
return std::move(m_data[m_size]);
}
template<typename T>
inline T& dynarray<T>::front(void) noexcept
{ return *m_data; }
template<typename T>
inline const T& dynarray<T>::front(void) const noexcept
{ return *m_data; }
template<typename T>
inline T& dynarray<T>::back(void) noexcept
{ return m_data[m_size - 1]; }
template<typename T>
inline const T& dynarray<T>::back(void) const noexcept
{ return m_data[m_size-1]; }
template<typename T>
inline void dynarray<T>::resize(const size_t new_size)
{
if (new_size > m_capacity)
this->expand(new_size);
else if (new_size < m_size && std::is_trivially_destructible<T>::value)
for (size_t i = new_size; i < m_size; ++i)
m_data[i].~T();
m_size = new_size;
}
template<typename T>
inline void dynarray<T>::expand(const size_t extra_capacity)
{ this->reserve(m_capacity + extra_capacity); }
template<typename T>
inline void dynarray<T>::reserve(const size_t new_capacity)
{
if (new_capacity <= m_capacity) return;
T* new_data = (T*)malloc(new_capacity*sizeof(T));
if (new_data == nullptr) return;
if constexpr (std::is_trivially_copyable<T>::value)
memcpy(new_data, m_data, m_size * sizeof(T));
else if constexpr (std::is_move_assignable<T>::value)
{
for (size_t i = 0; i < m_size; ++i)
new (new_data + i) T(std::move(m_data[i]));
}
else if constexpr (std::is_copy_assignable<T>::value)
{
for (size_t i = 0; i < m_size; ++i)
new (new_data+i) T(m_data[i]);
}
if (!std::is_trivially_destructible<T>::value)
{
for (size_t i = 0; i < m_size; ++i)
m_data[i].~T();
}
free(m_data);
m_data = new_data;
m_capacity = new_capacity;
}
template<typename T>
inline void dynarray<T>::shrink_to_fit(void)
{
if (m_capacity == m_size) return;
if (m_size == 0)
{
delete[] m_data;
m_capacity = 0;
m_data = nullptr;
}
T* new_data = (T*)malloc(m_size*sizeof(T));
if (std::is_trivially_copyable<T>::value)
memcpy(new_data, m_data, m_size * sizeof(T));
else if (std::is_move_assignable<T>::value)
for (size_t i = 0; i < m_size; ++i)
new (new_data+i) T(std::move(m_data[i]));
else if (std::is_copy_assignable<T>::value)
for (size_t i = 0; i < m_size; ++i)
new (new_data+i) T(m_data[i]);
delete[] m_data;
m_data = new_data;
m_capacity = m_size;
}

View File

@@ -1,667 +0,0 @@
#include "lua_dependency_tree.h"
#include <string.h>
dependency_tree::node& dependency_tree::operator[](const ref_t idx) noexcept
{ return m_nodes[idx]; }
const dependency_tree::node& dependency_tree::operator[](const ref_t idx) const noexcept
{ return m_nodes[idx]; }
dynarray<dependency_tree::ref_t> dependency_tree::get_leaf_nodes(const bool ignore_fulfilled) const
{
dynarray<ref_t> result;
for (size_t i = 0; i < m_nodes.size(); ++i)
{
if (m_nodes[i].m_num_pending_dependencies == 0 &&
(!ignore_fulfilled || m_nodes[i].m_state != FULFILLED))
result.push_back(i);
}
return result;
}
dynarray<const char*> dependency_tree::get_leaf_node_names(const bool ignore_fulfilled) const
{
dynarray<ref_t> refs = this->get_leaf_nodes(ignore_fulfilled);
dynarray<const char*> names;
for (const ref_t ref : refs)
names.push_back(m_nodes[ref].m_name);
return names;
}
dynarray<dependency_tree::ref_t> dependency_tree::filter_nodes(const state_cmp_t state_cmp, const ref_t num_dependencies_gt, const bool invert_num_deps) const
{
dynarray<ref_t> res;
for (ref_t i = 0; i < m_nodes.size(); ++i)
{
const node& n = m_nodes[i];
// Ignore nodes that do not match the num_dependencies requirement
if (!((n.m_num_pending_dependencies > num_dependencies_gt) ^ invert_num_deps)) continue;
bool inc = false;
inc |= (state_cmp & CMP_FULFILED) > 0 && n.m_state == FULFILLED;
inc |= (state_cmp & CMP_FAILED ) > 0 && n.m_state == FAILED;
inc |= (state_cmp & CMP_WORKING ) > 0 && n.m_state == WORKING;
inc |= (state_cmp & CMP_WAITING ) > 0 && n.m_state == WAITING;
if (!inc) continue;
res.push_back(i);
}
return res;
}
// Sets the state of a node
// If the node transitions from a fulfilled state it will recursively update its parents
void dependency_tree::set_node_state(const ref_t ref_node, const state_t new_state)
{
node& n = m_nodes[ref_node];
if (n.m_state == new_state) return;
if (new_state == WORKING && n.m_num_pending_dependencies != 0) return;
if (new_state == FULFILLED && n.m_num_pending_dependencies != 0) return;
else if (n.m_state == FULFILLED && new_state != FULFILLED)
{
for (const ref_t ref_p : n.m_dependents)
{
node& p = m_nodes[ref_p];
p.m_num_pending_dependencies++;
if (p.m_state == FULFILLED)
this->set_node_state(ref_p, WAITING);
}
}
// If changing from a non-resolved state to a resolved state,
// Decrement the node and update its parents
else if (new_state == FULFILLED)
{
for (const ref_t ref_p : n.m_dependents)
{
node& p = m_nodes[ref_p];
p.m_num_pending_dependencies--;
if (p.m_num_pending_dependencies == 0)
this->set_node_state(ref_p, WORKING);
}
}
n.m_state = new_state;
}
void dependency_tree::set_node_state(const char* node, const state_t new_state)
{ this->set_node_state(m_node_name_mapping[node], new_state); }
dependency_tree::ref_t dependency_tree::add_node(const char* node_name, const dynarray<ref_t>& dependents)
{
ref_t ret = -1;
// The node already exists, so just add non-duplicating dependents to it
if (m_node_name_mapping.contains(node_name))
{
ret = m_node_name_mapping[node_name];
for (const ref_t dep : dependents)
{
if (m_nodes[ret].m_dependents.insert(dep).second)
m_nodes[dep].m_num_pending_dependencies += m_nodes[ret].m_state != FULFILLED;
}
}
// Otherwise create the node and add the dependencies to it
else
{
ret = (ref_t)m_nodes.size();
node n;
n.m_dependents = std::set<ref_t>(dependents.cbegin(), dependents.cend());
size_t sl = strlen(node_name);
char* local_name = new char[sl + 1];
local_name[sl] = '\0';
memcpy(local_name, node_name, sl);
n.m_name = local_name;
n.m_num_pending_dependencies = 0;
n.m_state = WAITING;
n.m_userdata = 0;
m_nodes.push_back(std::move(n));
m_node_name_mapping[local_name] = ret;
for (const ref_t dep : dependents)
m_nodes[dep].m_num_pending_dependencies += m_nodes.back().m_state != FULFILLED;
}
return ret;
}
dependency_tree::ref_t dependency_tree::add_node(const char* node_name, const dynarray<const char*>& dependents)
{
dynarray<ref_t> ref_dependents;
for (const char* dep : dependents)
ref_dependents.push_back(m_node_name_mapping[dep]);
return this->add_node(node_name, ref_dependents);
}
dependency_tree::ref_t dependency_tree::add_node(const char* node_name, const std::initializer_list<ref_t>& dependents)
{ return this->add_node(node_name, dynarray<ref_t>(dependents)); }
dependency_tree::ref_t dependency_tree::add_node(const char* node_name, const std::initializer_list<const char*>& dependents)
{
dynarray<ref_t> ref_dependents;
for (const char* dep : dependents)
ref_dependents.push_back(m_node_name_mapping[dep]);
return this->add_node(node_name, ref_dependents);
}
dependency_tree::ref_t dependency_tree::add_node(const char* node_name)
{
if (!m_node_name_mapping.contains(node_name))
{
ref_t ret = (ref_t)m_nodes.size();
node n;
n.m_dependents = std::set<ref_t>();
size_t sl = strlen(node_name);
char* local_name = new char[sl + 1];
local_name[sl] = '\0';
memcpy(local_name, node_name, sl);
n.m_name = local_name;
n.m_num_pending_dependencies = 0;
n.m_state = WAITING;
n.m_userdata = 0;
m_nodes.push_back(std::move(n));
m_node_name_mapping[local_name] = ret;
return ret;
}
return m_node_name_mapping[node_name];
}
void dependency_tree::add_node_dependency(const ref_t node, const ref_t dependency)
{
if (m_nodes[dependency].m_dependents.insert(node).second)
m_nodes[node].m_num_pending_dependencies++;
}
void dependency_tree::add_node_dependency(const ref_t node, const char* dependency)
{ this->add_node_dependency(node, m_node_name_mapping[dependency]); }
void dependency_tree::add_node_dependency(const char* node, const ref_t dependency)
{ this->add_node_dependency(m_node_name_mapping[node], dependency); }
void dependency_tree::add_node_dependency(const char* node, const char* dependency)
{ this->add_node_dependency(m_node_name_mapping[node], m_node_name_mapping[dependency]); }
void dependency_tree::add_node_dependencies(const ref_t node, const std::initializer_list<ref_t>& dependencies)
{
for (const ref_t& dep : dependencies)
this->add_node_dependency(node, dep);
}
void dependency_tree::add_node_dependencies(const ref_t node, const dynarray<ref_t>& dependencies)
{
for (const ref_t& dep : dependencies)
this->add_node_dependency(node, dep);
}
void dependency_tree::add_node_dependencies(const ref_t node, const std::initializer_list<const char*>& dependencies)
{
for (const char* dep : dependencies)
this->add_node_dependency(node, dep);
}
void dependency_tree::add_node_dependencies(const ref_t node, const dynarray<const char*>& dependencies)
{
for (const char* dep : dependencies)
this->add_node_dependency(node, dep);
}
dependency_tree::~dependency_tree() {}
dependency_tree::node::node(node&& other) noexcept
{
this->m_dependents = std::move(other.m_dependents);
this->m_name = other.m_name;
this->m_num_pending_dependencies = other.m_num_pending_dependencies;
this->m_state = other.m_state;
this->m_userdata = other.m_userdata;
other.m_name = nullptr;
other.m_num_pending_dependencies = -1;
other.m_state = -1;
other.m_userdata = -1;
}
dependency_tree::node& dependency_tree::node::operator=(node&& other) noexcept
{
if (this == &other) return *this;
if (this->m_name != nullptr) delete[] m_name;
this->m_dependents = std::move(other.m_dependents);
this->m_name = other.m_name;
this->m_num_pending_dependencies = other.m_num_pending_dependencies;
this->m_state = other.m_state;
this->m_userdata = other.m_userdata;
other.m_name = nullptr;
other.m_num_pending_dependencies = -1;
other.m_state = -1;
other.m_userdata = -1;
return *this;
}
dependency_tree::node::node()
{
this->m_name = nullptr;
this->m_num_pending_dependencies = -1;
this->m_state = -1;
this->m_userdata = -1;
}
dependency_tree::node::~node()
{
if (m_name == nullptr) return;
delete[] m_name;
}
int luaopen_dependency_tree(lua_State* L)
{
luaL_newmetatable(L, "lbs.dependency_tree");
lua_pushcfunction(L, lua_dependency_destroy);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, lua_dependency_add_node);
lua_setfield(L, -2, "add_node");
lua_pushcfunction(L, lua_dependency_add_dependency);
lua_setfield(L, -2, "add_dependency");
lua_pushcfunction(L, lua_dependency_add_dependencies);
lua_setfield(L, -2, "add_dependencies");
lua_pushcfunction(L, lua_dependency_remove_dependency);
lua_setfield(L, -2, "remove_dependency");
lua_pushcfunction(L, lua_dependency_remove_dependencies);
lua_setfield(L, -2, "remove_dependencies");
lua_pushcfunction(L, lua_dependency_get_leafs);
lua_setfield(L, -2, "get_leafs");
lua_pushcfunction(L, lua_dependency_match_nodes);
lua_setfield(L, -2, "match");
lua_pushcfunction(L, lua_dependency_set_node_state);
lua_setfield(L, -2, "set_state");
lua_pushcfunction(L, lua_dependency_get_node_state);
lua_setfield(L, -2, "get_state");
lua_pushcfunction(L, lua_dependency_get_node_userval);
lua_setfield(L, -2, "get_userval");
lua_pushcfunction(L, lua_dependency_set_node_userval);
lua_setfield(L, -2, "set_userval");
lua_pushcfunction(L, lua_dependency_get_node_name);
lua_setfield(L, -2, "get_name");
lua_pushcfunction(L, lua_dependency_get_node);
lua_setfield(L, -2, "get_node");
//lua_pushcfunction(L, lua_dependency_index);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_newtable(L);
lua_pushcfunction(L, lua_dependency_create);
lua_setfield(L, -2, "create");
lua_pushcfunction(L, lua_dependency_add_node);
lua_setfield(L, -2, "add_node");
lua_pushcfunction(L, lua_dependency_add_dependency);
lua_setfield(L, -2, "add_dependency");
lua_pushcfunction(L, lua_dependency_add_dependencies);
lua_setfield(L, -2, "add_dependencies");
lua_pushcfunction(L, lua_dependency_remove_dependency);
lua_setfield(L, -2, "remove_dependency");
lua_pushcfunction(L, lua_dependency_remove_dependencies);
lua_setfield(L, -2, "remove_dependencies");
lua_pushcfunction(L, lua_dependency_get_leafs);
lua_setfield(L, -2, "get_leafs");
lua_pushcfunction(L, lua_dependency_match_nodes);
lua_setfield(L, -2, "match");
lua_pushcfunction(L, lua_dependency_set_node_state);
lua_setfield(L, -2, "set_state");
lua_pushcfunction(L, lua_dependency_get_node_state);
lua_setfield(L, -2, "get_state");
lua_pushcfunction(L, lua_dependency_get_node_userval);
lua_setfield(L, -2, "get_userval");
lua_pushcfunction(L, lua_dependency_set_node_userval);
lua_setfield(L, -2, "set_userval");
lua_pushcfunction(L, lua_dependency_get_node_name);
lua_setfield(L, -2, "get_name");
lua_pushcfunction(L, lua_dependency_get_node);
lua_setfield(L, -2, "get_node");
lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "dependency_tree");
return 1;
}
dependency_tree* get_lua_dependency_tree(lua_State* L, int idx)
{ return (dependency_tree*)luaL_checkudata(L, idx, "lbs.dependency_tree"); }
dependency_tree::ref_t get_lua_dependency_ref(dependency_tree* tree, lua_State* L, int idx)
{
if (lua_isnumber(L, idx))
{
if (lua_tonumber(L, idx) >= tree->m_nodes.size()) return -1;
return lua_tonumber(L, idx);
}
else if (lua_isstring(L, idx))
{
const char* name = lua_tostring(L, idx);
if (tree->m_node_name_mapping.contains(name)) return tree->m_node_name_mapping[name];
return -1;
}
return -1;
}
int lua_dependency_create(lua_State* L)
{
dependency_tree* tree = (dependency_tree*)lua_newuserdata(L, sizeof(dependency_tree));
new (tree) dependency_tree();
luaL_getmetatable(L, "lbs.dependency_tree");
lua_setmetatable(L, -2);
return 1;
}
int lua_dependency_destroy(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, -1);
if (tree == nullptr) return 0;
tree->~dependency_tree();
return 0;
}
int lua_dependency_add_node(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING, 2, "Expected first argument to be a string of the dependency nodes name");
int t = lua_type(L, 3);
luaL_argcheck(L, lua_type(L, 3) == LUA_TTABLE || lua_type(L, 3) <= LUA_TNIL, 3, "Expected second argument to be a table of the names or references of the dependents or nil");
const char* node_name = lua_tostring(L, 2);
if (lua_type(L, 3) == LUA_TTABLE)
{
static dynarray<dependency_tree::ref_t> dependent_nodes;
dependent_nodes.resize(0);
dependent_nodes.reserve(lua_objlen(L, 3));
for (size_t i = 1; i < lua_objlen(L, 3); ++i)
{
lua_rawgeti(L, 3, i);
if (lua_type(L, -1) != LUA_TSTRING && lua_type(L, -1) != LUA_TNUMBER)
return luaL_error(L, "Expected value %d in second argument to be a string or reference, not %s", i, lua_typename(L, lua_type(L, -1)));
dependency_tree::ref_t n = get_lua_dependency_ref(tree, L, -1);
if (n == -1)
return luaL_error(L, "Expected value %d in second argument to be a valid node name or reference", i);
dependent_nodes.push_back(n);
lua_pop(L, 1);
}
lua_pushinteger(L, tree->add_node(node_name, dependent_nodes));
}
else
lua_pushinteger(L, tree->add_node(node_name));
return 1;
}
int lua_dependency_add_dependency(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING || lua_type(L, 2) == LUA_TNUMBER, 2, "Expected first argument to be a string of the dependency nodes name or a reference to it");
luaL_argcheck(L, lua_type(L, 3) == LUA_TSTRING || lua_type(L, 3) == LUA_TNUMBER, 3, "Expected second argument to be a string of the dependent node's name or a reference to it");
dependency_tree::ref_t dependency = get_lua_dependency_ref(tree, L, 2);
if (dependency == -1)
return luaL_error(L, "Expected first argument to be a valid name or reference to a node");
dependency_tree::ref_t dependent = get_lua_dependency_ref(tree, L, 3);
if (dependent == -1)
return luaL_error(L, "Expected second argument to be a valid name or reference to a node");
tree->add_node_dependency(dependency, dependent);
return 0;
}
int lua_dependency_remove_dependency(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t dependency = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, dependency != -1, 2, "Expected first argument to be a valid node name or reference");
dependency_tree::ref_t dependent = get_lua_dependency_ref(tree, L, 3);
luaL_argcheck(L, dependent != -1, 3, "Expected second argument to be a valid node name or reference");
tree->m_nodes[dependent].m_dependents.erase(dependency);
return 0;
}
int lua_dependency_add_dependencies(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t dependency = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, dependency != -1, 1, "Expected first argument to be a valid name or reference to a node");
static dynarray<dependency_tree::ref_t> dependents;
dependents.resize(0); // Clear prior values
dependents.reserve(lua_gettop(L)); // This is approx. equal to top-2
for (size_t idx = 3; idx < lua_gettop(L); ++idx)
{
if (lua_type(L, idx) == LUA_TTABLE)
{
int tbl_len = lua_objlen(L, idx);
for (size_t i = 1; i < tbl_len; ++i)
{
lua_rawgeti(L, idx, i);
dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, idx);
if (ref == -1)
return luaL_error(L, "Expected argument %d[%d] to be a valid name or reference of a node", idx - 2, i);
dependents.push_back(ref);
lua_pop(L, 1);
}
}
else
{
dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, idx);
if (ref == -1)
return luaL_error(L, "Expected argument %d to be a valid name or reference of a node", idx-2);
dependents.push_back(ref);
}
}
tree->add_node_dependencies(dependency, dependents);
return 0;
}
int lua_dependency_remove_dependencies(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t dependency = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, dependency != -1, 2, "Expected first argument to be a valid node name or reference");
for (size_t idx = 3; idx < lua_gettop(L); ++idx)
{
if (lua_type(L, idx) == LUA_TTABLE)
{
int tbl_len = lua_objlen(L, 3);
for (int i = 1; i < tbl_len; ++i)
{
lua_rawgeti(L, idx, i);
dependency_tree::ref_t dependent = get_lua_dependency_ref(tree, L, -1);
if (dependent != -1)
return luaL_error(L, "Expected argument %d[%d] to be a valid name or reference to a node", idx-2,i);
tree->m_nodes[dependent].m_dependents.erase(dependency);
lua_pop(L, 1);
}
}
else
{
const dependency_tree::ref_t dependent = get_lua_dependency_ref(tree, L, idx);
if (dependent != -1)
return luaL_error(L, "Expected argument %d to be a valid name or reference to a node", idx - 2);
tree->m_nodes[dependent].m_dependents.erase(dependency);
}
}
return 0;
}
int lua_dependency_get_leafs(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dynarray<dependency_tree::ref_t> leafs;
if (lua_isboolean(L, 2) && (lua_toboolean(L, 2) > 0))
leafs = tree->get_leaf_nodes();
else
leafs = tree->get_leaf_nodes(false);
lua_newtable(L);
for (const dependency_tree::ref_t leaf : leafs)
{
lua_pushinteger(L, leaf);
lua_rawseti(L, -2, lua_objlen(L, -2) + 1);
}
return 1;
}
int lua_dependency_match_nodes(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
luaL_argcheck(L, lua_type(L, 2) == LUA_TNUMBER || lua_type(L, 2) <= LUA_TNIL, 2, "Expected first argument to be a number or nil. This is what states need to be true\n\tFulfiled = 1\n\tFailed = 2\n\tWorking = 4\n\tWaiting = 8");
luaL_argcheck(L, lua_type(L, 3) == LUA_TNUMBER || lua_type(L, 3) <= LUA_TNIL, 3, "Expected second argument to be a number or nil. This is the least number of pending dependencies a node needs to have to pass");
luaL_argcheck(L, lua_type(L, 4) == LUA_TBOOLEAN || lua_type(L, 4) <= LUA_TNIL, 4, "Expected third argument to be a boolean or nil. This controls whether the number of pending dependencies is greater or less-equal");
dynarray<dependency_tree::ref_t> matches = tree->filter_nodes(
lua_tointeger(L, 2) & 0b1111,
lua_tointeger(L, 3),
lua_toboolean(L, 3) > 0
);
lua_newtable(L);
for (const dependency_tree::ref_t match : matches)
{
lua_pushinteger(L, match);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
}
return 1;
}
int lua_dependency_set_node_state(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t node = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, node != -1, 2, "Expected first argument to be a valid node name or reference");
luaL_argcheck(L, lua_type(L, 3) == LUA_TNUMBER || lua_type(L, 3) == LUA_TSTRING, 3, "Expected second argument to be a number or string of the state\n\t0 = FULFILLED\n\t1 = FAILED\n\t2 = WAITING\n\t3 = WORKING");
uint8_t new_state;
if (lua_type(L, 3) == LUA_TNUMBER)
new_state = lua_tointeger(L, 3) & 0b11;
else
{
const char* state_name = lua_tostring(L, 3);
if (strcmp(state_name, "FULFILLED") == 0 || strcmp(state_name, "fulfilled") == 0)
new_state = dependency_tree::FULFILLED;
else if (strcmp(state_name, "FAILED") == 0 || strcmp(state_name, "failed") == 0)
new_state = dependency_tree::FAILED;
else if (strcmp(state_name, "WAITING") == 0 || strcmp(state_name, "waiting") == 0)
new_state = dependency_tree::WAITING;
else if (strcmp(state_name, "WORKING") == 0 || strcmp(state_name, "working") == 0)
new_state = dependency_tree::WORKING;
else return luaL_error(L, "Expected seconda argument to be fulfilled, failed, waiting or working not %s", state_name);
}
tree->set_node_state(node, new_state);
return 0;
}
int lua_dependency_get_node_userval(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, ref != -1, 2, "Expected first argument to be a valid node name or reference");
lua_pushinteger(L, tree->m_nodes[ref].m_userdata);
return 1;
}
int lua_dependency_set_node_userval(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, ref != -1, 2, "Expected first argument to be a valid node name or reference");
luaL_argcheck(L, lua_type(L, 3) == LUA_TNUMBER, 3, "Expected second argument to be a 30-bit number");
tree->m_nodes[ref].m_userdata = lua_tointeger(L, 3) & 0x3FFFFFFF;
return 0;
}
int lua_dependency_get_node_state(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, ref != -1, 2, "Expected first argument to be a valid node name or reference");
const dependency_tree::node& node = tree->m_nodes[ref];
if (node.m_state == dependency_tree::FULFILLED) lua_pushstring(L, "fulfilled");
else if (node.m_state == dependency_tree::FAILED) lua_pushstring(L, "failed");
else if (node.m_state == dependency_tree::WAITING) lua_pushstring(L, "waiting");
else if (node.m_state == dependency_tree::WORKING) lua_pushstring(L, "working");
return 1;
}
int lua_dependency_get_node_name(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, ref != -1, 2, "Expected first argument to be a valid node name or reference");
lua_pushstring(L, tree->m_nodes[ref].m_name);
return 1;
}
int lua_dependency_get_node(lua_State* L)
{
dependency_tree* tree = get_lua_dependency_tree(L, 1);
luaL_argcheck(L, tree != nullptr, 1, "Expected implicit argument to be userdata<dependency_tree>");
const dependency_tree::ref_t ref = get_lua_dependency_ref(tree, L, 2);
luaL_argcheck(L, ref != -1, 2, "Expected first argument to be a valid node name or reference");
const dependency_tree::node& node = tree->m_nodes[ref];
lua_newtable(L);
lua_pushstring(L, "name");
lua_pushstring(L, node.m_name);
lua_settable(L, -3);
lua_pushstring(L, "pending_dependencies");
lua_pushinteger(L, node.m_num_pending_dependencies);
lua_settable(L, -3);
lua_pushstring(L, "state");
if (node.m_state == dependency_tree::FULFILLED) lua_pushstring(L, "fulfilled");
else if (node.m_state == dependency_tree::FAILED) lua_pushstring(L, "failed");
else if (node.m_state == dependency_tree::WAITING) lua_pushstring(L, "waiting");
else if (node.m_state == dependency_tree::WORKING) lua_pushstring(L, "working");
lua_settable(L, -3);
lua_pushstring(L, "userdata");
lua_pushinteger(L, node.m_userdata);
lua_settable(L, -3);
lua_pushstring(L, "dependents");
lua_newtable(L);
for (const dependency_tree::ref_t dependent : node.m_dependents)
{
lua_pushinteger(L, dependent);
lua_rawseti(L, -2, lua_objlen(L, -2));
}
lua_settable(L, -3);
return 1;
}

View File

@@ -1,453 +0,0 @@
#include "lua_filesystem.h"
#include <filesystem>
#include <chrono>
int luaopen_filesystem(lua_State* L)
{
luaL_newmetatable(L, "fs.file_handle");
// Push iterator state metatables
luaL_newmetatable(L, "fs.stdlibcpp.filesystem.directory_iterator");
lua_pushcfunction(L, lua_fs_foreach_dir_dtor);
lua_setfield(L, -2, "__gc");
luaL_newmetatable(L, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
lua_pushcfunction(L, lua_fs_forall_dir_dtor);
lua_setfield(L, -2, "__gc");
// Push functions
lua_newtable(L);
lua_pushcfunction(L, lua_fs_is_newer);
lua_setfield(L, -2, "is_newer");
lua_pushcfunction(L, lua_fs_filter_newer);
lua_setfield(L, -2, "filter_newer");
lua_pushcfunction(L, lua_fs_absolute);
lua_setfield(L, -2, "absolute");
lua_pushcfunction(L, lua_fs_create_dir);
lua_setfield(L, -2, "create_dir");
lua_pushcfunction(L, lua_fs_exists);
lua_setfield(L, -2, "exists");
lua_pushcfunction(L, lua_fs_file_directory);
lua_setfield(L, -2, "dir");
lua_pushcfunction(L, lua_fs_file_extension);
lua_setfield(L, -2, "extension");
lua_pushcfunction(L, lua_fs_file_name);
lua_setfield(L, -2, "file_name");
lua_pushcfunction(L, lua_fs_forall_dir);
lua_setfield(L, -2, "forall");
lua_pushcfunction(L, lua_fs_foreach_dir);
lua_setfield(L, -2, "foreach");
lua_pushcfunction(L, lua_fs_is_dir);
lua_setfield(L, -2, "is_dir");
lua_pushcfunction(L, lua_fs_is_file);
lua_setfield(L, -2, "is_file");
lua_pushcfunction(L, lua_fs_last_modified);
lua_setfield(L, -2, "last_modified");
lua_pushcfunction(L, lua_fs_list_dir);
lua_setfield(L, -2, "list_dir");
lua_pushcfunction(L, lua_fs_list_alldirs);
lua_setfield(L, -2, "list_alldirs");
lua_pushcfunction(L, lua_fs_relative);
lua_setfield(L, -2, "relative_to");
lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "fs");
return 1;
}
int lua_fs_list_dir(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path to make the second argument relative to");
const char* fpath = lua_tostring(L, 1);
lua_newtable(L);
uint32_t i = 1;
for (const std::filesystem::path& p : std::filesystem::directory_iterator(fpath))
{
const std::string x = p.string();
lua_pushlstring(L, x.c_str(), x.size());
lua_rawseti(L, -2, i);
}
return 1;
}
int lua_fs_list_alldirs(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path to make the second argument relative to");
const char* fpath = lua_tostring(L, 1);
lua_newtable(L);
uint32_t i = 1;
for (const std::filesystem::path& p : std::filesystem::recursive_directory_iterator(fpath))
{
const std::string x = p.string();
lua_pushlstring(L, x.c_str(), x.size());
lua_rawseti(L, -2, i);
i++;
}
return 1;
}
int lua_fs_foreach_dir(lua_State* L)
{
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path");
const char* path = lua_tostring(L, 1);
void* ud = lua_newuserdata(L, sizeof(std::filesystem::directory_iterator));
new (ud) std::filesystem::directory_iterator(path);
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured creating fs.foreach iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
luaL_getmetatable(L, "fs.stdlibcpp.filesystem.directory_iterator");
lua_setmetatable(L, -2);
lua_pushcclosure(L, lua_fs_foreach_dir_next, 1);
return 1;
}
int lua_fs_forall_dir(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path");
const char* path = lua_tostring(L, 1);
try {
void* ud = lua_newuserdata(L, sizeof(std::filesystem::recursive_directory_iterator));
new (ud) std::filesystem::recursive_directory_iterator(path);
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured creating fs.forall iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
luaL_getmetatable(L, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
lua_setmetatable(L, -2);
lua_pushcclosure(L, lua_fs_forall_dir_next, 1);
return 1;
}
int lua_fs_foreach_dir_next(lua_State* L)
{
try {
int idx = lua_upvalueindex(1);
luaL_argcheck(L, lua_type(L, idx) == LUA_TUSERDATA, 1, "Expected first argument to fs.foreach_dir_next to be userdata<std::filesystem::directory_iterator>");
void* ud = luaL_checkudata(L, idx, "fs.stdlibcpp.filesystem.directory_iterator");
luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::directory_iterator");
std::filesystem::directory_iterator& x = *(std::filesystem::directory_iterator*)ud;
if (x._At_end())
return 0;
std::string p = (*x).path().string();
lua_pushlstring(L, p.c_str(), p.size());
x++;
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured iterating fs.foreach\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 1;
}
int lua_fs_forall_dir_next(lua_State* L)
{
try {
int idx = lua_upvalueindex(1);
luaL_argcheck(L, lua_type(L, idx) == LUA_TUSERDATA, 1, "Expected first argument to fs.forall_dir_next to be userdata<std::filesystem::recursive_directory_iterator>");
void* ud = luaL_checkudata(L, idx, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::recursive_directory_iterator");
std::filesystem::recursive_directory_iterator& x = *(std::filesystem::recursive_directory_iterator*)ud;
if (x == std::filesystem::recursive_directory_iterator{})
return 0;
std::string p = (*x).path().string();
lua_pushlstring(L, p.c_str(), p.size());
x++;
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured iterating fs.forall\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 1;
}
int lua_fs_foreach_dir_dtor(lua_State* L)
{
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TUSERDATA, 1, "Expected first argument to fs.foreach_dir_next to be userdata<std::filesystem::directory_iterator>");
void* ud = luaL_checkudata(L, 1, "fs.stdlibcpp.filesystem.directory_iterator");
luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::directory_iterator");
std::filesystem::directory_iterator& x = *(std::filesystem::directory_iterator*)ud;
x.~directory_iterator();
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured destroying fs.foreach iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 0;
}
int lua_fs_forall_dir_dtor(lua_State* L)
{
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TUSERDATA, 1, "Expected first argument to fs.forall_dir_next to be userdata<std::filesystem::recursive_directory_iterator>");
void* ud = luaL_checkudata(L, 1, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::recursive_directory_iterator");
std::filesystem::recursive_directory_iterator& x = *(std::filesystem::recursive_directory_iterator*)ud;
x.~recursive_directory_iterator();
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured destroying fs.forall iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 0;
}
int lua_fs_relative(lua_State* L)
{
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path to make the second argument relative to");
luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING, 2, "Expected second argument to be a string representing a full file path");
const std::string f = std::filesystem::relative(std::filesystem::path(lua_tostring(L, 1)), std::filesystem::path(lua_tostring(L, 2))).string();
lua_pushlstring(L, f.c_str(), f.size());
return 1;
} catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
}
int lua_fs_absolute(lua_State* L)
{
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
const std::string f = std::filesystem::absolute(std::filesystem::path(lua_tostring(L, 1))).string();
lua_pushlstring(L, f.c_str(), f.size());
return 1;
} catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
}
int lua_fs_file_name(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
const std::string f = std::filesystem::path(lua_tostring(L, 1)).filename().string();
lua_pushlstring(L, f.c_str(), f.size());
return 1;
} catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
}
int lua_fs_file_extension(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
const std::string f = std::filesystem::path(lua_tostring(L, 1)).extension().string();
lua_pushlstring(L, f.c_str(), f.size());
} catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 1;
}
int lua_fs_file_directory(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
const std::string f = std::filesystem::path(lua_tostring(L, 1)).remove_filename().string();
lua_pushlstring(L, f.c_str(), f.size());
return 1;
} catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 0;
}
int lua_fs_is_newer(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
const char* test_file = lua_tostring(L, 1);
if (!std::filesystem::exists(test_file))
{
lua_pushboolean(L, false);
return 1;
}
if (lua_type(L, 2) == LUA_TTABLE)
{
size_t tbl_len = lua_objlen(L, 2);
lua_newtable(L);
for (size_t i = 1; i <= tbl_len; ++i)
{
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TSTRING)
return luaL_error(L, "Expected second argument at index %llu to be a string, not %s\n",
i, lua_typename(L, lua_type(L, -1)));
const char* target = lua_tostring(L, 2);
lua_pushboolean(L,
std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target));
lua_rawseti(L, -2, i);
lua_pop(L, 1);
}
return 1;
}
else if (lua_type(L, 2) == LUA_TSTRING)
{
const char* target = lua_tostring(L, 2);
if (!std::filesystem::exists(target))
{
lua_pushboolean(L, true);
return 1;
}
lua_pushboolean(L,
std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target));
return 1;
}
else return luaL_error(L, "Expected second argument to be a string or table of strings");
}
int lua_fs_filter_newer(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
const char* test_file = lua_tostring(L, 1);
if (!std::filesystem::exists(test_file))
{
lua_pushnil(L);
return 1;
}
if (lua_type(L, 2) == LUA_TTABLE)
{
size_t tbl_len = lua_objlen(L, 2);
lua_newtable(L);
for (size_t i = 1; i <= tbl_len; ++i)
{
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TSTRING)
return luaL_error(L, "Expected second argument at index %llu to be a string, not %s\n",
i, lua_typename(L, lua_type(L, -1)));
const char* target = lua_tostring(L, 2);
if (std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target))
{
lua_pushvalue(L, -1);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
}
lua_pop(L, 1);
}
return 1;
}
else if (lua_type(L, 2) == LUA_TSTRING)
{
const char* target = lua_tostring(L, 2);
if (!std::filesystem::exists(target))
{
lua_pushvalue(L, 2);
return 1;
}
if (std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target))
lua_pushvalue(L, 2);
else lua_pushnil(L);
return 1;
}
else return luaL_error(L, "Expected second argument to be a string or table of strings");
}
int lua_fs_last_modified(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
const uint64_t file_last_write = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::clock_cast<std::chrono::system_clock>(
std::filesystem::last_write_time(lua_tostring(L, 1))
).time_since_epoch()
).count();
lua_pushinteger(L, file_last_write);
return 1;
} catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 0;
}
int lua_fs_exists(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
lua_pushboolean(L, std::filesystem::exists(lua_tostring(L, 1)));
return 1;
}
catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 0;
}
int lua_fs_is_dir(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
lua_pushboolean(L, std::filesystem::is_directory(lua_tostring(L, 1)));
return 1;
}
catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 0;
}
int lua_fs_is_file(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
lua_pushboolean(L, std::filesystem::is_regular_file(lua_tostring(L, 1)));
return 1;
}
catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 0;
}
int lua_fs_create_dir(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try {
lua_pushboolean(L, std::filesystem::create_directories(lua_tostring(L, 1)));
return 1;
}
catch (const std::exception& e)
{
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
return 0;
}

View File

@@ -1,215 +0,0 @@
#include "lua_platform.h"
#include "platform_agnostic.h"
#include "dynarray.hpp"
#include "mutex"
#include <chrono>
#include "simple_string.h"
#include "uniproc.h"
int luaopen_platform(lua_State* L)
{
lua_newtable(L);
lua_pushcfunction(L, lua_platform_exec);
lua_setfield(L, -2, "exec");
lua_pushcfunction(L, lua_platform_exec_parallel);
lua_setfield(L, -2, "exec_parallel");
lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "platform");
return 1;
}
int lua_platform_exec(lua_State* L)
{
luaL_argcheck(L, lua_isstring(L, 1), 1, "Expected first argument to be a string");
luaL_argcheck(L, lua_istable(L, 2), 2, "Expected second argument to be a table of strings");
size_t nargs = lua_objlen(L, 2);
const char** argv = (const char**)malloc(nargs * sizeof(const char*));
for (size_t i = 0; i < nargs; ++i)
{
lua_rawgeti(L, 2, i+1);
if (lua_type(L, -1) != LUA_TSTRING)
return luaL_error(L, "Expected all values in argument table to be a string. Index %d was a %s", i+1, lua_typename(L, lua_type(L, -1)));
argv[i] = lua_tostring(L, -1);
}
int retcode = 0;
std::string std_out;
std::string std_err;
char buf[64];
uniproc_process p = uniproc_create_process(lua_tostring(L, 1), nargs, argv);
if (uniproc_is_process_null(&p))
{
lua_pushnil(L);
lua_pushstring(L, "uniproc: failed to create process");
return 2;
}
uniproc_await_processes(&p, &retcode, 1);
while (fgets(buf, sizeof(buf), p.out) != NULL)
std_out.append(buf);
while (fgets(buf, sizeof(buf), p.err) != NULL)
std_err.append(buf);
uniproc_close_process(&p);
free(argv);
lua_pushinteger(L, retcode);
lua_pushlstring(L, std_out.c_str(), std_out.size());
lua_pushlstring(L, std_err.c_str(), std_err.size());
return 3;
}
int lua_platform_exec_parallel(lua_State* L)
{
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a command");
luaL_argcheck(L, lua_type(L, 2) == LUA_TNUMBER, 2, "Expected second argument to be an integer representing the maximum number of parallel jobs to start");
luaL_argcheck(L, lua_type(L, 3) == LUA_TTABLE, 3, "Expected third argument to be a table of table of strings representing the arguments");
const char* cmd = (lua_tostring(L, 1));
const size_t max_parallel = lua_tointeger(L, 2);
const size_t num_cmds = lua_objlen(L, 3);
uniproc_process* workers = nullptr;
int* retcodes = nullptr;
size_t* num_args = nullptr;
const char*** args = nullptr;
std::vector<std::string> std_outs;
std::vector<std::string> std_errs;
try {
num_args = (size_t*)malloc(num_cmds * sizeof(size_t));
memset(num_args, 0, sizeof(size_t) * num_cmds);
args = (const char***)malloc(num_cmds * sizeof(const char**));
// Generate the command stack from lua arguments
for (size_t i = 0; i < num_cmds; ++i)
{
lua_rawgeti(L, 3, i + 1);
if (lua_istable(L, -1))
{
const size_t tbl_num_args = lua_objlen(L, -1);
num_args[i] = tbl_num_args;
args[i] = (const char**)malloc(tbl_num_args * sizeof(const char*));
for (size_t j = 0; j < tbl_num_args; ++j)
{
lua_rawgeti(L, -1, j + 1);
if (!lua_isstring(L, -1))
return luaL_error(L, "Expected program argument to be a string. args[%d][%d] was a %s, not a string!\n", i, j, lua_typename(L, lua_type(L, -1)));
args[i][j] = lua_tostring(L, -1);
lua_pop(L, 1);
}
}
else if (lua_type(L, -1) == LUA_TSTRING)
{
num_args[i] = 1;
args[i] = (const char**)malloc(1 * sizeof(const char*));
args[i][0] = lua_tostring(L, -1);
}
else
return luaL_error(L, "Expected a table of program arguments. args[%d] was a %s, not a table!\n", i, lua_typename(L, lua_type(L, -1)));
lua_pop(L, 1);
}
workers = (uniproc_process*)malloc(max_parallel * sizeof(uniproc_process));
uniproc_nullify_processes(workers, max_parallel);
retcodes = (int*)malloc(num_cmds * sizeof(int));
memset(retcodes, 0, num_cmds);
std_outs.resize(num_cmds, "");
std_errs.resize(num_cmds, "");
}
catch (const std::exception& e) {
fprintf(stderr, "%s:%d: Exception occured in preparation for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
return luaL_error(L, "%s:%d: Exception occured in preparation for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
}
size_t cmd_idx = 0;
while (cmd_idx < num_cmds || !uniproc_are_processes_finished(workers, max_parallel))
{
try {
for (uniproc_process* p = workers; p != (workers + max_parallel); ++p)
{
// Check and handle a worker if it has completed
if (!uniproc_is_process_null(p) && uniproc_are_processes_finished(p, 1))
{
char buf[64];
// Read the std. streams into the output vectors
// TODO: Errors out here
fprintf(stderr, "p->out = %p\n", p->out);
while (fgets(buf, sizeof(buf), p->out) != NULL)
std_outs[p->userdata].append(buf);
fprintf(stderr, "C: %llu\n", cmd_idx);
fprintf(stderr, "p->err = %p\n", p->err);
while (fgets(buf, sizeof(buf), p->err) != NULL)
std_errs[p->userdata].append(buf);
fprintf(stderr, "D: %llu\n", cmd_idx);
// Add the process return code to retcode array
uniproc_await_processes(p, retcodes + p->userdata, 1);
// Close the process and zero it
uniproc_close_process(p);
}
// If a worker is null and there is more jobs to complete
// Create a new process
// Is a while loop to handle the possibility that process creation fails
while (uniproc_is_process_null(p) && cmd_idx < num_cmds)
{
// Create process
*p = uniproc_create_process(cmd, num_args[cmd_idx], args[cmd_idx]);
p->userdata = cmd_idx;
cmd_idx++;
// If we failed to create the process, set the output for it to an error value
if (uniproc_is_process_null(p))
{
std_outs[p->userdata] = "uniproc: failed to create process";
std_errs[p->userdata] = "uniproc: failed to create process";
retcodes[p->userdata] = -1;
}
}
}
// Await a process completing before trying to iterate again
// Prevents spinning the CPU
uniproc_await_any_processes(workers, max_parallel);
}
catch (const std::exception& e) {
fprintf(stderr, "%s:%d: Exception occured in parallel execution for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
return luaL_error(L, "%s:%d: Exception occured in parallel execution for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
}
}
for (size_t i = 0; i < num_cmds; ++i)
free(args[i]);
free(num_args);
free(args);
// Don't need to close processes as that is handled in the command loop
free(workers);
// Join thread results together and push them to lua stack
lua_newtable(L); // rc
lua_newtable(L); // stdout
lua_newtable(L); // stderr
for (size_t i = 0; i < num_cmds; ++i)
{
lua_pushinteger(L, retcodes[i]);
lua_rawseti(L, -4, i + 1);
lua_pushlstring(L, std_outs[i].c_str(), std_outs[i].size());
lua_rawseti(L, -3, i + 1);
lua_pushlstring(L, std_errs[i].c_str(), std_errs[i].size());
lua_rawseti(L, -2, i + 1);
}
return 3;
}
catch (const std::exception& e)
{
fprintf(stderr, "%s:%d: Exception occured in cleanup and result handling for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
return luaL_error(L, "%s:%d: Exception occured in cleanup and result handling for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
}
}

7
src/main.c Normal file
View File

@@ -0,0 +1,7 @@
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
return 0;
}

View File

@@ -1,116 +0,0 @@
#include "lua_dependency_tree.h"
#include "lua_platform.h"
#include "lua_filesystem.h"
#include <iostream>
#include <filesystem>
// From lua
static int msghandler(lua_State* L) {
if (!lua_isstring(L, 1)) /* 'message' not a string? */
return 1; /* keep it intact */
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
return 1;
}
int main(int argc, char** argv)
{
const char* exec_file = "lbs.lua";
int argstart = 1;
if (argc > 2 && (
strcmp(argv[1], "--buildscript") == 0 ||
strcmp(argv[1], "-s") == 0))
{
exec_file = argv[2];
argstart += 2;
}
if (!std::filesystem::exists(exec_file))
{
fprintf(stderr, "Failed to find buildscript %s to execute\n", exec_file);
return EXIT_FAILURE;
}
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luaopen_dependency_tree(L);
luaopen_platform(L);
luaopen_filesystem(L);
luaL_loadfile(L, exec_file);
for (int i = (argstart+1); i < argc; ++i)
lua_pushstring(L, argv[i]);
int err = LUA_OK;
int msg_base = lua_gettop(L) - argc - argstart - 1;
lua_pushcfunction(L, msghandler);
lua_insert(L, msg_base);
if ( (err = lua_pcall(L, argc - argstart - 1, LUA_MULTRET, msg_base)) != LUA_OK)
{
if (err == LUA_ERRRUN)
fprintf(stderr, "A runtime error when running the script %s has occured\n%s\n", exec_file, lua_tostring(L, -1));
else if (err == LUA_ERRMEM)
fprintf(stderr, "A memory allocation error when running the script %s has occured\n%s\n", exec_file, lua_tostring(L, -1));
else if (err == LUA_ERRERR)
fprintf(stderr, "A error handling function has errored when running the script %s\n%s\n", exec_file, lua_tostring(L, -1));
else
fprintf(stderr, "An unknown error has occured when running the script %s\n%s\n", exec_file, lua_tostring(L, -1));
lua_close(L);
return EXIT_FAILURE;
}
lua_remove(L, msg_base);
const char* lbs_cmd = argv[argstart];
if (argstart >= argc)
lbs_cmd = "main";
lua_getglobal(L, lbs_cmd);
if (lua_isnil(L, -1))
{
fprintf(stderr, "Failed to find command %s in buildscript %s to execute\n", lbs_cmd, exec_file);
lua_close(L);
return EXIT_FAILURE;
}
for (int i = (argstart + 1); i < argc; ++i)
lua_pushstring(L, argv[i]);
msg_base = lua_gettop(L) - argc - argstart - 1;
lua_pushcfunction(L, msghandler);
lua_insert(L, msg_base);
if ( (err = lua_pcall(L, argc - argstart - 1, LUA_MULTRET, msg_base)) != LUA_OK)
{
if (err == LUA_ERRRUN)
fprintf(stderr, "A runtime error when running the command %s in the script %s has occured\n%s\n", lbs_cmd, exec_file, lua_tostring(L, -1));
else if (err == LUA_ERRMEM)
fprintf(stderr, "A memory allocation error when running the command %s in the script %s has occured\n%s\n", lbs_cmd, exec_file, lua_tostring(L, -1));
else if (err == LUA_ERRERR)
fprintf(stderr, "A error handling function has errored when running the command %s in the script %s\n%s\n", lbs_cmd, exec_file, lua_tostring(L, -1));
else
fprintf(stderr, "An unknown error has occured when running the command %s in the script %s\n%s\n", lbs_cmd, exec_file, lua_tostring(L, -1));
lua_close(L);
return EXIT_FAILURE;
}
lua_remove(L, msg_base);
// If a return value was provided, return it
if (lua_isnumber(L, -1))
{
int rc = lua_tointeger(L, -1);
lua_close(L);
return rc;
}
// Otherwise return success
lua_close(L);
return EXIT_SUCCESS;
}

View File

@@ -1,216 +0,0 @@
#include "simple_string.h"
simple_string::simple_string(const simple_string& src)
{
m_data = (char*)malloc(src.m_size * sizeof(char));
m_size = src.m_size;
m_capacity = src.m_capacity;
memcpy(m_data, src.m_data, src.m_size * sizeof(char));
}
simple_string& simple_string::operator=(const simple_string& src)
{
if (this == &src) return *this;
this->~simple_string();
m_data = (char*)malloc(src.m_size * sizeof(char));
m_size = src.m_size;
m_capacity = src.m_capacity;
memcpy(m_data, src.m_data, src.m_size * sizeof(char));
return *this;
}
simple_string::simple_string(simple_string&& src) noexcept
{
m_data = src.m_data;
m_size = src.m_size;
m_capacity = src.m_capacity;
src.m_data = nullptr;
src.m_size = 0;
src.m_capacity = 0;
}
simple_string& simple_string::operator=(simple_string&& src) noexcept
{
if (this == &src) return *this;
this->~simple_string();
m_data = src.m_data;
m_size = src.m_size;
m_capacity = src.m_capacity;
src.m_data = nullptr;
src.m_size = 0;
src.m_capacity = 0;
return *this;
}
simple_string::simple_string(const char* beg, const char* end)
{
const ptrdiff_t slen = (ptrdiff_t)(end - beg);
m_size = slen;
m_capacity = slen;
m_data = (char*)malloc(slen*sizeof(char)+1);
memcpy(m_data, beg, slen * sizeof(char));
m_data[slen - 1] = '\0';
}
simple_string::simple_string(const char* str, const size_t slen)
{
m_size = slen;
m_capacity = slen;
m_data = (char*)malloc(slen * sizeof(char)+1);
memcpy(m_data, str, slen * sizeof(char));
m_data[slen - 1] = '\0';
}
simple_string::simple_string(const char* str)
{
size_t slen = strlen(str);
m_size = slen;
m_capacity = slen;
m_data = (char*)malloc(slen * sizeof(char)+1);
memcpy(m_data, str, slen * sizeof(char));
m_data[slen - 1] = '\0';
}
simple_string::simple_string(const size_t initial_capacity, const char fill_value)
{
m_size = 0;
m_capacity = initial_capacity;
m_data = (char*)malloc(initial_capacity * sizeof(char));
memset(m_data, fill_value, m_capacity);
}
simple_string::simple_string(const std::string& str)
{
m_capacity = str.size();
m_size = str.size();
m_data = (char*)malloc(m_size*sizeof(char)+1);
memcpy(m_data, str.data(), m_size*sizeof(char));
m_data[m_size - 1] = '\0';
}
simple_string::simple_string()
{
m_data = nullptr;
m_capacity = 0;
m_size = 0;
}
simple_string::~simple_string(void)
{
if (m_data != nullptr)
free(m_data);
}
simple_string& simple_string::append(const char* other)
{
const size_t slen = strlen(other);
if (slen == 0) return *this;
if (m_size + slen + 1 > m_capacity)
this->resize(m_size + slen);
memcpy(m_data + m_size, other, slen * sizeof(char));
m_size += slen;
m_data[m_size] = '\0';
return *this;
}
simple_string& simple_string::append(const char* beg, const char* end)
{
const ptrdiff_t slen = (ptrdiff_t)(end - beg);
if (slen == 0) return *this;
if (m_size + slen + 1 > m_capacity)
this->resize(m_size + slen);
memcpy(m_data + m_size, beg, slen * sizeof(char));
m_size += slen;
m_data[m_size] = '\0';
return *this;
}
simple_string& simple_string::append(const char* other, const size_t slen)
{
if (slen == 0) return *this;
if (m_size + slen + 1> m_capacity)
this->resize(m_size + slen);
memcpy(m_data + m_size, other, slen * sizeof(char));
m_size += slen;
m_data[m_size] = '\0';
return *this;
}
simple_string& simple_string::append(const std::string& other)
{
if (other.size() == 0) return *this;
if (m_size + other.size() > m_capacity)
this->resize(m_size + other.size());
memcpy(m_data + m_size, other.data(), other.size() * sizeof(char));
m_size += other.size();
return *this;
}
simple_string& simple_string::append(const simple_string& other)
{
if (other.m_size == 0) return *this;
if (m_size + other.m_size > m_capacity)
this->resize(m_size + other.m_size);
memcpy(m_data + m_size, other.m_data, other.m_size * sizeof(char));
m_size += other.m_size;
return *this;
}
simple_string& simple_string::append(const char x)
{
if (m_size + 1 > m_capacity)
this->expand(std::max(m_capacity, 1ULL));
m_data[m_size] = x;
++m_size;
return *this;
}
void simple_string::resize(const size_t new_size)
{
if (new_size >= m_capacity)
this->expand(std::max(new_size, m_capacity));
m_size = new_size;
}
void simple_string::expand(const size_t extra_capacity)
{ this->reserve(m_capacity + extra_capacity); }
void simple_string::reserve(const size_t new_capacity)
{
if (new_capacity <= m_capacity) return;
char* new_data = (char*)malloc(new_capacity*sizeof(char));
if (m_capacity != 0 && m_data != nullptr)
{
memcpy(new_data, m_data, m_size * sizeof(char));
free(m_data);
}
m_data = new_data;
m_capacity = new_capacity;
}
const char* simple_string::c_str(void) const noexcept
{ return m_data; }
char* simple_string::data(void) noexcept
{ return m_data; }
size_t simple_string::size(void) const noexcept
{ return m_size; }
char* simple_string::begin(void) noexcept
{ return m_data; }
char* simple_string::end(void) noexcept
{ return m_data + m_size; }
const char* simple_string::cbegin(void) const noexcept
{ return m_data; }
const char* simple_string::cend(void) const noexcept
{ return m_data; }
char& simple_string::operator[](const size_t idx) noexcept
{ return m_data[idx]; }
const char& simple_string::operator[](const size_t idx) const noexcept
{ return m_data[idx]; }