commit fbc209dda9487b8c1271330241c2abe0383add14 Author: Timerix Date: Fri Jul 18 22:07:30 2025 +0300 refactored code from tcpu diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..498924c --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# build results +bin/ +obj/ + +# IDE files +.vs/ +.vshistory/ +.editorconfig +*.user +*.vcxproj.filters + +# other files +.old*/ +old/ +tmp/ +temp/ +*.tmp +*.temp +logs/ +log/ +*.log diff --git a/.vscode/.gitignore b/.vscode/.gitignore new file mode 100755 index 0000000..c3d3a20 --- /dev/null +++ b/.vscode/.gitignore @@ -0,0 +1 @@ +settings.json \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100755 index 0000000..9578127 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "name": "all", + "defines": [], + "includePath": [ + "include", + "${default}" + ], + "cStandard": "c99" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..4bfb694 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "gdb_debug", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/test", + "windows": { "program": "${workspaceFolder}/bin/tcp-chat.exe" }, + "preLaunchTask": "build_exec_dbg", + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin", + "externalConsole": false, + "internalConsoleOptions": "neverOpen", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100755 index 0000000..17a453c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,31 @@ + +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build_exec_dbg", + "detail": "build project with debug symbols", + "type": "cppbuild", + "command": "bash", + "args": [ + "-c", + "cbuild build_exec_dbg" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": ["$gcc"], + "group": { + "kind": "build" + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": false, + "clear": true + } + } + ] + } \ No newline at end of file diff --git a/include/collections/Array.h b/include/collections/Array.h new file mode 100755 index 0000000..8b0f3a1 --- /dev/null +++ b/include/collections/Array.h @@ -0,0 +1,28 @@ +#pragma once +#include "../std.h" + +typedef struct Array { + void* data; + u32 size; +} Array; + +/// creates Array from a const array +#define Array_CONST(T, A...) Array_construct_size(((T[])A), sizeof((T[])A)) + +#define Array_construct(DATA, T, COUNT) Array_construct_size(DATA, (COUNT) * sizeof(T)) +#define Array_construct_size(DATA, LEN) ((Array){ .data = (DATA), .size = (LEN) }) + +#define Array_alloc(T, COUNT) Array_alloc((COUNT) * sizeof(T)) + +static inline Array Array_alloc_size(u32 size){ + return Array_construct_size(malloc(size), size); +} + +#define Array_realloc(AR, T, COUNT) Array_realloc_size(AR, (COUNT) * sizeof(T)) + +static inline void Array_realloc_size(Array* ar, u32 new_size){ + ar->data = realloc(ar->data, new_size); + ar->size = new_size; +} + +#define Array_len(AR, T) ((AR)->size / sizeof(T)) \ No newline at end of file diff --git a/include/collections/HashMap.h b/include/collections/HashMap.h new file mode 100755 index 0000000..02f4855 --- /dev/null +++ b/include/collections/HashMap.h @@ -0,0 +1,31 @@ +#pragma once +#include "../std.h" +#include "../string/str.h" +#include "Array.h" +#include "List.h" + +typedef void (*FreeFunction)(void*); + +typedef struct KeyHash { + str key; + u32 hash; +} KeyHash; + +typedef struct HashMapBucket { + List key_hash_list; + List value_list; +} HashMapBucket; + +typedef struct HashMap { + HashMapBucket* table; + FreeFunction NULLABLE(value_destructor); + u32 value_t_size; + u32 height; + u16 height_n; +} HashMap; + +void HashMap_alloc(HashMap* ptr, u32 value_t_size, FreeFunction NULLABLE(value_destructor)); +void HashMap_free(HashMap* ptr); +void* NULLABLE(HashMap_tryGetPtr)(HashMap* ptr, str key); +bool HashMap_tryPush(HashMap* ptr, str key, void* value_ptr); +bool HashMap_tryDelete(HashMap* ptr, str key); diff --git a/include/collections/List.h b/include/collections/List.h new file mode 100755 index 0000000..48e621f --- /dev/null +++ b/include/collections/List.h @@ -0,0 +1,28 @@ +#pragma once +#include "../std.h" + +typedef struct List { + void* data; + u32 size; + u32 allocated_size; +} List; + +#define List_construct(L, T, OCCUPIED_COUNT, ALLOCATED_COUNT) \ + List_construct_size(L, (OCCUPIED_COUNT) * sizeof(T), (ALLOCATED_COUNT) * sizeof(T)) + +static inline List List_construct_size(void* data_ptr, u32 size, u32 allocated_size) { + return (List){ .data = data_ptr, .size = size, .allocated_size = allocated_size }; +} + +#define List_alloc(L, T, INITIAL_COUNT) List_alloc_size(L, (INITIAL_COUNT) * sizeof(T)) +List List_alloc_size(u32 initial_size); + +void* List_expand(List* ptr, u32 expansion_size); +#define List_push(L, T, VALUE) *(T*)(List_expand(L, sizeof(T))) = VALUE +#define List_pushMany(L, T, VALUES_PTR, COUNT) List_push_size(L, VALUES_PTR, (COUNT) * sizeof(T)) +void List_push_size(List* ptr, void* values, u32 size); + +#define List_removeAt(L, T, I, COUNT) List_removeAt_size(L, (I)*sizeof(T), (COUNT) * sizeof(T)) +bool List_removeAt_size(List* ptr, u32 i, u32 remove_size); + +#define List_len(L, T) ((L)->size / sizeof(T)) \ No newline at end of file diff --git a/include/std.h b/include/std.h new file mode 100755 index 0000000..25b9661 --- /dev/null +++ b/include/std.h @@ -0,0 +1,67 @@ +#pragma once + +#if __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +typedef int8_t i8; +typedef uint8_t u8; +typedef int16_t i16; +typedef uint16_t u16; +typedef int32_t i32; +typedef uint32_t u32; +typedef int64_t i64; +typedef uint64_t u64; +typedef float f32; +typedef double f64; + +#if !__cplusplus +typedef u8 bool; +#define true 1 +#define false 0 +#endif + +typedef const char* cstr; + +#define dbg(N) printf("\e[95m%d\n",N) + +#define nameof(V) #V + +#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0])) +#define ALIGN_TO(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) + +#if defined(_WIN64) || defined(_WIN32) + #define IFWIN(YES, NO) YES +#else + #define IFWIN(YES, NO) NO +#endif + +#define __count_args( \ + a0, a1, a2, a3, a4, a5, a6, a7 , a8, a9, a10,a11,a12,a13,a14,a15, \ + a16,a17,a18,a19,a20,a21,a22,a23, a24,a25,a26,a27,a28,a29,a30,a31, \ + a32,a33,a34,a35,a36,a37,a38,a39, a40,a41,a42,a43,a44,a45,a46,a47, \ + a48,a49,a50,a51,a52,a53,a54,a55, a56,a57,a58,a59,a60,a61,a62,a63, \ + a64,...) a64 +// Macro for counting variadic arguments (max 64) +// (see usage in kprint.h) +#define count_args(ARGS...) __count_args(ARGS, \ + 64,63,62,61,60,59,58,57, 56,55,54,53,52,51,50,49, \ + 48,47,46,45,44,43,42,41, 40,39,38,37,36,35,34,33, \ + 32,31,30,29,28,27,26,25, 24,23,22,21,20,19,18,17, \ + 16,15,14,13,12,11,10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#define printfe(FORMAT, ...) fprintf(stderr, FORMAT ,##__VA_ARGS__) + +/// @warning pointer can be null +#define NULLABLE(NAME) NAME + + +#if __cplusplus +} +#endif \ No newline at end of file diff --git a/include/string/StringBuilder.h b/include/string/StringBuilder.h new file mode 100755 index 0000000..f9c165f --- /dev/null +++ b/include/string/StringBuilder.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../collections/List.h" +#include "str.h" + +typedef struct StringBuilder { + List buffer; +} StringBuilder; + +static inline StringBuilder StringBuilder_alloc(u32 initial_size) { + return (StringBuilder){ .buffer = List_alloc_size(initial_size) }; +} +void StringBuilder_free(StringBuilder* b); + +/// @param count set to -1 to clear StringBuilder +void StringBuilder_removeFromEnd(StringBuilder* b, u32 count); +void StringBuilder_append_char(StringBuilder* b, char c); +void StringBuilder_append_cstr(StringBuilder* b, char* s); +void StringBuilder_append_string(StringBuilder* b, str s); +void StringBuilder_append_i64(StringBuilder* b, i64 a); +void StringBuilder_append_u64(StringBuilder* b, u64 a); +void StringBuilder_append_f64(StringBuilder* b, f64 a); + +// adds '\0' to the buffer and returns pointer to buffer content +str StringBuilder_getStr(StringBuilder* b); diff --git a/include/string/char.h b/include/string/char.h new file mode 100755 index 0000000..f889909 --- /dev/null +++ b/include/string/char.h @@ -0,0 +1,6 @@ +#pragma once +#include "../std.h" + +static inline bool isAlphabeticalLower(char c) { return 'a' <= c && c <= 'z'; } +static inline bool isAlphabeticalUpper(char c) { return 'A' <= c && c <= 'Z'; } +static inline bool isDigit(char c) { return '0' <= c && c <= '9'; } \ No newline at end of file diff --git a/include/string/cstr.h b/include/string/cstr.h new file mode 100755 index 0000000..f6a7429 --- /dev/null +++ b/include/string/cstr.h @@ -0,0 +1,9 @@ +#pragma once +#include "../std.h" + +#define strcat_malloc(STR0, ...) _strcat_malloc(count_args(__VA_ARGS__), STR0, __VA_ARGS__) +char* _strcat_malloc(size_t n, cstr str0, ...); +char* _vstrcat_malloc(size_t n, cstr str0, va_list argv); + +char* NULLABLE(sprintf_malloc)(size_t buffer_size, cstr format, ...) __attribute__((__format__(__printf__, 2, 3))); +char* NULLABLE(vsprintf_malloc)(size_t buffer_size, cstr format, va_list argv); diff --git a/include/string/str.h b/include/string/str.h new file mode 100755 index 0000000..b13a43f --- /dev/null +++ b/include/string/str.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../std.h" +#include "char.h" +#include "cstr.h" + +typedef struct str { + char* data; + u32 size; + bool isZeroTerminated; +} str; + +/// creates str from a string literal +#define STR(LITERAL) str_construct(LITERAL, ARRAY_SIZE(LITERAL) - 1, true) + +#define str_construct(DATA, LEN, ZERO_TERMINATED) ((str){ .data = DATA, .size = LEN, .isZeroTerminated = ZERO_TERMINATED }) + +static const str str_null = str_construct(NULL, 0, 0); + +/// copies src content to new string and adds \0 at the end +str str_copy(str src); + +/// compares two strings, NullPtr-friendly +bool str_equals(str str0, str str1); + +/// allocates new string which is reversed variant of +str str_reverse(str s); + +i32 str_seek(str src, str fragment, u32 startIndex); +i32 str_seekReverse(str src, str fragment, u32 startIndex); + +i32 str_seekChar(str src, char c, u32 startIndex); +i32 str_seekCharReverse(str src, char c, u32 startIndex); + +bool str_startsWith(str src, str fragment); +bool str_endsWith(str src, str fragment); + +/// @brief calculates string hash using sdbm32 algorythm (something like lightweight crc32) +/// @return non-cryptografic hash of the string +u32 str_hash32(str s); + +str str_toUpper(str src); +str str_toLower(str src); diff --git a/project.config b/project.config new file mode 100644 index 0000000..7ccf667 --- /dev/null +++ b/project.config @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +CBUILD_VERSION=2.2.3 + +PROJECT="tlibc" +CMP_C="gcc" +CMP_CPP="g++" +STD_C="c99" +STD_CPP="c++11" +WARN_C="-Wall -Wextra" +WARN_CPP="-Wall -Wextra" +SRC_C="$(find src -name '*.c')" +SRC_CPP="$(find src -name '*.cpp')" + +# Directory with dependency configs. +# See cbuild/example_dependency_configs +DEPENDENCY_CONFIGS_DIR='.' +# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space. +ENABLED_DEPENDENCIES='' + +# OBJDIR structure: +# ├── objects/ - Compiled object files. Cleans on each call of build task +# ├── static_libs/ - Symbolic links to static libraries used by linker. Cleans on each call of build task. +# ├── static_libs/ - Symbolic links to dynamic libraries used by linker. Cleans on each call of build task. +# └── profile/ - gcc *.gcda profiling info files +OBJDIR="obj" +OUTDIR="bin" +STATIC_LIB_FILE="lib$PROJECT.a" + +INCLUDE="-I./include" + +# OS-specific options +case "$OS" in + WINDOWS) + EXEC_FILE="$PROJECT.exe" + SHARED_LIB_FILE="$PROJECT.dll" + INCLUDE="$INCLUDE " + LINKER_LIBS="" + ;; + LINUX) + EXEC_FILE="$PROJECT" + SHARED_LIB_FILE="$PROJECT.so" + INCLUDE="$INCLUDE " + LINKER_LIBS="" + ;; + *) + error "operating system $OS has no configuration variants" + ;; +esac + +# TASKS +case "$TASK" in + # creates executable using profiling info if it exists + build_exec) + # -flto applies more optimizations across object files + # -flto=auto is needed to multithreaded copilation + # -fuse-linker-plugin is required to use static libs with lto + # -fprofile-use enables compiler to use profiling info files to optimize executable + # -fprofile-prefix-path sets path where profiling info about objects are be saved + # -fdata-sections -ffunction-sections -Wl,--gc-sections removes unused code + C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-use -fprofile-prefix-path=$(realpath $OBJDIR)/objects -fdata-sections -ffunction-sections -Wl,--gc-sections" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" + PRE_TASK_SCRIPT= + TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + POST_TASK_SCRIPT= + ;; + # creates executable with debug info and no optimizations + build_exec_dbg) + C_ARGS="-O0 -g3" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" + PRE_TASK_SCRIPT= + TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + POST_TASK_SCRIPT= + ;; + # creates shared library + build_shared_lib) + C_ARGS="-O2 -fpic -flto -shared" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE" + PRE_TASK_SCRIPT= + TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh + POST_TASK_SCRIPT= + ;; + # creates shared library with debug symbols and no optimizations + build_shared_lib_dbg) + C_ARGS="-O0 -g3 -fpic -shared" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE" + PRE_TASK_SCRIPT= + TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh + POST_TASK_SCRIPT= + ;; + # creates static library + build_static_lib) + C_ARGS="-O2 -fpic -fdata-sections -ffunction-sections" + CPP_ARGS="$C_ARGS" + PRE_TASK_SCRIPT= + TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh + POST_TASK_SCRIPT= + ;; + # creates static library with debug symbols and no optimizations + build_static_lib_dbg) + C_ARGS="-O0 -g3" + CPP_ARGS="$C_ARGS" + PRE_TASK_SCRIPT= + TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh + POST_TASK_SCRIPT= + ;; + # executes $EXEC_FILE + exec) + TASK_SCRIPT=cbuild/default_tasks/exec.sh + ;; + # executes $EXEC_FILE with valgrind memory checker + valgrind) + VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all" + TASK_SCRIPT=cbuild/default_tasks/valgrind.sh + ;; + # generates profiling info + profile) + OUTDIR="$OUTDIR/profile" + # -flto applies more optimizations across object files + # -flto=auto is needed to multithreaded copilation + # -fuse-linker-plugin is required to use static libs with lto + # -pg adds code to executable, that generates file containing function call info (gmon.out) + # -fprofile-generate generates executable with profiling code + # -fprofile-prefix-path sets path where profiling info about objects will be saved + C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-generate -fprofile-prefix-path=$(realpath $OBJDIR)/objects" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" + PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + TASK_SCRIPT=cbuild/default_tasks/profile.sh + POST_TASK_SCRIPT= + ;; + # compiles program with -pg and runs it with gprof + # uses gprof2dot python script to generate function call tree (pip install gprof2dot) + # requires graphviz (https://www.graphviz.org/download/source/) + gprof) + OUTDIR="$OUTDIR/gprof" + # -pg adds code to executable, that generates file containing function call info (gmon.out) + C_ARGS="-O2 -flto=auto -fuse-linker-plugin -pg" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" + PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + TASK_SCRIPT=cbuild/default_tasks/gprof.sh + POST_TASK_SCRIPT= + ;; + # compiles program and runs it with callgrind (part of valgrind) + # uses gprof2dot python script to generate function call tree (pip install gprof2dot) + # requires graphviz (https://www.graphviz.org/download/source/) + # P.S. detailed results can be viewed in KCacheGrind + callgrind) + OUTDIR="$OUTDIR/callgrind" + # -pg adds code to executable, that generates file containing function call info (gmon.out) + C_ARGS="-O2 -flto=auto -fuse-linker-plugin" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" + PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + TASK_SCRIPT=cbuild/default_tasks/callgrind.sh + POST_TASK_SCRIPT= + ;; + # compiles executable with sanitizers and executes it to find errors and warnings + sanitize) + OUTDIR="$OUTDIR/sanitize" + C_ARGS="-O0 -g3 -fsanitize=undefined,address" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS $LINKER_LIBS" + PRE_TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + TASK_SCRIPT=cbuild/default_tasks/exec.sh + POST_TASK_SCRIPT= + ;; + # rebuilds specified dependencies + # EXAMPLE: `cbuild rebuild_dependencies=libexample1,fonts` + # 'all' can be specified to rebuild all dependencies + rebuild_dependencies) + TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh + ;; + # deletes generated files + clean) + TASK_SCRIPT=cbuild/default_tasks/clean.sh + ;; + # nothing to do + "" | no_task) + ;; + # unknown task + *) + error "task <$PROJECT/$TASK> not found" + ;; +esac diff --git a/src/collections/HashMap.c b/src/collections/HashMap.c new file mode 100755 index 0000000..ce78cb9 --- /dev/null +++ b/src/collections/HashMap.c @@ -0,0 +1,145 @@ +#include "collections/HashMap.h" +#include + +//TODO: sort bucket keys for binary search + +#define __HashMap_HASH_FUNC str_hash32 +#define __HashMapBucket_MAX_LEN 16 + +static const Array __HashMap_heights = Array_CONST(u32, { + 17, 31, 61, 127, 257, 521, 1021, 2053, 4099, 8191, 16381, 32771, + 65521, 131071, 262147, 524287, 1048583, 2097169, 4194319, + 8388617, 16777213, 33554467, 67108859, 134217757, 268435493 +}); + +void HashMap_alloc(HashMap* ptr, u32 value_t_size, FreeFunction NULLABLE(value_destructor)){ + ptr->value_t_size = value_t_size; + ptr->value_destructor = value_destructor; + ptr->height_n = 0; + ptr->height = ((u32*)__HashMap_heights.data)[0]; + u32 alloc_size = ptr->height * sizeof(HashMapBucket); + ptr->table = (HashMapBucket*)malloc(alloc_size); + memset(ptr->table, 0, alloc_size); +} + +void HashMap_free(HashMap* ptr){ + for(u32 i = 0; i < ptr->height; i++){ + HashMapBucket* bu = &ptr->table[i]; + u32 len = List_len(&bu->key_hash_list, KeyHash); + + // free key strings + for(u32 j = 0; j < len; j++){ + KeyHash* kh = (KeyHash*)bu->key_hash_list.data + j; + free(kh->key.data); + } + + // destroy values + if(ptr->value_destructor){ + u8* value_ptr = (u8*)bu->value_list.data; + u8* end = value_ptr + bu->value_list.size; + while(value_ptr < end){ + ptr->value_destructor(value_ptr); + value_ptr += ptr->value_t_size; + } + } + + free(bu->key_hash_list.data); + free(bu->value_list.data); + } + + free(ptr->table); +} + +typedef struct BucketAndIndex { + HashMapBucket* bu; + i32 i; // -1 if not found +} BucketAndIndex; + +#define BucketAndIndex_null ((BucketAndIndex){ .bu = NULL, .i = -1 }) + +static BucketAndIndex __HashMap_search(HashMap* ptr, str key, u32 hash){ + BucketAndIndex r; + r.bu = &ptr->table[hash % ptr->height]; + for(r.i = 0; r.i < (i32)List_len(&r.bu->key_hash_list, KeyHash); r.i++){ + KeyHash* kh = (KeyHash*)r.bu->key_hash_list.data + r.i; + if(kh->hash == hash && str_equals(kh->key, key)){ + return r; + } + } + + r.i = -1; + return r; +} + +void* NULLABLE(HashMap_tryGetPtr)(HashMap* ptr, str key){ + u32 hash = __HashMap_HASH_FUNC(key); + BucketAndIndex r = __HashMap_search(ptr, key, hash); + // key not found + if(r.i == -1) + return NULL; + return ((u8*)r.bu->value_list.data) + r.i * ptr->value_t_size; +} + + +static void __HashMap_expand(HashMap* ptr){ + u32 height_expanded_n = ptr->height_n + 1; + assert(height_expanded_n >= Array_len(&__HashMap_heights, u32) && "HashMap IS FULL! Fix your code."); + + // alloc new HashMapBucket array + u32 height_expanded = ((u32*)__HashMap_heights.data)[height_expanded_n]; + u32 table_expanded_size = height_expanded * sizeof(HashMapBucket); + HashMapBucket* table_expanded = (HashMapBucket*)malloc(table_expanded_size); + memset(table_expanded, 0, table_expanded_size); + + // copy values from old buckets to new + for(u32 i = 0; i < ptr->height; i++){ + HashMapBucket* old_bucket = &ptr->table[i]; + u32 len = List_len(&old_bucket->key_hash_list, KeyHash); + + for(u32 j = 0; j < len; j++){ + KeyHash kh = ((KeyHash*)old_bucket->key_hash_list.data)[j]; + HashMapBucket* new_bucket = &table_expanded[kh.hash % height_expanded]; + List_push(&new_bucket->key_hash_list, KeyHash, kh); + void* old_value_ptr = (u8*)old_bucket->value_list.data + j * ptr->value_t_size; + List_push_size(&new_bucket->value_list, old_value_ptr, ptr->value_t_size); + } + + free(old_bucket->key_hash_list.data); + free(old_bucket->value_list.data); + } + free(ptr->table); + ptr->table = table_expanded; + ptr->height = height_expanded; + ptr->height_n = height_expanded_n; +} + +bool HashMap_tryPush(HashMap* ptr, str key, void* value_ptr){ + u32 hash = __HashMap_HASH_FUNC(key); + BucketAndIndex r = __HashMap_search(ptr, key, hash); + // found existing item with the same key + if(r.i != -1) + return false; + + HashMapBucket* bu = r.bu; + if(List_len(&bu->key_hash_list, KeyHash) >= __HashMapBucket_MAX_LEN){ + __HashMap_expand(ptr); + bu = &ptr->table[hash % ptr->height]; + } + + KeyHash kh = { .key = str_copy(key), .hash = hash }; + List_push(&bu->key_hash_list, KeyHash, kh); + List_push_size(&bu->value_list, value_ptr, ptr->value_t_size); + return true; +} + +bool HashMap_tryDelete(HashMap* ptr, str key){ + u32 hash = __HashMap_HASH_FUNC(key); + BucketAndIndex r = __HashMap_search(ptr, key, hash); + // key not found + if(r.i == -1) + return false; + + List_removeAt(&r.bu->key_hash_list, KeyHash, r.i, 1); + List_removeAt_size(&r.bu->value_list, r.i, ptr->value_t_size); + return true; +} diff --git a/src/collections/List.c b/src/collections/List.c new file mode 100755 index 0000000..8247e7a --- /dev/null +++ b/src/collections/List.c @@ -0,0 +1,39 @@ +#include "collections/List.h" + +List List_alloc_size(u32 initial_size){ + if(initial_size == 0) + return List_construct_size(NULL, 0, 0); + u32 allocated_size = ALIGN_TO(initial_size, sizeof(void*)); + return List_construct_size(malloc(allocated_size), 0, allocated_size); +} + +void* List_expand(List* ptr, u32 expansion_size){ + u32 occupied_size = ptr->size; + u32 expanded_alloc_size = ptr->allocated_size; + if(expanded_alloc_size == 0) + expanded_alloc_size = 64; + ptr->size += expansion_size; + while(ptr->size > expanded_alloc_size){ + expanded_alloc_size *= 2; + } + // if ptr->data is null, realloc acts like malloc + ptr->data = realloc(ptr->data, expanded_alloc_size); + ptr->allocated_size = expanded_alloc_size; + return (u8*)(ptr->data) + occupied_size; +} + +void List_push_size(List* ptr, void* values, u32 size){ + void* empty_cell_ptr = List_expand(ptr, size); + memcpy(empty_cell_ptr, values, size); +} + +bool List_removeAt_size(List* ptr, u32 i, u32 remove_size){ + if(i + remove_size >= ptr->size) + return false; + + ptr->size -= remove_size; + u8* src = (u8*)ptr->data + i + remove_size; + u8* dst = (u8*)ptr->data + i; + memmove(dst, src, ptr->size - i - remove_size); + return true; +} diff --git a/src/string/StringBuilder.c b/src/string/StringBuilder.c new file mode 100755 index 0000000..07592ea --- /dev/null +++ b/src/string/StringBuilder.c @@ -0,0 +1,53 @@ +#include "string/StringBuilder.h" + +void StringBuilder_free(StringBuilder* b){ + free(b->buffer.data); + b->buffer = List_construct_size(NULL, 0, 0); +} + +str StringBuilder_getStr(StringBuilder* b){ + List_push(&b->buffer, u8, '\0'); + str result = str_construct((char*)b->buffer.data, b->buffer.size - 1, true); + return result; +} + +void StringBuilder_removeFromEnd(StringBuilder* b, u32 count){ + if(count < b->buffer.size){ + b->buffer.size -= count; + } + else{ + b->buffer.size = 0; + } +} + + +void StringBuilder_append_char(StringBuilder* b, char c){ + List_push(&b->buffer, u8, c); +} + + +void StringBuilder_append_string(StringBuilder* b, str s){ + List_push_size(&b->buffer, s.data, s.size); +} + +void StringBuilder_append_cstr(StringBuilder* b, char* s){ + StringBuilder_append_string(b, str_construct(s, strlen(s), true)); +} + +void StringBuilder_append_i64(StringBuilder* b, i64 n){ + char buf[32]; + sprintf(buf, IFWIN("%lli", "%li"), n); + StringBuilder_append_cstr(b, buf); +} + +void StringBuilder_append_u64(StringBuilder* b, u64 n){ + char buf[32]; + sprintf(buf, IFWIN("%llu", "%lu"), n); + StringBuilder_append_cstr(b, buf); +} + +void StringBuilder_append_f64(StringBuilder* b, f64 n){ + char buf[32]; + sprintf(buf, "%lf", n); + StringBuilder_append_cstr(b, buf); +} diff --git a/src/string/cstr.c b/src/string/cstr.c new file mode 100755 index 0000000..b147eb2 --- /dev/null +++ b/src/string/cstr.c @@ -0,0 +1,53 @@ +#include "string/cstr.h" +#include + +char* _strcat_malloc(size_t n, cstr str0, ...){ + va_list argv; + va_start(argv, str0); + char* heap_ptr = _vstrcat_malloc(n, str0, argv); + va_end(argv); + return heap_ptr; +} + +char* _vstrcat_malloc(size_t n, cstr str0, va_list argv){ + size_t str0_len = strlen(str0); + size_t total_len = str0_len; + cstr* const parts = malloc(sizeof(cstr) * n); + size_t* const part_lengths = malloc(sizeof(size_t) * n); + for(size_t i = 0; i < n; i++){ + cstr part = va_arg(argv, cstr); + size_t length = strlen(part); + parts[i] = part; + part_lengths[i] = length; + total_len += length; + } + char* const buf = malloc(total_len + 1); + memcpy(buf, str0, str0_len); + char* walking_ptr = buf + str0_len; + for(size_t i = 0; i < n; i++){ + memcpy(walking_ptr, parts[i], part_lengths[i]); + walking_ptr += part_lengths[i]; + } + buf[total_len] = '\0'; + free(parts); + free(part_lengths); + return buf; +} + +char* NULLABLE(sprintf_malloc)(size_t buffer_size, cstr format, ...){ + va_list argv; + va_start(argv, format); + char* NULLABLE(heap_ptr) = vsprintf_malloc(buffer_size, format, argv); + va_end(argv); + return heap_ptr; +} + +char* NULLABLE(vsprintf_malloc)(size_t buffer_size, cstr format, va_list argv){ + char* buf = malloc(buffer_size); + int r = vsprintf(buf, format, argv); + if(r < 0){ + free(buf); + return NULL; + } + return buf; +} diff --git a/src/string/str.c b/src/string/str.c new file mode 100755 index 0000000..748b0a5 --- /dev/null +++ b/src/string/str.c @@ -0,0 +1,125 @@ +#include "string/str.h" + +str str_copy(str src){ + if(src.data == NULL || src.size == 0) + return src; + + str nstr = str_construct((char*)malloc(src.size + 1), src.size, true); + memcpy(nstr.data, src.data, src.size); + nstr.data[nstr.size] = '\0'; + return nstr; +} + +bool str_equals(str s0, str s1){ + if(s0.size != s1.size) + return false; + + for(u32 i = 0; i < s0.size; i++) + if(s0.data[i] != s1.data[i]) + return false; + + return true; +} + +str str_reverse(str s){ + if(s.data == NULL || s.size == 0) + return s; + + str r = str_construct(malloc(s.size), s.size, s.isZeroTerminated); + for(u32 i = 0; i < s.size; i++ ) + r.data[i] = s.data[s.size - i - 1]; + return r; +} + +i32 str_seek(str src, str fragment, u32 startIndex){ + if(src.size == 0 || fragment.size == 0) + return -1; + + for(u32 i = startIndex; i < src.size - fragment.size + 1; i++){ + for(u32 j = 0;; j++){ + if(j == fragment.size) + return i; + if(src.data[i + j] != fragment.data[j]) + break; + } + } + return -1; +} + +i32 str_seekReverse(str src, str fragment, u32 startIndex){ + if(src.size == 0 || fragment.size == 0) + return -1; + + if(startIndex > src.size - 1) + startIndex = src.size - 1; + for(u32 i = startIndex; i >= fragment.size - 1; i--){ + for(u32 j = 0;; j++){ + if(j == fragment.size) + return i - j + 1; + if(src.data[i - j] != fragment.data[fragment.size - 1 - j]) + break; + } + } + return -1; +} + +i32 str_seekChar(str src, char c, u32 startIndex){ + for(u32 i = startIndex; i < src.size; i++){ + if(src.data[i] == c) + return i; + } + return -1; +} + +i32 str_seekCharReverse(str src, char c, u32 startIndex){ + if(startIndex > src.size - 1) + startIndex = src.size - 1; + for(u32 i = startIndex; i != (u32)-1; i--){ + if(src.data[i] == c) + return i; + } + return -1; +} + +bool str_startsWith(str src, str fragment){ + if(src.size < fragment.size) + return false; + + src.size = fragment.size; + return str_equals(src, fragment); +} + +bool str_endsWith(str src, str fragment){ + if(src.size < fragment.size) + return false; + + src.data = (char*)(src.data + src.size - fragment.size); + src.size = fragment.size; + return str_equals(src, fragment); +} + +u32 str_hash32(str s){ + u8* ubuf = (u8*)s.data; + u32 hash=0; + for (u32 i = 0; i < s.size; i++) + hash = (hash<<6) + (hash<<16) - hash + ubuf[i]; + return hash; +} + +str str_toUpper(str src){ + str r = str_copy(src); + for (u32 i = 0; i < r.size; i++){ + if(isAlphabeticalLower(r.data[i])) + r.data[i] = r.data[i] - 'a' + 'A'; + } + return r; +} + +str str_toLower(str src){ + str r = str_copy(src); + for (u32 i = 0; i < r.size; i++){ + if(isAlphabeticalUpper(r.data[i])) + r.data[i] = r.data[i] - 'A' + 'a'; + } + return r; +}