-#ifndef _COMMONLIB_H_\r
-#define _COMMONLIB_H_\r
-#include <stdio.h>\r
-#include <stdbool.h>\r
-#include <stdarg.h>\r
-#include <stdint.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <ctype.h>\r
-#include <assert.h>\r
-#include <limits.h>\r
-\r
-#if defined(_WIN32) && defined(_MSC_VER)\r
-// NOTE: Don't include unwanted files to speed up compilation\r
-#define WIN32_LEAN_AND_MEAN\r
-#define NOCOMM\r
-#include <windows.h>\r
-#undef C_ASSERT // Bruh\r
-#endif\r
-\r
-// Remove Prefix\r
-#ifdef COMMONLIB_REMOVE_PREFIX\r
-#define ASSERT C_ASSERT\r
-#define ARRAY_LEN C_ARRAY_LEN\r
-\r
-#define da_append c_da_append\r
-#define da_free c_da_free\r
-#define da_shift c_da_shift\r
-#define DYNAMIC_ARRAY_INITIAL_CAPACITY c_DYNAMIC_ARRAY_INITIAL_CAPACITY\r
-// #define c_DYNAMIC_ARRAY_INITIAL_CAPACITY\r
-\r
-#define os_get_timedate c_os_get_timedate\r
-#define os_file_exists c_os_file_exists\r
-\r
-#define log_error c_log_error\r
-#define log_info c_log_info\r
-#define log_warning c_log_warning\r
-#define log_debug c_log_debug\r
-\r
-#define read_file c_read_file\r
-#define touch_file_if_doesnt_exist c_touch_file_if_doesnt_exist\r
-\r
-#define Arena c_Arena\r
-#define arena_make c_arena_make\r
-#define arena_alloc c_arena_alloc\r
-#define arena_reset c_arena_reset\r
-#define arena_free c_arena_free\r
-#define arena_alloc_str c_arena_alloc_str\r
-#define arena_alloc_wstr c_arena_alloc_wstr\r
-\r
-#define String_view c_String_view\r
-\r
-#define shift_args c_shift_args\r
-\r
-#define SV_FMT c_SV_FMT\r
-#define SV_ARG c_SV_ARG\r
-\r
-#define SV c_SV\r
-\r
-#define sv_print_dumb c_sv_print_dumb\r
-#define sv_from_cstr c_sv_from_cstr\r
-#define sv_lpop c_sv_lpop\r
-#define sv_lpop_until_predicate c_sv_lpop_until_predicate\r
-#define sv_lpop_until_string c_sv_lpop_until_string\r
-#define sv_rpop_until_predicate c_sv_rpop_until_predicate\r
-#define sv_lpop_until_char c_sv_lpop_until_char\r
-#define sv_rpop_until_char c_sv_rpop_until_char\r
-#define sv_lremove c_sv_lremove\r
-#define sv_rremove c_sv_rremove\r
-#define sv_lremove_until_char c_sv_lremove_until_char\r
-#define sv_rremove_until_char c_sv_rremove_until_char\r
-#define sv_lremove_until_char_after c_sv_lremove_until_char_after\r
-#define sv_rremove_until_char_after c_sv_rremove_until_char_after\r
-#define sv_ltrim c_sv_ltrim\r
-#define sv_rtrim c_sv_rtrim\r
-#define sv_trim c_sv_trim\r
-#define sv_to_cstr c_sv_to_cstr\r
-#define sv_to_int c_sv_to_int\r
-#define sv_to_uint c_sv_to_uint\r
-#define sv_to_uint8_hex c_sv_to_uint8_hex\r
-#define sv_to_float c_sv_to_float\r
-#define sv_contains_char c_sv_contains_char\r
-#define sv_is_hex_numbers c_sv_is_hex_numbers\r
-#define sv_equals c_sv_equals\r
-#define sv_get_part c_sv_get_part\r
-\r
-\r
-#endif // COMMONLIB_REMOVE_PREFIX\r
-\r
-// Memory allocation\r
-#ifndef C_MALLOC\r
-#define C_MALLOC malloc\r
-#endif\r
-#ifndef C_FREE\r
-#define C_FREE free\r
-#endif\r
-#ifndef C_REALLOC\r
-#define C_REALLOC realloc\r
-#endif\r
-#ifndef C_MEMCPY\r
-#define C_MEMCPY memcpy\r
-#endif\r
-\r
-\r
-// typedefs\r
-typedef unsigned int uint;\r
-typedef uint8_t uint8;\r
-typedef uint16_t uint16;\r
-typedef uint32_t uint32;\r
-typedef uint64_t uint64;\r
-\r
-typedef int8_t int8;\r
-typedef int16_t int16;\r
-typedef int32_t int32;\r
-typedef int64_t int64;\r
-\r
-typedef float float32;\r
-typedef double float64;\r
-\r
-typedef wchar_t wchar;\r
-\r
-typedef const char* cstr;\r
-typedef const wchar* wstr;\r
-\r
-\r
-// Macros\r
-#if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER)\r
-#define C_ASSERT(condition, msg) do {\\r
- if (!(condition)) {\\r
- fprintf(stderr, "%s:%d:0 [ASSERTION FAILED] %s: %s", __FILE__, __LINE__, #condition, msg);\\r
- DebugBreak();\\r
- }\\r
- } while (0)\r
-#else\r
-#define C_ASSERT(condition, msg) do {\\r
- if (!(condition)) {\\r
- fprintf(stderr, "%s:%d:0 [ASSERTION FAILED] %s: %s", __FILE__, __LINE__, #condition, msg);\\r
- exit(1);\\r
- }\\r
- } while (0)\r
-#endif // defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER)\r
-\r
-#define C_ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))\r
-\r
-#define c_shift(xs, xsz) (assert(xsz > 0 && "Array is empty"), xsz--, *xs++)\r
-#define c_shift_args c_shift\r
-\r
-//\r
-// Math\r
-//\r
-\r
-int clampi(int v, int min, int max);\r
-float clampf(float v, float min, float max);\r
-\r
-//\r
-// Struct pre-decls\r
-//\r
-\r
-typedef struct c_Arena c_Arena;\r
-\r
-//\r
-// ## Data Structures\r
-//\r
-\r
-//\r
-// Dynamic-Array\r
-//\r
-\r
-// NOTE: To use c_da_append() the Dynamic-Array struct should be defined using\r
-// DEFINE_DYNAMIC_ARRAY or have the same members as below!\r
-// NOTE!!!: We actually don't want this since this makes the user want to\r
-// use this macro to define dynamic arrays. But the user might want extra fields\r
-// in the struct; So we recommend defining da structs manually like:\r
-// ```C\r
-// typedef struct {\r
-// <item-type> items;\r
-// size_t count;\r
-// size_t capacity;\r
-// [extra fields...];\r
-// }\r
-// ```\r
-// #define DEFINE_DYNAMIC_ARRAY(struct_name, elm_type) typedef struct {\r
-// elm_type *items;\r
-// size_t count;\r
-// size_t capacity;\r
-// }\r
-\r
-#define c_DYNAMIC_ARRAY_INITIAL_CAPACITY (sizeof(size_t))\r
-\r
-#define c_da_append(da, elm) do {\\r
- if ((da).items == NULL) {\\r
- (da).capacity = c_DYNAMIC_ARRAY_INITIAL_CAPACITY;\\r
- (da).count = 0;\\r
- (da).items = C_MALLOC(sizeof(elm) * (da).capacity);\\r
- }\\r
- if ((da).count >= (da).capacity) {\\r
- (da).capacity *= 2;\\r
- (da).items = C_REALLOC((da).items, (da).capacity * sizeof(elm));\\r
- C_ASSERT((da).items != NULL, "TODO: Log error instead of asserting");\\r
- }\\r
- (da).items[(da).count++] = elm;\\r
- } while (0)\r
-\r
-// NOTE: We cant do C_ASSERT() here because it aint one expression...\r
-#define c_da_shift(da) (assert((da).count > 0 && "Array is empty"), (da).count--, *(da).items++)\r
-#define c_da_free(da) C_FREE((da).items)\r
-\r
-//\r
-// OS\r
-//\r
-\r
-#if defined(__linux__)\r
-#include <sys/stat.h>\r
-#endif\r
-\r
-void c_os_get_timedate(c_Arena* a);\r
-bool c_os_file_exists(cstr filename);\r
-\r
-//\r
-// Logging\r
-//\r
-\r
-#define c_log_error(fmt, ...) do {\\r
- fprintf(stderr, "%s"fmt"\n", "[ERROR] ", ##__VA_ARGS__);\\r
- } while (0)\r
-#define c_log_info(fmt, ...) do {\\r
- fprintf(stdout, "%s"fmt"\n", "[INFO] ", ##__VA_ARGS__);\\r
- } while (0)\r
-#define c_log_warning(fmt, ...) do {\\r
- fprintf(stdout, "%s"fmt"\n", "[WARNING] ", ##__VA_ARGS__);\\r
- } while (0)\r
-#ifdef DEBUG\r
-#define c_log_debug(fmt, ...) do {\\r
- fprintf(stdout, "%s"fmt"\n", "[DEBUG] ", ##__VA_ARGS__);\\r
- } while (0)\r
-#else\r
-#define c_log_debug(...)\r
-#endif // DEBUG\r
-//\r
-// File\r
-//\r
-\r
-// reads entire file and gives back the file content and filesize in bytes. (caller must be responsible for freeing the string!)\r
-const char* c_read_file(const char* filename, int *file_size);\r
-void c_touch_file_if_doesnt_exist(cstr file);\r
-\r
-//\r
-// ### Allocators ###\r
-//\r
-\r
-//\r
-// c_Arena\r
-//\r
-\r
-#define ARENA_BUFF_INITIAL_SIZE (1024*4)\r
-\r
-struct c_Arena {\r
- void* buff;\r
- uint64 buff_size;\r
- void* ptr;\r
-};\r
-\r
-// pass size 0 to get ARENA_BUFF_INITIAL_SIZE\r
-c_Arena c_arena_make(size_t size);\r
-void* c_arena_alloc(c_Arena* a, size_t size);\r
-void c_arena_reset(c_Arena* a);\r
-void c_arena_free(c_Arena* a);\r
-\r
-// TODO: Do we embed stb_snprintf to use stbsp_snprintf?\r
-#define c_arena_alloc_str(a, fmt, ...) c_arena_alloc(&(a), sizeof(char)*snprintf((a).ptr, (int)((a).buff_size - ((uint8*)(a).ptr - (uint8*)(a).buff)), (fmt), __VA_ARGS__)+1)\r
-#define c_arena_alloc_wstr(a, fmt, ...) c_arena_alloc(&a, sizeof(char)*wprintf(a.ptr, a.buff_size - ((uint8*)a.ptr - (uint8*)a.buff), (fmt), __VA_ARGS__)+1)\r
-\r
-//\r
-// String Builder\r
-//\r
-\r
-typedef struct {\r
- char* data;\r
- size_t size;\r
- size_t capacity;\r
-} c_String_builder;\r
-\r
-void c_sb_append(c_String_builder* sb, char* data);\r
-\r
-//\r
-// String view\r
-//\r
-\r
-typedef struct {\r
- char *data;\r
- size_t count;\r
-} c_String_view;\r
-\r
-#define c_SV_FMT "%.*s"\r
-#define c_SV_ARG(sv) (int)(sv).count, (sv).data\r
-\r
-#define c_SV(cstr) (c_String_view){.data = (char*)cstr, strlen(cstr)}\r
-\r
-void c_sv_print_dumb(c_String_view sv);\r
-c_String_view c_sv_from_cstr(const char* cstr); // Actually just use SV(cstr) macro...\r
-c_String_view c_sv_lpop(c_String_view* sv, uint32 n);\r
-c_String_view c_sv_lpop_until_predicate(c_String_view* sv, int(*predicate)(int));\r
-c_String_view c_sv_lpop_until_string(c_String_view* sv, const char *string);\r
-c_String_view c_sv_rpop_until_predicate(c_String_view* sv, int(*predicate)(int));\r
-c_String_view c_sv_lpop_until_char(c_String_view* sv, char ch);\r
-c_String_view c_sv_rpop_until_char(c_String_view* sv, char ch);\r
-void c_sv_lremove(c_String_view* sv, size_t n);\r
-void c_sv_rremove(c_String_view* sv, size_t n);\r
-void c_sv_lremove_until_char(c_String_view* sv, char ch);\r
-void c_sv_rremove_until_char(c_String_view* sv, char ch);\r
-void c_sv_lremove_until_char_after(c_String_view* sv, char ch);\r
-void c_sv_rremove_until_char_after(c_String_view* sv, char ch);\r
-void c_sv_ltrim(c_String_view* sv);\r
-void c_sv_rtrim(c_String_view* sv);\r
-void c_sv_trim(c_String_view* sv);\r
-char* c_sv_to_cstr(c_String_view sv);\r
-int64 c_sv_to_int(c_String_view sv, int *count, int base);\r
-uint64 c_sv_to_uint(c_String_view sv, int *count, int base);\r
-float64 c_sv_to_float(c_String_view sv, int *count);\r
-bool c_sv_contains_char(c_String_view sv, char ch);\r
-bool c_sv_is_hex_numbers(c_String_view sv);\r
-bool c_sv_equals(c_String_view sv1, c_String_view sv2);\r
-c_String_view c_sv_get_part(c_String_view sv, int from, int to);\r
-\r
-#endif /* _COMMONLIB_H_ */\r
-\r
-//////////////////////////////////////////////////\r
-#ifdef COMMONLIB_IMPLEMENTATION\r
-#include <string.h>\r
-#include <errno.h>\r
-#include <stdlib.h>\r
-#include <assert.h>\r
-\r
-// My things implementation:\r
-\r
-//\r
-// Math\r
-//\r
-\r
-int clampi(int v, int min, int max) {\r
- v = v < min ? min : v;\r
- v = v > max ? max : v;\r
- return v;\r
-}\r
-\r
-float clampf(float v, float min, float max) {\r
- v = v < min ? min : v;\r
- v = v > max ? max : v;\r
- return v;\r
-}\r
-\r
-//\r
-// OS\r
-//\r
-\r
-#if defined(_WIN32) || defined(__CYGWIN__)\r
-void c_os_get_timedate(c_Arena* a) {\r
- (void)a;\r
- C_ASSERT(false, "Unimplemented!");\r
-}\r
-\r
-bool c_os_file_exists(cstr filename) {\r
- (void) filename;\r
- C_ASSERT(false, "Unimplemented!");\r
- return false;\r
-}\r
-\r
-#elif defined(__linux__)\r
-void c_os_get_timedate(c_Arena* a) {\r
- (void)a;\r
- C_ASSERT(false, "Unimplemented!");\r
-}\r
-\r
-bool c_os_file_exists(cstr filename) {\r
- struct stat buf;\r
- return stat(filename, &buf) == 0;\r
-}\r
-#endif\r
-\r
-// simple and dirty way to have defering in C (not recommended to use!)\r
-#define defer(ret_val) \\r
- result = ret_val;\\r
- goto defer\r
-\r
-const char *c_read_file(const char* filename, int *file_size) {\r
- FILE* f = fopen(filename, "r");\r
- char* result = NULL;\r
-\r
- if (f == NULL){\r
- // TODO: Replace every strerror with strerror_s\r
- c_log_error("'%s': %s", filename, strerror(errno));\r
- defer(NULL);\r
- }\r
-\r
- if (fseek(f, 0, SEEK_END) < 0) {\r
- c_log_error("'%s': %s", filename, strerror(errno));\r
- defer(NULL);\r
- }\r
-\r
- size_t fsize = ftell(f);\r
-\r
- if (fsize == (size_t)-1){\r
- c_log_error("'%s': %s", filename, strerror(errno));\r
- defer(NULL);\r
- }\r
-\r
- result = C_MALLOC(sizeof(char)*(fsize+1));\r
-\r
- if (result == NULL){\r
- c_log_error("'%s': %s", filename, strerror(errno));\r
- defer(NULL);\r
- }\r
-\r
- if (fseek(f, 0, SEEK_SET) < 0) {\r
- c_log_error("'%s': %s", filename, strerror(errno));\r
- defer(NULL);\r
- }\r
-\r
- size_t read = fread((char*)result, sizeof(char), fsize, f);\r
-\r
- // Process the result to remove '\r' characters\r
- char* cleaned_result = malloc(read + 1); // Allocate memory for cleaned result\r
- if (cleaned_result == NULL) {\r
- fprintf(stderr, "Memory allocation failed\n");\r
- free(result);\r
- return NULL;\r
- }\r
-\r
- size_t j = 0; // Index for cleaned_result\r
- for (size_t i = 0; i < read; i++) {\r
- if (result[i] != '\r') { // Skip '\r' characters\r
- cleaned_result[j++] = result[i];\r
- }\r
- }\r
- cleaned_result[j] = '\0'; // Null-terminate the cleaned result\r
-\r
- free(result); // Free the original result\r
- *file_size = (int)j; // Update the file size without '\r'\r
- return cleaned_result; // Return the cleaned result\r
-\r
- *file_size = (int)read;\r
-\r
- defer:\r
- if (f) fclose(f);\r
- if (result == NULL) *file_size = -1;\r
- return result;\r
-}\r
-\r
-void c_touch_file_if_doesnt_exist(cstr filename) {\r
- if (c_os_file_exists(filename)) return;\r
- FILE* file = fopen(filename, "w");\r
- if (file) fclose(file);\r
-}\r
-\r
-//\r
-// ### Allocators ###\r
-//\r
-\r
-// c_Arena\r
-\r
-c_Arena c_arena_make(size_t size) {\r
- c_Arena res = {0};\r
- res.buff_size = size == 0 ? ARENA_BUFF_INITIAL_SIZE : size;\r
- res.buff = C_MALLOC(res.buff_size);\r
- res.ptr = res.buff;\r
-\r
- C_ASSERT(res.buff, "Malloc failed?");\r
-\r
- return res;\r
-}\r
-\r
-void* c_arena_alloc(c_Arena* a, size_t size) {\r
- C_ASSERT(a->buff, "Bro pass an initialized arena!");\r
-\r
- void* res = a->ptr;\r
- a->ptr = (uint8*)a->ptr + size;\r
-\r
- size_t diff = (size_t)((uint8*)a->ptr - (uint8*)a->buff);\r
- if (diff > a->buff_size) {\r
- c_log_info("c_Arena resized from %zu to %zu", a->buff_size, a->buff_size*2);\r
- a->buff_size *= 2;\r
- a->buff = C_REALLOC(a->buff, a->buff_size);\r
- a->ptr = (uint8*)a->buff + diff;\r
- res = a->ptr;\r
- a->ptr = (uint8*)a->ptr + size;\r
- }\r
- /* C_ASSERT((size_t)((uint8*)a->ptr - (uint8*)a->buff) <= a->buff_size); */\r
-\r
- return res;\r
-}\r
-\r
-void c_arena_reset(c_Arena* a) {\r
- a->ptr = a->buff;\r
-}\r
-\r
-void c_arena_free(c_Arena* a) {\r
- C_FREE(a->buff);\r
-}\r
-\r
-//\r
-// String Builder\r
-//\r
-\r
-void c_sb_append(c_String_builder* sb, char* data) {\r
- size_t data_size = strlen(data);\r
- if (sb->size + data_size > sb->capacity) {\r
- sb->capacity *= 2;\r
- sb->data = C_REALLOC(sb->data, sb->capacity);\r
- }\r
-\r
- // void *memcpy(void dest[restrict .n], const void src[restrict .n],\r
- C_MEMCPY((void *)((uintptr_t)sb->data + (uintptr_t)sb->data), data, data_size);\r
- sb->size += data_size;\r
-}\r
-\r
-//\r
-// String view\r
-//\r
-\r
-void c_sv_print_dumb(c_String_view sv){\r
- for (size_t i = 0; i < (size_t)sv.count; ++i){\r
- putc(*(sv.data+i), stdout);\r
- }\r
-}\r
-\r
-c_String_view c_sv_from_cstr(const char* cstr){\r
- return (c_String_view){\r
- .data = (char *)cstr,\r
- .count = strlen(cstr),\r
- };\r
-}\r
-\r
-c_String_view c_sv_lpop(c_String_view* sv, uint32 n) {\r
- c_String_view res = {0};\r
- if (sv->count < n) return res;\r
- res.data = sv->data;\r
- res.count = n;\r
-\r
- sv->data += n;\r
- sv->count -= n;\r
-\r
- return res;\r
-}\r
-\r
-c_String_view c_sv_lpop_until_predicate(c_String_view* sv, int(*predicate)(int)){\r
- const char* old_sv_data = sv->data;\r
- while (sv->count > 0 && !predicate(*sv->data)){\r
- sv->data++;\r
- sv->count--;\r
- }\r
-\r
- return (c_String_view){\r
- .data = sv->data - (sv->data - old_sv_data),\r
- .count = (sv->data - old_sv_data),\r
- };\r
-}\r
-\r
-c_String_view c_sv_lpop_until_string(c_String_view* sv, const char *string) {\r
- size_t string_len = strlen(string);\r
-\r
- char *old_sv_data = sv->data;\r
-\r
- while (sv->count > string_len) {\r
- bool matched = true;\r
- for (size_t i = 0; i < string_len; ++i) {\r
- if (sv->data[i] != string[i]) matched = false;\r
- }\r
- if (matched) break;\r
- sv->data++;\r
- sv->count--;\r
- }\r
-\r
- return (c_String_view) {\r
- .data = old_sv_data,\r
- .count = (sv->data - old_sv_data),\r
- };\r
-}\r
-\r
-c_String_view c_sv_rpop_until_predicate(c_String_view* sv, int(*predicate)(int)){\r
- size_t old_sv_count = sv->count;\r
- while (sv->count > 0 && !predicate(*(sv->data+sv->count-1))){\r
- sv->count--;\r
- }\r
-\r
- return (c_String_view){\r
- .data = sv->data + sv->count,\r
- .count = old_sv_count - sv->count,\r
- };\r
-}\r
-\r
-c_String_view c_sv_lpop_until_char(c_String_view* sv, char ch){\r
- const char* old_sv_data = sv->data;\r
- while (sv->count > 0 && *sv->data != ch){\r
- sv->data++;\r
- sv->count--;\r
- }\r
-\r
- return (c_String_view){\r
- .data = sv->data - (sv->data - old_sv_data),\r
- .count = (sv->data - old_sv_data),\r
- };\r
-}\r
-\r
-c_String_view c_sv_rpop_until_char(c_String_view* sv, char ch){\r
- size_t old_sv_count = sv->count;\r
- while (sv->count > 0 && *(sv->data+sv->count-1) != ch){\r
- sv->count--;\r
- }\r
-\r
- return (c_String_view){\r
- .data = sv->data + sv->count,\r
- .count = old_sv_count - sv->count,\r
- };\r
-}\r
-\r
-void c_sv_lremove(c_String_view* sv, size_t n){\r
- if (n > sv->count) n = sv->count;\r
-\r
- sv->data += n;\r
- sv->count -= n;\r
-}\r
-\r
-void c_sv_rremove(c_String_view* sv, size_t n){\r
- if (n > sv->count)\r
- sv->count = 0;\r
- else\r
- sv->count -= n;\r
-}\r
-\r
-void c_sv_lremove_until_char(c_String_view* sv, char ch){\r
- while (sv->count > 0 && *sv->data != ch){\r
- sv->data++;\r
- sv->count--;\r
- }\r
-}\r
-\r
-void c_sv_rremove_until_char(c_String_view* sv, char ch){\r
- while (sv->count > 0 && *(sv->data+sv->count-1) != ch){\r
- sv->count--;\r
- }\r
-}\r
-\r
-void c_sv_lremove_until_char_after(c_String_view* sv, char ch){\r
- while (sv->count > 0 && *(sv->data-1) != ch){\r
- sv->data++;\r
- sv->count--;\r
- }\r
-}\r
-\r
-void c_sv_rremove_until_char_after(c_String_view* sv, char ch){\r
- while (sv->count > 0 && *(sv->data+sv->count) != ch){\r
- sv->count--;\r
- }\r
-}\r
-\r
-void c_sv_ltrim(c_String_view* sv){\r
- while (sv->count > 0 && isspace(*sv->data)){\r
- sv->data++;\r
- sv->count--;\r
- }\r
-}\r
-\r
-void c_sv_rtrim(c_String_view* sv){\r
- while (sv->count > 0 && isspace(*(sv->data+sv->count-1))){\r
- sv->count--;\r
- }\r
-}\r
-\r
-void c_sv_trim(c_String_view* sv){\r
- c_sv_ltrim(sv);\r
- c_sv_rtrim(sv);\r
-}\r
-\r
-char* c_sv_to_cstr(c_String_view sv){\r
- char* res = (char*)malloc(sizeof(char)*(sv.count + 1));\r
- if (res == NULL) {\r
- C_ASSERT(false, "Buy more RAM bruh");\r
- }\r
- C_MEMCPY(res, sv.data, sv.count);\r
- res[sv.count] = '\0';\r
- return res;\r
-}\r
-\r
-int64 c_sv_to_int(c_String_view sv, int *count_out, int base) {\r
- char *str = c_sv_to_cstr(sv);\r
-\r
- char *endptr = NULL;\r
-\r
- int64 res = strtol(str, &endptr, base);\r
-\r
- if (endptr == str) {\r
- *count_out = -1;\r
- return res;\r
- }\r
-\r
- *count_out = (int)(endptr - str);\r
-\r
- C_FREE(str);\r
- return res;\r
-}\r
-\r
-uint64 c_sv_to_uint(c_String_view sv, int *count, int base) {\r
- char* str = c_sv_to_cstr(sv);\r
-\r
- char *endptr = NULL;\r
- uint64 res = strtoul(str, &endptr, base);\r
-\r
- if (endptr == str) {\r
- *count = -1;\r
- return res;\r
- }\r
-\r
- *count = (int)(endptr - str);\r
-\r
- C_FREE(str);\r
- return res;\r
-}\r
-\r
-float64 c_sv_to_float(c_String_view sv, int *count) {\r
- char* str = c_sv_to_cstr(sv);\r
-\r
- char *endptr = NULL;\r
- float64 res = strtod(str, &endptr);\r
-\r
- if (endptr == str) {\r
- *count = -1;\r
- return res;\r
- }\r
-\r
- *count = (int)(endptr - str);\r
-\r
- C_FREE(str);\r
- return res;\r
-}\r
-\r
-bool c_sv_contains_char(c_String_view sv, char ch){\r
- for (size_t i = 0; i < sv.count; ++i){\r
- if (sv.data[i] == ch) return true;\r
- }\r
- return false;\r
-}\r
-\r
-bool c_sv_is_hex_numbers(c_String_view sv) {\r
- char hex[] = {\r
- '0', '1', '2', '3', '4', '5', '6', '7',\r
- '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f',\r
- 'A', 'B', 'C', 'D', 'E', 'F'\r
- };\r
- bool found = false;\r
- for (size_t i = 0; i < sv.count; ++i) {\r
- char c = sv.data[i];\r
- for (size_t j = 0; j < C_ARRAY_LEN(hex); ++j) {\r
- if (hex[j] == c) {\r
- found = true;\r
- }\r
- }\r
- }\r
-\r
- return found;\r
-}\r
-\r
-bool c_sv_equals(c_String_view sv1, c_String_view sv2) {\r
- if (sv1.count != sv2.count) return false;\r
- for (size_t i = 0; i < sv1.count; ++i) {\r
- if (sv1.data[i] != sv2.data[i]) {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-c_String_view c_sv_get_part(c_String_view sv, int from, int to) {\r
- from = clampi(from, 0, sv.count);\r
- to = clampi(to, from, sv.count);\r
-\r
- c_String_view range = {\r
- .data = (char*)(sv.data + from),\r
- .count = (size_t)(to - from),\r
- };\r
-\r
- return range;\r
-}\r
-#endif\r
+#ifndef _COMMONLIB_H_
+#define _COMMONLIB_H_
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+#if defined(_WIN32) && defined(_MSC_VER)
+// NOTE: Don't include unwanted files to speed up compilation
+#define WIN32_LEAN_AND_MEAN
+#define NOCOMM
+#include <windows.h>
+#undef C_ASSERT // Bruh
+#endif
+
+// Remove Prefix
+#ifdef COMMONLIB_REMOVE_PREFIX
+#define ASSERT C_ASSERT
+#define ARRAY_LEN C_ARRAY_LEN
+
+#define da_append c_da_append
+#define da_free c_da_free
+#define da_shift c_da_shift
+#define DYNAMIC_ARRAY_INITIAL_CAPACITY c_DYNAMIC_ARRAY_INITIAL_CAPACITY
+// #define c_DYNAMIC_ARRAY_INITIAL_CAPACITY
+
+#define os_get_timedate c_os_get_timedate
+#define os_file_exists c_os_file_exists
+
+#define log_error c_log_error
+#define log_info c_log_info
+#define log_warning c_log_warning
+#define log_debug c_log_debug
+
+#define read_file c_read_file
+#define touch_file_if_doesnt_exist c_touch_file_if_doesnt_exist
+
+#define Arena c_Arena
+#define arena_make c_arena_make
+#define arena_alloc c_arena_alloc
+#define arena_reset c_arena_reset
+#define arena_free c_arena_free
+#define arena_alloc_str c_arena_alloc_str
+#define arena_alloc_wstr c_arena_alloc_wstr
+
+#define String_view c_String_view
+
+#define shift_args c_shift_args
+
+#define SV_FMT c_SV_FMT
+#define SV_ARG c_SV_ARG
+
+#define SV c_SV
+
+#define sv_print_dumb c_sv_print_dumb
+#define sv_from_cstr c_sv_from_cstr
+#define sv_lpop c_sv_lpop
+#define sv_lpop_until_predicate c_sv_lpop_until_predicate
+#define sv_lpop_until_string c_sv_lpop_until_string
+#define sv_rpop_until_predicate c_sv_rpop_until_predicate
+#define sv_lpop_until_char c_sv_lpop_until_char
+#define sv_rpop_until_char c_sv_rpop_until_char
+#define sv_lremove c_sv_lremove
+#define sv_rremove c_sv_rremove
+#define sv_lremove_until_char c_sv_lremove_until_char
+#define sv_rremove_until_char c_sv_rremove_until_char
+#define sv_lremove_until_char_after c_sv_lremove_until_char_after
+#define sv_rremove_until_char_after c_sv_rremove_until_char_after
+#define sv_ltrim c_sv_ltrim
+#define sv_rtrim c_sv_rtrim
+#define sv_trim c_sv_trim
+#define sv_to_cstr c_sv_to_cstr
+#define sv_to_int c_sv_to_int
+#define sv_to_uint c_sv_to_uint
+#define sv_to_uint8_hex c_sv_to_uint8_hex
+#define sv_to_float c_sv_to_float
+#define sv_contains_char c_sv_contains_char
+#define sv_is_hex_numbers c_sv_is_hex_numbers
+#define sv_equals c_sv_equals
+#define sv_get_part c_sv_get_part
+
+
+#endif // COMMONLIB_REMOVE_PREFIX
+
+// Memory allocation
+#ifndef C_MALLOC
+#define C_MALLOC malloc
+#endif
+#ifndef C_FREE
+#define C_FREE free
+#endif
+#ifndef C_REALLOC
+#define C_REALLOC realloc
+#endif
+#ifndef C_MEMCPY
+#define C_MEMCPY memcpy
+#endif
+
+
+// typedefs
+typedef unsigned int uint;
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+typedef int8_t int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+
+typedef float float32;
+typedef double float64;
+
+typedef wchar_t wchar;
+
+typedef const char* cstr;
+typedef const wchar* wstr;
+
+
+// Macros
+#if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER)
+#define C_ASSERT(condition, msg) do {\
+ if (!(condition)) {\
+ fprintf(stderr, "%s:%d:0 [ASSERTION FAILED] %s: %s", __FILE__, __LINE__, #condition, msg);\
+ DebugBreak();\
+ }\
+ } while (0)
+#else
+#define C_ASSERT(condition, msg) do {\
+ if (!(condition)) {\
+ fprintf(stderr, "%s:%d:0 [ASSERTION FAILED] %s: %s", __FILE__, __LINE__, #condition, msg);\
+ exit(1);\
+ }\
+ } while (0)
+#endif // defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER)
+
+#define C_ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
+
+#define c_shift(xs, xsz) (assert(xsz > 0 && "Array is empty"), xsz--, *xs++)
+#define c_shift_args c_shift
+
+//
+// Math
+//
+
+int clampi(int v, int min, int max);
+float clampf(float v, float min, float max);
+
+//
+// Struct pre-decls
+//
+
+typedef struct c_Arena c_Arena;
+
+//
+// ## Data Structures
+//
+
+//
+// Dynamic-Array
+//
+
+// NOTE: To use c_da_append() the Dynamic-Array struct should be defined using
+// DEFINE_DYNAMIC_ARRAY or have the same members as below!
+// NOTE!!!: We actually don't want this since this makes the user want to
+// use this macro to define dynamic arrays. But the user might want extra fields
+// in the struct; So we recommend defining da structs manually like:
+// ```C
+// typedef struct {
+// <item-type> items;
+// size_t count;
+// size_t capacity;
+// [extra fields...];
+// }
+// ```
+// #define DEFINE_DYNAMIC_ARRAY(struct_name, elm_type) typedef struct {
+// elm_type *items;
+// size_t count;
+// size_t capacity;
+// }
+
+#define c_DYNAMIC_ARRAY_INITIAL_CAPACITY (sizeof(size_t))
+
+#define c_da_append(da, elm) do {\
+ if ((da).items == NULL) {\
+ (da).capacity = c_DYNAMIC_ARRAY_INITIAL_CAPACITY;\
+ (da).count = 0;\
+ (da).items = C_MALLOC(sizeof(elm) * (da).capacity);\
+ }\
+ if ((da).count >= (da).capacity) {\
+ (da).capacity *= 2;\
+ (da).items = C_REALLOC((da).items, (da).capacity * sizeof(elm));\
+ C_ASSERT((da).items != NULL, "TODO: Log error instead of asserting");\
+ }\
+ (da).items[(da).count++] = elm;\
+ } while (0)
+
+// NOTE: We cant do C_ASSERT() here because it aint one expression...
+// NOTE: da_shift will make the da loose its ptr, so store the ptr elsewhere if you want to free it later!!!
+#define c_da_shift(da) (assert((da).count > 0 && "Array is empty"), (da).count--, *(da).items++)
+#define c_da_free(da) C_FREE((da).items)
+#define c_da_remove(da, type, elm_ptr, idx) do {\
+ if ((idx) >= 0 && (idx) <= (da).count-1) {\
+ type temp = (da).items[(idx)];\
+ (da).items[(idx)] = (da).items[(da).count-1];\
+ (da).items[(da).count-1] = temp;\
+ if ((elm_ptr)) *(elm_ptr) = temp;\
+ (da).count--;\
+ }\
+ } while (0)
+
+//
+// OS
+//
+
+#if defined(__linux__)
+#include <sys/stat.h>
+#endif
+
+void c_os_get_timedate(c_Arena* a);
+bool c_os_file_exists(cstr filename);
+
+//
+// Logging
+//
+
+#define c_log_error(fmt, ...) do {\
+ fprintf(stderr, "%s"fmt"\n", "[ERROR] ", ##__VA_ARGS__);\
+ } while (0)
+#define c_log_info(fmt, ...) do {\
+ fprintf(stdout, "%s"fmt"\n", "[INFO] ", ##__VA_ARGS__);\
+ } while (0)
+#define c_log_warning(fmt, ...) do {\
+ fprintf(stdout, "%s"fmt"\n", "[WARNING] ", ##__VA_ARGS__);\
+ } while (0)
+#ifdef DEBUG
+#define c_log_debug(fmt, ...) do {\
+ fprintf(stdout, "%s"fmt"\n", "[DEBUG] ", ##__VA_ARGS__);\
+ } while (0)
+#else
+#define c_log_debug(...)
+#endif // DEBUG
+//
+// File
+//
+
+// reads entire file and gives back the file content and filesize in bytes. (caller must be responsible for freeing the string!)
+const char* c_read_file(const char* filename, int *file_size);
+void c_touch_file_if_doesnt_exist(cstr file);
+
+//
+// ### Allocators ###
+//
+
+//
+// c_Arena
+//
+
+#define ARENA_BUFF_INITIAL_SIZE (1024*4)
+
+struct c_Arena {
+ void* buff;
+ uint64 buff_size;
+ void* ptr;
+};
+
+// pass size 0 to get ARENA_BUFF_INITIAL_SIZE
+c_Arena c_arena_make(size_t size);
+void* c_arena_alloc(c_Arena* a, size_t size);
+void c_arena_reset(c_Arena* a);
+void c_arena_free(c_Arena* a);
+
+// TODO: Do we embed stb_snprintf to use stbsp_snprintf?
+#define c_arena_alloc_str(a, fmt, ...) c_arena_alloc(&(a), sizeof(char)*snprintf((a).ptr, (int)((a).buff_size - ((uint8*)(a).ptr - (uint8*)(a).buff)), (fmt), __VA_ARGS__)+1)
+#define c_arena_alloc_wstr(a, fmt, ...) c_arena_alloc(&a, sizeof(char)*wprintf(a.ptr, a.buff_size - ((uint8*)a.ptr - (uint8*)a.buff), (fmt), __VA_ARGS__)+1)
+
+//
+// String Builder
+//
+
+typedef struct {
+ char* data;
+ size_t size;
+ size_t capacity;
+} c_String_builder;
+
+void c_sb_append(c_String_builder* sb, char* data);
+
+//
+// String view
+//
+
+typedef struct {
+ char *data;
+ size_t count;
+} c_String_view;
+
+#define c_SV_FMT "%.*s"
+#define c_SV_ARG(sv) (int)(sv).count, (sv).data
+
+#define c_SV(cstr) (c_String_view){.data = (char*)cstr, strlen(cstr)}
+
+void c_sv_print_dumb(c_String_view sv);
+c_String_view c_sv_from_cstr(const char* cstr); // Actually just use SV(cstr) macro...
+c_String_view c_sv_lpop(c_String_view* sv, uint32 n);
+c_String_view c_sv_lpop_until_predicate(c_String_view* sv, int(*predicate)(int));
+c_String_view c_sv_lpop_until_string(c_String_view* sv, const char *string);
+c_String_view c_sv_rpop_until_predicate(c_String_view* sv, int(*predicate)(int));
+c_String_view c_sv_lpop_until_char(c_String_view* sv, char ch);
+c_String_view c_sv_rpop_until_char(c_String_view* sv, char ch);
+void c_sv_lremove(c_String_view* sv, size_t n);
+void c_sv_rremove(c_String_view* sv, size_t n);
+void c_sv_lremove_until_char(c_String_view* sv, char ch);
+void c_sv_rremove_until_char(c_String_view* sv, char ch);
+void c_sv_lremove_until_char_after(c_String_view* sv, char ch);
+void c_sv_rremove_until_char_after(c_String_view* sv, char ch);
+void c_sv_ltrim(c_String_view* sv);
+void c_sv_rtrim(c_String_view* sv);
+void c_sv_trim(c_String_view* sv);
+char* c_sv_to_cstr(c_String_view sv);
+int64 c_sv_to_int(c_String_view sv, int *count, int base);
+uint64 c_sv_to_uint(c_String_view sv, int *count, int base);
+float64 c_sv_to_float(c_String_view sv, int *count);
+bool c_sv_contains_char(c_String_view sv, char ch);
+bool c_sv_is_hex_numbers(c_String_view sv);
+bool c_sv_equals(c_String_view sv1, c_String_view sv2);
+c_String_view c_sv_get_part(c_String_view sv, int from, int to);
+
+#endif /* _COMMONLIB_H_ */
+
+//////////////////////////////////////////////////
+#ifdef COMMONLIB_IMPLEMENTATION
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+// My things implementation:
+
+//
+// Math
+//
+
+int clampi(int v, int min, int max) {
+ v = v < min ? min : v;
+ v = v > max ? max : v;
+ return v;
+}
+
+float clampf(float v, float min, float max) {
+ v = v < min ? min : v;
+ v = v > max ? max : v;
+ return v;
+}
+
+//
+// OS
+//
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+void c_os_get_timedate(c_Arena* a) {
+ (void)a;
+ C_ASSERT(false, "Unimplemented!");
+}
+
+bool c_os_file_exists(cstr filename) {
+ (void) filename;
+ C_ASSERT(false, "Unimplemented!");
+ return false;
+}
+
+#elif defined(__linux__)
+void c_os_get_timedate(c_Arena* a) {
+ (void)a;
+ C_ASSERT(false, "Unimplemented!");
+}
+
+bool c_os_file_exists(cstr filename) {
+ struct stat buf;
+ return stat(filename, &buf) == 0;
+}
+#endif
+
+// simple and dirty way to have defering in C (not recommended to use!)
+#define defer(ret_val) \
+ result = ret_val;\
+ goto defer
+
+const char *c_read_file(const char* filename, int *file_size) {
+ FILE* f = fopen(filename, "r");
+ char* result = NULL;
+
+ if (f == NULL){
+ // TODO: Replace every strerror with strerror_s
+ c_log_error("'%s': %s", filename, strerror(errno));
+ defer(NULL);
+ }
+
+ if (fseek(f, 0, SEEK_END) < 0) {
+ c_log_error("'%s': %s", filename, strerror(errno));
+ defer(NULL);
+ }
+
+ size_t fsize = ftell(f);
+
+ if (fsize == (size_t)-1){
+ c_log_error("'%s': %s", filename, strerror(errno));
+ defer(NULL);
+ }
+
+ result = C_MALLOC(sizeof(char)*(fsize+1));
+
+ if (result == NULL){
+ c_log_error("'%s': %s", filename, strerror(errno));
+ defer(NULL);
+ }
+
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ c_log_error("'%s': %s", filename, strerror(errno));
+ defer(NULL);
+ }
+
+ size_t read = fread((char*)result, sizeof(char), fsize, f);
+
+ // Process the result to remove '\r' characters
+ char* cleaned_result = malloc(read + 1); // Allocate memory for cleaned result
+ if (cleaned_result == NULL) {
+ fprintf(stderr, "Memory allocation failed\n");
+ free(result);
+ return NULL;
+ }
+
+ size_t j = 0; // Index for cleaned_result
+ for (size_t i = 0; i < read; i++) {
+ if (result[i] != '\r') { // Skip '\r' characters
+ cleaned_result[j++] = result[i];
+ }
+ }
+ cleaned_result[j] = '\0'; // Null-terminate the cleaned result
+
+ free(result); // Free the original result
+ *file_size = (int)j; // Update the file size without '\r'
+ return cleaned_result; // Return the cleaned result
+
+ *file_size = (int)read;
+
+ defer:
+ if (f) fclose(f);
+ if (result == NULL) *file_size = -1;
+ return result;
+}
+
+void c_touch_file_if_doesnt_exist(cstr filename) {
+ if (c_os_file_exists(filename)) return;
+ FILE* file = fopen(filename, "w");
+ if (file) fclose(file);
+}
+
+//
+// ### Allocators ###
+//
+
+// c_Arena
+
+c_Arena c_arena_make(size_t size) {
+ c_Arena res = {0};
+ res.buff_size = size == 0 ? ARENA_BUFF_INITIAL_SIZE : size;
+ res.buff = C_MALLOC(res.buff_size);
+ res.ptr = res.buff;
+
+ C_ASSERT(res.buff, "Malloc failed?");
+
+ return res;
+}
+
+void* c_arena_alloc(c_Arena* a, size_t size) {
+ C_ASSERT(a->buff, "Bro pass an initialized arena!");
+
+ void* res = a->ptr;
+ a->ptr = (uint8*)a->ptr + size;
+
+ size_t diff = (size_t)((uint8*)a->ptr - (uint8*)a->buff);
+ if (diff > a->buff_size) {
+ c_log_info("c_Arena resized from %zu to %zu", a->buff_size, a->buff_size*2);
+ a->buff_size *= 2;
+ a->buff = C_REALLOC(a->buff, a->buff_size);
+ a->ptr = (uint8*)a->buff + diff;
+ res = a->ptr;
+ a->ptr = (uint8*)a->ptr + size;
+ }
+ /* C_ASSERT((size_t)((uint8*)a->ptr - (uint8*)a->buff) <= a->buff_size); */
+
+ return res;
+}
+
+void c_arena_reset(c_Arena* a) {
+ a->ptr = a->buff;
+}
+
+void c_arena_free(c_Arena* a) {
+ C_FREE(a->buff);
+}
+
+//
+// String Builder
+//
+
+void c_sb_append(c_String_builder* sb, char* data) {
+ size_t data_size = strlen(data);
+ if (sb->size + data_size > sb->capacity) {
+ sb->capacity *= 2;
+ sb->data = C_REALLOC(sb->data, sb->capacity);
+ }
+
+ // void *memcpy(void dest[restrict .n], const void src[restrict .n],
+ C_MEMCPY((void *)((uintptr_t)sb->data + (uintptr_t)sb->data), data, data_size);
+ sb->size += data_size;
+}
+
+//
+// String view
+//
+
+void c_sv_print_dumb(c_String_view sv){
+ for (size_t i = 0; i < (size_t)sv.count; ++i){
+ putc(*(sv.data+i), stdout);
+ }
+}
+
+c_String_view c_sv_from_cstr(const char* cstr){
+ return (c_String_view){
+ .data = (char *)cstr,
+ .count = strlen(cstr),
+ };
+}
+
+c_String_view c_sv_lpop(c_String_view* sv, uint32 n) {
+ c_String_view res = {0};
+ if (sv->count < n) return res;
+ res.data = sv->data;
+ res.count = n;
+
+ sv->data += n;
+ sv->count -= n;
+
+ return res;
+}
+
+c_String_view c_sv_lpop_until_predicate(c_String_view* sv, int(*predicate)(int)){
+ const char* old_sv_data = sv->data;
+ while (sv->count > 0 && !predicate(*sv->data)){
+ sv->data++;
+ sv->count--;
+ }
+
+ return (c_String_view){
+ .data = sv->data - (sv->data - old_sv_data),
+ .count = (sv->data - old_sv_data),
+ };
+}
+
+c_String_view c_sv_lpop_until_string(c_String_view* sv, const char *string) {
+ size_t string_len = strlen(string);
+
+ char *old_sv_data = sv->data;
+
+ while (sv->count > string_len) {
+ bool matched = true;
+ for (size_t i = 0; i < string_len; ++i) {
+ if (sv->data[i] != string[i]) matched = false;
+ }
+ if (matched) break;
+ sv->data++;
+ sv->count--;
+ }
+
+ return (c_String_view) {
+ .data = old_sv_data,
+ .count = (sv->data - old_sv_data),
+ };
+}
+
+c_String_view c_sv_rpop_until_predicate(c_String_view* sv, int(*predicate)(int)){
+ size_t old_sv_count = sv->count;
+ while (sv->count > 0 && !predicate(*(sv->data+sv->count-1))){
+ sv->count--;
+ }
+
+ return (c_String_view){
+ .data = sv->data + sv->count,
+ .count = old_sv_count - sv->count,
+ };
+}
+
+c_String_view c_sv_lpop_until_char(c_String_view* sv, char ch){
+ const char* old_sv_data = sv->data;
+ while (sv->count > 0 && *sv->data != ch){
+ sv->data++;
+ sv->count--;
+ }
+
+ return (c_String_view){
+ .data = sv->data - (sv->data - old_sv_data),
+ .count = (sv->data - old_sv_data),
+ };
+}
+
+c_String_view c_sv_rpop_until_char(c_String_view* sv, char ch){
+ size_t old_sv_count = sv->count;
+ while (sv->count > 0 && *(sv->data+sv->count-1) != ch){
+ sv->count--;
+ }
+
+ return (c_String_view){
+ .data = sv->data + sv->count,
+ .count = old_sv_count - sv->count,
+ };
+}
+
+void c_sv_lremove(c_String_view* sv, size_t n){
+ if (n > sv->count) n = sv->count;
+
+ sv->data += n;
+ sv->count -= n;
+}
+
+void c_sv_rremove(c_String_view* sv, size_t n){
+ if (n > sv->count)
+ sv->count = 0;
+ else
+ sv->count -= n;
+}
+
+void c_sv_lremove_until_char(c_String_view* sv, char ch){
+ while (sv->count > 0 && *sv->data != ch){
+ sv->data++;
+ sv->count--;
+ }
+}
+
+void c_sv_rremove_until_char(c_String_view* sv, char ch){
+ while (sv->count > 0 && *(sv->data+sv->count-1) != ch){
+ sv->count--;
+ }
+}
+
+void c_sv_lremove_until_char_after(c_String_view* sv, char ch){
+ while (sv->count > 0 && *(sv->data-1) != ch){
+ sv->data++;
+ sv->count--;
+ }
+}
+
+void c_sv_rremove_until_char_after(c_String_view* sv, char ch){
+ while (sv->count > 0 && *(sv->data+sv->count) != ch){
+ sv->count--;
+ }
+}
+
+void c_sv_ltrim(c_String_view* sv){
+ while (sv->count > 0 && isspace(*sv->data)){
+ sv->data++;
+ sv->count--;
+ }
+}
+
+void c_sv_rtrim(c_String_view* sv){
+ while (sv->count > 0 && isspace(*(sv->data+sv->count-1))){
+ sv->count--;
+ }
+}
+
+void c_sv_trim(c_String_view* sv){
+ c_sv_ltrim(sv);
+ c_sv_rtrim(sv);
+}
+
+char* c_sv_to_cstr(c_String_view sv){
+ char* res = (char*)malloc(sizeof(char)*(sv.count + 1));
+ if (res == NULL) {
+ C_ASSERT(false, "Buy more RAM bruh");
+ }
+ C_MEMCPY(res, sv.data, sv.count);
+ res[sv.count] = '\0';
+ return res;
+}
+
+int64 c_sv_to_int(c_String_view sv, int *count_out, int base) {
+ char *str = c_sv_to_cstr(sv);
+
+ char *endptr = NULL;
+
+ int64 res = strtol(str, &endptr, base);
+
+ if (endptr == str) {
+ *count_out = -1;
+ return res;
+ }
+
+ *count_out = (int)(endptr - str);
+
+ C_FREE(str);
+ return res;
+}
+
+uint64 c_sv_to_uint(c_String_view sv, int *count, int base) {
+ char* str = c_sv_to_cstr(sv);
+
+ char *endptr = NULL;
+ uint64 res = strtoul(str, &endptr, base);
+
+ if (endptr == str) {
+ *count = -1;
+ return res;
+ }
+
+ *count = (int)(endptr - str);
+
+ C_FREE(str);
+ return res;
+}
+
+float64 c_sv_to_float(c_String_view sv, int *count) {
+ char* str = c_sv_to_cstr(sv);
+
+ char *endptr = NULL;
+ float64 res = strtod(str, &endptr);
+
+ if (endptr == str) {
+ *count = -1;
+ return res;
+ }
+
+ *count = (int)(endptr - str);
+
+ C_FREE(str);
+ return res;
+}
+
+bool c_sv_contains_char(c_String_view sv, char ch){
+ for (size_t i = 0; i < sv.count; ++i){
+ if (sv.data[i] == ch) return true;
+ }
+ return false;
+}
+
+bool c_sv_is_hex_numbers(c_String_view sv) {
+ char hex[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+ bool found = false;
+ for (size_t i = 0; i < sv.count; ++i) {
+ char c = sv.data[i];
+ for (size_t j = 0; j < C_ARRAY_LEN(hex); ++j) {
+ if (hex[j] == c) {
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
+bool c_sv_equals(c_String_view sv1, c_String_view sv2) {
+ if (sv1.count != sv2.count) return false;
+ for (size_t i = 0; i < sv1.count; ++i) {
+ if (sv1.data[i] != sv2.data[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+c_String_view c_sv_get_part(c_String_view sv, int from, int to) {
+ from = clampi(from, 0, sv.count);
+ to = clampi(to, from, sv.count);
+
+ c_String_view range = {
+ .data = (char*)(sv.data + from),
+ .count = (size_t)(to - from),
+ };
+
+ return range;
+}
+#endif