// ================================================================ // // kit.inl.h // https://guattari.tech/kit.git // // Single header-only cross-platform utility library for C. // // ================================================================ // // The MIT License // // Copyright (c) 2022-2023 Mitya Selivanov // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // ================================================================ #ifndef KIT_INL_H #define KIT_INL_H // ================================================================ // // File: source/kit/types.h // // ================================================================ #ifndef KIT_TYPES_H #define KIT_TYPES_H #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif typedef signed char i8; typedef signed short i16; typedef signed int i32; typedef signed long long i64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; typedef float f32; typedef double f64; #endif // ================================================================ // // File: source/kit/status.h // // ================================================================ #ifndef KIT_STATUS_H #define KIT_STATUS_H enum { KIT_OK = 0, KIT_ERROR_INTERNAL = 1, KIT_ERROR_BAD_ALLOC = (1 << 1), KIT_ERROR_INVALID_ARGUMENT = (1 << 2), KIT_ERROR_MKDIR_FAILED = (1 << 3), KIT_ERROR_RMDIR_FAILED = (1 << 4), KIT_ERROR_UNLINK_FAILED = (1 << 5), KIT_ERROR_FILE_ALREADY_EXISTS = (1 << 6), KIT_ERROR_FILE_DOES_NOT_EXIST = (1 << 7), KIT_ERROR_PATH_TOO_LONG = (1 << 8), KIT_ERROR_SOCKETS_STARTUP_FAILED = (1 << 9), KIT_ERROR_SOCKET_CONTROL_FAILED = (1 << 10), KIT_ERROR_NAME_TOO_LONG = (1 << 11), KIT_ERROR_INVALID_SIZE = (1 << 12), KIT_ERROR_INVALID_NAME = (1 << 13), KIT_ERROR_INVALID_PATH = (1 << 14), KIT_ERROR_OPEN_FAILED = (1 << 15), KIT_ERROR_TRUNCATE_FAILED = (1 << 16), KIT_ERROR_MAP_FAILED = (1 << 17), KIT_ERROR_UNMAP_FAILED = (1 << 18), KIT_ERROR_SYNC_FAILED = (1 << 19), KIT_ERROR_CLOSE_FAILED = (1 << 20), KIT_ERROR_NOT_IMPLEMENTED = (1 << 30), }; typedef i32 kit_status_t; #endif // ================================================================ // // File: source/kit/allocator.h // // ================================================================ #ifndef KIT_ALLOCATOR_H #define KIT_ALLOCATOR_H #ifdef __cplusplus extern "C" { #endif enum { KIT_ALLOC_TYPE_NONE, KIT_ALLOC_TYPE_DEFAULT, KIT_ALLOC_TYPE_BUFFER }; enum { KIT_ALLOCATE, KIT_ALLOCATE_ZERO, KIT_DEALLOCATE, KIT_REALLOCATE, KIT_REALLOCATE_ZERO, KIT_DEALLOCATE_ALL }; typedef struct { i32 type; i64 size; union { u8 *bytes; void *data; }; } kit_allocator_t; // Application should implement this function if custom allocator // dispatch is enabled. // // See KIT_ENABLE_CUSTOM_ALLOC_DISPATCH macro. // void *kit_alloc_dispatch(kit_allocator_t *alloc, i32 request, i64 size, i64 previous_size, void *pointer); kit_allocator_t kit_alloc_default(void); kit_allocator_t kit_alloc_buffer(i64 size, void *buffer); #ifdef __cplusplus } #endif #endif // ================================================================ // // File: source/kit/time.h // // ================================================================ #ifndef KIT_TIME_H #define KIT_TIME_H #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #ifdef __cplusplus extern "C" { #endif #ifndef TIME_UTC # define TIME_UTC 1 #endif #ifdef KIT_REQUIRE_TIMESPEC_GET # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN 1 # endif # ifndef NOMINMAX # define NOMINMAX 1 # endif # include # define KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull # define KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull static int timespec_get(struct timespec *ts, int base) { if (ts == NULL || base != TIME_UTC) return 0; FILETIME ft; ULARGE_INTEGER date; LONGLONG ticks; GetSystemTimeAsFileTime(&ft); date.HighPart = ft.dwHighDateTime; date.LowPart = ft.dwLowDateTime; ticks = (LONGLONG) (date.QuadPart - KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS); ts->tv_sec = ticks / KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS; ts->tv_nsec = (ticks % KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100; return base; } #endif #ifdef __cplusplus } #endif #endif // ================================================================ // // File: source/kit/atomic.h // // ================================================================ #ifndef KIT_ATOMIC_H #define KIT_ATOMIC_H #ifndef _MSC_VER # include #else # include # ifdef __cplusplus extern "C" { # endif # define _Atomic volatile enum { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst }; void kit_atomic_store_explicit_8(u8 volatile *var, u8 value, i32 memory_order); void kit_atomic_store_explicit_16(u16 volatile *var, u16 value, i32 memory_order); void kit_atomic_store_explicit_32(u32 volatile *var, u32 value, i32 memory_order); void kit_atomic_store_explicit_64(u64 volatile *var, u64 value, i32 memory_order); u8 kit_atomic_load_explicit_8(u8 volatile *var, i32 memory_order); u16 kit_atomic_load_explicit_16(u16 volatile *var, i32 memory_order); u32 kit_atomic_load_explicit_32(u32 volatile *var, i32 memory_order); u64 kit_atomic_load_explicit_64(u64 volatile *var, i32 memory_order); u8 kit_atomic_exchange_explicit_8(u8 volatile *var, u8 value, i32 memory_order); u16 kit_atomic_exchange_explicit_16(u16 volatile *var, u16 value, i32 memory_order); u32 kit_atomic_exchange_explicit_32(u32 volatile *var, u32 value, i32 memory_order); u64 kit_atomic_exchange_explicit_64(u64 volatile *var, u64 value, i32 memory_order); i32 kit_atomic_compare_exchange_explicit_8(u8 volatile *var, u8 *expected, u8 value, i32 memory_order_succ_, i32 memory_order_fail_); i32 kit_atomic_compare_exchange_explicit_16(u16 volatile *var, u16 *expected, u16 value, i32 memory_order_succ_, i32 memory_order_fail_); i32 kit_atomic_compare_exchange_explicit_32(u32 volatile *var, u32 *expected, u32 value, i32 memory_order_succ_, i32 memory_order_fail_); i32 kit_atomic_compare_exchange_explicit_64(u64 volatile *var, u64 *expected, u64 value, i32 memory_order_succ_, i32 memory_order_fail_); u8 kit_atomic_fetch_add_explicit_8(u8 volatile *var, u8 value, i32 memory_order); u16 kit_atomic_fetch_add_explicit_16(u16 volatile *var, u16 value, i32 memory_order); u32 kit_atomic_fetch_add_explicit_32(u32 volatile *var, u32 value, i32 memory_order); u64 kit_atomic_fetch_add_explicit_64(u64 volatile *var, u64 value, i32 memory_order); # define atomic_store_explicit(var_, value_, memory_order_) \ do { \ assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 || \ sizeof *(var_) == 4 || sizeof *(var_) == 8); \ if (sizeof *(var_) == 1) \ kit_atomic_store_explicit_8((u8 volatile *) (var_), \ (u8) (value_), (memory_order_)); \ if (sizeof *(var_) == 2) \ kit_atomic_store_explicit_16((u16 volatile *) (var_), \ (u16) (value_), \ (memory_order_)); \ if (sizeof *(var_) == 4) \ kit_atomic_store_explicit_32((u32 volatile *) (var_), \ (u32) (value_), \ (memory_order_)); \ if (sizeof *(var_) == 8) \ kit_atomic_store_explicit_64((u64 volatile *) (var_), \ (u64) (value_), \ (memory_order_)); \ } while (0) # define atomic_load_explicit(var_, memory_order_) \ (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 || \ sizeof *(var_) == 4 || sizeof *(var_) == 8), \ (sizeof *(var_) == 1 \ ? kit_atomic_load_explicit_8((u8 volatile *) (var_), \ (memory_order_)) \ : sizeof *(var_) == 2 \ ? kit_atomic_load_explicit_16((u16 volatile *) (var_), \ (memory_order_)) \ : sizeof *(var_) == 4 \ ? kit_atomic_load_explicit_32((u32 volatile *) (var_), \ (memory_order_)) \ : kit_atomic_load_explicit_64((u64 volatile *) (var_), \ (memory_order_)))) # define atomic_exchange_explicit(var_, value_, memory_order_) \ (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 || \ sizeof *(var_) == 4 || sizeof *(var_) == 8), \ (sizeof *(var_) == 1 ? kit_atomic_exchange_explicit_8( \ (u8 volatile *) (var_), \ (u8) (value_), (memory_order_)) \ : sizeof *(var_) == 2 ? kit_atomic_exchange_explicit_16( \ (u16 volatile *) (var_), \ (u16) (value_), (memory_order_)) \ : sizeof *(var_) == 4 \ ? kit_atomic_exchange_explicit_32((u32 volatile *) (var_), \ (u32) (value_), \ (memory_order_)) \ : kit_atomic_exchange_explicit_64((u64 volatile *) (var_), \ (u64) (value_), \ (memory_order_)))) # define atomic_compare_exchange_strong_explicit( \ var_, expected_, value_, memory_order_succ_, \ memory_order_fail_) \ (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 || \ sizeof *(var_) == 4 || sizeof *(var_) == 8), \ (sizeof *(var_) == 1 \ ? kit_atomic_compare_exchange_explicit_8( \ (u8 volatile *) (var_), (u8 *) (expected_), \ (u8) (value_), (memory_order_succ_), \ (memory_order_fail_)) \ : sizeof *(var_) == 2 \ ? kit_atomic_compare_exchange_explicit_16( \ (u16 volatile *) (var_), (u16 *) (expected_), \ (u16) (value_), (memory_order_succ_), \ (memory_order_fail_)) \ : sizeof *(var_) == 4 \ ? kit_atomic_compare_exchange_explicit_32( \ (u32 volatile *) (var_), (u32 *) (expected_), \ (u32) (value_), (memory_order_succ_), \ (memory_order_fail_)) \ : kit_atomic_compare_exchange_explicit_64( \ (u64 volatile *) (var_), (u64 *) (expected_), \ (u64) (value_), (memory_order_succ_), \ (memory_order_fail_)))) # define atomic_compare_exchange_weak_explicit( \ var_, expected_, value_, memory_order_succ_, \ memory_order_fail_) \ atomic_compare_exchange_strong_explicit(var_, expected_, value_, \ memory_order_succ_, \ memory_order_fail_) # define atomic_fetch_add_explicit(var_, value_, memory_order_) \ (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 || \ sizeof *(var_) == 4 || sizeof *(var_) == 8), \ (sizeof *(var_) == 1 ? kit_atomic_fetch_add_explicit_8( \ (u8 volatile *) (var_), \ (u8) (value_), (memory_order_)) \ : sizeof *(var_) == 2 ? kit_atomic_fetch_add_explicit_16( \ (u16 volatile *) (var_), \ (u16) (value_), (memory_order_)) \ : sizeof *(var_) == 4 ? kit_atomic_fetch_add_explicit_32( \ (u32 volatile *) (var_), \ (u32) (value_), (memory_order_)) \ : kit_atomic_fetch_add_explicit_64( \ (u64 volatile *) (var_), \ (u64) (value_), (memory_order_)))) # define atomic_store(var_, value_) \ atomic_store(var_, value_, memory_order_seq_cst) # define atomic_load(var_) atomic_load(var_, memory_order_seq_cst) # define atomic_exchange(var_, value_) \ atomic_exchange(var_, value_, memory_order_seq_cst) # define atomic_compare_exchange_strong(var_, expected_, value_) \ atomic_compare_exchange_strong_explicit(var_, expected_, value_, \ memory_order_seq_cst, \ memory_order_seq_cst) # define atomic_compare_exchange_weak(var_, expected_, value_) \ atomic_compare_exchange_weak_explicit(var_, expected_, value_, \ memory_order_seq_cst, \ memory_order_seq_cst) # define atomic_fetch_add(var_, value_) \ atomic_fetch_add(var_, value_, memory_order_seq_cst) # ifdef __cplusplus } # endif #endif #ifndef KIT_DISABLE_SHORT_NAMES # define ATOMIC KIT_ATOMIC #endif #endif // ================================================================ // // File: source/kit/threads.h // // ================================================================ #ifndef KIT_THREADS_H #define KIT_THREADS_H #ifndef KIT_DISABLE_SYSTEM_THREADS # include # include # if !defined(_WIN32) || defined(__CYGWIN__) # include # endif # if defined(__cplusplus) # define _Noreturn [[noreturn]] # elif defined(_MSC_VER) # define _Noreturn __declspec(noreturn) # endif # ifndef _Thread_local # if defined(__cplusplus) // C++11 doesn't need `_Thread_local` keyword or macro # elif !defined(__STDC_NO_THREADS__) // threads are optional in C11, _Thread_local present in this // condition # elif defined(_MSC_VER) # define _Thread_local __declspec(thread) # elif defined(__GNUC__) # define _Thread_local __thread # else // Leave _Thread_local undefined so that use of _Thread_local would // not promote to a non-thread-local global variable # endif # endif # if !defined(__cplusplus) // C11 thread_local() macro C++11 and above already have thread_local // keyword # ifndef thread_local # if _MSC_VER # define thread_local __declspec(thread) # else # define thread_local _Thread_local # endif # endif # endif # ifdef __cplusplus extern "C" { # endif enum { mtx_plain, mtx_recursive, mtx_timed, }; enum { thrd_success, thrd_timedout, thrd_error, thrd_busy, thrd_nomem, thrd_wrong_stack_size }; # if defined(_WIN32) && !defined(__CYGWIN__) typedef struct { void *DebugInfo; long LockCount; long RecursionCount; void *OwningThread; void *LockSemaphore; uintptr_t SpinCount; } mtx_t; typedef struct { void *Ptr; } cnd_t; typedef struct { volatile uintptr_t status; } once_flag; typedef struct { void *handle; } thrd_t; typedef unsigned long tss_t; # else typedef pthread_mutex_t mtx_t; typedef pthread_cond_t cnd_t; typedef pthread_once_t once_flag; typedef pthread_t thrd_t; typedef pthread_key_t tss_t; # endif typedef void (*tss_dtor_t)(void *); typedef int (*thrd_start_t)(void *); void mtx_destroy(mtx_t *mtx_); int mtx_init(mtx_t *mtx_, int); int mtx_lock(mtx_t *mtx_); int mtx_timedlock(mtx_t *__restrict mtx_, struct timespec const *__restrict); int mtx_trylock(mtx_t *mtx_); int mtx_unlock(mtx_t *mtx_); void call_once(once_flag *, void (*)(void)); int cnd_broadcast(cnd_t *); void cnd_destroy(cnd_t *); int cnd_init(cnd_t *); int cnd_signal(cnd_t *); int cnd_timedwait(cnd_t *__restrict, mtx_t *__restrict mtx_, struct timespec const *__restrict); int cnd_wait(cnd_t *, mtx_t *mtx_); int thrd_create(thrd_t *, thrd_start_t, void *); int thrd_create_with_stack(thrd_t *, thrd_start_t, void *, ptrdiff_t stack_size); thrd_t thrd_current(void); int thrd_detach(thrd_t); int thrd_equal(thrd_t, thrd_t); _Noreturn void thrd_exit(int); int thrd_join(thrd_t, int *); int thrd_sleep(struct timespec const *, struct timespec *); void thrd_yield(void); # ifdef __cplusplus } # endif #endif #endif // ================================================================ // // File: source/kit/array_ref.h // // ================================================================ #ifndef KIT_ARRAY_REF_H #define KIT_ARRAY_REF_H #ifdef __cplusplus extern "C" { #endif typedef i8 (*kit_ar_compare_fn)(void *left, void *right); i8 kit_ar_equal_bytes(i64 left_element_size, i64 left_size, void *left_data, i64 right_element_size, i64 right_size, void *right_data); i8 kit_ar_compare(i64 left_element_size, i64 left_size, void *left_data, i64 right_element_size, i64 right_size, void *right_data, kit_ar_compare_fn compare); #define KIT_AR(type_) \ struct { \ i64 size; \ type_ *values; \ } #define KIT_AR_WRAP(name_, element_type_, array_) \ struct { \ i64 size; \ element_type_ *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_)) #ifdef __cplusplus } #endif #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 KIT_AR # define AR_WRAP KIT_AR_WRAP # define AR_EQUAL KIT_AR_EQUAL # define AR_COMPARE KIT_AR_COMPARE #endif #endif // ================================================================ // // File: source/kit/dynamic_array.h // // ================================================================ #ifndef KIT_DYNAMIC_ARRAY_H #define KIT_DYNAMIC_ARRAY_H #include #ifdef __cplusplus extern "C" { #endif typedef struct { i64 capacity; i64 size; void *values; kit_allocator_t *alloc; } kit_da_void_t; void kit_da_init(kit_da_void_t *array, i64 element_size, i64 size, kit_allocator_t *alloc); void kit_da_resize(kit_da_void_t *array, i64 element_size, i64 size); void kit_da_resize_exact(kit_da_void_t *array, i64 element_size, i64 size); /* Dynamic array type definition. */ #define KIT_DA(element_type_) \ struct { \ i64 capacity; \ i64 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_), NULL) /* Destroy dynamic array. */ #define KIT_DA_DESTROY(array_) \ do { \ if ((array_).values != NULL) \ kit_alloc_dispatch((array_).alloc, KIT_DEALLOCATE, 0, \ (array_).capacity * \ sizeof((array_).values[0]), \ (array_).values); \ memset(&(array_), 0, sizeof(array_)); \ } while (0) /* Resize dynamic array. */ #define KIT_DA_RESIZE(array_, size_) \ kit_da_resize((kit_da_void_t *) &(array_), \ sizeof((array_).values[0]), size_) /* Resize dynamic array with exact capacity. */ #define KIT_DA_RESIZE_EXACT(array_, capacity_) \ kit_da_resize_exact((kit_da_void_t *) &(array_), \ sizeof((array_).values[0]), capacity_) /* Append a value to dynamic array. */ #define KIT_DA_APPEND(array_, value_) \ do { \ i64 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 { \ i64 kit_i_; \ i64 kit_index_back_ = (array_).size; \ i64 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 { \ i64 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) #ifdef __cplusplus } #endif #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 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_RESIZE_EXACT KIT_DA_RESIZE_EXACT # define DA_APPEND KIT_DA_APPEND # define DA_INSERT KIT_DA_INSERT # define DA_ERASE KIT_DA_ERASE #endif #endif // ================================================================ // // File: source/kit/string_ref.h // // ================================================================ #ifndef KIT_STRING_REF_H #define KIT_STRING_REF_H #include #ifdef __cplusplus extern "C" { #endif typedef KIT_AR(char) kit_str_t; #if defined(__GNUC__) || defined(__clang__) # 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(i64 size, char const *static_string) { kit_str_t s = { .size = size, .values = (char *) static_string }; return s; } // Make a barbarian string for C standard library functions. // Not thread safe. // Use with caution. // static char *kit_make_bs(kit_str_t s) { static char buf[8][4096]; static i32 index = 0; i64 n = s.size; if (n > 4095) n = 4095; if (n > 0) memcpy(buf[index], s.values, n); buf[index][n] = '\0'; char *result = buf[index]; index = (index + 1) % 8; return result; } #if defined(__GNUC__) || defined(__clang__) # 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_BS(string_) kit_str(strlen(string_), (string_)) #define KIT_WRAP_STR(...) \ kit_str((__VA_ARGS__).size, (__VA_ARGS__).values) #ifndef KIT_DISABLE_SHORT_NAMES # define BS(...) kit_make_bs(KIT_WRAP_STR(__VA_ARGS__)) # define str_t kit_str_t # define SZ KIT_SZ # define WRAP_BS KIT_WRAP_BS # define WRAP_STR KIT_WRAP_STR #endif #ifdef __cplusplus } #endif #endif // ================================================================ // // File: source/kit/string_builder.h // // ================================================================ #ifndef KIT_STRING_BUILDER_H #define KIT_STRING_BUILDER_H typedef KIT_DA(char) kit_str_builder_t; #ifndef KIT_DISABLE_SHORT_NAMES # define str_builder_t kit_str_builder_t #endif #endif // ================================================================ // // File: source/kit/lower_bound.h // // ================================================================ #ifndef KIT_LOWER_BOUND_H #define KIT_LOWER_BOUND_H #ifdef __cplusplus extern "C" { #endif #define KIT_LOWER_BOUND_INL(return_val, size, ...) \ do { \ i64 position_ = 0; \ i64 count_ = (size); \ while (count_ > 0) { \ i64 delta_ = count_ / 2; \ i64 index_ = position_ + delta_; \ if (__VA_ARGS__) { \ position_ += delta_ + 1; \ count_ -= delta_ + 1; \ } else \ count_ = delta_; \ } \ (return_val) = position_; \ } while (0) #define KIT_LOWER_BOUND(return_val, array, value, op) \ KIT_LOWER_BOUND_INL(return_val, (array).size, \ (op) ((array).values[index_], (value))) #define KIT_LOWER_BOUND_REF(return_val, array, value, op) \ KIT_LOWER_BOUND_INL(return_val, (array).size, \ (op) ((array).values + index_, (value))) #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define LOWER_BOUND_INL KIT_LOWER_BOUND_INL # define LOWER_BOUND KIT_LOWER_BOUND # define LOWER_BOUND_REF KIT_LOWER_BOUND_REF #endif #endif // ================================================================ // // File: source/kit/move_back.h // // ================================================================ #ifndef KIT_MOVE_BACK_H #define KIT_MOVE_BACK_H #include #ifdef __cplusplus extern "C" { #endif #define KIT_MOVE_BACK_INL(new_size, array, ...) \ do { \ i64 index_; \ i64 end_ = (array).size; \ u8 temp_[sizeof *(array).values]; \ for (index_ = 0; index_ < end_;) { \ if (__VA_ARGS__) { \ end_--; \ if (index_ != end_) { \ memcpy(temp_, (array).values + end_, \ sizeof *(array).values); \ (array).values[end_] = (array).values[index_]; \ memcpy((array).values + index_, temp_, \ sizeof *(array).values); \ } \ } else \ index_++; \ } \ (new_size) = end_; \ } while (0) #define KIT_MOVE_BACK(new_size, array, value, cond) \ KIT_MOVE_BACK_INL(new_size, array, \ (cond) ((array).values[index_], (value))) #define KIT_MOVE_BACK_REF(new_size, array, value, cond) \ KIT_MOVE_BACK_INL(new_size, array, \ (cond) ((array).values + index_, (value))) #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define MOVE_BACK_INL KIT_MOVE_BACK_INL # define MOVE_BACK KIT_MOVE_BACK # define MOVE_BACK_REF KIT_MOVE_BACK_REF #endif #endif // ================================================================ // // File: source/kit/bigint.h // // ================================================================ #ifndef KIT_BIGINT_H #define KIT_BIGINT_H #include #include #ifdef __cplusplus extern "C" { #endif #ifndef KIT_BIGINT_SIZE # define KIT_BIGINT_SIZE 64 #endif typedef struct { u32 v[KIT_BIGINT_SIZE / 4]; } kit_bigint_t; #if defined(__GNUC__) || defined(__clang__) # 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_bigint_t kit_bi_u32(u32 x) { kit_bigint_t z; memset(&z, 0, sizeof z); z.v[0] = x; return z; } static kit_bigint_t kit_bi_u64(u64 x) { kit_bigint_t z; memset(&z, 0, sizeof z); z.v[0] = (u32) (x & 0xffffffff); z.v[1] = (u32) (x >> 32); return z; } static kit_bigint_t kit_bi_i32(i32 x) { kit_bigint_t z; memset(&z, x < 0 ? -1 : 0, sizeof z); z.v[0] = x; return z; } static kit_bigint_t kit_bi_i64(i64 x) { kit_bigint_t z; memset(&z, x < 0 ? -1 : 0, sizeof z); z.v[0] = (u32) (((u64) x) & 0xffffffff); z.v[1] = (u32) (((u64) x) >> 32); return z; } static int kit_bi_is_zero(kit_bigint_t x) { i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) if (x.v[i] != 0) return 0; return 1; } static int kit_bi_is_neg(kit_bigint_t x) { return (x.v[KIT_BIGINT_SIZE / 4 - 1] & 0x80000000) != 0; } static int kit_bi_equal(kit_bigint_t x, kit_bigint_t y) { return kit_ar_equal_bytes(1, KIT_BIGINT_SIZE, x.v, 1, KIT_BIGINT_SIZE, y.v); } static int kit_bi_compare(kit_bigint_t x, kit_bigint_t y) { i64 i; for (i = KIT_BIGINT_SIZE / 4 - 1; i >= 0; i--) if (x.v[i] < y.v[i]) return -1; else if (x.v[i] > y.v[i]) return 1; return 0; } static i64 kit_bi_significant_bit_count(kit_bigint_t x) { i64 n = KIT_BIGINT_SIZE / 4 - 1; while (n > 0 && x.v[n] == 0) n--; u32 val = x.v[n]; if (val == 0) return 0; i64 bits = (val & 0x80000000u) != 0 ? 32 : (val & 0x40000000u) != 0 ? 31 : (val & 0x20000000u) != 0 ? 30 : (val & 0x10000000u) != 0 ? 29 : (val & 0x8000000u) != 0 ? 28 : (val & 0x4000000u) != 0 ? 27 : (val & 0x2000000u) != 0 ? 26 : (val & 0x1000000u) != 0 ? 25 : (val & 0x800000u) != 0 ? 24 : (val & 0x400000u) != 0 ? 23 : (val & 0x200000u) != 0 ? 22 : (val & 0x100000u) != 0 ? 21 : (val & 0x80000u) != 0 ? 20 : (val & 0x40000u) != 0 ? 19 : (val & 0x20000u) != 0 ? 18 : (val & 0x10000u) != 0 ? 17 : (val & 0x8000u) != 0 ? 16 : (val & 0x4000u) != 0 ? 15 : (val & 0x2000u) != 0 ? 14 : (val & 0x1000u) != 0 ? 13 : (val & 0x800u) != 0 ? 12 : (val & 0x400u) != 0 ? 11 : (val & 0x200u) != 0 ? 10 : (val & 0x100u) != 0 ? 9 : (val & 0x80u) != 0 ? 8 : (val & 0x40u) != 0 ? 7 : (val & 0x20u) != 0 ? 6 : (val & 0x10u) != 0 ? 5 : (val & 0x08u) != 0 ? 4 : (val & 0x04u) != 0 ? 3 : (val & 0x02u) != 0 ? 2 : 1; return n * 32 + bits; } static kit_bigint_t kit_bi_and(kit_bigint_t x, kit_bigint_t y) { kit_bigint_t z; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) z.v[i] = x.v[i] & y.v[i]; return z; } static kit_bigint_t kit_bi_or(kit_bigint_t x, kit_bigint_t y) { kit_bigint_t z; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) z.v[i] = x.v[i] | y.v[i]; return z; } static kit_bigint_t kit_bi_xor(kit_bigint_t x, kit_bigint_t y) { kit_bigint_t z; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) z.v[i] = x.v[i] ^ y.v[i]; return z; } static kit_bigint_t kit_bi_shl_uint(kit_bigint_t x, u32 y) { kit_bigint_t z; memset(&z, 0, sizeof z); i64 words = (i64) (y / 32); i64 bits = (i64) (y % 32); i64 i; for (i = words; i < KIT_BIGINT_SIZE / 4; i++) { z.v[i] |= x.v[i - words] << bits; if (bits != 0 && i + 1 < KIT_BIGINT_SIZE / 4) z.v[i + 1] = x.v[i - words] >> (32 - bits); } return z; } static kit_bigint_t kit_bi_shr_uint(kit_bigint_t x, u32 y) { kit_bigint_t z; memset(&z, 0, sizeof z); i64 words = (i64) (y / 32); i64 bits = (i64) (y % 32); i64 i; for (i = KIT_BIGINT_SIZE / 4 - words - 1; i >= 0; i--) { z.v[i] |= x.v[i + words] >> bits; if (bits != 0 && i > 0) z.v[i - 1] = x.v[i + words] << (32 - bits); } return z; } static i8 kit_bi_carry(u32 x, u32 y, i8 carry) { assert(carry == 0 || carry == 1); return 0xffffffffu - x < y || 0xffffffffu - x - y < carry ? 1 : 0; } /* Increment. */ static kit_bigint_t kit_bi_inc(kit_bigint_t x) { kit_bigint_t z; i8 carry = 1; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { z.v[i] = x.v[i] + carry; carry = kit_bi_carry(x.v[i], 0, carry); } return z; } /* Decrement */ static kit_bigint_t kit_bi_dec(kit_bigint_t x) { kit_bigint_t z; i8 carry = 0; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { z.v[i] = x.v[i] + 0xffffffff + carry; carry = kit_bi_carry(x.v[i], 0xffffffff, carry); } return z; } /* Addition. */ static kit_bigint_t kit_bi_add(kit_bigint_t x, kit_bigint_t y) { kit_bigint_t z; i8 carry = 0; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { z.v[i] = x.v[i] + y.v[i] + carry; carry = kit_bi_carry(x.v[i], y.v[i], carry); } return z; } /* Negation. */ static kit_bigint_t kit_bi_neg(kit_bigint_t x) { kit_bigint_t y; i8 carry = 1; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { y.v[i] = (x.v[i] ^ 0xffffffff) + carry; carry = kit_bi_carry(x.v[i] ^ 0xffffffff, 0, carry); } return y; } /* Subtraction. */ static kit_bigint_t kit_bi_sub(kit_bigint_t x, kit_bigint_t y) { kit_bigint_t z; i8 carry = 1; i64 i; for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { z.v[i] = x.v[i] + (y.v[i] ^ 0xffffffff) + carry; carry = kit_bi_carry(x.v[i], (y.v[i] ^ 0xffffffff), carry); } return z; } static kit_bigint_t kit_bi_mul_u32(kit_bigint_t x, u32 y) { kit_bigint_t z; i64 i, k; memset(&z, 0, sizeof z); if (y != 0) for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { if (x.v[i] == 0) continue; u64 carry = ((u64) x.v[i]) * ((u64) y); for (k = i; k < KIT_BIGINT_SIZE / 4 && carry != 0; k++) { u64 sum = ((u64) z.v[k]) + carry; z.v[k] = ((u32) (sum & 0xffffffffull)); carry = sum >> 32; } } return z; } /* Multiplication. */ static kit_bigint_t kit_bi_mul(kit_bigint_t x, kit_bigint_t y) { kit_bigint_t z; i64 i, j, k; memset(&z, 0, sizeof z); for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { if (x.v[i] == 0) continue; for (j = 0; i + j < KIT_BIGINT_SIZE / 4; j++) { if (y.v[j] == 0) continue; u64 carry = ((u64) x.v[i]) * ((u64) y.v[j]); for (k = i + j; k < KIT_BIGINT_SIZE / 4 && carry != 0; k++) { u64 sum = ((u64) z.v[k]) + carry; z.v[k] = ((u32) (sum & 0xffffffffull)); carry = sum >> 32; } } } return z; } typedef struct { i8 undefined; kit_bigint_t quotient; kit_bigint_t remainder; } kit_bi_division_t; /* Unsigned division. */ static kit_bi_division_t kit_bi_udiv(kit_bigint_t x, kit_bigint_t y) { kit_bi_division_t z; memset(&z, 0, sizeof z); i64 y_bits = kit_bi_significant_bit_count(y); if (y_bits == 0) { z.undefined = 1; return z; } i64 x_bits = kit_bi_significant_bit_count(x); i64 shift = x_bits - y_bits; z.remainder = x; z.quotient = kit_bi_u32(0); y = kit_bi_shl_uint(y, (u32) shift); while (shift >= 0) { if (kit_bi_compare(z.remainder, y) >= 0) { z.remainder = kit_bi_sub(z.remainder, y); z.quotient.v[shift / 32] |= (1u << (shift % 32)); } y = kit_bi_shr_uint(y, 1); shift--; } return z; } /* Signed division. * * Remainder is always a non-negative value less than absolute value * of y. */ static kit_bi_division_t kit_bi_div(kit_bigint_t x, kit_bigint_t y) { int x_neg = kit_bi_is_neg(x); int y_neg = kit_bi_is_neg(y); kit_bigint_t x_abs = x_neg ? kit_bi_neg(x) : x; kit_bigint_t y_abs = y_neg ? kit_bi_neg(y) : y; if (x_neg == y_neg) return kit_bi_udiv(x_abs, y_abs); kit_bi_division_t z = kit_bi_udiv(x_abs, y_abs); if (!kit_bi_is_zero(z.remainder) && !y_neg) z.quotient = kit_bi_dec(kit_bi_neg(z.quotient)); else z.quotient = kit_bi_neg(z.quotient); return z; } static void kit_bi_serialize(kit_bigint_t in, u8 *out) { i64 i; assert(out != NULL); for (i = 0; i < KIT_BIGINT_SIZE / 4; i++) { out[i * 4] = (u8) (in.v[i] & 0xff); out[i * 4 + 1] = (u8) ((in.v[i] >> 8) & 0xff); out[i * 4 + 2] = (u8) ((in.v[i] >> 16) & 0xff); out[i * 4 + 3] = (u8) ((in.v[i] >> 24) & 0xff); } } static kit_bigint_t kit_bi_deserialize(u8 *in) { i64 i; kit_bigint_t out; assert(in != NULL); memset(&out, 0, sizeof out); for (i = 0; i < KIT_BIGINT_SIZE; i++) out.v[i / 4] |= ((u32) in[i]) << (8 * (i % 4)); return out; } static u8 kit_bin_digit(char hex) { assert(hex == '0' || hex == '1'); return hex == '1' ? 1 : 0; } static kit_bigint_t kit_bi_from_bin(kit_str_t bin) { kit_bigint_t z; i64 i; memset(&z, 0, sizeof z); for (i = 0; i < bin.size && i / 8 < KIT_BIGINT_SIZE; i++) { u8 digit = kit_bin_digit(bin.values[bin.size - i - 1]); z.v[i / 32] |= digit << (i % 32); } return z; } static u8 kit_dec_digit(char c) { assert('c' >= '0' && c <= '9'); return c >= '0' && c <= '9' ? (u8) (c - '0') : 0; } static kit_bigint_t kit_bi_from_dec(kit_str_t dec) { kit_bigint_t z = kit_bi_u32(0); kit_bigint_t factor = kit_bi_u32(1); i64 i; for (i = 0; i < dec.size; i++) { u32 digit = kit_dec_digit(dec.values[dec.size - i - 1]); z = kit_bi_add(z, kit_bi_mul_u32(factor, digit)); factor = kit_bi_mul_u32(factor, 10); } return z; } static u8 kit_hex_digit(char hex) { assert((hex >= '0' && hex <= '9') || (hex >= 'a' && hex <= 'f') || (hex >= 'A' && hex <= 'F')); if (hex >= '0' && hex <= '9') return hex - '0'; if (hex >= 'a' && hex <= 'f') return hex - 'a'; if (hex >= 'A' && hex <= 'F') return hex - 'A'; return 0; } static kit_bigint_t kit_bi_from_hex(kit_str_t hex) { kit_bigint_t z; i64 i; memset(&z, 0, sizeof z); for (i = 0; i < hex.size && i / 2 < KIT_BIGINT_SIZE; i++) { u8 digit = kit_hex_digit(hex.values[hex.size - i - 1]); z.v[i / 8] |= digit << (4 * (i % 8)); } return z; } static u8 KIT_BASE32_DIGITS[] = { ['1'] = 0, ['2'] = 1, ['3'] = 2, ['4'] = 3, ['5'] = 4, ['6'] = 5, ['7'] = 6, ['8'] = 7, ['9'] = 8, ['a'] = 9, ['b'] = 10, ['c'] = 11, ['d'] = 12, ['e'] = 13, ['f'] = 14, ['g'] = 15, ['h'] = 16, ['j'] = 17, ['k'] = 18, ['m'] = 19, ['n'] = 20, ['p'] = 21, ['q'] = 22, ['r'] = 23, ['s'] = 24, ['t'] = 25, ['u'] = 26, ['v'] = 27, ['w'] = 28, ['x'] = 29, ['y'] = 30, ['z'] = 31 }; static u8 kit_base32_digit(char c) { assert(c >= '\0' && c < sizeof KIT_BASE32_DIGITS); assert(c == '1' || KIT_BASE32_DIGITS[(size_t) (u8) c] != 0); return c >= '\0' && c < sizeof KIT_BASE32_DIGITS ? KIT_BASE32_DIGITS[(size_t) (u8) c] : 0; } static kit_bigint_t kit_bi_from_base32(kit_str_t base32) { kit_bigint_t z; i64 i; memset(&z, 0, sizeof z); for (i = 0; i < base32.size; i++) { z = kit_bi_shl_uint(z, 5 * i); z.v[0] |= kit_base32_digit(base32.values[i]); } return z; } static u8 KIT_BASE58_DIGITS[] = { ['1'] = 0, ['2'] = 1, ['3'] = 2, ['4'] = 3, ['5'] = 4, ['6'] = 5, ['7'] = 6, ['8'] = 7, ['9'] = 8, ['A'] = 9, ['B'] = 10, ['C'] = 11, ['D'] = 12, ['E'] = 13, ['F'] = 14, ['G'] = 15, ['H'] = 16, ['J'] = 17, ['K'] = 18, ['L'] = 19, ['M'] = 20, ['N'] = 21, ['P'] = 22, ['Q'] = 23, ['R'] = 24, ['S'] = 25, ['T'] = 26, ['U'] = 27, ['V'] = 28, ['W'] = 29, ['X'] = 30, ['Y'] = 31, ['Z'] = 32, ['a'] = 33, ['b'] = 34, ['c'] = 35, ['d'] = 36, ['e'] = 37, ['f'] = 38, ['g'] = 39, ['h'] = 40, ['i'] = 41, ['j'] = 42, ['k'] = 43, ['m'] = 44, ['n'] = 45, ['o'] = 46, ['p'] = 47, ['q'] = 48, ['r'] = 49, ['s'] = 50, ['t'] = 51, ['u'] = 52, ['v'] = 53, ['w'] = 54, ['x'] = 55, ['y'] = 56, ['z'] = 57 }; static u8 kit_base58_digit(char c) { assert(c >= '\0' && c < sizeof KIT_BASE58_DIGITS); assert(c == '1' || KIT_BASE58_DIGITS[(size_t) (u8) c] != 0); return c >= '\0' && c < sizeof KIT_BASE58_DIGITS ? KIT_BASE58_DIGITS[(size_t) (u8) c] : 0; } static kit_bigint_t kit_bi_from_base58(kit_str_t base58) { kit_bigint_t z = kit_bi_u32(0); kit_bigint_t factor = kit_bi_u32(1); i64 i; for (i = 0; i < base58.size; i++) { u32 digit = kit_base58_digit(base58.values[base58.size - i - 1]); z = kit_bi_add(z, kit_bi_mul_u32(factor, digit)); factor = kit_bi_mul_u32(factor, 58); } return z; } #if defined(__GNUC__) || defined(__clang__) # pragma GCC pop_options # pragma GCC diagnostic pop #endif #define KIT_BIN(static_str_) \ kit_bi_from_bin(kit_str(sizeof(static_str_) - 1, (static_str_))) #define KIT_DEC(static_str_) \ kit_bi_from_dec(kit_str(sizeof(static_str_) - 1, (static_str_))) #define KIT_HEX(static_str_) \ kit_bi_from_hex(kit_str(sizeof(static_str_) - 1, (static_str_))) #define KIT_BASE32(static_str_) \ kit_bi_from_base32(kit_str(sizeof(static_str_) - 1, (static_str_))) #define KIT_BASE58(static_str_) \ kit_bi_from_base58(kit_str(sizeof(static_str_) - 1, (static_str_))) #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define bigint_t kit_bigint_t # define bi_u32 kit_bi_u32 # define bi_u64 kit_bi_u64 # define bi_i32 kit_bi_i32 # define bi_i64 kit_bi_i64 # define bi_is_zero kit_bi_is_zero # define bi_is_neg kit_bi_is_neg # define bi_equal kit_bi_equal # define bi_compare kit_bi_compare # define bi_carry kit_bi_carry # define bi_inc kit_bi_inc # define bi_dec kit_bi_dec # define bi_add kit_bi_add # define bi_neg kit_bi_neg # define bi_sub kit_bi_sub # define bi_mul kit_bi_mul # define bi_div kit_bi_div # define bi_serialize kit_bi_serialize # define bi_deserialize kit_bi_deserialize # define BIN KIT_BIN # define DEC KIT_DEC # define HEX KIT_HEX # define BASE32 KIT_BASE32 # define BASE58 KIT_BASE58 #endif #endif // ================================================================ // // File: source/kit/input_stream.h // // ================================================================ #ifndef KIT_INPUT_STREAM_H #define KIT_INPUT_STREAM_H #include #ifdef __cplusplus extern "C" { #endif typedef i64 (*kit_is_read_fn)(void *state, kit_str_t destination); typedef struct { void *state; kit_is_read_fn read; } kit_is_handle_t; kit_is_handle_t kit_is_wrap_string(kit_str_t string, kit_allocator_t *alloc); kit_is_handle_t kit_is_wrap_file(FILE *f, kit_allocator_t *alloc); void kit_is_destroy(kit_is_handle_t in); #define KIT_IS_WRAP_STRING(string) kit_is_wrap_string((string), NULL) #define KIT_IS_WRAP_FILE(f) kit_is_wrap_file((f), NULL) #define KIT_IS_READ(in, destination) \ (in).read((in).state, (destination)) #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define is_read_fn kit_is_read_fn # define is_handle_t kit_is_handle_t # define is_wrap_string kit_is_wrap_string # define is_wrap_file kit_is_wrap_file # define is_destroy kit_is_destroy # define IS_WRAP_STRING KIT_IS_WRAP_STRING # define IS_WRAP_FILE KIT_IS_WRAP_FILE # define IS_READ KIT_IS_READ #endif #endif // ================================================================ // // File: source/kit/input_buffer.h // // ================================================================ #ifndef KIT_INPUT_BUFFER_H #define KIT_INPUT_BUFFER_H #ifdef __cplusplus extern "C" { #endif typedef struct { kit_status_t status; i64 offset; void *internal; kit_str_builder_t data; } kit_ib_t; typedef i8 (*kit_ib_read_condition_fn)(kit_str_t data, void *context); kit_ib_t kit_ib_wrap(kit_is_handle_t upstream, kit_allocator_t *alloc); kit_ib_t kit_ib_copy(kit_ib_t buf); kit_ib_t kit_ib_read(kit_ib_t buf, i64 size); kit_ib_t kit_ib_any(kit_ib_t buf, kit_str_t data); kit_ib_t kit_ib_none(kit_ib_t buf, kit_str_t data); kit_ib_t kit_ib_exact(kit_ib_t buf, kit_str_t data); kit_ib_t kit_ib_until(kit_ib_t buf, kit_str_t data); kit_ib_t kit_ib_while(kit_ib_t buf, kit_ib_read_condition_fn condition, void *context); void kit_ib_destroy(kit_ib_t buf); #define KIT_IB_WRAP(upstream) kit_ib_wrap(upstream, NULL) #define KIT_IB_SKIP(buf_, proc_, ...) \ do { \ kit_ib_t temp_buf_ = (buf_); \ (buf_) = proc_((buf_), __VA_ARGS__); \ kit_ib_destroy((temp_buf_)); \ } while (0) #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define ib_t kit_ib_t # define ib_read_condition_fn kit_ib_read_condition_fn # define ib_wrap kit_ib_wrap # define ib_copy kit_ib_copy # define ib_read kit_ib_read # define ib_any kit_ib_any # define ib_none kit_ib_none # define ib_exact kit_ib_exact # define ib_until kit_ib_until # define ib_while kit_ib_while # define ib_destroy kit_ib_destroy # define IB_WRAP KIT_IB_WRAP # define IB_SKIP KIT_IB_SKIP #endif #endif // ================================================================ // // File: source/kit/async_function.h // // ================================================================ #ifndef KIT_ASYNC_FUNCTION_H #define KIT_ASYNC_FUNCTION_H #include #ifdef __cplusplus extern "C" { #endif typedef struct { i32 _; } kit_af_void; typedef void (*kit_af_state_machine)(void *self_void_); #define KIT_AF_STATE_DATA \ struct { \ i32 _index; \ i32 _id; \ kit_af_state_machine _state_machine; \ } typedef struct { KIT_AF_STATE_DATA; } kit_af_type_void; #if defined(__GNUC__) || defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-function" # pragma GCC diagnostic ignored "-Wunknown-pragmas" #endif #define KIT_AF_INTERNAL(coro_) (*((kit_af_type_void *) (coro_))) #ifdef KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH // Application should implement this function if custom async // function dispatch is enabled. // // See KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH macro. // void kit_async_function_dispatch(void *promise); #else static void kit_async_function_dispatch(void *promise) { // Dynamic dispatch by default. // KIT_AF_INTERNAL(promise)._state_machine(promise); } #endif #if defined(__GNUC__) || defined(__clang__) # pragma GCC diagnostic pop #endif #define KIT_AF_STATE(ret_type_, name_, ...) \ struct name_##_coro_state_ { \ KIT_AF_STATE_DATA; \ ret_type_ return_value; \ __VA_ARGS__ \ } #define KIT_AF_DECL(name_) void name_(void *self_void_) #define KIT_CORO_IMPL(name_) \ KIT_AF_DECL(name_) { \ struct name_##_coro_state_ *self = \ (struct name_##_coro_state_ *) self_void_; \ switch (self->_index) { \ case 0:; #define KIT_AF_LINE_() __LINE__ #define KIT_CORO_END \ } \ self->_index = -1; \ } #define KIT_CORO_DECL(ret_type_, name_, ...) \ KIT_AF_STATE(ret_type_, name_, __VA_ARGS__); \ KIT_AF_DECL(name_) #define KIT_CORO(ret_type_, name_, ...) \ KIT_AF_STATE(ret_type_, name_, __VA_ARGS__); \ KIT_CORO_IMPL(name_) #define KIT_CORO_DECL_VOID(name_, ...) \ KIT_CORO_DECL(kit_af_void, name_, __VA_ARGS__) #define KIT_CORO_VOID(name_, ...) \ KIT_CORO(kit_af_void, name_, __VA_ARGS__) #define KIT_STATIC_CORO(ret_type_, name_, ...) \ KIT_AF_STATE(ret_type_, name_, __VA_ARGS__); \ static KIT_CORO_IMPL(name_) #define KIT_STATIC_CORO_VOID(name_, ...) \ KIT_STATIC_CORO(kit_af_void, name_, __VA_ARGS__) #define KIT_AF_EXECUTE(promise_) \ kit_async_function_dispatch(&(promise_)) #define KIT_AF_NEXT(promise_) \ (kit_async_function_dispatch(&(promise_)), (promise_).return_value) #define KIT_AF_YIELD(...) \ do { \ self->_index = KIT_AF_LINE_(); \ self->return_value = __VA_ARGS__; \ return; \ case KIT_AF_LINE_():; \ } while (0) #define KIT_AF_YIELD_VOID \ do { \ self->_index = KIT_AF_LINE_(); \ return; \ case KIT_AF_LINE_():; \ } while (0) #define KIT_AF_RETURN(...) \ do { \ self->_index = -1; \ self->return_value = __VA_ARGS__; \ return; \ } while (0) #define KIT_AF_RETURN_VOID \ do { \ self->_index = -1; \ return; \ } while (0) #define KIT_AF_AWAIT(promise_) \ do { \ case KIT_AF_LINE_(): \ if ((promise_)._index != -1) { \ self->_index = KIT_AF_LINE_(); \ kit_async_function_dispatch(&(promise_)); \ } \ if ((promise_)._index != -1) \ return; \ } while (0) #define KIT_AF_YIELD_AWAIT(promise_) \ do { \ case KIT_AF_LINE_(): \ if ((promise_)._index != -1) { \ self->_index = KIT_AF_LINE_(); \ kit_async_function_dispatch(&(promise_)); \ self->return_value = (promise_).return_value; \ return; \ } \ } while (0) #define KIT_AF_TYPE(coro_) struct coro_##_coro_state_ #define KIT_AF_INITIAL(id_, coro_) \ ._index = 0, ._id = (id_), ._state_machine = (coro_) #define KIT_AF_CREATE(promise_, coro_, ...) \ KIT_AF_TYPE(coro_) \ promise_ = { KIT_AF_INITIAL(0, coro_), __VA_ARGS__ } #define KIT_AF_CREATE_ID(promise_, id_, ...) \ KIT_AF_TYPE(coro_) \ promise_ = { KIT_AF_INITIAL(id_, NULL), __VA_ARGS__ } #define KIT_AF_INIT(promise_, coro_, ...) \ do { \ KIT_AF_CREATE(kit_af_temp_, coro_, __VA_ARGS__); \ memcpy(&(promise_), &kit_af_temp_, sizeof kit_af_temp_); \ } while (0) #define KIT_AF_INIT_ID(promise_, id_, ...) \ do { \ KIT_AF_CREATE_ID(kit_af_temp_, id_, __VA_ARGS__); \ memcpy(&(promise_), &kit_af_temp_, sizeof kit_af_temp_); \ } while (0) #define KIT_AF_FINISHED(promise_) ((promise_)._index == -1) #define KIT_AF_FINISHED_N(return_, promises_, size_) \ do { \ i32 kit_af_index_; \ (return_) = 1; \ for (kit_af_index_ = 0; kit_af_index_ < (size_); \ kit_af_index_++) \ if (!KIT_AF_FINISHED((promises_)[kit_af_index_])) { \ (return_) = 0; \ break; \ } \ } while (0) #define KIT_AF_FINISHED_ALL(return_, promises_) \ KIT_AF_FINISHED_N((return_), (promises_), \ sizeof(promises_) / sizeof((promises_)[0])) #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define af_void kit_af_void # define af_state_machine kit_af_state_machine # define af_type_void kit_af_type_void # define AF_STATE_DATA KIT_AF_STATE_DATA # define AF_STATE KIT_AF_STATE # define AF_DECL KIT_AF_DECL # define CORO_IMPL KIT_CORO_IMPL # define CORO_END KIT_CORO_END # define CORO_DECL KIT_CORO_DECL # define CORO KIT_CORO # define CORO_DECL_VOID KIT_CORO_DECL_VOID # define STATIC_CORO KIT_STATIC_CORO # define STATIC_CORO_VOID KIT_STATIC_CORO_VOID # define CORO_VOID KIT_CORO_VOID # define AF_EXECUTE KIT_AF_EXECUTE # define AF_NEXT KIT_AF_NEXT # define AF_YIELD KIT_AF_YIELD # define AF_YIELD_VOID KIT_AF_YIELD_VOID # define AF_RETURN KIT_AF_RETURN # define AF_RETURN_VOID KIT_AF_RETURN_VOID # define AF_AWAIT KIT_AF_AWAIT # define AF_YIELD_AWAIT KIT_AF_YIELD_AWAIT # define AF_TYPE KIT_AF_TYPE # define AF_INITIAL KIT_AF_INITIAL # define AF_CREATE KIT_AF_CREATE # define AF_INIT KIT_AF_INIT # define AF_FINISHED KIT_AF_FINISHED # define AF_FINISHED_N KIT_AF_FINISHED_N # define AF_FINISHED_ALL KIT_AF_FINISHED_ALL #endif #endif // ================================================================ // // File: source/kit/file.h // // ================================================================ #ifndef KIT_FILE_H #define KIT_FILE_H #include #ifdef __cplusplus extern "C" { #endif typedef enum { KIT_PATH_NONE, KIT_PATH_FILE, KIT_PATH_FOLDER } kit_path_type_t; typedef struct { kit_status_t status; i64 time_modified_sec; i32 time_modified_nsec; i64 size; } kit_file_info_t; typedef struct { kit_status_t status; KIT_DA(kit_str_builder_t) files; } kit_path_list_t; enum { KIT_FILE_MAP_PRIVATE, KIT_FILE_MAP_SHARED }; typedef struct { kit_status_t status; i64 size; u8 *bytes; #if defined(_WIN32) && !defined(__CYGWIN__) void *_file; void *_map; #else int _fd; #endif } kit_mapped_file_t; #if defined(_WIN32) && !defined(__CYGWIN__) # define KIT_PATH_DELIM_C '\\' # define KIT_PATH_DELIM "\\" # define KIT_ENV_HOME "USERPROFILE" #else # define KIT_PATH_DELIM_C '/' # define KIT_PATH_DELIM "/" # define KIT_ENV_HOME "HOME" #endif kit_str_builder_t kit_path_norm(kit_str_t path, kit_allocator_t *alloc); kit_str_builder_t kit_path_join(kit_str_t left, kit_str_t right, kit_allocator_t *alloc); kit_str_builder_t kit_path_user(kit_allocator_t *alloc); kit_str_builder_t kit_path_cache(kit_allocator_t *alloc); kit_str_t kit_path_index(kit_str_t path, i64 index); kit_str_t kit_path_take(kit_str_t path, i64 count); kit_status_t kit_folder_create(kit_str_t path); kit_status_t kit_folder_create_recursive(kit_str_t path); kit_status_t kit_file_remove(kit_str_t path); kit_status_t kit_folder_remove(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); kit_file_info_t kit_file_info(kit_str_t path); kit_path_list_t kit_folder_enum(kit_str_t path, kit_allocator_t *alloc); void kit_path_list_destroy(kit_path_list_t list); kit_mapped_file_t kit_file_map(kit_str_t path, i64 size, i32 mode); kit_status_t kit_file_sync(kit_mapped_file_t *mf); kit_status_t kit_file_unmap(kit_mapped_file_t *mf); #ifdef __cplusplus } #endif #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_cache kit_path_cache # define path_index kit_path_index # define path_take kit_path_take # define folder_create kit_folder_create # define folder_create_recursive kit_folder_create_recursive # define file_remove kit_file_remove # define folder_remove kit_folder_remove # define file_remove_recursive kit_file_remove_recursive # define path_type_t kit_path_type_t # define path_type kit_path_type # define file_info_t kit_file_info_t # define file_info kit_file_info # define path_list_t kit_path_list_t # define folder_enum kit_folder_enum # define path_list_destroy kit_path_list_destroy # define mapped_file_t kit_mapped_file_t # define file_map kit_file_map # define file_sync kit_file_sync # define file_unmap kit_file_unmap # define FILE_MAP_PRIVATE KIT_FILE_MAP_PRIVATE # define FILE_MAP_SHARED KIT_FILE_MAP_SHARED # define PATH_DELIM_C KIT_PATH_DELIM_C # define PATH_DELIM KIT_PATH_DELIM # define ENV_HOME KIT_ENV_HOME # define PATH_NONE KIT_PATH_NONE # define PATH_FILE KIT_PATH_FILE # define PATH_FOLDER KIT_PATH_FOLDER #endif #endif // ================================================================ // // File: source/kit/mersenne_twister_64.h // // ================================================================ #ifndef KIT_MERSENNE_TWISTER_64_H #define KIT_MERSENNE_TWISTER_64_H #ifdef __cplusplus extern "C" { #endif enum { KIT_MT64_N = 312, }; typedef struct { u64 mt[KIT_MT64_N]; u64 index; } kit_mt64_state_t; void kit_mt64_init_array(kit_mt64_state_t *state, i64 size, u64 *seed); void kit_mt64_init(kit_mt64_state_t *state, u64 seed); void kit_mt64_rotate(kit_mt64_state_t *state); u64 kit_mt64_generate(kit_mt64_state_t *state); #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define mt64_state_t kit_mt64_state_t # define mt64_init_array kit_mt64_init_array # define mt64_init kit_mt64_init # define mt64_rotate kit_mt64_rotate # define mt64_generate kit_mt64_generate #endif #endif // ================================================================ // // File: source/kit/secure_random.h // // ================================================================ #ifndef KIT_SECURE_RANDOM_H #define KIT_SECURE_RANDOM_H #ifdef __cplusplus extern "C" { #endif void kit_secure_random(i64 size, void *data); #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define secure_random kit_secure_random #endif #endif // ================================================================ // // File: source/kit/sha256.h // // ================================================================ #ifndef KIT_SHA256_H #define KIT_SHA256_H #ifdef __cplusplus extern "C" { #endif enum { KIT_SHA256_BLOCK_SIZE = 32 }; typedef struct { u8 v[KIT_SHA256_BLOCK_SIZE]; } kit_sha256_hash_t; kit_sha256_hash_t kit_sha256(i64 size, u8 *data); #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define SHA256_BLOCK_SIZE KIT_SHA256_BLOCK_SIZE # define sha256_hash_t kit_sha256_hash_t # define sha256 kit_sha256 #endif #endif // ================================================================ // // File: source/kit/sockets.h // // ================================================================ #ifndef KIT_SOCKETS_H #define KIT_SOCKETS_H #ifndef KIT_DISABLE_SYSTEM_SOCKETS # ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-function" # pragma GCC diagnostic ignored "-Wunknown-pragmas" # endif # ifdef __cplusplus extern "C" { # endif # if defined(_WIN32) && !defined(__CYGWIN__) # define WIN32_LEAN_AND_MEAN # include # include # define socket_t SOCKET # define socklen_t i32 static kit_status_t kit_sockets_init(void) { WSADATA data; memset(&data, 0, sizeof data); WORD version = MAKEWORD(2, 2); return WSAStartup(version, &data) == ERROR_SUCCESS ? KIT_OK : KIT_ERROR_SOCKETS_STARTUP_FAILED; } static kit_status_t kit_sockets_cleanup(void) { WSACleanup(); return KIT_OK; } static i32 kit_socket_set_blocking(socket_t s) { u_long flag = 0; return ioctlsocket(s, FIONBIO, &flag) == 0 ? KIT_OK : KIT_ERROR_SOCKET_CONTROL_FAILED; } static i32 kit_socket_set_nonblocking(socket_t s) { u_long flag = 1; return ioctlsocket(s, FIONBIO, &flag) == 0 ? KIT_OK : KIT_ERROR_SOCKET_CONTROL_FAILED; } # else # include # include # include # include # include # include # include # include # include # include # define socket_t i32 # define closesocket close # define INVALID_SOCKET -1 static kit_status_t kit_sockets_init(void) { signal(SIGPIPE, SIG_IGN); return KIT_OK; } static kit_status_t kit_sockets_cleanup(void) { return KIT_OK; } static i32 kit_socket_set_blocking(socket_t s) { i32 flags = fcntl(s, F_GETFL, 0); return fcntl(s, F_SETFL, flags & ~O_NONBLOCK) == 0 ? KIT_OK : KIT_ERROR_SOCKET_CONTROL_FAILED; } static i32 kit_socket_set_nonblocking(socket_t s) { i32 flags = fcntl(s, F_GETFL, 0); return fcntl(s, F_SETFL, flags | O_NONBLOCK) == 0 ? KIT_OK : KIT_ERROR_SOCKET_CONTROL_FAILED; } # endif # ifdef __cplusplus } # endif # ifdef __GNUC__ # pragma GCC diagnostic pop # endif #endif #endif // ================================================================ // // File: source/kit/shared_memory.h // // ================================================================ #ifndef KIT_SHARED_MEMORY_H #define KIT_SHARED_MEMORY_H #if !defined(_WIN32) || defined(__CYGWIN__) # include #endif #ifdef __cplusplus extern "C" { #endif typedef struct { kit_status_t status; i64 size; u8 *bytes; #if defined(_WIN32) && !defined(__CYGWIN__) void *_handle; #else i8 _owned; char _name[NAME_MAX + 1]; #endif } kit_shared_memory_t; enum { KIT_SHARED_MEMORY_OPEN, KIT_SHARED_MEMORY_CREATE, }; kit_shared_memory_t kit_shared_memory_open(kit_str_t name, i64 size, i32 mode); kit_status_t kit_shared_memory_close(kit_shared_memory_t *mem); kit_status_t kit_shared_memory_clean(kit_str_t name); #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define shared_memory_t kit_shared_memory_t # define shared_memory_clean kit_shared_memory_clean # define shared_memory_open kit_shared_memory_open # define shared_memory_close kit_shared_memory_close # define SHARED_MEMORY_OPEN KIT_SHARED_MEMORY_OPEN # define SHARED_MEMORY_CREATE KIT_SHARED_MEMORY_CREATE #endif #endif // ================================================================ // // File: source/kit/xml.h // // ================================================================ #ifndef KIT_XML_H #define KIT_XML_H #ifdef __cplusplus extern "C" { #endif typedef struct kit_xml_ kit_xml_t; typedef struct { kit_str_builder_t name; kit_str_builder_t value; } kit_xml_property_t; typedef KIT_DA(kit_xml_property_t) kit_da_xml_property_t; typedef KIT_DA(kit_xml_t) kit_da_xml_t; struct kit_xml_ { i8 is_declaration; kit_str_builder_t tag; kit_str_builder_t text; kit_str_builder_t tail; kit_da_xml_property_t properties; kit_da_xml_t children; }; typedef struct { kit_status_t status; kit_xml_t xml; } kit_xml_parse_result_t; typedef struct { kit_status_t status; kit_str_builder_t text; } kit_xml_text_t; kit_xml_parse_result_t kit_xml_parse(kit_is_handle_t is, kit_allocator_t *alloc); kit_xml_text_t kit_xml_print(kit_xml_t *xml, kit_allocator_t *alloc); kit_xml_text_t kit_xml_full_text(kit_xml_t *xml, kit_allocator_t *alloc); void kit_xml_destroy(kit_xml_t *xml); #ifdef __cplusplus } #endif #ifndef KIT_DISABLE_SHORT_NAMES # define xml_parse kit_xml_parse # define xml_print kit_xml_print # define xml_full_text kit_xml_full_text # define xml_destroy kit_xml_destroy # define xml_t kit_xml_t # define xml_property_t kit_xml_property_t # define xml_parse_result_t kit_xml_parse_result_t # define xml_text_t kit_xml_text_t #endif #endif #endif // ================================================================ // // IMPLEMENTATION // // ================================================================ #if defined(KIT_IMPLEMENTATION) && !defined(KIT_INL_H_IMPL) #define KIT_INL_H_IMPL // ================================================================ // // File: source/kit/allocator.c // // ================================================================ #include #include #ifndef KIT_DISABLE_SYSTEM_MALLOC # include #endif static void *kit_allocate_default_(i32 request, i64 size, i64 previous_size, void *pointer) { #ifndef KIT_DISABLE_SYSTEM_MALLOC switch (request) { case KIT_ALLOCATE: case KIT_ALLOCATE_ZERO: { assert(size >= 0); assert(previous_size == 0); assert(pointer == NULL); if (size <= 0) return NULL; void *p = malloc(size); if (p != NULL && request == KIT_ALLOCATE_ZERO) memset(p, 0, size); return p; } case KIT_REALLOCATE: case KIT_REALLOCATE_ZERO: { assert(size >= 0); assert(previous_size != 0 || pointer == NULL); assert(previous_size == 0 || pointer != NULL); if (previous_size == 0 && pointer != NULL) return NULL; if (previous_size != 0 && pointer == NULL) return NULL; if (size == previous_size) return pointer; u8 *p = NULL; if (size > 0) { p = (u8 *) malloc(size); if (p != NULL) { if (size > 0 && previous_size > 0) memcpy(p, pointer, size < previous_size ? size : previous_size); if (request == KIT_REALLOCATE_ZERO && size > previous_size) memset(p + previous_size, 0, size - previous_size); } } free(pointer); return p; } case KIT_DEALLOCATE: assert(size == 0); assert(pointer != NULL); if (pointer != NULL) free(pointer); return NULL; case KIT_DEALLOCATE_ALL: // Do nothing. // return NULL; default:; } #endif assert(0); return NULL; } static void *kit_allocate_from_buffer_(kit_allocator_t *alloc, i32 request, i64 size, i64 previous_size, void *pointer) { assert(alloc != NULL); assert(pointer == NULL || pointer < alloc->data); if (alloc == NULL) return NULL; switch (request) { case KIT_ALLOCATE: case KIT_ALLOCATE_ZERO: { assert(size >= 0); assert(previous_size == 0); assert(pointer == NULL); if (size <= 0) return NULL; if (alloc->size < size) return NULL; void *p = alloc->data; alloc->bytes += size; alloc->size -= size; if (request == KIT_ALLOCATE_ZERO) memset(p, 0, size); return p; } case KIT_REALLOCATE: case KIT_REALLOCATE_ZERO: { assert(size >= 0); assert(previous_size != 0 || pointer == NULL); assert(previous_size == 0 || pointer != NULL); if (size <= 0) return NULL; if (size <= previous_size) return pointer; if (previous_size == 0 && pointer != NULL) return NULL; if (previous_size != 0 && pointer == NULL) return NULL; if ((u8 *) pointer + previous_size == alloc->data) { if (alloc->size < size - previous_size) return NULL; alloc->bytes += size - previous_size; alloc->size -= size - previous_size; return pointer; } if (alloc->size < size) return NULL; u8 *p = alloc->bytes; alloc->bytes += size; alloc->size -= size; if (previous_size > 0) memcpy(p, pointer, previous_size); if (request == KIT_REALLOCATE_ZERO) memset(p + previous_size, 0, size - previous_size); return p; } case KIT_DEALLOCATE: case KIT_DEALLOCATE_ALL: return NULL; default:; } assert(0); return NULL; } #ifndef KIT_ENABLE_CUSTOM_ALLOC_DISPATCH void *kit_alloc_dispatch(kit_allocator_t *alloc, i32 request, i64 size, i64 previous_size, void *pointer) { if (alloc == NULL) return kit_allocate_default_(request, size, previous_size, pointer); switch (alloc->type) { case KIT_ALLOC_TYPE_DEFAULT: return kit_allocate_default_(request, size, previous_size, pointer); case KIT_ALLOC_TYPE_BUFFER: return kit_allocate_from_buffer_(alloc, request, // alignment ((size + 7) / 8) * 8, previous_size, pointer); default:; } return NULL; } #endif kit_allocator_t kit_alloc_default(void) { kit_allocator_t alloc = { .type = KIT_ALLOC_TYPE_DEFAULT, .size = 0, .data = NULL }; return alloc; } kit_allocator_t kit_alloc_buffer(i64 size, void *buffer) { kit_allocator_t alloc = { .type = KIT_ALLOC_TYPE_BUFFER, .size = size, .data = buffer }; return alloc; } // ================================================================ // // File: source/kit/atomic.win32.c // // ================================================================ #ifdef _MSC_VER static_assert(sizeof(char) == 1, "Wrong char size"); static_assert(sizeof(short) == 2, "Wrong short size"); static_assert(sizeof(int) == 4, "Wrong int size"); # include void kit_atomic_store_explicit_8(u8 volatile *var, u8 value, i32 memory_order) { char volatile *dst = (char volatile *) var; char src = (char) value; switch (memory_order) { case memory_order_relaxed: *dst = src; break; default: _InterlockedExchange8(dst, src); } } void kit_atomic_store_explicit_16(u16 volatile *var, u16 value, i32 memory_order) { short volatile *dst = (short volatile *) var; short src = (short) value; switch (memory_order) { case memory_order_relaxed: *dst = src; break; default: _InterlockedExchange16(dst, src); } } void kit_atomic_store_explicit_32(u32 volatile *var, u32 value, i32 memory_order) { int volatile *dst = (int volatile *) var; int src = (int) value; switch (memory_order) { case memory_order_relaxed: *dst = src; break; default: _InterlockedExchange(dst, src); } } void kit_atomic_store_explicit_64(u64 volatile *var, u64 value, i32 memory_order) { __int64 volatile *dst = (__int64 volatile *) var; __int64 src = (__int64) value; switch (memory_order) { case memory_order_relaxed: *dst = src; break; default: # ifdef _WIN64 _InterlockedExchange64(dst, src); # else assert(0); _InterlockedExchange((int volatile *) dst, (int) src); # endif } } u8 kit_atomic_load_explicit_8(volatile u8 *var, i32 memory_order) { char volatile *dst = (char volatile *) var; if (memory_order == memory_order_relaxed) return (u8) *dst; return (u8) _InterlockedOr8(dst, 0); } u16 kit_atomic_load_explicit_16(u16 volatile *var, i32 memory_order) { short volatile *dst = (short volatile *) var; if (memory_order == memory_order_relaxed) return (u16) *dst; return (u16) _InterlockedOr16(dst, 0); } u32 kit_atomic_load_explicit_32(u32 volatile *var, i32 memory_order) { int volatile *dst = (int volatile *) var; if (memory_order == memory_order_relaxed) return (u32) *dst; return (u32) _InterlockedOr(dst, 0); } u64 kit_atomic_load_explicit_64(u64 volatile *var, i32 memory_order) { __int64 volatile *dst = (__int64 volatile *) var; if (memory_order == memory_order_relaxed) return (u64) *dst; # ifdef _WIN64 return (u64) _InterlockedOr64(dst, 0); # else assert(0); return (u64) _InterlockedOr((int volatile *) dst, 0); # endif } u8 kit_atomic_exchange_explicit_8(volatile u8 *var, u8 value, i32 memory_order) { char volatile *dst = (char volatile *) var; char src = (char) value; return (u8) _InterlockedExchange8(dst, src); } u16 kit_atomic_exchange_explicit_16(u16 volatile *var, u16 value, i32 memory_order) { short volatile *dst = (short volatile *) var; short src = (short) value; return (u16) _InterlockedExchange16(dst, src); } u32 kit_atomic_exchange_explicit_32(u32 volatile *var, u32 value, i32 memory_order) { int volatile *dst = (int volatile *) var; int src = (int) value; return (u32) _InterlockedExchange(dst, src); } u64 kit_atomic_exchange_explicit_64(u64 volatile *var, u64 value, i32 memory_order) { __int64 volatile *dst = (__int64 volatile *) var; __int64 src = (__int64) value; # ifdef _WIN64 return (u64) _InterlockedExchange64(dst, src); # else assert(0); return (u64) _InterlockedExchange((int volatile *) dst, (int) src); # endif } int kit_atomic_compare_exchange_explicit_8(volatile u8 *var, u8 *expected, u8 value, i32 memory_order_succ_, i32 memory_order_fail_) { char volatile *dst = (char volatile *) var; char src = (char) value; char exp = (char) *expected; *expected = (u8) _InterlockedCompareExchange8(dst, src, exp); return exp == (char) *expected; } int kit_atomic_compare_exchange_explicit_16(u16 volatile *var, u16 *expected, u16 value, i32 memory_order_succ_, i32 memory_order_fail_) { short volatile *dst = (short volatile *) var; short src = (short) value; short exp = (short) *expected; *expected = (u16) _InterlockedCompareExchange16(dst, src, exp); return exp == (short) *expected; } int kit_atomic_compare_exchange_explicit_32(u32 volatile *var, u32 *expected, u32 value, i32 memory_order_succ_, i32 memory_order_fail_) { int volatile *dst = (int volatile *) var; int src = (int) value; int exp = (int) *expected; *expected = (u32) _InterlockedCompareExchange(dst, src, exp); return exp == (int) *expected; } int kit_atomic_compare_exchange_explicit_64(u64 volatile *var, u64 *expected, u64 value, i32 memory_order_succ_, i32 memory_order_fail_) { __int64 volatile *dst = (__int64 volatile *) var; __int64 src = (__int64) value; __int64 exp = (__int64) *expected; # ifdef _WIN64 *expected = (u64) _InterlockedCompareExchange64(dst, src, exp); # else assert(0); *expected = (u64) _InterlockedCompareExchange((int volatile *) dst, (int) src, (int) exp); # endif return exp == (__int64) *expected; } u8 kit_atomic_fetch_add_explicit_8(volatile u8 *var, u8 value, i32 memory_order) { char volatile *dst = (char volatile *) var; char src = (char) value; return (u8) _InterlockedExchangeAdd8(dst, src); } u16 kit_atomic_fetch_add_explicit_16(u16 volatile *var, u16 value, i32 memory_order) { short volatile *dst = (short volatile *) var; short src = (short) value; return (u16) _InterlockedExchangeAdd16(dst, src); } u32 kit_atomic_fetch_add_explicit_32(u32 volatile *var, u32 value, i32 memory_order) { int volatile *dst = (int volatile *) var; int src = (int) value; return (u32) _InterlockedExchangeAdd(dst, src); } u64 kit_atomic_fetch_add_explicit_64(u64 volatile *var, u64 value, i32 memory_order) { __int64 volatile *dst = (__int64 volatile *) var; __int64 src = (__int64) value; # ifdef _WIN64 return (u64) _InterlockedExchangeAdd64(dst, src); # else assert(0); return (u64) _InterlockedExchangeAdd((int volatile *) dst, (int) src); # endif } #endif // ================================================================ // // File: source/kit/threads.posix.c // // ================================================================ #ifndef KIT_DISABLE_SYSTEM_THREADS # if !defined(_WIN32) || defined(__CYGWIN__) # include # include # include # include # include # include # ifndef PTHREAD_STACK_MIN # define PTHREAD_STACK_MIN 16384 # endif /* Configuration macro: EMULATED_THREADS_USE_NATIVE_TIMEDLOCK Use pthread_mutex_timedlock() for `mtx_timedlock()' Otherwise use mtx_trylock() + *busy loop* emulation. */ # if !defined(__CYGWIN__) && !defined(__APPLE__) && \ !defined(__NetBSD__) # define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK # endif /* Implementation limits: - Conditionally emulation for "mutex with timeout" (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro) */ typedef struct { thrd_start_t func; void *arg; } impl_thrd_param_t; static void *impl_thrd_routine(void *p) { impl_thrd_param_t pack = *((impl_thrd_param_t *) p); kit_alloc_dispatch(NULL, KIT_DEALLOCATE, 0, 0, p); return (void *) (intptr_t) pack.func(pack.arg); } void call_once(once_flag *flag, void (*func)(void)) { pthread_once(flag, func); } int cnd_broadcast(cnd_t *cond) { assert(cond != NULL); return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error; } void cnd_destroy(cnd_t *cond) { assert(cond); pthread_cond_destroy(cond); } int cnd_init(cnd_t *cond) { assert(cond != NULL); return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error; } int cnd_signal(cnd_t *cond) { assert(cond != NULL); return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error; } int cnd_timedwait(cnd_t *cond, mtx_t *mtx, struct timespec const *abs_time) { int rt; assert(mtx != NULL); assert(cond != NULL); assert(abs_time != NULL); rt = pthread_cond_timedwait(cond, mtx, abs_time); if (rt == ETIMEDOUT) return thrd_timedout; return (rt == 0) ? thrd_success : thrd_error; } int cnd_wait(cnd_t *cond, mtx_t *mtx) { assert(mtx != NULL); assert(cond != NULL); return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error; } void mtx_destroy(mtx_t *mtx) { assert(mtx != NULL); pthread_mutex_destroy(mtx); } /* * XXX: Workaround when building with -O0 and without pthreads link. * * In such cases constant folding and dead code elimination won't be * available, thus the compiler will always add the pthread_mutexattr* * functions into the binary. As we try to link, we'll fail as the * symbols are unresolved. * * Ideally we'll enable the optimisations locally, yet that does not * seem to work. * * So the alternative workaround is to annotate the symbols as weak. * Thus the linker will be happy and things don't clash when building * with -O1 or greater. */ # if defined(KIT_HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__) __attribute__((weak)) int pthread_mutexattr_init( pthread_mutexattr_t *attr); __attribute__((weak)) int pthread_mutexattr_settype( pthread_mutexattr_t *attr, int type); __attribute__((weak)) int pthread_mutexattr_destroy( pthread_mutexattr_t *attr); # endif int mtx_init(mtx_t *mtx, int type) { # ifdef KIT_HAVE_PTHREAD_MUTEXATTR_SETTYPE pthread_mutexattr_t attr; # endif assert(mtx != NULL); if (type != mtx_plain && type != mtx_timed && type != (mtx_plain | mtx_recursive) && type != (mtx_timed | mtx_recursive)) return thrd_error; if ((type & mtx_recursive) == 0) { pthread_mutex_init(mtx, NULL); return thrd_success; } # ifdef KIT_HAVE_PTHREAD_MUTEXATTR_SETTYPE pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(mtx, &attr); pthread_mutexattr_destroy(&attr); return thrd_success; # else return thrd_error; # endif } int mtx_lock(mtx_t *mtx) { assert(mtx != NULL); return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error; } int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) { assert(mtx != NULL); assert(ts != NULL); { # ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK int rt; rt = pthread_mutex_timedlock(mtx, ts); if (rt == 0) return thrd_success; return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error; # else time_t expire = time(NULL); expire += ts->tv_sec; while (mtx_trylock(mtx) != thrd_success) { time_t now = time(NULL); if (expire < now) return thrd_timedout; // busy loop! thrd_yield(); } return thrd_success; # endif } } int mtx_trylock(mtx_t *mtx) { assert(mtx != NULL); return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; } int mtx_unlock(mtx_t *mtx) { assert(mtx != NULL); return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error; } int thrd_create_with_stack(thrd_t *thr, thrd_start_t func, void *arg, ptrdiff_t const require_stack_size) { impl_thrd_param_t *pack; assert(thr != NULL); assert(require_stack_size == 0 || require_stack_size >= PTHREAD_STACK_MIN); pthread_attr_t attr; pthread_attr_t *attr_p = NULL; if (require_stack_size > 0) { ptrdiff_t const page_size = (ptrdiff_t) sysconf(_SC_PAGESIZE); ptrdiff_t const delta = require_stack_size % page_size; ptrdiff_t const stack_size = delta == 0 ? require_stack_size : require_stack_size + page_size - delta; if (pthread_attr_init(&attr) != 0) return thrd_nomem; if (pthread_attr_setstacksize(&attr, (size_t) stack_size) != 0) return thrd_wrong_stack_size; attr_p = &attr; } pack = (impl_thrd_param_t *) kit_alloc_dispatch( NULL, KIT_ALLOCATE, sizeof(impl_thrd_param_t), 0, NULL); if (!pack) { if (attr_p) pthread_attr_destroy(attr_p); return thrd_nomem; } pack->func = func; pack->arg = arg; if (pthread_create(thr, attr_p, impl_thrd_routine, pack) != 0) { kit_alloc_dispatch(NULL, KIT_DEALLOCATE, 0, 0, pack); if (attr_p) pthread_attr_destroy(attr_p); return thrd_error; } if (attr_p) pthread_attr_destroy(attr_p); return thrd_success; } int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) { return thrd_create_with_stack(thr, func, arg, 0); } thrd_t thrd_current(void) { return pthread_self(); } int thrd_detach(thrd_t thr) { return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; } int thrd_equal(thrd_t thr0, thrd_t thr1) { return pthread_equal(thr0, thr1); } _Noreturn void thrd_exit(int res) { pthread_exit((void *) (intptr_t) res); } int thrd_join(thrd_t thr, int *res) { void *code; if (pthread_join(thr, &code) != 0) return thrd_error; if (res) *res = (int) (intptr_t) code; return thrd_success; } int thrd_sleep(const struct timespec *time_point, struct timespec *remaining) { assert(time_point != NULL); return nanosleep(time_point, remaining); } void thrd_yield(void) { sched_yield(); } # endif #endif // ================================================================ // // File: source/kit/threads.win32.c // // ================================================================ #ifndef KIT_DISABLE_SYSTEM_THREADS # if defined(_WIN32) && !defined(__CYGWIN__) # include # include # include # include # include # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN 1 # endif # ifndef NOMINMAX # define NOMINMAX # endif # include /* Configuration macro: EMULATED_THREADS_USE_NATIVE_CALL_ONCE Use native WindowsAPI one-time initialization function. (requires WinVista or later) Otherwise emulate by mtx_trylock() + *busy loop* for WinXP. EMULATED_THREADS_TSS_DTOR_SLOTNUM Max registerable TSS dtor number. */ # if _WIN32_WINNT >= 0x0600 /* Prefer native WindowsAPI on newer environment. */ # if !defined(__MINGW32__) # define EMULATED_THREADS_USE_NATIVE_CALL_ONCE # endif # endif # define EMULATED_THREADS_TSS_DTOR_SLOTNUM \ 64 /* see TLS_MINIMUM_AVAILABLE */ /* check configuration */ # if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && \ (_WIN32_WINNT < 0x0600) # error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600 # endif static_assert(sizeof(cnd_t) == sizeof(CONDITION_VARIABLE), "The size of cnd_t must equal to CONDITION_VARIABLE"); static_assert(sizeof(thrd_t) == sizeof(HANDLE), "The size of thrd_t must equal to HANDLE"); static_assert(sizeof(tss_t) == sizeof(DWORD), "The size of tss_t must equal to DWORD"); static_assert(sizeof(mtx_t) == sizeof(CRITICAL_SECTION), "The size of mtx_t must equal to CRITICAL_SECTION"); static_assert(sizeof(once_flag) == sizeof(INIT_ONCE), "The size of once_flag must equal to INIT_ONCE"); /* Implementation limits: - Conditionally emulation for "Initialization functions" (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro) - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop* */ typedef struct { thrd_start_t func; void *arg; thrd_t thrd; } impl_thrd_param_t; struct thrd_state { thrd_t thrd; int handle_need_close; }; static thread_local struct thrd_state impl_current_thread = { 0 }; static unsigned __stdcall impl_thrd_routine(void *p) { impl_thrd_param_t *pack_p = (impl_thrd_param_t *) p; impl_thrd_param_t pack; int code; impl_current_thread.thrd = pack_p->thrd; impl_current_thread.handle_need_close = 0; memcpy(&pack, pack_p, sizeof(impl_thrd_param_t)); kit_alloc_dispatch(NULL, KIT_DEALLOCATE, 0, 0, p); code = pack.func(pack.arg); return (unsigned) code; } static time_t impl_timespec2msec(const struct timespec *ts) { return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L); } static DWORD impl_abs2relmsec(const struct timespec *abs_time) { const time_t abs_ms = impl_timespec2msec(abs_time); struct timespec now; timespec_get(&now, TIME_UTC); const time_t now_ms = impl_timespec2msec(&now); const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD) (abs_ms - now_ms) : 0; return rel_ms; } # ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE struct impl_call_once_param { void (*func)(void); }; static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) { struct impl_call_once_param *param = (struct impl_call_once_param *) Parameter; (param->func)(); ((void) InitOnce); ((void) Context); /* suppress warning */ return TRUE; } # endif /* ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE */ static struct impl_tss_dtor_entry { tss_t key; tss_dtor_t dtor; } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM]; static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) { int i; for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { if (!impl_tss_dtor_tbl[i].dtor) break; } if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM) return 1; impl_tss_dtor_tbl[i].key = key; impl_tss_dtor_tbl[i].dtor = dtor; return 0; } static void impl_tss_dtor_invoke(void) { int i; for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { if (impl_tss_dtor_tbl[i].dtor) { void *val = (void *) (size_t) TlsGetValue( impl_tss_dtor_tbl[i].key); if (val) (impl_tss_dtor_tbl[i].dtor)(val); } } } void call_once(once_flag *flag, void (*func)(void)) { assert(flag && func); # ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE { struct impl_call_once_param param; param.func = func; InitOnceExecuteOnce((PINIT_ONCE) flag, impl_call_once_callback, (PVOID) ¶m, NULL); } # else if (InterlockedCompareExchangePointer( (PVOID volatile *) &flag->status, (PVOID) 1, (PVOID) 0) == 0) { (func)(); InterlockedExchangePointer((PVOID volatile *) &flag->status, (PVOID) 2); } else { while (flag->status == 1) { // busy loop! thrd_yield(); } } # endif } int cnd_broadcast(cnd_t *cond) { assert(cond != NULL); WakeAllConditionVariable((PCONDITION_VARIABLE) cond); return thrd_success; } void cnd_destroy(cnd_t *cond) { assert(cond != NULL); /* do nothing */ (void) cond; } int cnd_init(cnd_t *cond) { assert(cond != NULL); InitializeConditionVariable((PCONDITION_VARIABLE) cond); return thrd_success; } int cnd_signal(cnd_t *cond) { assert(cond != NULL); WakeConditionVariable((PCONDITION_VARIABLE) cond); return thrd_success; } int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time) { assert(cond != NULL); assert(mtx != NULL); assert(abs_time != NULL); const DWORD timeout = impl_abs2relmsec(abs_time); if (SleepConditionVariableCS((PCONDITION_VARIABLE) cond, (PCRITICAL_SECTION) mtx, timeout)) return thrd_success; return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout : thrd_error; } int cnd_wait(cnd_t *cond, mtx_t *mtx) { assert(cond != NULL); assert(mtx != NULL); SleepConditionVariableCS((PCONDITION_VARIABLE) cond, (PCRITICAL_SECTION) mtx, INFINITE); return thrd_success; } void mtx_destroy(mtx_t *mtx) { assert(mtx); DeleteCriticalSection((PCRITICAL_SECTION) mtx); } int mtx_init(mtx_t *mtx, int type) { assert(mtx != NULL); if (type != mtx_plain && type != mtx_timed && type != (mtx_plain | mtx_recursive) && type != (mtx_timed | mtx_recursive)) return thrd_error; InitializeCriticalSection((PCRITICAL_SECTION) mtx); return thrd_success; } int mtx_lock(mtx_t *mtx) { assert(mtx != NULL); EnterCriticalSection((PCRITICAL_SECTION) mtx); return thrd_success; } int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) { assert(mtx != NULL); assert(ts != NULL); while (mtx_trylock(mtx) != thrd_success) { if (impl_abs2relmsec(ts) == 0) return thrd_timedout; /* busy loop! */ thrd_yield(); } return thrd_success; } int mtx_trylock(mtx_t *mtx) { assert(mtx != NULL); return TryEnterCriticalSection((PCRITICAL_SECTION) mtx) ? thrd_success : thrd_busy; } int mtx_unlock(mtx_t *mtx) { assert(mtx != NULL); LeaveCriticalSection((PCRITICAL_SECTION) mtx); return thrd_success; } int thrd_create_with_stack(thrd_t *thr, thrd_start_t func, void *arg, ptrdiff_t const stack_size) { impl_thrd_param_t *pack; uintptr_t handle; assert(thr != NULL); assert(stack_size >= 0 && stack_size < 0x100000000); pack = (impl_thrd_param_t *) kit_alloc_dispatch( NULL, KIT_ALLOCATE, (sizeof(impl_thrd_param_t)), 0, NULL); if (!pack) return thrd_nomem; pack->func = func; pack->arg = arg; handle = _beginthreadex(NULL, (unsigned) stack_size, impl_thrd_routine, pack, CREATE_SUSPENDED, NULL); if (handle == 0) { kit_alloc_dispatch(NULL, KIT_DEALLOCATE, 0, 0, pack); if (errno == EAGAIN || errno == EACCES) return thrd_nomem; return thrd_error; } thr->handle = (void *) handle; pack->thrd = *thr; ResumeThread((HANDLE) handle); return thrd_success; } int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) { return thrd_create_with_stack(thr, func, arg, 0); } thrd_t thrd_current(void) { /* GetCurrentThread() returns a pseudo-handle, which we need * to pass to DuplicateHandle(). Only the resulting handle can be * used from other threads. * * Note that neither handle can be compared to the one by * thread_create. Only the thread IDs - as returned by GetThreadId() * and GetCurrentThreadId() can be compared directly. * * Other potential solutions would be: * - define thrd_t as a thread Ids, but this would mean we'd need to * OpenThread for many operations * - use malloc'ed memory for thrd_t. This would imply using TLS for * current thread. * * Neither is particularly nice. * * Life would be much easier if C11 threads had different * abstractions for threads and thread IDs, just like C++11 threads * does... */ struct thrd_state *state = &impl_current_thread; if (state->thrd.handle == NULL) { if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &(state->thrd.handle), 0, FALSE, DUPLICATE_SAME_ACCESS)) { abort(); } state->handle_need_close = 1; } return state->thrd; } int thrd_detach(thrd_t thr) { CloseHandle(thr.handle); return thrd_success; } int thrd_equal(thrd_t thr0, thrd_t thr1) { return GetThreadId(thr0.handle) == GetThreadId(thr1.handle); } _Noreturn void thrd_exit(int res) { _endthreadex((unsigned) res); } int thrd_join(thrd_t thr, int *res) { DWORD w, code; if (thr.handle == NULL) { return thrd_error; } w = WaitForSingleObject(thr.handle, INFINITE); if (w != WAIT_OBJECT_0) return thrd_error; if (res) { if (!GetExitCodeThread(thr.handle, &code)) { CloseHandle(thr.handle); return thrd_error; } *res = (int) code; } CloseHandle(thr.handle); return thrd_success; } int thrd_sleep(const struct timespec *time_point, struct timespec *remaining) { (void) remaining; assert(time_point); assert(!remaining); /* not implemented */ Sleep((DWORD) impl_timespec2msec(time_point)); return 0; } void thrd_yield(void) { SwitchToThread(); } # endif #endif // ================================================================ // // File: source/kit/array_ref.c // // ================================================================ #include i8 kit_ar_equal_bytes(i64 left_element_size, i64 left_size, void *left_data, i64 right_element_size, i64 right_size, void *right_data) { i64 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((u8 *) left_data + i * left_element_size, (u8 *) right_data + i * left_element_size, left_element_size) != 0) return 0; return 1; } i8 kit_ar_compare(i64 left_element_size, i64 left_size, void *left_data, i64 right_element_size, i64 right_size, void *right_data, kit_ar_compare_fn compare) { i64 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++) { i8 c = compare((u8 *) left_data + i * left_element_size, (u8 *) 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 void kit_da_init(kit_da_void_t *array, i64 element_size, i64 size, kit_allocator_t *alloc) { assert(array != NULL); assert(element_size > 0); assert(size >= 0); 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 i64 eval_capacity(i64 current_cap, i64 required_cap) { if (current_cap == 0) return required_cap; i64 cap = current_cap; while (cap < required_cap) cap *= 2; return cap; } void kit_da_resize(kit_da_void_t *array, i64 element_size, i64 size) { assert(array != NULL); assert(element_size > 0); assert(size >= 0); if (size <= array->capacity) { array->size = size; } else { i64 capacity = eval_capacity(array->capacity, size); 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; } } } void kit_da_resize_exact(kit_da_void_t *array, i64 element_size, i64 capacity) { assert(array != NULL); assert(element_size > 0); assert(capacity >= 0); void *bytes = capacity <= 0 ? NULL : kit_alloc_dispatch(array->alloc, KIT_ALLOCATE, element_size * capacity, 0, NULL); if (bytes != NULL || capacity == 0) { if (array->size > 0 && capacity > 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 = capacity; array->values = bytes; } } // ================================================================ // // File: source/kit/string_builder.c // // ================================================================ // ================================================================ // // File: source/kit/input_stream.c // // ================================================================ #include enum { KIT_INPUT_STREAM_STR, KIT_INPUT_STREAM_FILE }; typedef struct { i64 type; kit_allocator_t *alloc; } kit_is_state_basic_t; typedef struct { i64 type; kit_allocator_t *alloc; kit_str_t string; } kit_is_state_str_t; typedef struct { i64 type; kit_allocator_t *alloc; FILE *file; } kit_is_state_file_t; static int kit_is_check_type_(void *state, i64 type) { kit_is_state_basic_t *basic = (kit_is_state_basic_t *) state; return basic != NULL && basic->type == type; } static i64 kit_read_str_(void *state, kit_str_t destination) { if (!kit_is_check_type_(state, KIT_INPUT_STREAM_STR)) return 0; kit_is_state_str_t *str = (kit_is_state_str_t *) state; i64 size = destination.size < str->string.size ? destination.size : str->string.size; memcpy(destination.values, str->string.values, size); str->string.values += size; str->string.size -= size; return size; } static i64 kit_read_file_(void *state, kit_str_t destination) { if (!kit_is_check_type_(state, KIT_INPUT_STREAM_FILE)) return 0; kit_is_state_file_t *f = (kit_is_state_file_t *) state; if (f->file == NULL || feof(f->file)) return 0; i64 size = (i64) fread(destination.values, 1, destination.size, f->file); if (size <= 0) return 0; return size; } kit_is_handle_t kit_is_wrap_string(kit_str_t string, kit_allocator_t *alloc) { kit_is_handle_t in; memset(&in, 0, sizeof in); kit_is_state_str_t *state = (kit_is_state_str_t *) kit_alloc_dispatch(alloc, KIT_ALLOCATE, sizeof(kit_is_state_str_t), 0, NULL); if (state != NULL) { memset(state, 0, sizeof *state); state->type = KIT_INPUT_STREAM_STR; state->string = string; state->alloc = alloc; in.state = state; in.read = kit_read_str_; } return in; } kit_is_handle_t kit_is_wrap_file(FILE *f, kit_allocator_t *alloc) { kit_is_handle_t in; memset(&in, 0, sizeof in); kit_is_state_file_t *state = (kit_is_state_file_t *) kit_alloc_dispatch(alloc, KIT_ALLOCATE, sizeof(kit_is_state_file_t), 0, NULL); if (state != NULL) { memset(state, 0, sizeof *state); state->type = KIT_INPUT_STREAM_FILE; state->file = f; state->alloc = alloc; in.state = state; in.read = kit_read_file_; } return in; } void kit_is_destroy(kit_is_handle_t in) { kit_is_state_basic_t *basic = (kit_is_state_basic_t *) in.state; if (basic != NULL) kit_alloc_dispatch(basic->alloc, KIT_DEALLOCATE, 0, 0, in.state); } // ================================================================ // // File: source/kit/input_buffer.c // // ================================================================ #include #include typedef struct { i64 ref_count; kit_is_handle_t upstream; kit_allocator_t *alloc; kit_str_builder_t data; } internal_buffer_t; static internal_buffer_t *kit_buf_init_(kit_is_handle_t upstream, kit_allocator_t *alloc) { internal_buffer_t *buf = kit_alloc_dispatch(alloc, KIT_ALLOCATE, sizeof *buf, 0, NULL); if (buf != NULL) { memset(buf, 0, sizeof *buf); buf->ref_count = 1; buf->upstream = upstream; buf->alloc = alloc; DA_INIT(buf->data, 0, alloc); } return buf; } static kit_allocator_t *kit_buf_alloc_(void *p) { assert(p != NULL); return ((internal_buffer_t *) p)->alloc; } static void kit_buf_acquire_(void *p) { assert(p != NULL); ((internal_buffer_t *) p)->ref_count++; } static void kit_buf_release_(void *p) { if (p == NULL) return; internal_buffer_t *buf = (internal_buffer_t *) p; if (--buf->ref_count == 0) { DA_DESTROY(buf->data); kit_alloc_dispatch(buf->alloc, KIT_DEALLOCATE, 0, 0, buf); } } static void kit_buf_adjust_(void *p, i64 size) { assert(p != NULL); assert(size >= 0); internal_buffer_t *buf = (internal_buffer_t *) p; i64 offset = buf->data.size; if (offset < size) { DA_RESIZE(buf->data, size); kit_str_t destination = { .size = size - offset, .values = buf->data.values + offset }; i64 n = KIT_IS_READ(buf->upstream, destination); DA_RESIZE(buf->data, offset + n); } } static i64 kit_buf_read_(void *p, i64 offset, kit_str_t destination) { internal_buffer_t *buf = (internal_buffer_t *) p; i64 n = destination.size < buf->data.size - offset ? destination.size : buf->data.size - offset; memcpy(destination.values, buf->data.values + offset, n); return n; } kit_ib_t kit_ib_wrap(kit_is_handle_t upstream, kit_allocator_t *alloc) { kit_ib_t buf; memset(&buf, 0, sizeof buf); buf.status = KIT_OK; DA_INIT(buf.data, 0, alloc); buf.internal = kit_buf_init_(upstream, alloc); if (buf.internal == NULL) buf.status = KIT_ERROR_BAD_ALLOC; return buf; } kit_ib_t kit_ib_copy(kit_ib_t buf) { kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { next.status = buf.status; return next; } kit_buf_acquire_(buf.internal); next.offset = buf.offset; next.internal = buf.internal; return next; } kit_ib_t kit_ib_read(kit_ib_t buf, i64 size) { kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { next.status = buf.status; return next; } kit_buf_acquire_(buf.internal); kit_buf_adjust_(buf.internal, buf.offset + size); DA_INIT(next.data, size, kit_buf_alloc_(buf.internal)); if (next.data.size != size) next.status = KIT_ERROR_BAD_ALLOC; kit_str_t destination = { .size = next.data.size, .values = next.data.values }; i64 n = kit_buf_read_(buf.internal, buf.offset, destination); next.offset = buf.offset + n; next.internal = buf.internal; DA_RESIZE(next.data, n); if (next.data.size != n) next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_ib_t kit_ib_any(kit_ib_t buf, kit_str_t data) { kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { next.status = buf.status; return next; } kit_buf_acquire_(buf.internal); DA_INIT(next.data, 0, kit_buf_alloc_(buf.internal)); i64 size = 0; for (;; ++size) { kit_buf_adjust_(buf.internal, buf.offset + size + 1); DA_RESIZE(next.data, size + 1); assert(next.data.size == size + 1); if (next.data.size != size + 1) { next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_str_t destination = { .size = 1, .values = next.data.values + size }; i64 n = kit_buf_read_(buf.internal, buf.offset + size, destination); if (n != 1) break; i8 found = 0; for (i64 i = 0; i < data.size; i++) if (data.values[i] == destination.values[0]) { found = 1; break; } if (!found) break; } next.offset = buf.offset + size; next.internal = buf.internal; DA_RESIZE(next.data, size); if (next.data.size != size) next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_ib_t kit_ib_none(kit_ib_t buf, kit_str_t data) { kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { next.status = buf.status; return next; } kit_buf_acquire_(buf.internal); DA_INIT(next.data, 0, kit_buf_alloc_(buf.internal)); i64 size = 0; for (;; ++size) { kit_buf_adjust_(buf.internal, buf.offset + size + 1); DA_RESIZE(next.data, size + 1); assert(next.data.size == size + 1); if (next.data.size != size + 1) { next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_str_t destination = { .size = 1, .values = next.data.values + size }; i64 n = kit_buf_read_(buf.internal, buf.offset + size, destination); if (n != 1) break; i8 found = 0; for (i64 i = 0; i < data.size; i++) if (data.values[i] == destination.values[0]) { found = 1; break; } if (found) break; } next.offset = buf.offset + size; next.internal = buf.internal; DA_RESIZE(next.data, size); if (next.data.size != size) next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_ib_t kit_ib_exact(kit_ib_t buf, kit_str_t data) { kit_ib_t res = kit_ib_read(buf, data.size); if (!AR_EQUAL(res.data, data)) res.status = KIT_ERROR_INTERNAL; return res; } kit_ib_t kit_ib_until(kit_ib_t buf, kit_str_t data) { kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { next.status = buf.status; return next; } kit_buf_acquire_(buf.internal); DA_INIT(next.data, 0, kit_buf_alloc_(buf.internal)); i64 size = 0; for (;; ++size) { kit_buf_adjust_(buf.internal, buf.offset + size + 1); DA_RESIZE(next.data, size + 1); assert(next.data.size == size + 1); if (next.data.size != size + 1) { next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_str_t destination = { .size = 1, .values = next.data.values + size }; i64 n = kit_buf_read_(buf.internal, buf.offset + size, destination); if (n != 1) break; if (size + 1 >= data.size && AR_EQUAL(kit_str(data.size, next.data.values + (size + 1 - data.size)), data)) { size -= data.size - 1; break; } } next.offset = buf.offset + size; next.internal = buf.internal; DA_RESIZE(next.data, size); if (next.data.size != size) next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_ib_t kit_ib_while(kit_ib_t buf, kit_ib_read_condition_fn condition, void *context) { kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { next.status = buf.status; return next; } kit_buf_acquire_(buf.internal); DA_INIT(next.data, 0, kit_buf_alloc_(buf.internal)); i64 size = 0; for (;; ++size) { kit_buf_adjust_(buf.internal, buf.offset + size + 1); DA_RESIZE(next.data, size + 1); assert(next.data.size == size + 1); if (next.data.size != size + 1) { next.status = KIT_ERROR_BAD_ALLOC; return next; } kit_str_t destination = { .size = 1, .values = next.data.values + size }; i64 n = kit_buf_read_(buf.internal, buf.offset + size, destination); kit_str_t data = { .size = size + 1, .values = next.data.values }; if (n != 1 || condition == NULL || condition(data, context) == 0) break; } next.offset = buf.offset + size; next.internal = buf.internal; DA_RESIZE(next.data, size); if (next.data.size != size) next.status = KIT_ERROR_BAD_ALLOC; return next; } void kit_ib_destroy(kit_ib_t buf) { kit_buf_release_(buf.internal); DA_DESTROY(buf.data); } // ================================================================ // // File: source/kit/file.c // // ================================================================ #include #include #include enum { PATH_BUF_SIZE = 4096 }; #if defined(_WIN32) && !defined(__CYGWIN__) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # ifndef NOMINMAX # define NOMINMAX # endif # include # include #else # include # include # include # include # include # include #endif #ifdef __APPLE__ # define st_mtim st_mtimespec #endif #ifndef PATH_MAX # define PATH_MAX MAX_PATH #endif static i32 is_delim(char c) { return c == '/' || c == '\\'; } static kit_str_builder_t kit_get_env_(char *name, kit_allocator_t *alloc) { char *val = getenv(name); i64 size = val != NULL ? (i64) strlen(val) : 0; str_builder_t result; DA_INIT(result, size, alloc); assert(result.size == size); if (result.size == size && size > 0) memcpy(result.values, val, result.size); else DA_RESIZE(result, 0); return result; } kit_str_builder_t kit_path_norm(kit_str_t path, kit_allocator_t *alloc) { str_t parent = SZ(".."); i64 i, i1, j; str_builder_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 s = { .size = i - i1 - 1, .values = path.values + i1 + 1 }; if (AR_EQUAL(s, parent)) { i32 have_parent = 0; i64 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; } i64 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_C; else norm.values[size] = norm.values[i]; size++; } } norm.size = size; return norm; } kit_str_builder_t kit_path_join(kit_str_t left, kit_str_t right, kit_allocator_t *alloc) { i64 left_size = left.size; i64 right_size = right.size; char *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_str_builder_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_C; memcpy(joined.values + left_size + 1, right_values, right_size); return joined; } kit_str_builder_t kit_path_user(kit_allocator_t *alloc) { kit_str_builder_t user = kit_get_env_(KIT_ENV_HOME, alloc); if (user.size == 0) { DA_RESIZE(user, 1); if (user.size == 1) user.values[0] = '.'; } return user; } kit_str_builder_t kit_path_cache(kit_allocator_t *alloc) { kit_str_builder_t cache, user; #if defined(_WIN32) && !defined(__CYGWIN__) cache = kit_get_env_("LOCALAPPDATA", alloc); if (cache.size != 0) return cache; DA_DESTROY(cache); #endif cache = kit_get_env_("XDG_CACHE_HOME", alloc); if (cache.size != 0) return cache; DA_DESTROY(cache); user = kit_path_user(alloc); cache = #ifdef __APPLE__ kit_path_join(WRAP_STR(user), SZ("Library" PATH_DELIM "Caches"), alloc); #else kit_path_join(WRAP_STR(user), SZ(".cache"), alloc); #endif DA_DESTROY(user); return cache; } kit_str_t kit_path_index(kit_str_t path, i64 index) { str_t s = { .size = 0, .values = NULL }; i64 i0 = 0; i64 i = 0; i64 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 path, i64 count) { str_t s = { .size = 0, .values = path.values }; i64 i0 = 0; i64 i = 0; i64 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; } static void kit_prepare_path_(char *buf, kit_str_t 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]; \ kit_prepare_path_(buf, path) kit_status_t kit_folder_create(kit_str_t path) { PREPARE_PATH_BUF_; #if defined(_WIN32) && !defined(__CYGWIN__) return CreateDirectoryA(buf, NULL) ? KIT_OK : KIT_ERROR_MKDIR_FAILED; #else return mkdir(buf, 0775) == 0 ? KIT_OK : KIT_ERROR_MKDIR_FAILED; #endif } kit_status_t kit_folder_create_recursive(kit_str_t path) { for (i32 i = 0;; i++) { str_t part = kit_path_take(path, i); i32 type = kit_path_type(part); if (type == KIT_PATH_FILE) return KIT_ERROR_FILE_ALREADY_EXISTS; if (type == KIT_PATH_NONE) { kit_status_t s = kit_folder_create(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 path) { PREPARE_PATH_BUF_; #if defined(_WIN32) && !defined(__CYGWIN__) return DeleteFileA(buf) ? KIT_OK : KIT_ERROR_UNLINK_FAILED; #else return unlink(buf) == 0 ? KIT_OK : KIT_ERROR_UNLINK_FAILED; #endif } kit_status_t kit_folder_remove(kit_str_t path) { PREPARE_PATH_BUF_; #if defined(_WIN32) && !defined(__CYGWIN__) return RemoveDirectoryA(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 path, kit_allocator_t *alloc) { i32 type = kit_path_type(path); i64 i; switch (type) { case KIT_PATH_FILE: { kit_status_t s = kit_file_remove(path); assert(s == KIT_OK); return s; } case KIT_PATH_FOLDER: { kit_path_list_t list = kit_folder_enum(path, alloc); assert(list.status == KIT_OK); if (list.status != KIT_OK) { kit_path_list_destroy(list); return list.status; } for (i = 0; i < list.files.size; i++) { str_builder_t full_path = kit_path_join( path, WRAP_STR(list.files.values[i]), alloc); kit_status_t s = kit_file_remove_recursive( WRAP_STR(full_path), alloc); DA_DESTROY(full_path); assert(s == KIT_OK); } kit_path_list_destroy(list); kit_status_t s = kit_folder_remove(path); assert(s == KIT_OK); return s; } default:; } return KIT_ERROR_FILE_DOES_NOT_EXIST; } kit_path_type_t kit_path_type(kit_str_t path) { PREPARE_PATH_BUF_; #if defined(_WIN32) && !defined(__CYGWIN__) if (PathFileExistsA(buf)) { if ((GetFileAttributesA(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 path) { kit_file_info_t result; memset(&result, 0, sizeof result); PREPARE_PATH_BUF_; #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE f = CreateFileA(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) { i64 nsec100 = (((u64) ft.dwHighDateTime) << 32) | (u64) ft.dwLowDateTime; result.time_modified_sec = (i64) (nsec100 / 10000000); result.time_modified_nsec = (i32) (100 * (nsec100 % 10000000)); } else { assert(0); } DWORD high; DWORD low = GetFileSize(f, &high); result.size = (i64) ((((u64) high) << 32) | (u64) 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 = (i64) info.st_size; # ifndef st_mtime // No support for nanosecond timestamps. // result.time_modified_sec = (i64) info.st_mtime; # else result.time_modified_sec = (i64) info.st_mtim.tv_sec; result.time_modified_nsec = (i32) info.st_mtim.tv_nsec; # endif result.status = KIT_OK; return result; } #endif result.status = KIT_ERROR_FILE_DOES_NOT_EXIST; return result; } kit_path_list_t kit_folder_enum(kit_str_t path, kit_allocator_t *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 + 3 >= PATH_BUF_SIZE) { result.status = KIT_ERROR_PATH_TOO_LONG; return result; } buf[path.size] = '\\'; buf[path.size + 1] = '*'; WIN32_FIND_DATAA data; HANDLE find = FindFirstFileA(buf, &data); if (find == INVALID_HANDLE_VALUE) return result; do { if (strcmp(data.cFileName, ".") == 0 || strcmp(data.cFileName, "..") == 0) continue; i64 n = result.files.size; DA_RESIZE(result.files, n + 1); if (result.files.size != n + 1) { result.status = KIT_ERROR_BAD_ALLOC; break; } i64 size = (i64) strlen(data.cFileName); 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; } memcpy(result.files.values[n].values, data.cFileName, size); } while (FindNextFileA(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; i64 n = result.files.size; DA_RESIZE(result.files, n + 1); if (result.files.size != n + 1) { result.status = KIT_ERROR_BAD_ALLOC; break; } i64 size = (i64) 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) { i64 i; for (i = 0; i < list.files.size; i++) DA_DESTROY(list.files.values[i]); DA_DESTROY(list.files); } kit_mapped_file_t kit_file_map(kit_str_t path, i64 size, i32 mode) { assert(size > 0); assert(path.size > 0); assert(path.size <= PATH_MAX); assert(path.values != NULL); kit_mapped_file_t mf; memset(&mf, 0, sizeof mf); if (size <= 0) { mf.status = KIT_ERROR_INVALID_SIZE; return mf; } if (path.size <= 0) { mf.status = KIT_ERROR_INVALID_ARGUMENT; return mf; } if (path.size > PATH_MAX) { mf.status = KIT_ERROR_PATH_TOO_LONG; return mf; } #if defined(_WIN32) && !defined(__CYGWIN__) char buf[MAX_PATH + 1]; memcpy(buf, path.values, path.size); buf[path.size] = '\0'; HANDLE file = CreateFileA( buf, GENERIC_READ | GENERIC_WRITE, mode == FILE_MAP_SHARED ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file == INVALID_HANDLE_VALUE) { mf.status = KIT_ERROR_OPEN_FAILED; return mf; } LONG high = (LONG) (size >> 32); if (SetFilePointer(file, (LONG) size, &high, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { CloseHandle(file); assert(0); mf.status = KIT_ERROR_TRUNCATE_FAILED; return mf; } if (!SetEndOfFile(file)) { CloseHandle(file); assert(0); mf.status = KIT_ERROR_TRUNCATE_FAILED; return mf; } HANDLE map = CreateFileMappingA(file, NULL, PAGE_READWRITE, (DWORD) (size >> 32), (DWORD) size, NULL); if (map == INVALID_HANDLE_VALUE) { CloseHandle(file); assert(0); mf.status = KIT_ERROR_MAP_FAILED; return mf; } void *p = MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, (SIZE_T) size); if (p == NULL) { CloseHandle(map); CloseHandle(file); assert(0); mf.status = KIT_ERROR_MAP_FAILED; return mf; } mf.status = KIT_OK; mf.size = size; mf.bytes = p; mf._file = file; mf._map = map; #else char buf[PATH_MAX + 1]; memcpy(buf, path.values, path.size); buf[path.size] = '\0'; i32 fd = open(buf, O_RDWR | O_CREAT, 0664); if (fd == -1) { mf.status = KIT_ERROR_OPEN_FAILED; return mf; } if (ftruncate(fd, size) == -1) { close(fd); assert(0); mf.status = KIT_ERROR_TRUNCATE_FAILED; return mf; } void *p = mmap( NULL, size, PROT_READ | PROT_WRITE, mode == KIT_FILE_MAP_SHARED ? MAP_SHARED : MAP_PRIVATE, fd, 0); if (p == MAP_FAILED) { close(fd); assert(0); mf.status = KIT_ERROR_MAP_FAILED; return mf; } mf.status = KIT_OK; mf.size = size; mf.bytes = (u8 *) p; mf._fd = fd; #endif return mf; } kit_status_t kit_file_sync(kit_mapped_file_t *mf) { assert(mf != NULL); if (mf == NULL) return KIT_ERROR_INVALID_ARGUMENT; kit_status_t status = KIT_OK; #if !defined(_WIN32) || defined(__CYGWIN__) if (msync(mf->bytes, mf->size, MS_SYNC) != 0) status |= KIT_ERROR_SYNC_FAILED; #endif return status; } kit_status_t kit_file_unmap(kit_mapped_file_t *mf) { assert(mf != NULL); if (mf == NULL) return KIT_ERROR_INVALID_ARGUMENT; kit_status_t status = KIT_OK; #if defined(_WIN32) && !defined(__CYGWIN__) if (!UnmapViewOfFile(mf->bytes)) status |= KIT_ERROR_UNMAP_FAILED; if (!CloseHandle(mf->_map) || !CloseHandle(mf->_file)) status |= KIT_ERROR_CLOSE_FAILED; #else if (munmap(mf->bytes, mf->size) != 0) status |= KIT_ERROR_UNMAP_FAILED; if (close(mf->_fd) != 0) status |= KIT_ERROR_CLOSE_FAILED; #endif return status; } // ================================================================ // // File: source/kit/mersenne_twister_64.c // // ================================================================ #define MM 156 #define MATRIX_A 0xb5026f5aa96619e9ull #define UM 0xffffffff80000000ull #define LM 0x7fffffffull void kit_mt64_init_array(kit_mt64_state_t *state, i64 size, u64 *seed) { i64 i; for (i = 0; i < size && i < KIT_MT64_N; i++) state->mt[i] = seed[i]; for (state->index = size; state->index < KIT_MT64_N; state->index++) state->mt[state->index] = (6364136223846793005ull * (state->mt[state->index - 1] ^ (state->mt[state->index - 1] >> 62u)) + state->index); } void kit_mt64_init(kit_mt64_state_t *state, u64 seed) { kit_mt64_init_array(state, 1, &seed); } void kit_mt64_rotate(kit_mt64_state_t *state) { static u64 mag01[2] = { 0ull, MATRIX_A }; u64 x; i32 i; for (i = 0; i < KIT_MT64_N - MM; i++) { x = (state->mt[i] & UM) | (state->mt[i + 1] & LM); state->mt[i] = state->mt[i + MM] ^ (x >> 1u) ^ mag01[(i32) (x & 1ull)]; } for (; i < KIT_MT64_N - 1; i++) { x = (state->mt[i] & UM) | (state->mt[i + 1] & LM); state->mt[i] = state->mt[i + (MM - KIT_MT64_N)] ^ (x >> 1u) ^ mag01[(i32) (x & 1ull)]; } x = (state->mt[KIT_MT64_N - 1] & UM) | (state->mt[0] & LM); state->mt[KIT_MT64_N - 1] = state->mt[MM - 1] ^ (x >> 1u) ^ mag01[(i32) (x & 1ull)]; state->index = 0; } u64 kit_mt64_generate(kit_mt64_state_t *state) { if (state->index >= KIT_MT64_N) kit_mt64_rotate(state); u64 x = state->mt[state->index++]; x ^= (x >> 29u) & 0x5555555555555555ull; x ^= (x << 17u) & 0x71d67fffeda60000ull; x ^= (x << 37u) & 0xfff7eee000000000ull; x ^= (x >> 43u); return x; } #undef MM #undef MATRIX_A #undef UM #undef LM // ================================================================ // // File: source/kit/secure_random.c // // ================================================================ #include #include #include #if defined(_WIN32) && !defined(__CYGWIN__) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN 1 # endif # include # include #else # include #endif void kit_secure_random(i64 size, void *data) { assert(size >= 0); assert(data != NULL); if (size <= 0 || data == NULL) return; #if defined(_WIN32) && !defined(__CYGWIN__) HCRYPTPROV prov = 0; if (!CryptAcquireContextW(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT) || !CryptGenRandom(prov, (DWORD) size, (BYTE *) data) || !CryptReleaseContext(prov, 0)) abort(); #else FILE *f = fopen("/dev/urandom", "rb"); if (f == NULL) abort(); i64 n = (i64) fread(data, 1, size, f); fclose(f); if (n != size) abort(); #endif } // ================================================================ // // File: source/kit/sha256.c // // ================================================================ #include #include #define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) #define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) #define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) #define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) #define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) static u32 kit_sha256_k[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; static void kit_sha256_transform(u32 *state, u8 *data) { assert(state != NULL); assert(data != NULL); u32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = ((u32) data[j] << 24) | ((u32) data[j + 1] << 16) | ((u32) data[j + 2] << 8) | ((u32) data[j + 3]); for (; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; f = state[5]; g = state[6]; h = state[7]; for (i = 0; i < 64; ++i) { t1 = h + EP1(e) + CH(e, f, g) + kit_sha256_k[i] + m[i]; t2 = EP0(a) + MAJ(a, b, c); h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } kit_sha256_hash_t kit_sha256(i64 in_size, u8 *in_data) { assert(in_size >= 0); assert(in_data != NULL); u32 state[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; u8 data[64]; i64 i; i64 datalen = 0; u64 bitlen = 0; if (in_data != NULL) for (i = 0; i < in_size; ++i) { data[datalen] = in_data[i]; datalen++; if (datalen != 64) continue; kit_sha256_transform(state, data); bitlen += 512; datalen = 0; } i = datalen; if (datalen < 56) { data[i++] = 0x80; while (i < 56) data[i++] = 0x00; } else { data[i++] = 0x80; while (i < 64) data[i++] = 0x00; kit_sha256_transform(state, data); memset(data, 0, 56); } bitlen += datalen * 8; data[63] = bitlen; data[62] = bitlen >> 8; data[61] = bitlen >> 16; data[60] = bitlen >> 24; data[59] = bitlen >> 32; data[58] = bitlen >> 40; data[57] = bitlen >> 48; data[56] = bitlen >> 56; kit_sha256_transform(state, data); kit_sha256_hash_t hash; memset(&hash, 0, sizeof hash); for (i = 0; i < 4; ++i) { hash.v[i] = (state[0] >> (24 - i * 8)) & 0xff; hash.v[i + 4] = (state[1] >> (24 - i * 8)) & 0xff; hash.v[i + 8] = (state[2] >> (24 - i * 8)) & 0xff; hash.v[i + 12] = (state[3] >> (24 - i * 8)) & 0xff; hash.v[i + 16] = (state[4] >> (24 - i * 8)) & 0xff; hash.v[i + 20] = (state[5] >> (24 - i * 8)) & 0xff; hash.v[i + 24] = (state[6] >> (24 - i * 8)) & 0xff; hash.v[i + 28] = (state[7] >> (24 - i * 8)) & 0xff; } return hash; } #undef ROTLEFT #undef ROTRIGHT #undef CH #undef MAJ #undef EP0 #undef EP1 #undef SIG0 #undef SIG1 // ================================================================ // // File: source/kit/shared_memory.posix.c // // ================================================================ #if !defined(_WIN32) || defined(__CYGWIN__) # include # include # include # include # include # include # include kit_shared_memory_t kit_shared_memory_open(kit_str_t name, i64 size, i32 mode) { kit_shared_memory_t mem; memset(&mem, 0, sizeof mem); assert(size > 0); assert(name.size > 0); assert(name.size + 1 <= NAME_MAX); assert(name.values != NULL); if (size <= 0) { mem.status = KIT_ERROR_INVALID_SIZE; return mem; } if (name.size <= 0) { mem.status = KIT_ERROR_INVALID_NAME; return mem; } if (name.size + 1 > NAME_MAX) { mem.status = KIT_ERROR_NAME_TOO_LONG; return mem; } for (i64 i = 0; i < name.size; i++) if (name.values[i] == '/' || name.values[i] == '\\') { mem.status = KIT_ERROR_INVALID_NAME; return mem; } mem._name[0] = '/'; memcpy(mem._name + 1, name.values, name.size); mem._name[1 + name.size] = '\0'; i32 fd = shm_open(mem._name, mode == KIT_SHARED_MEMORY_CREATE ? O_RDWR | O_CREAT | O_EXCL : O_RDWR, mode == KIT_SHARED_MEMORY_CREATE ? 0660 : 0); if (fd == -1) { mem.status = KIT_ERROR_OPEN_FAILED; return mem; } if (mode == KIT_SHARED_MEMORY_CREATE && ftruncate(fd, size) == -1) { shm_unlink(mem._name); assert(0); mem.status = KIT_ERROR_TRUNCATE_FAILED; return mem; } void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (p == MAP_FAILED) { shm_unlink(mem._name); assert(0); mem.status = KIT_ERROR_MAP_FAILED; return mem; } mem.status = KIT_OK; mem.size = size; mem.bytes = (u8 *) p; mem._owned = (mode == KIT_SHARED_MEMORY_CREATE); return mem; } kit_status_t kit_shared_memory_close(kit_shared_memory_t *mem) { assert(mem != NULL); if (mem == NULL) return KIT_ERROR_INVALID_ARGUMENT; kit_status_t status = KIT_OK; if (munmap(mem->bytes, mem->size) != 0) status |= KIT_ERROR_UNMAP_FAILED; if (mem->_owned && shm_unlink(mem->_name) != 0) status |= KIT_ERROR_UNLINK_FAILED; return status; } kit_status_t kit_shared_memory_clean(kit_str_t name) { assert(name.size > 0); assert(name.size + 1 <= NAME_MAX); assert(name.values != NULL); if (name.size <= 0) return KIT_ERROR_INVALID_NAME; if (name.size + 1 > NAME_MAX) return KIT_ERROR_NAME_TOO_LONG; for (i64 i = 0; i < name.size; i++) if (name.values[i] == '/' || name.values[i] == '\\') return KIT_ERROR_INVALID_NAME; char buf[NAME_MAX + 1] = "/"; memcpy(buf + 1, name.values, name.size); buf[1 + name.size] = '\0'; if (shm_unlink(buf) != 0) return KIT_ERROR_UNLINK_FAILED; return KIT_OK; } #endif // ================================================================ // // File: source/kit/shared_memory.win32.c // // ================================================================ #if defined(_WIN32) && !defined(__CYGWIN__) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # ifndef NOMINMAX # define NOMINMAX # endif # include kit_shared_memory_t kit_shared_memory_open(kit_str_t name, i64 size, i32 mode) { kit_shared_memory_t mem; memset(&mem, 0, sizeof mem); char buf[264] = "Global\\"; assert(size > 0); assert(name.size > 0); assert(name.size + 8 < sizeof buf); assert(name.values != NULL); if (name.size <= 0) { mem.status = KIT_ERROR_INVALID_NAME; return mem; } if (name.size + 8 >= sizeof buf) { mem.status = KIT_ERROR_NAME_TOO_LONG; return mem; } for (i64 i = 0; i < name.size; i++) if (name.values[i] == '/' || name.values[i] == '\\') { mem.status = KIT_ERROR_INVALID_NAME; return mem; } memcpy(buf + 7, name.values, name.size); buf[7 + name.size] = '\0'; HANDLE h = mode == KIT_SHARED_MEMORY_CREATE ? CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD) (size >> 32), (DWORD) size, buf) : OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, buf); if (h == INVALID_HANDLE_VALUE) { mem.status = KIT_ERROR_OPEN_FAILED; return mem; } void *p = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, (SIZE_T) size); if (p == NULL) { CloseHandle(h); mem.status = KIT_ERROR_MAP_FAILED; return mem; } mem.status = KIT_OK; mem.size = size; mem.bytes = (u8 *) p; mem._handle = h; return mem; } kit_status_t kit_shared_memory_close(kit_shared_memory_t *mem) { assert(mem != NULL); i32 status = KIT_OK; if (!UnmapViewOfFile(mem->bytes)) status |= KIT_ERROR_UNMAP_FAILED; if (!CloseHandle(mem->_handle)) status |= KIT_ERROR_UNLINK_FAILED; return status; } kit_status_t kit_shared_memory_clean(kit_str_t name) { // Do nothing. // return KIT_OK; } #endif // ================================================================ // // File: source/kit/xml.c // // ================================================================ #include typedef struct { ib_t last; kit_str_builder_t text; kit_da_xml_t tags; } kit_xml_intermediate_t; static kit_status_t kit_xml_unescape_(str_builder_t *str) { assert(str != NULL); str_builder_t buf; DA_INIT(buf, str->size, str->alloc); buf.size = 0; for (i64 i = 0; i < str->size; i++) if (str->values[i] != '&') buf.values[buf.size++] = str->values[i]; else { i64 n = 1; while (i + n < str->size && str->values[i + n] != ';') n++; if (i + n >= str->size) { DA_DESTROY(buf); return KIT_ERROR_INTERNAL; } if (n == 3 && memcmp(str->values + i, "<", 4) == 0) buf.values[buf.size++] = '<'; else if (n == 3 && memcmp(str->values + i, ">", 4) == 0) buf.values[buf.size++] = '>'; else if (n == 4 && memcmp(str->values + i, "&", 5) == 0) buf.values[buf.size++] = '&'; else if (n == 5 && memcmp(str->values + i, """, 6) == 0) buf.values[buf.size++] = '"'; else if (n == 5 && memcmp(str->values + i, "'", 6) == 0) buf.values[buf.size++] = '\''; else { DA_DESTROY(buf); return KIT_ERROR_INTERNAL; } i += n; } DA_DESTROY(*str); *str = buf; return KIT_OK; } static ib_t kit_xml_parse_text_(ib_t begin) { ib_t text = ib_until(begin, SZ("<")); ib_t last = ib_copy(text); for (;;) { ib_t comment_open = ib_exact(last, SZ("")); ib_t comment_close = ib_exact(comment_text, SZ("-->")); ib_t next_text = ib_until(comment_close, SZ("<")); if (next_text.status == KIT_OK && next_text.data.size > 0) { i64 n = text.data.size; DA_RESIZE(text.data, n + next_text.data.size); assert(text.data.size == n + next_text.data.size); if (text.data.size != n + next_text.data.size) next_text.status = KIT_ERROR_BAD_ALLOC; else memcpy(text.data.values + n, next_text.data.values, next_text.data.size); } ib_destroy(last); last = ib_copy(next_text); ib_destroy(comment_open); ib_destroy(comment_text); ib_destroy(comment_close); ib_destroy(next_text); } // move DA_DESTROY(last.data); last.data = text.data; memset(&text.data, 0, sizeof text.data); kit_status_t s = kit_xml_unescape_(&last.data); if (s != KIT_OK) last.status = s; ib_destroy(text); return last; } static ib_t kit_xml_parse_string_(ib_t begin) { ib_t quotes_open = ib_exact(begin, SZ("\"")); ib_t apostr_open = ib_exact(begin, SZ("'")); ib_t open = quotes_open.status == KIT_OK ? quotes_open : apostr_open; ib_t text = ib_until(open, WRAP_STR(open.data)); ib_t close = ib_exact(text, WRAP_STR(open.data)); // move DA_DESTROY(close.data); close.data = text.data; memset(&text.data, 0, sizeof text.data); kit_status_t s = kit_xml_unescape_(&close.data); if (s == KIT_OK) close.status = s; ib_destroy(quotes_open); ib_destroy(apostr_open); ib_destroy(text); return close; } static kit_xml_intermediate_t kit_xml_parse_buf_( ib_t begin, kit_allocator_t *alloc) { kit_xml_intermediate_t res; memset(&res, 0, sizeof res); ib_t last, spaces; memset(&last, 0, sizeof last); memset(&spaces, 0, sizeof spaces); ib_t tag_text = kit_xml_parse_text_(begin); last = ib_copy(tag_text); DA_INIT(res.tags, 0, alloc); for (;;) { ib_t tagend_open = ib_exact(last, SZ("")); ib_destroy(spaces); DA_INIT(tag.properties, 0, alloc); ib_destroy(last); last = ib_copy(tag_name); for (;;) { spaces = ib_any(last, SZ(" \t\r\n")); ib_t property = ib_none(spaces, SZ(" \t\r\n=?/>")); ib_destroy(spaces); if (property.status != KIT_OK || property.data.size == 0) { ib_destroy(property); break; } spaces = ib_any(property, SZ(" \t\r\n")); ib_t equals = ib_exact(spaces, SZ("=")); ib_destroy(spaces); spaces = ib_any(equals, SZ(" \t\r\n")); ib_t value = kit_xml_parse_string_(spaces); ib_destroy(spaces); ib_destroy(last); last = ib_copy(value); if (last.status == KIT_OK) { i64 n = tag.properties.size; DA_RESIZE(tag.properties, n + 1); assert(tag.properties.size == n + 1); if (tag.properties.size != n + 1) { last.status = KIT_ERROR_BAD_ALLOC; DA_DESTROY(tag.properties); } else { // move tag.properties.values[n].name = property.data; memset(&property.data, 0, sizeof property.data); // move tag.properties.values[n].value = value.data; memset(&value.data, 0, sizeof value.data); } } ib_destroy(property); ib_destroy(equals); ib_destroy(value); } spaces = ib_any(last, SZ(" \t\r\n")); if (tag.is_declaration) { ib_t tag_decl_close = ib_exact(spaces, SZ("?>")); ib_destroy(spaces); ib_destroy(last); last = tag_decl_close; DA_INIT(tag.text, 0, alloc); DA_INIT(tag.children, 0, alloc); } else { ib_t tag_close = ib_exact(spaces, SZ(">")); ib_t tag_close_empty = ib_exact(spaces, SZ("/>")); ib_destroy(spaces); if (tag_close.status == KIT_OK) { kit_xml_intermediate_t im = kit_xml_parse_buf_(tag_close, alloc); tag.text = im.text; tag.children = im.tags; tagend_open = ib_exact(im.last, SZ("")); ib_destroy(spaces); ib_destroy(tagend_open); ib_destroy(tagend_name); ib_destroy(last); last = tagend_close; } else if (tag_close_empty.status == KIT_OK) { ib_destroy(last); last = ib_copy(tag_close_empty); DA_INIT(tag.text, 0, alloc); DA_INIT(tag.children, 0, alloc); } else last.status = KIT_ERROR_INTERNAL; ib_destroy(tag_close); ib_destroy(tag_close_empty); } ib_t tag_tail = kit_xml_parse_text_(last); ib_destroy(last); last = ib_copy(tag_tail); if (last.status == KIT_OK) { i64 n = res.tags.size; DA_RESIZE(res.tags, n + 1); assert(res.tags.size == n + 1); if (res.tags.size != n + 1) { last.status = KIT_ERROR_BAD_ALLOC; xml_destroy(&tag); } else { // move tag.tag = tag_name.data; memset(&tag_name.data, 0, sizeof tag_name.data); // move tag.tail = tag_tail.data; memset(&tag_tail.data, 0, sizeof tag_tail.data); res.tags.values[n] = tag; } } else xml_destroy(&tag); ib_destroy(tag_open); ib_destroy(tag_name); ib_destroy(tag_tail); } if (last.status != KIT_OK) { for (i64 i = 0; i < res.tags.size; i++) xml_destroy(res.tags.values + i); DA_DESTROY(res.text); DA_DESTROY(res.tags); } else { // move res.text = tag_text.data; memset(&tag_text.data, 0, sizeof tag_text.data); } ib_destroy(tag_text); res.last = last; return res; } kit_xml_parse_result_t kit_xml_parse(kit_is_handle_t is, kit_allocator_t *alloc) { ib_t ib = ib_wrap(is, alloc); kit_xml_intermediate_t im = kit_xml_parse_buf_(ib, alloc); ib_destroy(ib); kit_xml_parse_result_t res; memset(&res, 0, sizeof res); res.status = im.last.status; ib_destroy(im.last); if (res.status != KIT_OK) return res; if (im.text.size == 0 && im.tags.size == 1) { res.xml = im.tags.values[0]; DA_DESTROY(im.text); DA_DESTROY(im.tags); return res; } DA_INIT(res.xml.tag, 0, alloc); DA_INIT(res.xml.tail, 0, alloc); DA_INIT(res.xml.properties, 0, alloc); res.xml.text = im.text; res.xml.children = im.tags; return res; } kit_xml_text_t kit_xml_print(kit_xml_t *xml, kit_allocator_t *alloc) { assert(xml != NULL); xml_text_t result; memset(&result, 0, sizeof result); result.status = KIT_ERROR_NOT_IMPLEMENTED; return result; } static kit_status_t kit_xml_append_text_(str_builder_t *buf, xml_t *xml) { assert(buf != NULL); assert(xml != NULL); i64 n = buf->size; DA_RESIZE(*buf, n + xml->text.size); assert(buf->size == n + xml->text.size); if (buf->size != n + xml->text.size) return KIT_ERROR_BAD_ALLOC; if (xml->text.size > 0) memcpy(buf->values + n, xml->text.values, xml->text.size); for (i64 i = 0; i < xml->children.size; i++) { kit_status_t s = kit_xml_append_text_(buf, xml->children.values + i); if (s != KIT_OK) return s; str_t tail = WRAP_STR(xml->children.values[i].tail); if (tail.size <= 0) continue; n = buf->size; DA_RESIZE(*buf, n + tail.size); assert(buf->size == n + tail.size); if (buf->size != n + tail.size) return KIT_ERROR_BAD_ALLOC; if (tail.size > 0) memcpy(buf->values + n, tail.values, tail.size); } return KIT_OK; } kit_xml_text_t kit_xml_full_text(kit_xml_t *xml, kit_allocator_t *alloc) { kit_xml_text_t res; res.status = KIT_OK; DA_INIT(res.text, 0, alloc); if (xml != NULL) res.status = kit_xml_append_text_(&res.text, xml); else res.status = KIT_ERROR_INVALID_ARGUMENT; return res; } void kit_xml_destroy(kit_xml_t *xml) { assert(xml != NULL); if (xml == NULL) return; for (i64 i = 0; i < xml->properties.size; i++) { DA_DESTROY(xml->properties.values[i].name); DA_DESTROY(xml->properties.values[i].value); } for (i64 i = 0; i < xml->children.size; i++) kit_xml_destroy(xml->children.values + i); DA_DESTROY(xml->tag); DA_DESTROY(xml->text); DA_DESTROY(xml->tail); DA_DESTROY(xml->properties); DA_DESTROY(xml->children); } #endif