diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | gen_cmake.c | 223 | ||||
-rw-r--r-- | gen_inl.c | 134 | ||||
-rw-r--r-- | include/kit.inl.h | 1118 | ||||
-rw-r--r-- | source/kit/CMakeLists.txt | 10 | ||||
-rw-r--r-- | source/kit/array_ref.h | 4 | ||||
-rw-r--r-- | source/kit/file.h | 4 | ||||
-rw-r--r-- | source/kit/string_ref.h | 4 | ||||
-rw-r--r-- | source/kit_test/CMakeLists.txt | 6 | ||||
-rw-r--r-- | source/test/unittests/CMakeLists.txt | 11 |
11 files changed, 1502 insertions, 16 deletions
@@ -1,2 +1,4 @@ /*build*/ *.swp +gen_inl +gen_cmake @@ -17,4 +17,6 @@ Condition variables, mutual exclusions and threads implementation was forked from [Mesa][mesa_link] source code. +Folder `include` contains header-only version of this library. + [mesa_link]: https://gitlab.freedesktop.org/mesa/mesa diff --git a/gen_cmake.c b/gen_cmake.c new file mode 100644 index 0000000..c3bc110 --- /dev/null +++ b/gen_cmake.c @@ -0,0 +1,223 @@ +#if 0 +gcc -fsanitize=address,leak gen_cmake.c -o gen_cmake && ./gen_cmake +exit +#endif + +#include "include/kit.inl.h" + +#include <stdio.h> + +kit_allocator_t ALLOC; + +typedef struct { + kit_status_t status; + int have_cmakelists; +} gen_result_t; + +/* FIXME + * Barbarian strings!!! + */ +char const *sz_(str_t const s) { + static char buf[1000]; + int n = s.size; + if (n > 999) + n = 999; + memcpy(buf, s.values, s.size); + buf[s.size] = '\0'; + return buf; +} + +int make_sz(string_t *const s) { + ptrdiff_t const n = s->size; + DA_RESIZE(*s, n + 1); + if (s->size != n + 1) { + printf("Error: Bad alloc.\n"); + return 0; + } + s->values[n] = '\0'; + return 1; +} + +int is_header(str_t const file) { + return file.size > 2 && file.values[file.size - 2] == '.' && + file.values[file.size - 1] == 'h'; +} + +int is_source(str_t const file) { + return file.size > 2 && file.values[file.size - 2] == '.' && + file.values[file.size - 1] == 'c'; +} + +gen_result_t gen_cmakelists_for(str_t const target, + str_t const folder) { + gen_result_t result = { KIT_OK, 0 }; + + path_list_t const list = file_enum_folder(folder, ALLOC); + + if (list.status != KIT_OK) { + printf("Error: file_enum_folder failed with code %d.\n", + list.status); + result.status = list.status; + return result; + } + + int have_headers = 0; + int have_sources = 0; + int have_folders = 0; + + FILE *out = NULL; + + string_t cmakelists_path = path_join(folder, SZ("CMakeLists.txt"), + ALLOC); + + if (!make_sz(&cmakelists_path)) { + result.status = KIT_ERROR_BAD_ALLOC; + result.have_cmakelists = 0; + DA_DESTROY(cmakelists_path); + path_list_destroy(list); + return result; + } + +#define OPEN_CMAKELISTS \ + do { \ + if (out == NULL) { \ + out = fopen(cmakelists_path.values, "wt"); \ + if (out == NULL) { \ + result.status = 101; \ + result.have_cmakelists = 0; \ + DA_DESTROY(cmakelists_path); \ + path_list_destroy(list); \ + return result; \ + } \ + } \ + } while (0) + +#define CHECK_TYPE(type_) \ + string_t const full_path_ = path_join(folder, WRAP_STR(item), \ + ALLOC); \ + int check_ = path_type(WRAP_STR(full_path_)) == (type_); \ + DA_DESTROY(full_path_); \ + if (!check_) \ + continue + + for (ptrdiff_t i = 0; i < list.files.size; i++) { + str_t const item = WRAP_STR(list.files.values[i]); + + CHECK_TYPE(PATH_FILE); + + if (!is_header(item)) + continue; + + OPEN_CMAKELISTS; + + if (!have_headers) + fprintf(out, "target_sources(\n %*s\n PUBLIC", + (int) target.size, sz_(target)); + + fprintf( + out, + "\n $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/%*s>", + (int) item.size, sz_(item)); + + have_headers = 1; + } + + ptrdiff_t const LINE_LIMIT = 80; + ptrdiff_t line_length = LINE_LIMIT; + + for (ptrdiff_t i = 0; i < list.files.size; i++) { + str_t const item = WRAP_STR(list.files.values[i]); + + CHECK_TYPE(PATH_FILE); + + if (!is_source(item)) + continue; + + OPEN_CMAKELISTS; + + if (!have_headers && !have_sources) + fprintf(out, "target_sources(\n %*s\n PRIVATE", + (int) target.size, sz_(target)); + else if (!have_sources) + fprintf(out, "\n PRIVATE"); + + if (line_length + item.size > LINE_LIMIT) { + fprintf(out, "\n "); + line_length = 5; + } + + fprintf(out, " %*s", (int) item.size, sz_(item)); + line_length += 1 + item.size; + + have_sources = 1; + } + + if (have_headers || have_sources) + fprintf(out, ")\n"); + + for (ptrdiff_t i = 0; i < list.files.size; i++) { + str_t const item = WRAP_STR(list.files.values[i]); + + CHECK_TYPE(PATH_FOLDER); + + string_t const subfolder = path_join(folder, item, ALLOC); + gen_result_t const r = gen_cmakelists_for(target, + WRAP_STR(subfolder)); + DA_DESTROY(subfolder); + if (r.status != KIT_OK) { + result.status = r.status; + break; + } + + if (!r.have_cmakelists) + continue; + + OPEN_CMAKELISTS; + + if (have_headers || have_sources) + fprintf(out, "\n"); + + fprintf(out, "add_subdirectory(%*s)\n", (int) item.size, + sz_(item)); + + have_folders = 1; + } + + if (out != NULL) + fclose(out); + + DA_DESTROY(cmakelists_path); + path_list_destroy(list); + + if (result.status == KIT_OK) + result.have_cmakelists = have_headers || have_sources || + have_folders; + + if (!result.have_cmakelists && out != NULL) + file_remove(WRAP_STR(cmakelists_path)); + + return result; +} + +int main(int argc, char **argv) { + ALLOC = kit_alloc_default(); + + str_t const targets[] = { SZ("kit"), SZ("kit_test"), + SZ("kit_test_suite") }; + + string_t folders[] = { path_norm(SZ("./source/kit"), ALLOC), + path_norm(SZ("./source/kit_test"), ALLOC), + path_norm(SZ("./source/test/unittests"), + ALLOC) }; + + int ok = 1; + + for (ptrdiff_t i = 0; i < sizeof folders / sizeof *folders; i++) + ok = ok && gen_cmakelists_for(targets[i], WRAP_STR(folders[i])) + .status == KIT_OK; + + for (ptrdiff_t i = 0; i < sizeof folders / sizeof *folders; i++) + DA_DESTROY(folders[i]); + + return ok ? 0 : 1; +} diff --git a/gen_inl.c b/gen_inl.c new file mode 100644 index 0000000..dab36dc --- /dev/null +++ b/gen_inl.c @@ -0,0 +1,134 @@ +#if 0 +gcc gen_inl.c -o gen_inl && ./gen_inl +exit +#endif + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char const *const SOURCES[] = { + "./source/kit/status.h", "./source/kit/allocator.h", + "./source/kit/array_ref.h", "./source/kit/dynamic_array.h", + "./source/kit/string_ref.h", "./source/kit/file.h", + "./source/kit/allocator.c", "./source/kit/array_ref.c", + "./source/kit/dynamic_array.c", "./source/kit/file.c" +}; + +char const *repeat(int n, char c) { + static char buf[200]; + for (int i = 0; i < n && i < 199; i++) buf[i] = c; + buf[n] = '\0'; + return buf; +} + +int skip_whitespaces(char const *const line, int i) { + while (line[i] == ' ') i++; + return i; +} + +int skip(char const *const line, int i, char const *const s) { + int j = 0; + while (s[j] != '\0') { + if (line[i] != s[j]) + return -1; + i++; + j++; + } + return i; +} + +int is_local_include(char const *const line) { + if (line[0] != '#') + return 0; + int i = skip_whitespaces(line, 1); + i = skip(line, i, "include"); + if (i == -1) + return 0; + i = skip_whitespaces(line, i); + if (line[i] != '"') + return 0; + return 1; +} + +int is_empty_line(char const *const line) { + int i = skip_whitespaces(line, 0); + return line[i] == '\0' || line[i] == '\n'; +} + +int write_file(FILE *out, char const *const source) { + assert(out != NULL); + assert(source != NULL); + + FILE *in = fopen(source, "rt"); + + if (in == NULL) { + fprintf(stderr, "File not found: %s\n", source); + return 1; + } + + fprintf(out, "/*%s*\n", repeat(70, '*')); + fprintf(out, " *%s*\n", repeat(70, ' ')); + fprintf(out, " *%s*\n", repeat(70, ' ')); + fprintf(out, " * FILE: %s%s*\n", source, + repeat(58 - strlen(source), ' ')); + fprintf(out, " *%s*\n", repeat(70, ' ')); + fprintf(out, " *%s*\n", repeat(70, ' ')); + fprintf(out, " *%s*/\n", repeat(70, '*')); + + char line[200]; + char buf[400]; + int prev_empty = 0; + + while (fgets(line, 199, in) != NULL) { + int empty = is_empty_line(line) || is_local_include(line); + + if (empty) { + if (!prev_empty) + fprintf(out, "\n"); + } else { + int j = 0; + for (int i = 0; line[i] != '\0'; i++) { + if (line[i] == '\t') { + buf[j++] = ' '; + buf[j++] = ' '; + } else if (line[i] != '\n' && line[i] != '\r') + buf[j++] = line[i]; + } + buf[j] = '\0'; + fprintf(out, "%s\n", buf); + } + + prev_empty = empty; + } + + fclose(in); + fprintf(out, "\n"); + return 0; +} + +int main(int argc, char **argv) { + char const *const out_file = "./include/kit.inl.h"; + FILE *out = fopen(out_file, "wt"); + if (out == NULL) { + fprintf(stderr, "Can't write: %s\n", out_file); + return 1; + } + + fprintf(out, "#ifndef KIT_INL_H\n"); + fprintf(out, "#define KIT_INL_H\n"); + fprintf(out, "\n"); + + for (int i = 0; i < sizeof SOURCES / sizeof *SOURCES; i++) + if (write_file(out, SOURCES[i]) != 0) { + fclose(out); + return 1; + } + + fprintf(out, "\n"); + fprintf(out, "#endif\n"); + + fclose(out); + return 0; +} diff --git a/include/kit.inl.h b/include/kit.inl.h new file mode 100644 index 0000000..cfa123e --- /dev/null +++ b/include/kit.inl.h @@ -0,0 +1,1118 @@ +#ifndef KIT_INL_H +#define KIT_INL_H + +/************************************************************************ + * * + * * + * FILE: ./source/kit/status.h * + * * + * * + ************************************************************************/ +#ifndef KIT_STATUS_H +#define KIT_STATUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + KIT_OK, + KIT_ERROR_BAD_ALLOC, + KIT_ERROR_MKDIR_FAILED, + KIT_ERROR_RMDIR_FAILED, + KIT_ERROR_UNLINK_FAILED, + KIT_ERROR_FILE_ALREADY_EXISTS, + KIT_ERROR_FILE_DO_NOT_EXIST, + KIT_ERROR_PATH_TOO_LONG +}; + +typedef int kit_status_t; + +#ifdef __cplusplus +} +#endif + +#endif + +/************************************************************************ + * * + * * + * FILE: ./source/kit/allocator.h * + * * + * * + ************************************************************************/ +#ifndef KIT_ALLOCATOR_H +#define KIT_ALLOCATOR_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum { KIT_ALLOCATE, KIT_DEALLOCATE, KIT_REALLOCATE }; + +typedef void *(*kit_allocate_fn)(int request, void *state, + ptrdiff_t size, + ptrdiff_t previous_size, + void *pointer); + +typedef struct { + void *state; + kit_allocate_fn allocate; +} kit_allocator_t; + +/* Application should implement this function if custom allocator + * dispatch is enabled. + * + * See KIT_ENABLE_CUSTOM_ALLOC_DISPATCH + */ +void *kit_alloc_dispatch(kit_allocator_t alloc, int request, + ptrdiff_t size, ptrdiff_t previous_size, + void *pointer); + +kit_allocator_t kit_alloc_default(void); + +#ifdef __cplusplus +} +#endif + +#endif + +/************************************************************************ + * * + * * + * FILE: ./source/kit/array_ref.h * + * * + * * + ************************************************************************/ +#ifndef KIT_ARRAY_REF_H +#define KIT_ARRAY_REF_H + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*kit_ar_compare_fn)(void const *left, void const *right); + +int kit_ar_equal_bytes(ptrdiff_t left_element_size, + ptrdiff_t left_size, void const *left_data, + ptrdiff_t right_element_size, + ptrdiff_t right_size, void const *right_data); + +int kit_ar_compare(ptrdiff_t left_element_size, ptrdiff_t left_size, + void const *left_data, + ptrdiff_t right_element_size, ptrdiff_t right_size, + void const *right_data, kit_ar_compare_fn compare); + +#define KIT_AR_MUT(type_) \ + struct { \ + ptrdiff_t size; \ + type_ *values; \ + } + +#define KIT_AR(type_) \ + struct { \ + ptrdiff_t size; \ + type_ const *values; \ + } + +#define KIT_AR_MUT_WRAP(name_, element_type_, array_) \ + struct { \ + ptrdiff_t size; \ + element_type_ *values; \ + } name_ = { .size = (sizeof(array_) / sizeof((array_)[0])), \ + .values = (array_) } + +#define KIT_AR_WRAP(name_, element_type_, array_) \ + struct { \ + ptrdiff_t size; \ + element_type_ const *values; \ + } name_ = { .size = (sizeof(array_) / sizeof((array_)[0])), \ + .values = (array_) } + +#define KIT_AR_EQUAL(left_, right_) \ + kit_ar_equal_bytes(sizeof((left_).values[0]), (left_).size, \ + (left_).values, sizeof((right_).values[0]), \ + (right_).size, (right_).values) + +#define KIT_AR_COMPARE(left_, right_, compare_) \ + kit_ar_compare(sizeof((left_).values[0]), (left_).size, \ + (left_).values, sizeof((right_).values[0]), \ + (right_).size, (right_).values, \ + (kit_ar_compare_fn) (compare_)) + +#ifndef KIT_DISABLE_SHORT_NAMES +# define ar_compare_fn kit_ar_compare_fn +# define ar_equal_bytes kit_ar_equal_bytes +# define ar_compare kit_ar_compare + +# define AR_MUT KIT_AR_MUT +# define AR KIT_AR +# define AR_MUT_WRAP KIT_AR_MUT_WRAP +# define AR_WRAP KIT_AR_WRAP +# define AR_EQUAL KIT_AR_EQUAL +# define AR_COMPARE KIT_AR_COMPARE +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/************************************************************************ + * * + * * + * FILE: ./source/kit/dynamic_array.h * + * * + * * + ************************************************************************/ +#ifndef KIT_DYNAMIC_ARRAY_H +#define KIT_DYNAMIC_ARRAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + ptrdiff_t capacity; + ptrdiff_t size; + void *values; + kit_allocator_t alloc; +} kit_da_void_t; + +void kit_da_init(kit_da_void_t *array, ptrdiff_t element_size, + ptrdiff_t size, kit_allocator_t alloc); + +void kit_da_resize(kit_da_void_t *array, ptrdiff_t element_size, + ptrdiff_t size); + +/* Dynamic array type definition. + */ +#define KIT_DA(element_type_) \ + struct { \ + ptrdiff_t capacity; \ + ptrdiff_t size; \ + element_type_ *values; \ + kit_allocator_t alloc; \ + } + +/* Initialize dynamic array. + */ +#define KIT_DA_INIT(array_, size_, alloc_) \ + kit_da_init((kit_da_void_t *) &(array_), \ + sizeof((array_).values[0]), (size_), (alloc_)) + +/* Declare and initialize dynamic array. + */ +#define KIT_DA_CREATE(name_, element_type_, size_) \ + KIT_DA(element_type_) name_; \ + KIT_DA_INIT(name_, (size_), kit_alloc_default()) + +/* Destroy dynamic array. + */ +#define KIT_DA_DESTROY(array_) \ + do { \ + if ((array_).values != NULL) \ + kit_alloc_dispatch((array_).alloc, KIT_DEALLOCATE, 0, 0, \ + (array_).values); \ + } while (0) + +/* Resize dynamic array. + */ +#define KIT_DA_RESIZE(array_, size_) \ + kit_da_resize((kit_da_void_t *) &(array_), \ + sizeof((array_).values[0]), size_) + +/* Append a value to dynamic array. + */ +#define KIT_DA_APPEND(array_, value_) \ + do { \ + ptrdiff_t const kit_index_back_ = (array_).size; \ + KIT_DA_RESIZE((array_), kit_index_back_ + 1); \ + if (kit_index_back_ < (array_).size) \ + (array_).values[kit_index_back_] = (value_); \ + } while (0) + +/* Insert a value into dynamic array. + */ +#define KIT_DA_INSERT(array_, index_, value_) \ + do { \ + ptrdiff_t kit_i_; \ + ptrdiff_t const kit_index_back_ = (array_).size; \ + ptrdiff_t const kit_indert_n_ = (index_); \ + KIT_DA_RESIZE((array_), kit_index_back_ + 1); \ + if (kit_index_back_ + 1 == (array_).size) { \ + for (kit_i_ = kit_index_back_; kit_i_ > kit_indert_n_; \ + kit_i_--) \ + (array_).values[kit_i_] = (array_).values[kit_i_ - 1]; \ + (array_).values[kit_indert_n_] = (value_); \ + } \ + } while (0) + +/* Erase a value from dynamic array. + */ +#define KIT_DA_ERASE(array_, index_) \ + do { \ + ptrdiff_t i_; \ + for (i_ = (index_) + 1; i_ < (array_).size; i_++) \ + (array_).values[i_ - 1] = (array_).values[i_]; \ + KIT_DA_RESIZE((array_), (array_).size - 1); \ + } while (0) + +typedef KIT_DA(char) kit_string_t; + +#ifndef KIT_DISABLE_SHORT_NAMES +# define da_void_t kit_da_void_t +# define da_init kit_da_init +# define da_resize kit_da_resize +# define string_t kit_string_t + +# define DA KIT_DA +# define DA_INIT KIT_DA_INIT +# define DA_CREATE KIT_DA_CREATE +# define DA_DESTROY KIT_DA_DESTROY +# define DA_RESIZE KIT_DA_RESIZE +# define DA_APPEND KIT_DA_APPEND +# define DA_INSERT KIT_DA_INSERT +# define DA_ERASE KIT_DA_ERASE +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/************************************************************************ + * * + * * + * FILE: ./source/kit/string_ref.h * + * * + * * + ************************************************************************/ +#ifndef KIT_STRING_REF_H +#define KIT_STRING_REF_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef KIT_AR_MUT(char) kit_string_mut_t; +typedef KIT_AR(char) kit_string_ref_t; + +typedef kit_string_mut_t kit_out_str_t; +typedef kit_string_ref_t kit_str_t; + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +# pragma GCC push_options +# pragma GCC optimize("O3") +#endif + +static kit_str_t kit_str(ptrdiff_t const size, + char const *const static_string) { + kit_str_t const s = { .size = size, .values = static_string }; + return s; +} + +#ifdef __GNUC__ +# pragma GCC pop_options +# pragma GCC diagnostic pop +#endif + +#define KIT_SZ(static_str_) \ + kit_str(sizeof(static_str_) - 1, (static_str_)) + +#define KIT_WRAP_STR(string_) \ + kit_str((string_).size, (string_).values) + +#ifndef KIT_DISABLE_SHORT_NAMES +# define string_mut_t kit_string_mut_t +# define string_ref_t kit_string_ref_t +# define out_str_t kit_out_str_t +# define str_t kit_str_t + +# define SZ KIT_SZ +# define WRAP_STR KIT_WRAP_STR +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/************************************************************************ + * * + * * + * FILE: ./source/kit/file.h * + * * + * * + ************************************************************************/ +#ifndef KIT_FILE_H +#define KIT_FILE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) && !defined(__CYGWIN__) +# define KIT_PATH_DELIM '\\' +# define KIT_ENV_HOME "USERPROFILE" +#else +# define KIT_PATH_DELIM '/' +# define KIT_ENV_HOME "HOME" +#endif + +typedef enum { + KIT_PATH_NONE, + KIT_PATH_FILE, + KIT_PATH_FOLDER +} kit_path_type_t; + +enum { KIT_FILE_SIZE_ERROR = -1 }; + +kit_string_t kit_path_norm(kit_str_t path, kit_allocator_t alloc); + +kit_string_t kit_path_join(kit_str_t left, kit_str_t right, + kit_allocator_t alloc); + +kit_string_t kit_path_user(kit_allocator_t alloc); + +kit_str_t kit_path_index(kit_str_t path, ptrdiff_t index); + +kit_str_t kit_path_take(kit_str_t path, ptrdiff_t count); + +kit_status_t kit_file_create_folder(kit_str_t path); + +kit_status_t kit_file_create_folder_recursive(kit_str_t path); + +kit_status_t kit_file_remove(kit_str_t path); + +kit_status_t kit_file_remove_folder(kit_str_t path); + +kit_status_t kit_file_remove_recursive(kit_str_t path, + kit_allocator_t alloc); + +kit_path_type_t kit_path_type(kit_str_t path); + +typedef struct { + kit_status_t status; + + int64_t time_modified_sec; + int32_t time_modified_nsec; + int64_t size; +} kit_file_info_t; + +kit_file_info_t kit_file_info(kit_str_t path); + +typedef struct { + kit_status_t status; + KIT_DA(kit_string_t) files; +} kit_path_list_t; + +kit_path_list_t kit_file_enum_folder(kit_str_t path, + kit_allocator_t alloc); + +void kit_path_list_destroy(kit_path_list_t list); + +#ifndef KIT_DISABLE_SHORT_NAMES +# define path_norm kit_path_norm +# define path_join kit_path_join +# define path_user kit_path_user +# define path_index kit_path_index +# define path_take kit_path_take +# define file_create_folder kit_file_create_folder +# define file_create_folder_recursive \ + kit_file_create_folder_recursive +# define file_remove kit_file_remove +# define file_remove_folder kit_file_remove_folder +# define file_remove_recursive kit_file_remove_recursive +# define path_type kit_path_type +# define file_size_result_t kit_file_size_result_t +# define file_size kit_file_size +# define path_type_t kit_path_type_t +# define path_list_t kit_path_list_t +# define file_enum_folder kit_file_enum_folder +# define path_list_destroy kit_path_list_destroy + +# define PATH_NONE KIT_PATH_NONE +# define PATH_FILE KIT_PATH_FILE +# define PATH_FOLDER KIT_PATH_FOLDER +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/************************************************************************ + * * + * * + * FILE: ./source/kit/allocator.c * + * * + * * + ************************************************************************/ + +#include <assert.h> + +#ifndef KIT_DISABLE_SYSTEM_MALLOC +# include <stdlib.h> +#endif + +static void *allocate(int request, void *state, ptrdiff_t size, + ptrdiff_t previous_size, void *pointer) { +#ifndef KIT_DISABLE_SYSTEM_MALLOC + switch (request) { + case KIT_ALLOCATE: + assert(previous_size == 0); + assert(pointer == NULL); + return malloc(size); + + case KIT_DEALLOCATE: + assert(size == 0); + assert(pointer != NULL); + free(pointer); + return NULL; + + case KIT_REALLOCATE: + /* FIXME + * Not implemented. + */ + assert(0); + return NULL; + + default: assert(0); + } +#else + assert(0); +#endif + return NULL; +} + +#ifndef KIT_ENABLE_CUSTOM_ALLOC_DISPATCH +void *kit_alloc_dispatch(kit_allocator_t alloc, int request, + ptrdiff_t size, ptrdiff_t previous_size, + void *pointer) { + assert(alloc.allocate != NULL); + if (alloc.allocate == NULL) + return NULL; + return alloc.allocate(request, alloc.state, size, previous_size, + pointer); +} +#endif + +kit_allocator_t kit_alloc_default(void) { + kit_allocator_t alloc = { .state = NULL, .allocate = allocate }; + return alloc; +} + +/************************************************************************ + * * + * * + * FILE: ./source/kit/array_ref.c * + * * + * * + ************************************************************************/ + +#include <string.h> + +int kit_ar_equal_bytes(ptrdiff_t left_element_size, + ptrdiff_t left_size, void const *left_data, + ptrdiff_t right_element_size, + ptrdiff_t right_size, void const *right_data) { + ptrdiff_t i; + if (left_element_size != right_element_size) + return 0; + if (left_size != right_size) + return 0; + for (i = 0; i < left_size; i++) + if (memcmp((char const *) left_data + i * left_element_size, + (char const *) right_data + i * left_element_size, + left_element_size) != 0) + return 0; + return 1; +} + +int kit_ar_compare(ptrdiff_t left_element_size, ptrdiff_t left_size, + void const *left_data, + ptrdiff_t right_element_size, ptrdiff_t right_size, + void const *right_data, + kit_ar_compare_fn compare) { + ptrdiff_t i; + if (left_element_size < right_element_size) + return -1; + if (left_element_size > right_element_size) + return 1; + for (i = 0; i < left_size && i < right_size; i++) { + int const c = compare( + (char const *) left_data + i * left_element_size, + (char const *) right_data + i * left_element_size); + if (c != 0) + return c; + } + if (left_size < right_size) + return -1; + if (left_size > right_size) + return 1; + return 0; +} + +/************************************************************************ + * * + * * + * FILE: ./source/kit/dynamic_array.c * + * * + * * + ************************************************************************/ + +#include <assert.h> +#include <string.h> + +void kit_da_init(kit_da_void_t *array, ptrdiff_t element_size, + ptrdiff_t size, kit_allocator_t alloc) { + assert(array != NULL); + assert(element_size > 0); + assert(size >= 0); + assert(alloc.allocate != NULL); + + memset(array, 0, sizeof(kit_da_void_t)); + + if (size > 0) + array->values = kit_alloc_dispatch(alloc, KIT_ALLOCATE, + element_size * size, 0, NULL); + + if (array->values != NULL) { + array->capacity = size; + array->size = size; + } + + array->alloc = alloc; +} + +static ptrdiff_t eval_capacity(ptrdiff_t current_cap, + ptrdiff_t required_cap) { + if (current_cap == 0) + return required_cap; + ptrdiff_t cap = current_cap; + while (cap < required_cap) cap *= 2; + return cap; +} + +void kit_da_resize(kit_da_void_t *array, ptrdiff_t element_size, + ptrdiff_t size) { + assert(array != NULL); + assert(element_size > 0); + assert(size >= 0); + + if (size <= array->capacity) { + array->size = size; + } else { + ptrdiff_t capacity = eval_capacity(array->capacity, size); + + assert(array->alloc.allocate != NULL); + + void *bytes = kit_alloc_dispatch( + array->alloc, KIT_ALLOCATE, element_size * capacity, 0, NULL); + + if (bytes != NULL) { + if (array->size > 0) + memcpy(bytes, array->values, element_size * array->size); + if (array->values != NULL) + kit_alloc_dispatch(array->alloc, KIT_DEALLOCATE, 0, 0, + array->values); + array->capacity = capacity; + array->size = size; + array->values = bytes; + } + } +} + +/************************************************************************ + * * + * * + * FILE: ./source/kit/file.c * + * * + * * + ************************************************************************/ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +enum { PATH_BUF_SIZE = 4096 }; + +#if defined(_WIN32) && !defined(__CYGWIN__) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# include <stdint.h> +# include <Windows.h> +#else +# include <dirent.h> +# include <sys/stat.h> +# include <unistd.h> +#endif + +#ifdef __APPLE__ +# define st_mtim st_mtimespec +#endif + +static int is_delim(char const c) { + return c == '/' || c == '\\'; +} + +kit_string_t kit_path_norm(kit_str_t const path, + kit_allocator_t const alloc) { + str_t const parent = SZ(".."); + ptrdiff_t i, i1, j; + + string_t norm; + DA_INIT(norm, path.size, alloc); + assert(norm.size == path.size); + + if (norm.size != path.size) + return norm; + + memcpy(norm.values, path.values, path.size); + + for (i1 = 0, i = 0; i < path.size; i++) { + if (!is_delim(path.values[i])) + continue; + + str_t const s = { .size = i - i1 - 1, + .values = path.values + i1 + 1 }; + if (AR_EQUAL(s, parent)) { + int have_parent = 0; + ptrdiff_t i0 = 0; + + for (j = 0; j < i1; j++) { + if (norm.values[j] != '\0') + have_parent = 1; + if (is_delim(norm.values[j])) + i0 = j; + } + + if (have_parent) { + memset(norm.values + i0, '\0', i - i0); + + if (!is_delim(path.values[i0])) + norm.values[i] = '\0'; + } + } + + i1 = i; + } + + ptrdiff_t size = 0; + + for (i = 0; i < norm.size; i++) { + if (norm.values[i] != '\0') { + if (is_delim(norm.values[i])) + norm.values[size] = KIT_PATH_DELIM; + else + norm.values[size] = norm.values[i]; + size++; + } + } + + norm.size = size; + return norm; +} + +kit_string_t kit_path_join(kit_str_t const left, + kit_str_t const right, + kit_allocator_t const alloc) { + ptrdiff_t left_size = left.size; + ptrdiff_t right_size = right.size; + char const *right_values = right.values; + + if (left_size > 0 && is_delim(left.values[left_size - 1])) + left_size--; + if (right_size > 0 && is_delim(right.values[0])) { + right_size--; + right_values++; + } + + kit_string_t joined; + DA_INIT(joined, left_size + right_size + 1, alloc); + assert(joined.size == left_size + right_size + 1); + + if (joined.size != left_size + right_size + 1) + return joined; + + memcpy(joined.values, left.values, left_size); + joined.values[left_size] = KIT_PATH_DELIM; + memcpy(joined.values + left_size + 1, right_values, right_size); + + return joined; +} + +kit_string_t kit_path_user(kit_allocator_t const alloc) { + char *home = getenv(KIT_ENV_HOME); + ptrdiff_t const size = home != NULL ? (ptrdiff_t) strlen(home) : 0; + + string_t user; + DA_INIT(user, size, alloc); + assert(user.size == size); + + if (user.size == size) + memcpy(user.values, home, user.size); + else { + DA_RESIZE(user, 1); + assert(user.size == 1); + + if (user.size == 1) + user.values[0] = '.'; + } + + return user; +} + +kit_str_t kit_path_index(kit_str_t const path, + ptrdiff_t const index) { + str_t s = { .size = 0, .values = NULL }; + + ptrdiff_t i0 = 0; + ptrdiff_t i = 0; + ptrdiff_t n = 0; + + for (; i < path.size; i++) { + if (!is_delim(path.values[i])) + continue; + + if (i0 < i) { + if (n++ == index) { + s.values = path.values + i0; + s.size = i - i0; + return s; + } + } + + i0 = i + 1; + } + + if (n == index) { + s.values = path.values + i0; + s.size = i - i0; + } + + return s; +} + +kit_str_t kit_path_take(kit_str_t const path, ptrdiff_t const count) { + str_t s = { .size = 0, .values = path.values }; + + ptrdiff_t i0 = 0; + ptrdiff_t i = 0; + ptrdiff_t n = 0; + + for (; i < path.size; i++) { + if (!is_delim(path.values[i])) + continue; + + if (i0 < i) { + if (n++ == count) { + s.size = i; + return s; + } + } + + i0 = i + 1; + } + + if (n == count) + s.size = i; + + return s; +} + +#if defined(_WIN32) && !defined(__CYGWIN__) +static void win32_prepare_path_(WCHAR *const buf, + kit_str_t const path) { + assert(path.size == 0 || path.values != NULL); + assert(path.size + 5 < PATH_BUF_SIZE); + + memset(buf, 0, PATH_BUF_SIZE); + buf[0] = L'\\'; + buf[1] = L'\\'; + buf[2] = L'?'; + buf[3] = L'\\'; + if (path.size > 0 && path.size + 5 < PATH_BUF_SIZE) + for (ptrdiff_t i = 0; i < path.size; i++) { + if (path.values[i] == '/') + buf[4 + i] = L'\\'; + else + buf[4 + i] = path.values[i]; + } +} +# define PREPARE_PATH_BUF_ \ + WCHAR buf[PATH_BUF_SIZE]; \ + win32_prepare_path_(buf, path) +#else +static void unix_prepare_path_(char *const buf, + kit_str_t const path) { + assert(path.size == 0 || path.values != NULL); + assert(path.size + 1 < PATH_BUF_SIZE); + + memset(buf, 0, PATH_BUF_SIZE); + if (path.size > 0 && path.size + 1 < PATH_BUF_SIZE) + memcpy(buf, path.values, path.size); +} +# define PREPARE_PATH_BUF_ \ + char buf[PATH_BUF_SIZE]; \ + unix_prepare_path_(buf, path) +#endif + +kit_status_t kit_file_create_folder(kit_str_t const path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + return CreateDirectoryW(buf, NULL) ? KIT_OK + : KIT_ERROR_MKDIR_FAILED; +#else + return mkdir(buf, 0755) == 0 ? KIT_OK : KIT_ERROR_MKDIR_FAILED; +#endif +} + +kit_status_t kit_file_create_folder_recursive(kit_str_t const path) { + ptrdiff_t i; + + for (i = 0;; i++) { + str_t const part = kit_path_take(path, i); + int const type = kit_path_type(part); + if (type == KIT_PATH_FILE) + return KIT_ERROR_FILE_ALREADY_EXISTS; + if (type == KIT_PATH_NONE) { + kit_status_t const s = kit_file_create_folder(part); + if (s != KIT_OK) + return s; + } + if (part.size == path.size) + break; + } + + return KIT_OK; +} + +kit_status_t kit_file_remove(kit_str_t const path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + return DeleteFileW(buf) ? KIT_OK : KIT_ERROR_UNLINK_FAILED; +#else + return unlink(buf) == 0 ? KIT_OK : KIT_ERROR_UNLINK_FAILED; +#endif +} + +kit_status_t kit_file_remove_folder(kit_str_t const path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + return RemoveDirectoryW(buf) ? KIT_OK : KIT_ERROR_RMDIR_FAILED; +#else + return rmdir(buf) == 0 ? KIT_OK : KIT_ERROR_RMDIR_FAILED; +#endif +} + +kit_status_t kit_file_remove_recursive(kit_str_t const path, + kit_allocator_t const alloc) { + int type = kit_path_type(path); + ptrdiff_t i; + + switch (type) { + case KIT_PATH_FILE: return kit_file_remove(path); + + case KIT_PATH_FOLDER: { + kit_path_list_t list = kit_file_enum_folder(path, alloc); + if (list.status != KIT_OK) { + kit_path_list_destroy(list); + return list.status; + } + for (i = 0; i < list.files.size; i++) { + str_t const s = { .size = list.files.values[i].size, + .values = list.files.values[i].values }; + kit_file_remove_recursive(s, alloc); + } + kit_path_list_destroy(list); + return kit_file_remove_folder(path); + } + + default:; + } + + return KIT_ERROR_FILE_DO_NOT_EXIST; +} + +kit_path_type_t kit_path_type(kit_str_t const path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + if (PathFileExistsW(buf)) { + if ((GetFileAttributesW(buf) & FILE_ATTRIBUTE_DIRECTORY) != 0) + return KIT_PATH_FOLDER; + else + return KIT_PATH_FILE; + } +#else + struct stat info; + if (stat(buf, &info) == 0) { + if (S_ISREG(info.st_mode)) + return KIT_PATH_FILE; + if (S_ISDIR(info.st_mode)) + return KIT_PATH_FOLDER; + } +#endif + return KIT_PATH_NONE; +} + +kit_file_info_t kit_file_info(kit_str_t const path) { + kit_file_info_t result; + memset(&result, 0, sizeof result); + + PREPARE_PATH_BUF_; + +#if defined(_WIN32) && !defined(__CYGWIN__) + HANDLE f = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (f != INVALID_HANDLE_VALUE) { + FILETIME ft; + if (GetFileTime(f, NULL, NULL, &ft) != 0) { + uint64_t const nsec100 = (((uint64_t) ft.dwHighDateTime) + << 32) | + (uint64_t) ft.dwLowDateTime; + result.time_modified_sec = (int64_t) (nsec100 / 10000000); + result.time_modified_nsec = (int32_t) (100 * + (nsec100 % 10000000)); + } else { + assert(0); + } + + DWORD high; + DWORD low = GetFileSize(f, &high); + + result.size = (int64_t) ((((uint64_t) high) << 32) | + (uint64_t) low); + result.status = KIT_OK; + + CloseHandle(f); + return result; + } +#else + struct stat info; + if (stat(buf, &info) == 0 && S_ISREG(info.st_mode)) { + result.size = (int64_t) info.st_size; +# ifndef st_mtime + /* No support for nanosecond timestamps. + */ + result.time_modified_sec = (int64_t) info.st_mtime; +# else + result.time_modified_sec = (int64_t) info.st_mtim.tv_sec; + result.time_modified_nsec = (int32_t) info.st_mtim.tv_nsec; +# endif + result.status = KIT_OK; + return result; + } +#endif + + result.status = KIT_ERROR_FILE_DO_NOT_EXIST; + return result; +} + +kit_path_list_t kit_file_enum_folder(kit_str_t const path, + kit_allocator_t const alloc) { + PREPARE_PATH_BUF_; + + kit_path_list_t result = { .status = KIT_OK }; + DA_INIT(result.files, 0, alloc); + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (path.size + 7 >= PATH_BUF_SIZE) { + result.status = KIT_ERROR_PATH_TOO_LONG; + return result; + } + + buf[path.size + 4] = '\\'; + buf[path.size + 5] = '*'; + + WIN32_FIND_DATAW data; + HANDLE find = FindFirstFileW(buf, &data); + + if (find == INVALID_HANDLE_VALUE) + return result; + + do { + ptrdiff_t const n = result.files.size; + DA_RESIZE(result.files, n + 1); + if (result.files.size != n + 1) { + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + ptrdiff_t size = 0; + while (size < MAX_PATH && data.cFileName[size] != L'\0') size++; + DA_INIT(result.files.values[n], size, alloc); + if (result.files.values[n].size != size) { + DA_RESIZE(result.files, n); + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + for (ptrdiff_t i = 0; i < size; i++) + result.files.values[n].values[i] = data.cFileName[i]; + } while (FindNextFileW(find, &data) != 0); + + FindClose(find); +#else + DIR *directory = opendir(buf); + + if (directory == NULL) + return result; + + for (;;) { + struct dirent *entry = readdir(directory); + + if (entry == NULL) + break; + + if (entry->d_name[0] == '.') + continue; + + ptrdiff_t const n = result.files.size; + DA_RESIZE(result.files, n + 1); + if (result.files.size != n + 1) { + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + ptrdiff_t const size = (ptrdiff_t) strlen(entry->d_name); + DA_INIT(result.files.values[n], size, alloc); + if (result.files.values[n].size != size) { + DA_RESIZE(result.files, n); + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + if (size > 0) + memcpy(result.files.values[n].values, entry->d_name, size); + } + + closedir(directory); +#endif + + return result; +} + +void kit_path_list_destroy(kit_path_list_t list) { + ptrdiff_t i; + for (i = 0; i < list.files.size; i++) + DA_DESTROY(list.files.values[i]); + DA_DESTROY(list.files); +} + + +#endif diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt index a0206b3..2e89133 100644 --- a/source/kit/CMakeLists.txt +++ b/source/kit/CMakeLists.txt @@ -1,9 +1,5 @@ target_sources( kit - PRIVATE - input_buffer.c secure_random.c sha256.c thread.posix.c - atomic.win32.c condition_variable.c thread.win32.c input_stream.c file.c - allocator.c array_ref.c dynamic_array.c mutex.c mersenne_twister_64.c PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mutex.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/status.h> @@ -24,4 +20,8 @@ target_sources( $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/sha256.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/file.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/array_ref.h> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mersenne_twister_64.h>) + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mersenne_twister_64.h> + PRIVATE + input_buffer.c secure_random.c sha256.c thread.posix.c atomic.win32.c + condition_variable.c thread.win32.c input_stream.c file.c allocator.c + array_ref.c dynamic_array.c mutex.c mersenne_twister_64.c) diff --git a/source/kit/array_ref.h b/source/kit/array_ref.h index 029c20c..39f1e8b 100644 --- a/source/kit/array_ref.h +++ b/source/kit/array_ref.h @@ -24,13 +24,13 @@ int kit_ar_compare(ptrdiff_t left_element_size, ptrdiff_t left_size, struct { \ ptrdiff_t size; \ type_ *values; \ - } + } #define KIT_AR(type_) \ struct { \ ptrdiff_t size; \ type_ const *values; \ - } + } #define KIT_AR_MUT_WRAP(name_, element_type_, array_) \ struct { \ diff --git a/source/kit/file.h b/source/kit/file.h index b00ec4f..a773e02 100644 --- a/source/kit/file.h +++ b/source/kit/file.h @@ -88,6 +88,10 @@ void kit_path_list_destroy(kit_path_list_t list); # define path_list_t kit_path_list_t # define file_enum_folder kit_file_enum_folder # define path_list_destroy kit_path_list_destroy + +# define PATH_NONE KIT_PATH_NONE +# define PATH_FILE KIT_PATH_FILE +# define PATH_FOLDER KIT_PATH_FOLDER #endif #ifdef __cplusplus diff --git a/source/kit/string_ref.h b/source/kit/string_ref.h index 4b2cabd..1497bb0 100644 --- a/source/kit/string_ref.h +++ b/source/kit/string_ref.h @@ -35,6 +35,9 @@ static kit_str_t kit_str(ptrdiff_t const size, #define KIT_SZ(static_str_) \ kit_str(sizeof(static_str_) - 1, (static_str_)) +#define KIT_WRAP_STR(string_) \ + kit_str((string_).size, (string_).values) + #ifndef KIT_DISABLE_SHORT_NAMES # define string_mut_t kit_string_mut_t # define string_ref_t kit_string_ref_t @@ -42,6 +45,7 @@ static kit_str_t kit_str(ptrdiff_t const size, # define str_t kit_str_t # define SZ KIT_SZ +# define WRAP_STR KIT_WRAP_STR #endif #ifdef __cplusplus diff --git a/source/kit_test/CMakeLists.txt b/source/kit_test/CMakeLists.txt index 0e61fe3..56aa9a4 100644 --- a/source/kit_test/CMakeLists.txt +++ b/source/kit_test/CMakeLists.txt @@ -1,7 +1,7 @@ target_sources( kit_test - PRIVATE - test.c bench.c PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/bench.h> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/test.h>) + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/test.h> + PRIVATE + test.c bench.c) diff --git a/source/test/unittests/CMakeLists.txt b/source/test/unittests/CMakeLists.txt index b8a6dca..e071ed4 100644 --- a/source/test/unittests/CMakeLists.txt +++ b/source/test/unittests/CMakeLists.txt @@ -1,9 +1,8 @@ target_sources( kit_test_suite PRIVATE - async_function.test.c bigint.test.c mutex.test.c - test_duration.test.c main.test.c string_ref.test.c atomic.test.c foo.bench.c - thread.test.c array_ref.test.c input_stream.test.c sha256.test.c - lower_bound.test.c secure_random.test.c condition_variable.test.c - mersenne_twister_64.test.c input_buffer.test.c move_back.test.c dynamic_array.test.c - file.test.c) + async_function.test.c bigint.test.c mutex.test.c test_duration.test.c + main.test.c string_ref.test.c atomic.test.c foo.bench.c thread.test.c + array_ref.test.c input_stream.test.c sha256.test.c lower_bound.test.c + secure_random.test.c condition_variable.test.c mersenne_twister_64.test.c + input_buffer.test.c move_back.test.c dynamic_array.test.c file.test.c) |