From 44ee9e210fdac47c65a737484320f97acb0823fa Mon Sep 17 00:00:00 2001 From: timerix Date: Wed, 12 Jul 2023 13:53:20 +0300 Subject: [PATCH] kerep sources --- build.sh | 29 +- detect_arch.sh | 2 +- detect_os.sh | 2 +- kerep-headers/DtsodParser/DtsodV24.h | 32 -- kerep/.gitignore | 2 + kerep/build.sh | 41 +++ kerep/src/Autoarr/Autoarr.c | 17 + .../src}/Autoarr/Autoarr.h | 0 .../src}/Autoarr/Autoarr_declare.h | 0 .../src}/Autoarr/Autoarr_define.h | 0 .../src}/Autoarr/README.md | 0 kerep/src/DtsodParser/DtsodV24.h | 17 + kerep/src/DtsodParser/DtsodV24_deserialize.c | 344 ++++++++++++++++++ kerep/src/DtsodParser/DtsodV24_serialize.c | 130 +++++++ .../src}/DtsodParser/README.md | 0 kerep/src/Filesystem/dir.c | 69 ++++ {kerep-headers => kerep/src}/Filesystem/dir.h | 8 +- kerep/src/Filesystem/file.c | 138 +++++++ .../src}/Filesystem/file.h | 0 .../src}/Filesystem/filesystem.h | 0 .../src}/Filesystem/io_includes.h | 0 kerep/src/Filesystem/path.c | 84 +++++ .../src}/Filesystem/path.h | 0 kerep/src/HashFunctions/hash.c | 111 ++++++ .../src}/HashFunctions/hash.h | 0 kerep/src/Hashtable/Hashtable.c | 133 +++++++ .../src}/Hashtable/Hashtable.h | 0 kerep/src/Hashtable/KeyValuePair.c | 18 + .../src}/Hashtable/KeyValuePair.h | 0 kerep/src/String/StringBuilder.c | 147 ++++++++ .../src}/String/StringBuilder.h | 0 kerep/src/String/string.c | 47 +++ {kerep-headers => kerep/src}/String/string.h | 0 {kerep-headers => kerep/src}/base/base.h | 0 kerep/src/base/cptr.c | 217 +++++++++++ {kerep-headers => kerep/src}/base/cptr.h | 0 kerep/src/base/endian.c | 13 + {kerep-headers => kerep/src}/base/endian.h | 0 kerep/src/base/errors.c | 59 +++ {kerep-headers => kerep/src}/base/errors.h | 0 {kerep-headers => kerep/src}/base/optime.h | 0 {kerep-headers => kerep/src}/base/std.h | 4 + .../src}/base/type_system/README.md | 0 kerep/src/base/type_system/base_toString.c | 238 ++++++++++++ .../src}/base/type_system/base_toString.h | 0 kerep/src/base/type_system/init.c | 65 ++++ .../src}/base/type_system/init.h | 0 .../src}/base/type_system/ktDescriptor.h | 0 kerep/src/base/type_system/kt_functions.c | 90 +++++ .../src}/base/type_system/kt_functions.h | 0 .../src}/base/type_system/ktid.h | 0 .../src}/base/type_system/type_system.h | 0 .../src}/base/type_system/typedef_macros.h | 0 kerep/src/base/type_system/unitype.c | 100 +++++ .../src}/base/type_system/unitype.h | 0 {kerep-headers => kerep/src}/kprint/README.md | 0 kerep/src/kprint/kprint.c | 211 +++++++++++ {kerep-headers => kerep/src}/kprint/kprint.h | 0 .../src}/kprint/kprint_colors.h | 0 .../src}/kprint/kprint_format.h | 0 .../src}/kprint/kprint_format.md | 0 kerep/src/kprint/kprintf.c | 155 ++++++++ {kerep-headers => kerep/src}/kprint/kprintf.h | 0 {kerep-headers => kerep/src}/random/krandom.h | 0 kerep/src/random/splitmix64/splitmix64.c | 34 ++ .../src}/random/splitmix64/splitmix64.h | 0 .../random/xoroshiro/32bitValue/xoroshiro64.h | 0 .../xoroshiro/32bitValue/xoroshiro64star.c | 49 +++ .../32bitValue/xoroshiro64starstar.c | 37 ++ .../xoroshiro/64bitValue/xoroshiro128.h | 0 .../xoroshiro/64bitValue/xoroshiro128plus.c | 59 +++ .../64bitValue/xoroshiro128plusplus.c | 39 ++ .../64bitValue/xoroshiro128starstar.c | 39 ++ .../src}/random/xoroshiro/xoroshiro.h | 0 .../src}/random/xoshiro-xoroshiro.md | 0 .../random/xoshiro/32bitValue/xoshiro128.h | 0 .../xoshiro/32bitValue/xoshiro128plus.c | 54 +++ .../xoshiro/32bitValue/xoshiro128plusplus.c | 42 +++ .../xoshiro/32bitValue/xoshiro128starstar.c | 45 +++ .../random/xoshiro/64bitValue/xoshiro256.h | 0 .../xoshiro/64bitValue/xoshiro256plus.c | 58 +++ .../xoshiro/64bitValue/xoshiro256plusplus.c | 37 ++ .../xoshiro/64bitValue/xoshiro256starstar.c | 42 +++ .../src}/random/xoshiro/xoshiro.h | 0 libkerep.a.tar.xz | Bin 48248 -> 0 bytes src/CompilationScenario.c | 2 +- src/CompilationScenario.h | 2 +- src/cbuilld.c | 87 ++--- 88 files changed, 3051 insertions(+), 98 deletions(-) delete mode 100644 kerep-headers/DtsodParser/DtsodV24.h create mode 100644 kerep/.gitignore create mode 100644 kerep/build.sh create mode 100644 kerep/src/Autoarr/Autoarr.c rename {kerep-headers => kerep/src}/Autoarr/Autoarr.h (100%) rename {kerep-headers => kerep/src}/Autoarr/Autoarr_declare.h (100%) rename {kerep-headers => kerep/src}/Autoarr/Autoarr_define.h (100%) rename {kerep-headers => kerep/src}/Autoarr/README.md (100%) create mode 100644 kerep/src/DtsodParser/DtsodV24.h create mode 100644 kerep/src/DtsodParser/DtsodV24_deserialize.c create mode 100644 kerep/src/DtsodParser/DtsodV24_serialize.c rename {kerep-headers => kerep/src}/DtsodParser/README.md (100%) create mode 100644 kerep/src/Filesystem/dir.c rename {kerep-headers => kerep/src}/Filesystem/dir.h (81%) create mode 100644 kerep/src/Filesystem/file.c rename {kerep-headers => kerep/src}/Filesystem/file.h (100%) rename {kerep-headers => kerep/src}/Filesystem/filesystem.h (100%) rename {kerep-headers => kerep/src}/Filesystem/io_includes.h (100%) create mode 100644 kerep/src/Filesystem/path.c rename {kerep-headers => kerep/src}/Filesystem/path.h (100%) create mode 100644 kerep/src/HashFunctions/hash.c rename {kerep-headers => kerep/src}/HashFunctions/hash.h (100%) create mode 100644 kerep/src/Hashtable/Hashtable.c rename {kerep-headers => kerep/src}/Hashtable/Hashtable.h (100%) create mode 100644 kerep/src/Hashtable/KeyValuePair.c rename {kerep-headers => kerep/src}/Hashtable/KeyValuePair.h (100%) create mode 100644 kerep/src/String/StringBuilder.c rename {kerep-headers => kerep/src}/String/StringBuilder.h (100%) create mode 100644 kerep/src/String/string.c rename {kerep-headers => kerep/src}/String/string.h (100%) rename {kerep-headers => kerep/src}/base/base.h (100%) create mode 100644 kerep/src/base/cptr.c rename {kerep-headers => kerep/src}/base/cptr.h (100%) create mode 100644 kerep/src/base/endian.c rename {kerep-headers => kerep/src}/base/endian.h (100%) create mode 100644 kerep/src/base/errors.c rename {kerep-headers => kerep/src}/base/errors.h (100%) rename {kerep-headers => kerep/src}/base/optime.h (100%) rename {kerep-headers => kerep/src}/base/std.h (98%) rename {kerep-headers => kerep/src}/base/type_system/README.md (100%) create mode 100644 kerep/src/base/type_system/base_toString.c rename {kerep-headers => kerep/src}/base/type_system/base_toString.h (100%) create mode 100644 kerep/src/base/type_system/init.c rename {kerep-headers => kerep/src}/base/type_system/init.h (100%) rename {kerep-headers => kerep/src}/base/type_system/ktDescriptor.h (100%) create mode 100644 kerep/src/base/type_system/kt_functions.c rename {kerep-headers => kerep/src}/base/type_system/kt_functions.h (100%) rename {kerep-headers => kerep/src}/base/type_system/ktid.h (100%) rename {kerep-headers => kerep/src}/base/type_system/type_system.h (100%) rename {kerep-headers => kerep/src}/base/type_system/typedef_macros.h (100%) create mode 100644 kerep/src/base/type_system/unitype.c rename {kerep-headers => kerep/src}/base/type_system/unitype.h (100%) rename {kerep-headers => kerep/src}/kprint/README.md (100%) create mode 100644 kerep/src/kprint/kprint.c rename {kerep-headers => kerep/src}/kprint/kprint.h (100%) rename {kerep-headers => kerep/src}/kprint/kprint_colors.h (100%) rename {kerep-headers => kerep/src}/kprint/kprint_format.h (100%) rename {kerep-headers => kerep/src}/kprint/kprint_format.md (100%) create mode 100644 kerep/src/kprint/kprintf.c rename {kerep-headers => kerep/src}/kprint/kprintf.h (100%) rename {kerep-headers => kerep/src}/random/krandom.h (100%) create mode 100644 kerep/src/random/splitmix64/splitmix64.c rename {kerep-headers => kerep/src}/random/splitmix64/splitmix64.h (100%) rename {kerep-headers => kerep/src}/random/xoroshiro/32bitValue/xoroshiro64.h (100%) create mode 100644 kerep/src/random/xoroshiro/32bitValue/xoroshiro64star.c create mode 100644 kerep/src/random/xoroshiro/32bitValue/xoroshiro64starstar.c rename {kerep-headers => kerep/src}/random/xoroshiro/64bitValue/xoroshiro128.h (100%) create mode 100644 kerep/src/random/xoroshiro/64bitValue/xoroshiro128plus.c create mode 100644 kerep/src/random/xoroshiro/64bitValue/xoroshiro128plusplus.c create mode 100644 kerep/src/random/xoroshiro/64bitValue/xoroshiro128starstar.c rename {kerep-headers => kerep/src}/random/xoroshiro/xoroshiro.h (100%) rename {kerep-headers => kerep/src}/random/xoshiro-xoroshiro.md (100%) rename {kerep-headers => kerep/src}/random/xoshiro/32bitValue/xoshiro128.h (100%) create mode 100644 kerep/src/random/xoshiro/32bitValue/xoshiro128plus.c create mode 100644 kerep/src/random/xoshiro/32bitValue/xoshiro128plusplus.c create mode 100644 kerep/src/random/xoshiro/32bitValue/xoshiro128starstar.c rename {kerep-headers => kerep/src}/random/xoshiro/64bitValue/xoshiro256.h (100%) create mode 100644 kerep/src/random/xoshiro/64bitValue/xoshiro256plus.c create mode 100644 kerep/src/random/xoshiro/64bitValue/xoshiro256plusplus.c create mode 100644 kerep/src/random/xoshiro/64bitValue/xoshiro256starstar.c rename {kerep-headers => kerep/src}/random/xoshiro/xoshiro.h (100%) delete mode 100644 libkerep.a.tar.xz diff --git a/build.sh b/build.sh index f497caf..cead1a7 100644 --- a/build.sh +++ b/build.sh @@ -1,31 +1,38 @@ #!/bin/bash set -eo pipefail + CMP="gcc" -ARGS="-Wall -Wno-discarded-qualifiers -O2" -SRC="$(find . -name '*.c')" -LINK="-L. -lkerep" +WARN="-Wall -Wextra -Wno-discarded-qualifiers -Wno-unused-parameter" +ARGS="-O2 -flto -fdata-sections -ffunction-sections -Wl,--gc-sections" +SRC="$(find src -name '*.c')" -OS=$(./detect_os.sh) -ARCH=$(./detect_arch.sh) +OS="$(./detect_os.sh)" +ARCH="$(./detect_arch.sh)" +LINK="-Lkerep/bin -lkerep-$OS-$ARCH" -if [[ OS == "windows" ]]; then +if [[ OS -eq "windows" ]]; then OUT_FILE="cbuild.exe" else OUT_FILE="cbuild" fi command="$CMP $ARGS - -DOS=$OS -DARCH=$ARCH + $WARN + -DOS=\"$OS\" -DARCH=\"$ARCH\" $SRC $LINK -o bin/$OUT_FILE" +if [ ! -f "kerep/bin/libkerep-$OS-$ARCH.a" ]; then + echo "libkerep-$OS-$ARCH.a not found" + cd kerep + ./build.sh + cd .. +fi + rm -rf bin mkdir bin +echo "----------------[cbuild]----------------" echo "$command" -tar xJf libkerep.a.tar.xz - $($command) - -rm libkerep.a diff --git a/detect_arch.sh b/detect_arch.sh index 5779297..08d3e0c 100644 --- a/detect_arch.sh +++ b/detect_arch.sh @@ -20,4 +20,4 @@ case "$uname_rezult" in ;; esac -echo "\"$ARCH\"" +echo "$ARCH" diff --git a/detect_os.sh b/detect_os.sh index 673c5f7..541dd96 100644 --- a/detect_os.sh +++ b/detect_os.sh @@ -23,4 +23,4 @@ case "$uname_rezult" in ;; esac -echo "\"$OS\"" +echo "$OS" diff --git a/kerep-headers/DtsodParser/DtsodV24.h b/kerep-headers/DtsodParser/DtsodV24.h deleted file mode 100644 index a49f07e..0000000 --- a/kerep-headers/DtsodParser/DtsodV24.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#if __cplusplus -extern "C" { -#endif - -#include "../Hashtable/Hashtable.h" - -// parses text to binary values -Maybe DtsodV24_deserialize(char* text); - -// creates text representation of dtsod -Maybe DtsodV24_serialize(Hashtable* dtsod); - -// returns value or UniNull if key not found -Unitype DtsodV24_get(Hashtable* dtsod, char* key); - -// adds or sets value -void DtsodV24_addOrSet(Hashtable* dtsod, char* key, Unitype value); - -// checks for dtsod contains value or dont -bool DtsodV24_contains(Hashtable* dtsod, char* key); - -// replaces value with UniNull if key exists in dtsod -bool DtsodV24_remove(Hashtable* dtsod, char* key); - -// frees memory including memory of elements (hashtables, autoarrs, etc.) -void DtsodV24_free(Hashtable* dtsod); - -#if __cplusplus -} -#endif \ No newline at end of file diff --git a/kerep/.gitignore b/kerep/.gitignore new file mode 100644 index 0000000..56cdcd2 --- /dev/null +++ b/kerep/.gitignore @@ -0,0 +1,2 @@ +obj/ +libkerep.* diff --git a/kerep/build.sh b/kerep/build.sh new file mode 100644 index 0000000..8bfebca --- /dev/null +++ b/kerep/build.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -eo pipefail + +CMP="gcc" +WARN="-std=c11 -Wall -Wno-discarded-qualifiers -Wno-unused-parameter" +ARGS="-O2 -flto -fpic -fdata-sections -ffunction-sections" +SRC="$(find src -name '*.c')" + +OS=$(../detect_os.sh) +ARCH=$(../detect_arch.sh) +OUTFILE="bin/libkerep-$OS-$ARCH.a" + +rm -rf obj +mkdir obj +mkdir -p bin + +echo "----------------[kerep]-----------------" +compiler_call="$CMP $WARN $ARGS -c" +echo "$compiler_call" +OBJECTS= +for srcfile in $SRC +do + echo " $srcfile" + object="obj/$(basename $srcfile).o" + OBJECTS="$OBJECTS + $object" + command="$compiler_call $srcfile -o $object" + if ! $($command); then + exit 1 + fi +done + +command="ar rcs $OUTFILE $OBJECTS" +echo "$command" +if $($command) +then + echo "static lib $OUTFILE created" + rm -r obj +else + exit 1 +fi diff --git a/kerep/src/Autoarr/Autoarr.c b/kerep/src/Autoarr/Autoarr.c new file mode 100644 index 0000000..f2159c8 --- /dev/null +++ b/kerep/src/Autoarr/Autoarr.c @@ -0,0 +1,17 @@ +#include "Autoarr.h" + +Autoarr_define(Pointer, true) +Autoarr_define(char, false) +Autoarr_define(bool, false) +Autoarr_define(f32, false) +Autoarr_define(f64, false) +Autoarr_define(u8, false) +Autoarr_define(i8, false) +Autoarr_define(u16, false) +Autoarr_define(i16, false) +Autoarr_define(u32, false) +Autoarr_define(i32, false) +Autoarr_define(u64, false) +Autoarr_define(i64, false) + +Autoarr_define(Unitype, false) diff --git a/kerep-headers/Autoarr/Autoarr.h b/kerep/src/Autoarr/Autoarr.h similarity index 100% rename from kerep-headers/Autoarr/Autoarr.h rename to kerep/src/Autoarr/Autoarr.h diff --git a/kerep-headers/Autoarr/Autoarr_declare.h b/kerep/src/Autoarr/Autoarr_declare.h similarity index 100% rename from kerep-headers/Autoarr/Autoarr_declare.h rename to kerep/src/Autoarr/Autoarr_declare.h diff --git a/kerep-headers/Autoarr/Autoarr_define.h b/kerep/src/Autoarr/Autoarr_define.h similarity index 100% rename from kerep-headers/Autoarr/Autoarr_define.h rename to kerep/src/Autoarr/Autoarr_define.h diff --git a/kerep-headers/Autoarr/README.md b/kerep/src/Autoarr/README.md similarity index 100% rename from kerep-headers/Autoarr/README.md rename to kerep/src/Autoarr/README.md diff --git a/kerep/src/DtsodParser/DtsodV24.h b/kerep/src/DtsodParser/DtsodV24.h new file mode 100644 index 0000000..d4987e1 --- /dev/null +++ b/kerep/src/DtsodParser/DtsodV24.h @@ -0,0 +1,17 @@ +#pragma once + +#if __cplusplus +extern "C" { +#endif + +#include "../Hashtable/Hashtable.h" + +// parses text to binary values +Maybe DtsodV24_deserialize(char* text); + +// creates text representation of dtsod +Maybe DtsodV24_serialize(Hashtable* dtsod); + +#if __cplusplus +} +#endif \ No newline at end of file diff --git a/kerep/src/DtsodParser/DtsodV24_deserialize.c b/kerep/src/DtsodParser/DtsodV24_deserialize.c new file mode 100644 index 0000000..57ac4d0 --- /dev/null +++ b/kerep/src/DtsodParser/DtsodV24_deserialize.c @@ -0,0 +1,344 @@ +#include "DtsodV24.h" +#include "../String/StringBuilder.h" + +#define ARR_BC 64 +#define ARR_BL 1024 + + +typedef struct DeserializeSharedData{ + const char* sh_text_first; + char* sh_text; + bool sh_partOfDollarList; + bool sh_calledRecursively; +} DeserializeSharedData; + +#define text shared->sh_text +#define partOfDollarList shared->sh_partOfDollarList +#define calledRecursively shared->sh_calledRecursively + + +// special func for throwing error messages about wrong characters in deserializing text +Maybe ERROR_WRONGCHAR(const char c, char* _text, char* text_first, const char* srcfile, i32 line, const char* funcname){ + char errBuf[68]; + for(u8 n=0; n at:\n" + " \"%s\"\n" + "\\___[%s:%d] %s()", + c,errBuf, srcfile,line,funcname), + sprintf(errmsg, "unexpected <%c> at:\n" + " \"%s\"\n" + " \\___[%s:%d] %s()", + c,errBuf, srcfile,line,funcname) + ); + safethrow(errmsg,;); +} +#define safethrow_wrongchar(C, freeMem) { freeMem; return ERROR_WRONGCHAR(C, text, shared->sh_text_first, __FILE__,__LINE__,__func__); } + + +Maybe __SkipComment(DeserializeSharedData* shared) { + char c; + + while ((c=*++text) != '\n') + if (!c) safethrow(ERR_ENDOFSTR,;); + + return MaybeNull; +} +#define SkipComment() __SkipComment(shared) + +Maybe __ReadName(DeserializeSharedData* shared){ + char c; + string nameStr={text+1,0}; + + while ((c=*++text)) switch (c){ + case ' ': case '\t': + case '\r': case '\n': + if(nameStr.length!=0) + safethrow_wrongchar(c,;); + nameStr.ptr++; + break; + case '=': case ';': + case '\'': case '"': + case '[': case ']': + case '{': + safethrow_wrongchar(c,;); + case '#': ; + char _c=c; + char* _text=text; + try(SkipComment(),_,;); + if(nameStr.length!=0){ + text=_text; + safethrow_wrongchar(_c,;); + } + nameStr.ptr=text+1; // skips '\n' + break; + case '}': + if(!calledRecursively || nameStr.length!=0) + safethrow_wrongchar(c,;); + return SUCCESS(UniHeapPtr(char,NULL)); + case ':': + return SUCCESS(UniHeapPtr(char,string_extract(nameStr))); + case '$': + if(nameStr.length!=0) + safethrow_wrongchar(c,;); + nameStr.ptr++; + partOfDollarList=true; + break; + default: + nameStr.length++; + break; + } + + if(nameStr.length>0) safethrow(ERR_ENDOFSTR,;); + return SUCCESS(UniHeapPtr(char,NULL)); +} +#define ReadName() __ReadName(shared) + +Maybe __deserialize(char** _text, bool _calledRecursively); +Maybe __ReadValue(DeserializeSharedData* shared, bool* readingList); +#define ReadValue(rL) __ReadValue(shared, rL) + +// returns part of without quotes +Maybe __ReadString(DeserializeSharedData* shared){ + char c; + bool prevIsBackslash=false; + StringBuilder* b=StringBuilder_create(); + + while ((c=*++text)){ + if(c=='"') { + if(prevIsBackslash) { + // replacing <\"> with <"> + StringBuilder_rmchar(b); + StringBuilder_append_char(b,c); + prevIsBackslash=false; + } + else { + char* str=StringBuilder_build(b).ptr; + return SUCCESS(UniHeapPtr(char,str)); + } + } + else { + prevIsBackslash= c=='\\' && !prevIsBackslash; + StringBuilder_append_char(b,c); + } + } + + safethrow(ERR_ENDOFSTR, StringBuilder_free(b)); +} +#define ReadString() __ReadString(shared) + +Maybe __ReadList(DeserializeSharedData* shared){ + Autoarr(Unitype)* list=Autoarr_create(Unitype,ARR_BC,ARR_BL); + bool readingList=true; + while (true){ + try(ReadValue((&readingList)), m_val, Autoarr_free(list, true)) + Autoarr_add(list,m_val.value); + if (!readingList){ + if(Unitype_isUniNull(m_val.value)) + Autoarr_pop(list); + break; + } + } + + return SUCCESS(UniHeapPtr(Autoarr_Unitype,list)); +}; +#define ReadList() __ReadList(shared) + +Maybe __ParseValue(DeserializeSharedData* shared, string str){ + const string trueStr={"true",4}; + const string falseStr={"false",5}; + + switch(str.ptr[str.length-1]){ + // Bool + case 'e': + if(string_compare(str,trueStr)) + return SUCCESS(UniTrue); + else if(string_compare(str,falseStr)) + return SUCCESS(UniFalse); + else safethrow_wrongchar(*str.ptr,;); + // Float64 + case 'f': { + char* _c=string_extract(str); + Unitype rez=UniFloat64(strtod(_c,NULL)); + free(_c); + return SUCCESS(rez); + } + // UInt64 + case 'u': { + u64 lu=0; + char* _c=string_extract(str); + if(sscanf(_c, IFWIN("%llu", "%lu"), &lu)!=1){ + char err[64]; + IFMSC( + sprintf_s(err,64,"can't parse to int: <%s>",_c), + sprintf(err,"can't parse to int: <%s>",_c) + ); + safethrow(err,free(_c)); + } + free(_c); + return SUCCESS(UniUInt64(lu)); + } + // Int64 + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + i64 li=0; + char* _c=string_extract(str); + if(sscanf(_c, IFWIN("%lli", "%li"), &li)!=1){ + char err[64]; + IFMSC( + sprintf_s(err,64,"can't parse to int: <%s>",_c), + sprintf(err,"can't parse to int: <%s>",_c) + ); + safethrow(err,free(_c)); + } + free(_c); + return SUCCESS(UniInt64(li)); + } + // wrong type + default: + safethrow_wrongchar(str.ptr[str.length-1],;); + } + + safethrow(ERR_ENDOFSTR,;); +}; +#define ParseValue(str) __ParseValue(shared, str) + +Maybe __ReadValue(DeserializeSharedData* shared, bool* readingList){ + char c; + string valueStr={text+1,0}; + Unitype value=UniNull; + bool spaceAfterVal=false; + + while ((c=*++text)) switch (c){ + case ' ': case '\t': + case '\r': case '\n': + if(valueStr.length!=0) + spaceAfterVal=true; + else valueStr.ptr++; + break; + case '=': case ':': + case '}': case '$': + case '\'': + safethrow_wrongchar(c,Unitype_free(value)); + case '#':; + char _c=c; + char* _text=text; + try(SkipComment(),_,;); + if(valueStr.length!=0){ + text=_text; + safethrow_wrongchar(_c,Unitype_free(value)); + } + valueStr.ptr=text+1; // skips '\n' + break; + case '"': + if(valueStr.length!=0) safethrow_wrongchar(c,Unitype_free(value)); + try(ReadString(),maybeString,;) + value=maybeString.value; + break; + case '{': + if(valueStr.length!=0) safethrow_wrongchar(c,Unitype_free(value)); + ++text; // skips '{' + try(__deserialize(&text,true), val,Unitype_free(value)) + value=val.value; + break; + case '[': + if(valueStr.length!=0) safethrow_wrongchar(c,Unitype_free(value)); + try(ReadList(),maybeList,Unitype_free(value)) + value=maybeList.value; + break; + case ']': + if(!readingList) safethrow_wrongchar(c,Unitype_free(value)); + *readingList=false; + goto return_value; + case ';': + case ',': + return_value: + if(valueStr.length!=0){ + if(!Unitype_isUniNull(value)) + safethrow_wrongchar(c,Unitype_free(value)); + try(ParseValue(valueStr),maybeParsed,;) + value=maybeParsed.value; + } + return SUCCESS(value); + default: + if(spaceAfterVal) + safethrow_wrongchar(c,Unitype_free(value)); + valueStr.length++; + break; + } + + safethrow(ERR_ENDOFSTR,;); +} + + +Maybe __deserialize(char** _text, bool _calledRecursively) { + DeserializeSharedData _shared={ + .sh_text_first=*_text, + .sh_text=*_text, + .sh_partOfDollarList=false, + .sh_calledRecursively=_calledRecursively + }; + DeserializeSharedData* shared=&_shared; + Hashtable* dict=Hashtable_create(); + + text--; + while(true){ + try(ReadName(), maybeName, Hashtable_free(dict)) + if(!maybeName.value.VoidPtr) // end of file or '}' in recursive call + goto END; + char* nameCPtr=maybeName.value.VoidPtr; + try(ReadValue(NULL), val, { + Hashtable_free(dict); + free(nameCPtr); + }) { + if(partOfDollarList){ + partOfDollarList=false; + Autoarr(Unitype)* list; + Unitype lu; + if(Hashtable_tryGet(dict,nameCPtr, &lu)){ + list=(Autoarr(Unitype)*)lu.VoidPtr; + // Key is not used in that case, because it is already added + // to the table with the first dollar list item. + free(nameCPtr); + } + else{ + list=Autoarr_create(Unitype,ARR_BC,ARR_BL); + Hashtable_add(dict,nameCPtr,UniHeapPtr(Autoarr_Unitype,list)); + } + Autoarr_add(list,val.value); + } + else Hashtable_add(dict,nameCPtr,val.value); + } + } + + END: + *_text=text; + return SUCCESS(UniHeapPtr(Hashtable,dict)); +} + +Maybe DtsodV24_deserialize(char* _text) { + return __deserialize(&_text, false); +} diff --git a/kerep/src/DtsodParser/DtsodV24_serialize.c b/kerep/src/DtsodParser/DtsodV24_serialize.c new file mode 100644 index 0000000..1c81feb --- /dev/null +++ b/kerep/src/DtsodParser/DtsodV24_serialize.c @@ -0,0 +1,130 @@ +#include "DtsodV24.h" +#include "../String/StringBuilder.h" + + +STRUCT(SerializeSharedData, + StringBuilder* sh_builder; + u8 sh_tabs; +) +#define b shared->sh_builder +#define tabs shared->sh_tabs + +Maybe __serialize(StringBuilder* _b, u8 _tabs, Hashtable* dtsod); + +#define addc(C) StringBuilder_append_char(b,C) + + +void __AppendTabs(SerializeSharedData* shared) { + for (u8 t = 0; t < tabs; t++) + addc( '\t'); +}; +#define AppendTabs() __AppendTabs(shared) + +Maybe __AppendValue(SerializeSharedData* shared, Unitype u); +#define AppendValue(UNI) __AppendValue(shared, UNI) +Maybe __AppendValue(SerializeSharedData* shared, Unitype u){ + if(u.typeId==ktid_name(i64)){ + StringBuilder_append_i64(b,u.Int64); + } + else if(u.typeId==ktid_name(u64)){ + StringBuilder_append_u64(b,u.UInt64); + addc('u'); + } + else if(u.typeId==ktid_name(f64)){ + StringBuilder_append_f64(b,u.Float64); + addc('f'); + } + else if(u.typeId==ktid_ptrName(char)){ + addc('"'); + char c; + while((c=*(char*)(u.VoidPtr++))){ + if(c=='\"') addc('\\'); + addc(c); + } + addc('"'); + } + else if(u.typeId==ktid_name(bool)){ + StringBuilder_append_cptr(b, u.Bool ? "true" : "false"); + } + else if(Unitype_isUniNull(u)){ + safethrow("Null isn't supported in DtsodV24",;); + } + else if(u.typeId==ktid_ptrName(Autoarr_Unitype)){ + if(Autoarr_length(((Autoarr_Unitype*)(u.VoidPtr)))){ + addc('\n'); + AppendTabs(); + addc('['); + tabs++; + Autoarr_foreach(((Autoarr_Unitype*)(u.VoidPtr)), e, + addc('\n'); + AppendTabs(); + try(AppendValue(e),__,;); + addc(','); + ); + StringBuilder_rmchar(b); + addc('\n'); + tabs--; + AppendTabs(); + addc(']'); + } + else { + addc('['); + addc(']'); + } + } + else if(u.typeId==ktid_ptrName(Hashtable)){ + // check hashtable is blank + bool hashtableNotBlank=false; + Hashtable_foreach(((Hashtable*)u.VoidPtr), __, + hashtableNotBlank=true; + if(__.key) {} // weird way to disable warning + break; + ); + + if(hashtableNotBlank){ + addc('\n'); + AppendTabs(); + addc('{'); + addc('\n'); + try(__serialize(b,tabs+1,u.VoidPtr),___,;); + AppendTabs(); + addc('}'); + } + else { + addc('{'); + addc('}'); + } + } + else { + dbg((u.typeId)); + safethrow(ERR_WRONGTYPE,;); + } + return MaybeNull; +}; + +Maybe __serialize(StringBuilder* _b, u8 _tabs, Hashtable* dtsod){ + SerializeSharedData _shared={ + .sh_builder=_b, + .sh_tabs=_tabs + }; + SerializeSharedData* shared=&_shared; + + Hashtable_foreach(dtsod, p, + AppendTabs(); + StringBuilder_append_cptr(b,p.key); + addc(':'); + addc(' '); + try(AppendValue(p.value),__,;); + addc(';'); + addc('\n'); + ); + + return MaybeNull; +} + +Maybe DtsodV24_serialize(Hashtable* dtsod){ + StringBuilder* sb=StringBuilder_create(); + try(__serialize(sb,0,dtsod),__, StringBuilder_free(sb)); + char* str=StringBuilder_build(sb).ptr; + return SUCCESS(UniHeapPtr(char, str)); +} diff --git a/kerep-headers/DtsodParser/README.md b/kerep/src/DtsodParser/README.md similarity index 100% rename from kerep-headers/DtsodParser/README.md rename to kerep/src/DtsodParser/README.md diff --git a/kerep/src/Filesystem/dir.c b/kerep/src/Filesystem/dir.c new file mode 100644 index 0000000..812c755 --- /dev/null +++ b/kerep/src/Filesystem/dir.c @@ -0,0 +1,69 @@ +#include "filesystem.h" +#include "io_includes.h" +#include "../kprint/kprint.h" + +bool dir_exists(const char* path){ + if(path[0]=='.'){ + if(path[1]==0 || (path[1]==path_sep && path[1]==0)) + return true; // dir . or ./ always exists + // else if(path[1]=='.' && path[2]==path_sep) + //TODO path_resolve because windows doesnt recognize .\ pattern + } +#if KFS_USE_WINDOWS_H + DWORD dwAttrib = GetFileAttributes(path); + return (bool)( + (dwAttrib != INVALID_FILE_ATTRIBUTES) && // file exists + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); // file is a directory +#else + struct stat stats; + i32 rez=stat(path, &stats); + return (bool)( + (rez!=-1) && // file exists + (S_ISDIR(stats.st_mode))); // file is a directory +#endif +} + +Maybe dir_create(const char* path){ + if (dir_exists(path)) + return MaybeNull; + char* parentDir=path_parentDir(path); + dir_create(parentDir); + free(parentDir); +#if KFS_USE_WINDOWS_H + if(!CreateDirectory(path, NULL)) +#else + if(mkdir(path, 0777) == -1) +#endif + { + char err[512]; + IFWIN( + sprintf_s(err, 512, "can't create dicectory <%s>", path), + sprintf(err, "can't create dicectory <%s>", path)); + safethrow(err,;); + } + + return MaybeNull; +} + +Maybe dir_delete(const char* path){ + throw(ERR_NOTIMPLEMENTED); + return MaybeNull; +} + +Maybe dir_getFiles(const char* path, bool recursive){ + throw(ERR_NOTIMPLEMENTED); + return MaybeNull; +} +Maybe dir_getDirs(const char* path, bool recursive){ + throw(ERR_NOTIMPLEMENTED); + return MaybeNull; +} + +Maybe dir_findFiles(const char* path, char* searchPattern, bool recursive){ + throw(ERR_NOTIMPLEMENTED); + return MaybeNull; +} +Maybe dir_findDirs(const char* path, char* searchPattern, bool recursive){ + throw(ERR_NOTIMPLEMENTED); + return MaybeNull; +} diff --git a/kerep-headers/Filesystem/dir.h b/kerep/src/Filesystem/dir.h similarity index 81% rename from kerep-headers/Filesystem/dir.h rename to kerep/src/Filesystem/dir.h index 3821229..385c0be 100644 --- a/kerep-headers/Filesystem/dir.h +++ b/kerep/src/Filesystem/dir.h @@ -13,14 +13,14 @@ Maybe dir_create(const char* path); ///@return Maybe Maybe dir_delete(const char* path); -///@return Maybe +///@return Maybe Maybe dir_getFiles(const char* path, bool recursive); -///@return Maybe +///@return Maybe Maybe dir_getDirs(const char* path, bool recursive); -///@return Maybe +///@return Maybe Maybe dir_findFiles(const char* path, char* searchPattern, bool recursive); -///@return Maybe +///@return Maybe Maybe dir_findDirs(const char* path, char* searchPattern, bool recursive); #if __cplusplus diff --git a/kerep/src/Filesystem/file.c b/kerep/src/Filesystem/file.c new file mode 100644 index 0000000..5c38523 --- /dev/null +++ b/kerep/src/Filesystem/file.c @@ -0,0 +1,138 @@ +#include "filesystem.h" +#include "../String/StringBuilder.h" +#include "io_includes.h" + +void __file_freeMembers(void* _f){ fclose((FileHandle)_f); } + +kt_define(FileHandle, __file_freeMembers, NULL) + +bool file_exists(const char* path){ + if(path[0]=='.'){ + if(path[1]==0 || (path[1]==path_sep && path[2]==0)) + return false; // . or ./ is not a file + // else if(path[1]=='.' && path[2]==path_sep) + //TODO path_resolve because windows doesnt recognize .\ pattern + } + +#if KFS_USE_WINDOWS_H + DWORD dwAttrib = GetFileAttributes(path); + return (bool)( + (dwAttrib != INVALID_FILE_ATTRIBUTES) && // file exists + !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); // file is not directory +#else + struct stat stats; + i32 rez=stat(path, &stats); + return (bool)( + (rez!=-1) && // file exists + !(S_ISDIR(stats.st_mode))); // file is not directory +#endif +} + +Maybe file_delete(const char* path, bool recursive){ + throw(ERR_NOTIMPLEMENTED); + return MaybeNull; +} + +char* FileOpenMode_toStr(FileOpenMode m){ + char* p; + switch(m){ + case FileOpenMode_Read: p="rb"; break; + case FileOpenMode_Write: p="wb"; break; + case FileOpenMode_Append: p="ab"; break; + case FileOpenMode_ReadWrite: p="wb+"; break; + case FileOpenMode_ReadAppend: p="ab+"; break; + default: + dbg(m); + throw(ERR_UNEXPECTEDVAL); + } + return p; +} + +Maybe file_open(const char* path, FileOpenMode mode){ + FileHandle file=fopen(path, FileOpenMode_toStr(mode)); + if(!file) + safethrow(cptr_concat("can't open file ", (char*)path),;); + return SUCCESS(UniHeapPtr(FileHandle,file)); +} + +Maybe file_close(FileHandle file){ + if(!file) + safethrow(ERR_NULLPTR,;); + if(fclose(file)) + safethrow(ERR_IO,;); + return MaybeNull; +} + +#define ioWriteCheck() \ + if(rezult==EOF) \ + safethrow(ERR_IO_EOF,;); \ + if(rezult!=0) \ + safethrow(ERR_IO,;); + +Maybe file_writeChar(FileHandle file, char byte){ + i32 rezult=fputc(byte, file); + ioWriteCheck(); + return MaybeNull; +} + +Maybe file_writeBuffer(FileHandle file, char* buffer, u64 length){ + i32 rezult=0; + for(u64 i=0; i uses <..>, that's not allowed"),); + return MaybeNull; +} + +char* path_parentDir(char* dir){ + char* copy=cptr_copy(dir); + i32 length=cptr_length(copy); + i32 i=cptr_lastIndexOfChar(copy,path_sep); + if(i!=-1 && i==length-1){ + copy[length-1]=0; + i=cptr_lastIndexOfChar(copy,path_sep); + } + if(i==-1){ + free(copy); + copy=malloc(2); + copy[0]='.'; + copy[1]=0; + } + return copy; +} + + +char* path_basename(char* path, bool with_extension){ + i32 nameIndex=cptr_lastIndexOfChar(path, path_sep)+1; + string rezult=string_fromCptr(path+nameIndex); + if(!with_extension){ + i32 extIndex=cptr_lastIndexOfChar(rezult.ptr, '.'); + if(extIndex!=0 && extIndex!=-1) + rezult.length=extIndex; + } + return string_extract(rezult); +} diff --git a/kerep-headers/Filesystem/path.h b/kerep/src/Filesystem/path.h similarity index 100% rename from kerep-headers/Filesystem/path.h rename to kerep/src/Filesystem/path.h diff --git a/kerep/src/HashFunctions/hash.c b/kerep/src/HashFunctions/hash.c new file mode 100644 index 0000000..3f2761b --- /dev/null +++ b/kerep/src/HashFunctions/hash.c @@ -0,0 +1,111 @@ +#include "hash.h" + +u32 hash_sdbm32(u32 oldhash, void* buf, u32 len){ + u8* ubuf=(u8*)buf; + register u32 hash=oldhash; + for (; len ; len--, ubuf++) + hash=(hash<<6)+(hash<<16)-hash+*ubuf; + return hash; +} + +static const u32 crc_32_tab[]={ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 hash_crc32(u32 oldhash, void* buf, u32 len){ + u8* ubuf=(u8*)buf; + register u32 crc=oldhash; + for (; len; --len, ++ubuf) + crc=crc_32_tab[(crc^(*ubuf)) & 0xff] ^ (crc>>8); + return ~crc; +} + + +// bool hashf_crc32c(char *name, u32 *crc, long *charcnt) { +// register FILE *fin; +// register u32 oldcrc32; +// register i32 c; + +// oldcrc32 = 0xFFFFFFFF; *charcnt = 0; +// if ((fin=fopen(name, "r"))==NULL) { +// perror(name); +// return false; +// } +// while ((c=getc(fin))!=EOF) { +// ++*charcnt; +// oldcrc32 = UPDC32(c, oldcrc32); +// } + +// if (ferror(fin)) { +// perror(name); +// *charcnt = -1; +// } + +// fclose(fin); + +// *crc = oldcrc32 = ~oldcrc32; +// return true; +// } diff --git a/kerep-headers/HashFunctions/hash.h b/kerep/src/HashFunctions/hash.h similarity index 100% rename from kerep-headers/HashFunctions/hash.h rename to kerep/src/HashFunctions/hash.h diff --git a/kerep/src/Hashtable/Hashtable.c b/kerep/src/Hashtable/Hashtable.c new file mode 100644 index 0000000..9c8fe15 --- /dev/null +++ b/kerep/src/Hashtable/Hashtable.c @@ -0,0 +1,133 @@ +#include "Hashtable.h" + +kt_define(Hashtable, __Hashtable_free, NULL); + +// amount of rows +static const u16 HT_HEIGHTS[]={17,61,257,1021,4099,16381,65521}; +#define HT_HEIN_MIN 0 +#define HT_HEIN_MAX 6 + +#define ARR_BC 2 +#define ARR_BL 8 + +Hashtable* Hashtable_create(){ + Hashtable* ht=malloc(sizeof(Hashtable)); + ht->hein=HT_HEIN_MIN; + ht->rows=malloc(HT_HEIGHTS[HT_HEIN_MIN]*sizeof(Autoarr(KVPair)*)); + for(u16 i=0;irows[i]=Autoarr_create(KVPair,ARR_BC,ARR_BL); + return ht; +} + +void __Hashtable_free(void* _ht){ + Hashtable* ht=_ht; + for(u16 i=0;ihein];i++) + Autoarr_free(ht->rows[i], true); + free(ht->rows); +} +void Hashtable_free(Hashtable* ht){ + __Hashtable_free(ht); + free(ht); +} + +u16 Hashtable_height(Hashtable* ht) { return HT_HEIGHTS[ht->hein]; } + + +void Hashtable_expand(Hashtable* ht){ + if(ht->hein>=HT_HEIN_MAX) throw(ERR_MAXLENGTH); + + Autoarr(KVPair)** newrows=malloc(HT_HEIGHTS[++ht->hein]*sizeof(Autoarr(KVPair)*)); + for(u16 i=0;ihein];i++) + newrows[i]=Autoarr_create(KVPair,ARR_BC,ARR_BL); + + for(u16 i=0;ihein-1];i++){ + Autoarr(KVPair)* ar=ht->rows[i]; + u32 arlen=Autoarr_length(ar); + for(u32 k=0;khein]; + Autoarr(KVPair)* newar=newrows[newrown]; + Autoarr_add(newar,p); + } + // there is no need to free array values, because they are copied into new array + // so dont replace this incorrect auto-generated function + Autoarr_freeWithoutMembers(ar, true); + } + + free(ht->rows); + ht->rows=newrows; +} + +Autoarr(KVPair)* getrow(Hashtable* ht, char* key, bool can_expand){ + u32 hash=hashs(hash_sdbm32, key); + Autoarr(KVPair)* ar=ht->rows[hash%HT_HEIGHTS[ht->hein]]; + if(can_expand && Autoarr_length(ar)==Autoarr_max_length(ar)) + Hashtable_expand(ht); + ar=ht->rows[hash%HT_HEIGHTS[ht->hein]]; + return ar; +} + +/// @param key must be heap allocated +/// Hashtable_free will free this pointer +void Hashtable_add(Hashtable* ht, char* key, Unitype u){ + KVPair p={ .key=key, .value=u }; + Autoarr_add(getrow(ht,key,true),p); +} + +void Hashtable_addMany(Hashtable* ht, KVPair* pair_array, u32 count){ + for(u32 i=0; ikey)) return &p->value; + } + return NULL; +} + +Unitype Hashtable_get(Hashtable* ht, char* key){ + Autoarr(KVPair)* ar=getrow(ht,key,false); + u32 arlen=Autoarr_length(ar); + for(u32 i=0;icompl_bufs) + b->compl_bufs=Autoarr_create(string,BL_C,BL_L); + u32 len=Autoarr_length(b->curr_buf); + if(!len) return; + string str={.length=len, .ptr=malloc(len)}; + u32 i=0; + Autoarr_foreach(b->curr_buf, c, + str.ptr[i++]=c; + ); + Autoarr_add(b->compl_bufs,str); + Autoarr_free(b->curr_buf, true); + b->curr_buf=Autoarr_create(i8,BL_C,BL_L); +} + +void try_complete_buf(StringBuilder* b){ + if(b->curr_buf->blocks_count==BL_C) + complete_buf(b); +} + + +StringBuilder* StringBuilder_create(){ + StringBuilder* b=malloc(sizeof(StringBuilder)); + b->compl_bufs=NULL; + b->curr_buf=Autoarr_create(i8,BL_C,BL_L); + return b; +} + +void __StringBuilder_free(void* _b){ + StringBuilder* b=_b; + if(b->compl_bufs) Autoarr_free(b->compl_bufs, true); + Autoarr_free(b->curr_buf, true); +} +void StringBuilder_free(StringBuilder* b){ + __StringBuilder_free(b); + free(b); +} + +string StringBuilder_build(StringBuilder* b){ + complete_buf(b); + u32 len=0; + Autoarr_foreach(b->compl_bufs, cs, + len+=cs.length; + ); + string str= { .length=len, .ptr=malloc(len+1) }; + str.ptr[len]='\0'; + u32 i=0; + Autoarr_foreach(b->compl_bufs, cs, + for(u32 n=0;ncurr_buf->block_length!=0) + Autoarr_pop(b->curr_buf) + else { + if(!b->compl_bufs) throw(ERR_NULLPTR); + string* lastcb=Autoarr_getPtr(b->compl_bufs, (Autoarr_length(b->compl_bufs)-1)); + lastcb->length--; + } +} + + +void StringBuilder_append_char(StringBuilder* b, char c){ + try_complete_buf(b); + Autoarr_add(b->curr_buf,c); +} + + +void StringBuilder_append_string(StringBuilder* b, string s){ + complete_buf(b); + Autoarr_add(b->compl_bufs, string_copy(s)); +} + +void StringBuilder_append_cptr(StringBuilder* b, char* s){ + string str={ + .ptr=s, + .length=cptr_length(s) + }; + StringBuilder_append_string(b,str); +} + +void curr_buf_add_string(StringBuilder* b, string s){ + for(u32 i=0; icurr_buf,s.ptr[i]); +} + +void StringBuilder_append_i64(StringBuilder* b, i64 a){ + try_complete_buf(b); + u8 i=0; + if(a==0){ + Autoarr_add(b->curr_buf,'0'); + return; + } + else if(a<0){ + Autoarr_add(b->curr_buf,'-'); + a=-a; + } + char buf[24]; + while(a!=0){ + buf[i++]='0'+a%10; + a/=10; + } + string rev=string_reverse((string){buf,i}); + curr_buf_add_string(b,rev); + free(rev.ptr); +} + +void StringBuilder_append_u64(StringBuilder* b, u64 a){ + try_complete_buf(b); + u8 i=0; + if(a==0){ + Autoarr_add(b->curr_buf,'0'); + return; + } + char buf[24]; + while(a!=0){ + buf[i++]='0'+a%10; + a/=10; + } + string rev=string_reverse((string){buf,i}); + curr_buf_add_string(b,rev); + free(rev.ptr); +} + +void StringBuilder_append_f64(StringBuilder* b, f64 a){ + try_complete_buf(b); + char buf[32]; + IFMSC( + sprintf_s(buf,32,"%lf",a), + sprintf(buf,"%lf",a) + ); + curr_buf_add_string(b, (string){.ptr=buf, .length=cptr_length(buf)}); +} diff --git a/kerep-headers/String/StringBuilder.h b/kerep/src/String/StringBuilder.h similarity index 100% rename from kerep-headers/String/StringBuilder.h rename to kerep/src/String/StringBuilder.h diff --git a/kerep/src/String/string.c b/kerep/src/String/string.c new file mode 100644 index 0000000..ffa17ce --- /dev/null +++ b/kerep/src/String/string.c @@ -0,0 +1,47 @@ +#include "string.h" + +kt_define(string, NULL, NULL); +Autoarr_define(string, false); + +// copies str content to new char pointer value (adding '\0' at the end) +char* string_extract(string str){ + if(str.length==0) return NULL; + char* cptr=malloc(str.length*sizeof(char)+1); + cptr[str.length]=0; + while(str.length-->0) + cptr[str.length]=str.ptr[str.length]; + return cptr; +} + +// copies src.ptr content to new string and adds \0 at the end +string string_copy(string src){ + if(!src.ptr) + return src; + string nstr; + nstr.length=src.length; + nstr.ptr=malloc(nstr.length+1); + for(u32 i=0;i0) + if(*str0.ptr++ != *str1.ptr++) + return false; + return true; +} + +// creates new string which is reversed variant of +string string_reverse(string s){ + if(s.length==0) return s; + string r={malloc(s.length), s.length}; + for(u32 i=0; i + +// returns length of char buffer (without \0) +u32 cptr_length(const char* str){ + const char *const str_first=str; + while(*str) + str++; + return str-str_first; +} + +// allocates new char[] and copies src there +char* cptr_copy(const char* src){ + u32 len=cptr_length(src)+1; + char* dst=malloc(len); + while(len--!=0) + dst[len]=src[len]; + return dst; +} + +// multiplies char n times +char* char_multiply(char c, u32 n){ + char* rez=malloc(n+1); + rez[n]=0; + while(n--!=0) + rez[n]=c; + return rez; +} + +bool cptr_equals(const char* key0, const char* key1){ + char c0=*key0; + char c1=*key1; + bool eq=c0==c1; + while(c0 && c1 && eq) { + c0=*++key0; + c1=*++key1; + eq=c0==c1; + } + return eq; +} + +bool cptr_startsWith(const char* src, const char* fragment){ + char c0=*src; + char c1=*fragment; + bool eq=c0==c1 && c0 !=0 && c1!=0; + while(c0 && c1 && eq) { + c0=*++src; + c1=*++fragment; + eq=c0==c1; + if(c1==0) + return true; + } + return eq; +} + +bool cptr_endsWith(const char* src, const char* fragment){ + u32 src_len=cptr_length(src); + u32 fr_len=cptr_length(fragment); + if(src_len0) + StringBuilder_append_string(sb, (string){.ptr=(char*)src, .length=src_remains_len}); + string rezult=StringBuilder_build(sb); + return rezult.ptr; +} diff --git a/kerep-headers/base/cptr.h b/kerep/src/base/cptr.h similarity index 100% rename from kerep-headers/base/cptr.h rename to kerep/src/base/cptr.h diff --git a/kerep/src/base/endian.c b/kerep/src/base/endian.c new file mode 100644 index 0000000..282a53b --- /dev/null +++ b/kerep/src/base/endian.c @@ -0,0 +1,13 @@ +#include "endian.h" + +static const union +{ + u16 number; + Endian bytes[2]; +} _endian_union={ .number=0x0102 }; + +Endian getEndian(){ + // if 0x0102 == { 1, 2 } then BigEndian + // if 0x0102 == { 2, 1 } then LittleEndian + return _endian_union.bytes[1]; +} diff --git a/kerep-headers/base/endian.h b/kerep/src/base/endian.h similarity index 100% rename from kerep-headers/base/endian.h rename to kerep/src/base/endian.h diff --git a/kerep/src/base/errors.c b/kerep/src/base/errors.c new file mode 100644 index 0000000..6f093f1 --- /dev/null +++ b/kerep/src/base/errors.c @@ -0,0 +1,59 @@ +#include "std.h" +#include "errors.h" +#include "cptr.h" +#include "../kprint/kprintf.h" + +char* errname(ErrorId err){ + switch(err){ + case SUCCESS: return nameof(SUCCESS); + case ERR_MAXLENGTH: return nameof(ERR_MAXLENGTH); + case ERR_WRONGTYPE: return nameof(ERR_WRONGTYPE); + case ERR_WRONGINDEX: return nameof(ERR_WRONGINDEX); + case ERR_NOTIMPLEMENTED: return nameof(ERR_NOTIMPLEMENTED); + case ERR_NULLPTR: return nameof(ERR_NULLPTR); + case ERR_ENDOFSTR: return nameof(ERR_ENDOFSTR); + case ERR_KEYNOTFOUND: return nameof(ERR_KEYNOTFOUND); + case ERR_FORMAT: return nameof(ERR_FORMAT); + case ERR_UNEXPECTEDVAL: return nameof(ERR_UNEXPECTEDVAL); + case ERR_IO: return nameof(ERR_IO); + case ERR_IO_EOF: return nameof(ERR_IO_EOF); + default: return "UNKNOWN_ERROR"; + } +} + +#define ERRMSG_MAXLENGTH 1024 + +char* __genErrMsg(const char* errmsg, const char* srcfile, i32 line, const char* funcname){ + size_t bufsize=ERRMSG_MAXLENGTH; + char* rezult=malloc(bufsize); + IFMSC( + sprintf_s(rezult,bufsize,"[%s:%d] %s() throwed error: %s",srcfile,line,funcname,errmsg), + sprintf(rezult,"[%s:%d] %s() throwed error: %s",srcfile,line,funcname,errmsg) + ); + return rezult; +} + +char* __extendErrMsg(const char* errmsg, const char* srcfile, i32 line, const char* funcname){ + size_t bufsize=cptr_length(errmsg)+ERRMSG_MAXLENGTH; + char* rezult=malloc(bufsize); + IFMSC( + sprintf_s(rezult,bufsize,"%s\n \\___[%s:%d] %s()",errmsg,srcfile,line,funcname), + sprintf(rezult,"%s\n \\___[%s:%d] %s()",errmsg,srcfile,line,funcname) + ); + free(errmsg); + return rezult; +} + +void Maybe_free(Maybe e){ + free(e.errmsg); + Unitype_free(e.value); +} + +void printMaybe(Maybe e){ + if(e.errmsg) kprintf("%s\n",e.errmsg); + else printuni(e.value); +} + +char* __doNothing(char* a) {return a;} + +char* __unknownErr() {return "UNKNOWN ERROR";} diff --git a/kerep-headers/base/errors.h b/kerep/src/base/errors.h similarity index 100% rename from kerep-headers/base/errors.h rename to kerep/src/base/errors.h diff --git a/kerep-headers/base/optime.h b/kerep/src/base/optime.h similarity index 100% rename from kerep-headers/base/optime.h rename to kerep/src/base/optime.h diff --git a/kerep-headers/base/std.h b/kerep/src/base/std.h similarity index 98% rename from kerep-headers/base/std.h rename to kerep/src/base/std.h index 816d328..225f57b 100644 --- a/kerep-headers/base/std.h +++ b/kerep/src/base/std.h @@ -38,6 +38,10 @@ typedef u8 bool; #define false 0 #endif +#ifndef typeof +#define typeof __typeof__ +#endif + #define dbg(N) kprintf("\e[95m%d\n",N) #define nameof(V) #V diff --git a/kerep-headers/base/type_system/README.md b/kerep/src/base/type_system/README.md similarity index 100% rename from kerep-headers/base/type_system/README.md rename to kerep/src/base/type_system/README.md diff --git a/kerep/src/base/type_system/base_toString.c b/kerep/src/base/type_system/base_toString.c new file mode 100644 index 0000000..b63d82d --- /dev/null +++ b/kerep/src/base/type_system/base_toString.c @@ -0,0 +1,238 @@ +#include "base_toString.h" +#include "../base.h" +#include "../../kprint/kprint_format.h" + + +// accepts char* (ptr to char) and char* (ptr to string) +// uses format kp_s and kp_c to determine what type is argument +char* __toString_char(void* c, u32 fmt) { + // *c=char* + if(kp_fmt_dataFormat(fmt)==kp_s){ + return cptr_copy((char*)c); // to avoid segmentation fault on free() when *c allocalet on stack + } + // *c=char + if(kp_fmt_dataFormat(fmt)==kp_c){ + char* cc=malloc(2); + cc[0]=*(char*)c; + cc[1]=0; + return cc; + } + else throw(ERR_FORMAT); +} + +char* __toString_bool(void* c, u32 fmt) { + static const char _strbool[4][6]={ "false", "true\0", "False", "True\0" }; + u8 strind=*(bool*)c==1 + kp_fmt_isUpper(fmt)*2; + char* rez=malloc(6); + rez[0]=_strbool[strind][0]; + rez[1]=_strbool[strind][1]; + rez[2]=_strbool[strind][2]; + rez[3]=_strbool[strind][3]; + rez[4]=_strbool[strind][4]; + rez[5]=0; + return rez; +} + +char* toString_i64(i64 n){ + i64 d=n<0 ? -1*n : n; + char str[32]; + u8 i=sizeof(str); + str[--i]=0; + if(d==0) + str[--i]='0'; + else while(d!=0){ + str[--i]='0' + d%10; + d/=10; + } + if(n<0) + str[--i]='-'; + return cptr_copy((char*)str+i); +} + +char* toString_u64(u64 n, bool withPostfix, bool uppercase){ + char str[32]; + u8 i=sizeof(str); + str[--i]=0; + if(withPostfix) + str[--i]= uppercase ? 'U' : 'u'; + if(n==0) + str[--i]='0'; + else while(n!=0){ + str[--i]='0' + n%10; + n/=10; + } + return cptr_copy((char*)str+i); +} + +#define _toString_float_impl(bufsize, maxPrecision) { \ + char str[bufsize]; \ + if(precision>maxPrecision) \ + throw("too big precision"); \ + if(precision==0) \ + precision=toString_float_default_precision; \ + i32 cn=IFMSC( \ + sprintf_s(str, bufsize, "%.*f", precision, n), \ + sprintf(str, "%.*f", precision, n) \ + ); \ + /* remove trailing zeroes except .0*/ \ + while(str[cn-1]=='0' && str[cn-2]!='.') \ + cn--; \ + if(withPostfix) \ + str[cn++]= uppercase ? 'F' : 'f'; \ + str[cn]='\0'; \ + return cptr_copy(str); \ +} + +char* toString_f32(f32 n, u8 precision, bool withPostfix, bool uppercase) + _toString_float_impl(48, toString_f32_max_precision) + +char* toString_f64(f64 n, u8 precision, bool withPostfix, bool uppercase) + _toString_float_impl(512, toString_f64_max_precision) + +#define byte_to_bits(byte) { \ + str[cn++]='0' + (u8)((byte>>7)&1); /* 8th bit */ \ + str[cn++]='0' + (u8)((byte>>6)&1); /* 7th bit */ \ + str[cn++]='0' + (u8)((byte>>5)&1); /* 6th bit */ \ + str[cn++]='0' + (u8)((byte>>4)&1); /* 5th bit */ \ + str[cn++]='0' + (u8)((byte>>3)&1); /* 4th bit */ \ + str[cn++]='0' + (u8)((byte>>2)&1); /* 3th bit */ \ + str[cn++]='0' + (u8)((byte>>1)&1); /* 2th bit */ \ + str[cn++]='0' + (u8)((byte>>0)&1); /* 1th bit */ \ +} + +char* toString_bin(void* _bytes, u32 size, bool inverse, bool withPrefix){ + char* bytes=_bytes; + char* str=malloc(size*8 + (withPrefix?2:0) +1); + u32 cn=0; // char number + if(withPrefix){ + str[cn++]='0'; + str[cn++]='b'; + } + if(inverse){ + // byte number + for(i32 bn=size-1; bn>=0; bn--) + byte_to_bits(bytes[bn]) + } else { + for(u32 bn=0; bn=0; bn--){ + unsigned char byte=bytes[bn]; + str[cn++]=_4bitsHex(byte/16, uppercase); + str[cn++]=_4bitsHex(byte%16, uppercase); + } + } + // right to left + else { + for(u32 bn=0; bnid, 0,0); + char *s1 = toString_u64(d->size, 0,0); + char *s2 = d->toString ? toString_hex(d->toString, sizeof(void*), 0,1,0) : n; + char *s3 = d->freeMembers ? toString_hex(d->freeMembers, sizeof(void*), 0,1,0) : n; + char *rez=cptr_concat("ktDescriptor {" + " name:", d->name, + " id:",s0, + " size:",s1, + " toString:",s2, + " freeMembers:",s3, + " }"); + free(s0); + free(s1); + if(s2!=n) free(s2); + if(s3!=n) free(s3); + return rez; +} + +char* _ktDescriptor_toString(void* _d, u32 fmt) { return ktDescriptor_toString(_d); } + +kt_define(ktDescriptor, NULL, _ktDescriptor_toString); + +typedef ktDescriptor* ktDescriptor_Ptr; + +// type descriptors are stored here during initialization +Autoarr(Pointer)* __descriptorPointers=NULL; +// here type descriptors are stored when initialization is complited +ktDescriptor** typeDescriptors=NULL; +ktid ktid_last=-1; + +ENUM(ktDescriptorsState, + NotInitialized, Initializing, Initialized +) +ktDescriptorsState initState=NotInitialized; + +void kt_beginInit(){ +#if DEBUG + kprintf("\e[94mtype descriptors initializing...\n"); +#endif + __descriptorPointers=Autoarr_create(Pointer, 256, 256); +} + +void kt_endInit(){ + if(__descriptorPointers==NULL) + throw(ERR_NULLPTR); + typeDescriptors=(ktDescriptor**)Autoarr_toArray(__descriptorPointers); + Autoarr_free(__descriptorPointers,true); + if(typeDescriptors==NULL) throw(ERR_NULLPTR); +#if DEBUG + kprintf("\e[92minitialized %u type descriptors\n", ktid_last); +#endif +} + +void __kt_register(ktDescriptor* descriptor){ + descriptor->id=++ktid_last; + Autoarr_add(__descriptorPointers, descriptor); +} + +ktDescriptor* ktDescriptor_get(ktid id){ + if(id>ktid_last || id==ktid_undefined) { + kprintf("\ntype id: %u\n",id); + throw("invalid type id"); + } + return typeDescriptors[id]; +} + +void kt_free(){ + free(typeDescriptors); +} diff --git a/kerep-headers/base/type_system/kt_functions.h b/kerep/src/base/type_system/kt_functions.h similarity index 100% rename from kerep-headers/base/type_system/kt_functions.h rename to kerep/src/base/type_system/kt_functions.h diff --git a/kerep-headers/base/type_system/ktid.h b/kerep/src/base/type_system/ktid.h similarity index 100% rename from kerep-headers/base/type_system/ktid.h rename to kerep/src/base/type_system/ktid.h diff --git a/kerep-headers/base/type_system/type_system.h b/kerep/src/base/type_system/type_system.h similarity index 100% rename from kerep-headers/base/type_system/type_system.h rename to kerep/src/base/type_system/type_system.h diff --git a/kerep-headers/base/type_system/typedef_macros.h b/kerep/src/base/type_system/typedef_macros.h similarity index 100% rename from kerep-headers/base/type_system/typedef_macros.h rename to kerep/src/base/type_system/typedef_macros.h diff --git a/kerep/src/base/type_system/unitype.c b/kerep/src/base/type_system/unitype.c new file mode 100644 index 0000000..a85c05e --- /dev/null +++ b/kerep/src/base/type_system/unitype.c @@ -0,0 +1,100 @@ +#include "../../kprint/kprint_format.h" +#include "../base.h" + +char *__Unitype_toString(void *_u, u32 fmt) +{ + return Unitype_toString(*(Unitype *)_u, fmt); +} + +kt_define(Unitype, __UnitypePtr_free, __Unitype_toString); + +void Unitype_free(Unitype u) +{ + if (u.typeId == ktid_undefined) + { + if (u.VoidPtr != NULL) + throw("unitype with undefined typeId has value"); + return; + } + + ktDescriptor *type = ktDescriptor_get(u.typeId); + if (type->freeMembers) + type->freeMembers(u.VoidPtr); + if (u.allocatedInHeap) + free(u.VoidPtr); +} +void __UnitypePtr_free(void *u) +{ + Unitype_free(*(Unitype *)u); +} + +char *Unitype_toString(Unitype u, u32 fmt) +{ + if (u.typeId == ktid_undefined) + { + if (u.VoidPtr != NULL) + throw("unitype with undefined typeId has value"); + return cptr_copy("{ERROR_TYPE}"); + } + + if (fmt == 0) + { + if (u.typeId == ktid_name(bool) || u.typeId == ktid_name(i8) || u.typeId == ktid_name(i16) || + u.typeId == ktid_name(i32) || u.typeId == ktid_name(i64)) + { + // auto format set + fmt = kp_i; + // replaces value with pointer to value to pass into toString_i64(void*, u32) + i64 value = u.Int64; + u.VoidPtr = &value; + } + else if (u.typeId == ktid_name(u8) || u.typeId == ktid_name(u16) || u.typeId == ktid_name(u32) || + u.typeId == ktid_name(u64)) + { + fmt = kp_u; + u64 value = u.UInt64; + u.VoidPtr = &value; + } + else if (u.typeId == ktid_name(f32) || u.typeId == ktid_name(f64)) + { + fmt = kp_f; + f64 value = u.Float64; + u.VoidPtr = &value; + } + else if (u.typeId == ktid_name(char)) + { + fmt = kp_c; + i64 value = u.Int64; + u.VoidPtr = &value; + } + else if (u.typeId == ktid_ptrName(char)) + { + fmt = kp_s; + } + else if (u.typeId == ktid_name(Pointer)) + { + if (u.VoidPtr == NULL) + return cptr_copy("{ UniNull }"); + fmt = kp_h; + } + } + + ktDescriptor *type = ktDescriptor_get(u.typeId); + char *valuestr; + if (type->toString) + valuestr = type->toString(u.VoidPtr, fmt); + else + valuestr = "ERR_NO_TOSTRING_FUNC"; + char *rezult = cptr_concat("{ type: ", type->name, ", allocated on heap: ", (u.allocatedInHeap ? "true" : "false"), + ", value:", valuestr, " }"); + if (type->toString) + free(valuestr); + return rezult; +} + +void printuni(Unitype v) +{ + char *s = Unitype_toString(v, 0); + fputs(s, stdout); + free(s); +} \ No newline at end of file diff --git a/kerep-headers/base/type_system/unitype.h b/kerep/src/base/type_system/unitype.h similarity index 100% rename from kerep-headers/base/type_system/unitype.h rename to kerep/src/base/type_system/unitype.h diff --git a/kerep-headers/kprint/README.md b/kerep/src/kprint/README.md similarity index 100% rename from kerep-headers/kprint/README.md rename to kerep/src/kprint/README.md diff --git a/kerep/src/kprint/kprint.c b/kerep/src/kprint/kprint.c new file mode 100644 index 0000000..046cf29 --- /dev/null +++ b/kerep/src/kprint/kprint.c @@ -0,0 +1,211 @@ +#include "../String/StringBuilder.h" +#include "kprint.h" + +ktid __typeFromFormat(kp_fmt f){ + ktid typeId=kp_fmt_ktid(f); + if(typeId) + return typeId; + switch(kp_fmt_dataFormat(f)){ + case kp_i: + case kp_h: + case kp_b: + return ktid_name(i64); + case kp_u: + return ktid_name(u64); + case kp_f: + return ktid_name(f64); + case kp_c: + return ktid_name(char); + case kp_s: + return ktid_ptrName(char); + default: + return ktid_undefined; + } +} + +Maybe __next_toString(kp_fmt f, void* object){ + // detecting type + ktid typeId=__typeFromFormat(f); + if(typeId==ktid_undefined) + safethrow("typeId is undefined, can't autodetect type",;); + + if(typeId==ktid_ptrName(char)) + object=*(char**)object; // dereferencing char** to char* + + ktDescriptor* type=ktDescriptor_get(typeId); + if(!type->toString) + safethrow("type descriptor doesnt have toString() func",;); + return SUCCESS(UniHeapPtr(char, type->toString(object, f))); +} + +Maybe check_argsN(u8 n){ + if(n%2 != 0) safethrow("kprint recieved non-even number of arguments",;); + if(n > 32) safethrow("kprint recieved >32 number of arguments",;); + return MaybeNull; +} + +Maybe __ksprint(u8 n, kp_fmt* formats, __kp_value_union* objects){ + try(check_argsN(n), _,;); + n/=2; + StringBuilder* strb=StringBuilder_create(); + for(u8 i=0; i +#define FOREGROUND_YELLOW FOREGROUND_GREEN | FOREGROUND_RED + +DWORD kp_fgColor_toWin(kp_fgColor f){ + //kprintf("fg: %x\n", f); + switch(f){ + case kp_fgBlack: return 0; + case kp_fgRedD: return FOREGROUND_RED; + case kp_fgGreenD: return FOREGROUND_GREEN; + case kp_fgYellowD: return FOREGROUND_GREEN | FOREGROUND_RED; + case kp_fgBlueD: return FOREGROUND_BLUE; + case kp_fgMagentaD: return FOREGROUND_RED | FOREGROUND_BLUE; + case kp_fgCyanD: return FOREGROUND_BLUE | FOREGROUND_GREEN; + case kp_fgGray: return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + case kp_fgGrayD: return FOREGROUND_INTENSITY; + case kp_fgRed: return FOREGROUND_RED | FOREGROUND_INTENSITY; + case kp_fgGreen: return FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case kp_fgYellow: return FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; + case kp_fgBlue: return FOREGROUND_BLUE | FOREGROUND_INTENSITY; + case kp_fgMagenta: return FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY; + case kp_fgCyan: return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case kp_fgWhite: return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + default: throw(ERR_FORMAT); + } +} + +DWORD kp_bgColor_toWin(kp_bgColor f){ + //kprintf("bg: %x\n", f); + switch(f){ + case kp_bgBlack: return 0; + case kp_bgRedD: return BACKGROUND_RED; + case kp_bgGreenD: return BACKGROUND_GREEN; + case kp_bgYellowD: return BACKGROUND_GREEN | BACKGROUND_RED; + case kp_bgBlueD: return BACKGROUND_BLUE; + case kp_bgMagentaD: return BACKGROUND_RED | BACKGROUND_BLUE; + case kp_bgCyanD: return BACKGROUND_BLUE | BACKGROUND_GREEN; + case kp_bgGray: return BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; + case kp_bgGrayD: return BACKGROUND_INTENSITY; + case kp_bgRed: return BACKGROUND_RED | BACKGROUND_INTENSITY; + case kp_bgGreen: return BACKGROUND_GREEN | BACKGROUND_INTENSITY; + case kp_bgYellow: return BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY; + case kp_bgBlue: return BACKGROUND_BLUE | BACKGROUND_INTENSITY; + case kp_bgMagenta: return BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY; + case kp_bgCyan: return BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY; + case kp_bgWhite: return BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY; + default: throw(ERR_FORMAT); + } +} + +void kprint_setColor(kp_fmt f){ + DWORD color=0; + if(!kp_fmt_fgColorSet(f) & !kp_fmt_bgColorSet(f)) + return; + if(kp_fmt_fgColorSet(f)) + color+=kp_fgColor_toWin(kp_fmt_fgColor(f)); + if(kp_fmt_bgColorSet(f)) + color+=kp_bgColor_toWin(kp_fmt_bgColor(f)); + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(hConsole, color); +} +#else +void kprint_setColor(kp_fmt f){ + if(kp_fmt_fgColorSet(f)){ + u8 fg=(f&0x0f000000)>>24; + if(fg<8) fg+=30; + else fg+=90-8; + printf("\e[%um", fg); + } + if(kp_fmt_bgColorSet(f)){ + u8 bg=(f&0x00f00000)>>20; + if(bg<8) bg+=40; + else bg+=100-8; + printf("\e[%um", bg); + } +} +#endif + +/* Maybe ksprint_ar(u32 count, kp_fmt format, ktid typeId, void* array){ + ktDescriptor* type=ktDescriptor_get(format.typeId); + if(!type->toString) + safethrow("type descriptor doesnt have toString() func",;); + StringBuilder* strb=StringBuilder_create(); + StringBuilder_append_char(strb, '['); + for (u16 e=1; etoString(array+type->size*e, &format); + StringBuilder_append_cptr(strb, elStr); + StringBuilder_append_char(strb, ','); + } + StringBuilder_rmchar(strb); + StringBuilder_append_char(strb, ' '); + StringBuilder_append_char(strb, ']'); +} */ + +static const char* _kp_colorNames[16]={ + "black", + "dark_red", + "dark_green", + "dark_yellow", + "dark_blue", + "dark_magenta", + "dark_cyan", + "gray", + "dark_gray", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white" +}; + +char* kp_bgColor_toString(kp_bgColor c){ + u32 color_index=(c&0x00f00000)>>20; + if(color_index>15) throw(ERR_WRONGINDEX); + return _kp_colorNames[color_index]; +} +char* kp_fgColor_toString(kp_fgColor c){ + u32 color_index=(c&0x00f00000)>>24; + if(color_index>15) throw(ERR_WRONGINDEX); + return _kp_colorNames[color_index]; +} diff --git a/kerep-headers/kprint/kprint.h b/kerep/src/kprint/kprint.h similarity index 100% rename from kerep-headers/kprint/kprint.h rename to kerep/src/kprint/kprint.h diff --git a/kerep-headers/kprint/kprint_colors.h b/kerep/src/kprint/kprint_colors.h similarity index 100% rename from kerep-headers/kprint/kprint_colors.h rename to kerep/src/kprint/kprint_colors.h diff --git a/kerep-headers/kprint/kprint_format.h b/kerep/src/kprint/kprint_format.h similarity index 100% rename from kerep-headers/kprint/kprint_format.h rename to kerep/src/kprint/kprint_format.h diff --git a/kerep-headers/kprint/kprint_format.md b/kerep/src/kprint/kprint_format.md similarity index 100% rename from kerep-headers/kprint/kprint_format.md rename to kerep/src/kprint/kprint_format.md diff --git a/kerep/src/kprint/kprintf.c b/kerep/src/kprint/kprintf.c new file mode 100644 index 0000000..0f9e0a6 --- /dev/null +++ b/kerep/src/kprint/kprintf.c @@ -0,0 +1,155 @@ +#include "kprintf.h" +#include "../base/base.h" + +#if defined(_WIN64) || defined(_WIN32) +#include + +WORD unixColorToWin(u8 c){ + switch(c){ + //foreground + case 30: return 0; + case 31: return FOREGROUND_RED; + case 32: return FOREGROUND_GREEN; + case 33: return FOREGROUND_GREEN | FOREGROUND_RED; + case 34: return FOREGROUND_BLUE; + case 35: return FOREGROUND_RED | FOREGROUND_BLUE; + case 36: return FOREGROUND_BLUE | FOREGROUND_GREEN; + case 37: return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + case 90: return FOREGROUND_INTENSITY; + case 91: return FOREGROUND_RED | FOREGROUND_INTENSITY; + case 92: return FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case 93: return FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; + case 94: return FOREGROUND_BLUE | FOREGROUND_INTENSITY; + case 95: return FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY; + case 96: return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case 97: return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + //background + case 40: return 0; + case 41: return BACKGROUND_RED; + case 42: return BACKGROUND_GREEN; + case 43: return BACKGROUND_GREEN | BACKGROUND_RED; + case 44: return BACKGROUND_BLUE; + case 45: return BACKGROUND_RED | BACKGROUND_BLUE; + case 46: return BACKGROUND_BLUE | BACKGROUND_GREEN; + case 47: return BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; + case 100: return BACKGROUND_INTENSITY; + case 101: return BACKGROUND_RED | BACKGROUND_INTENSITY; + case 102: return BACKGROUND_GREEN | BACKGROUND_INTENSITY; + case 103: return BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY; + case 104: return BACKGROUND_BLUE | BACKGROUND_INTENSITY; + case 105: return BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY; + case 106: return BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY; + case 107: return BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY; + default: return 0; + } +} +#endif + +void kprintf(const char* format, ...){ + va_list vl; + va_start(vl, format); + u32 i=0; + for(char c=format[i++]; c!=0; c=format[i++]){ + // value format specifiers + if(c=='%'){ + char* argstr=NULL; + bool l=false; + c=format[i++]; + format_escape_seq: + switch (c) { + case 'u': + argstr=toString_u64( + l ? va_arg(vl, u64) : va_arg(vl, u32) + ,0,0); + break; + case 'i': case 'd': + argstr=toString_i64( + l ? va_arg(vl, i64) : va_arg(vl, i32) + ); + break; + case 'f': + // f32 is promoted to f64 when passed through '...' + argstr=toString_f64(va_arg(vl, f64), toString_float_default_precision,0,0); + break; + case 'l': + l=true; + if((c=format[i++])) + goto format_escape_seq; + break; + case 'p': ; + void* phex=va_arg(vl, void*); + argstr=toString_hex(&phex,getEndian()==LittleEndian,sizeof(phex),1,0); + break; + case 'x': ; + if(l){ + u64 xhex=va_arg(vl, u64); + argstr=toString_hex(&xhex,getEndian()==LittleEndian,sizeof(xhex),0,1); + } + else { + u32 xhex=va_arg(vl, u32); + argstr=toString_hex(&xhex,getEndian()==LittleEndian,sizeof(xhex),0,1); + } + break; + case 's': ; + char* cptr=va_arg(vl,char*); + if(!cptr) + cptr=""; + if(*cptr) + fputs(cptr, stdout); + break; + case 'c': + argstr=malloc(2); + argstr[0]=(char)va_arg(vl,int); + argstr[1]=0; + break; + default: + putc('\n',stdout); + putc('<',stdout); + putc(c,stdout); + putc('>',stdout); + throw(ERR_FORMAT); + } + if(argstr){ + fputs(argstr, stdout); + free(argstr); + } + } + // escape sequences + else if(c=='\e'){ + IFWIN( + /* WINDOWS */ + ({ + if((c=format[i++])=='['){ + u8 colorUnix=0; + for(i8 n=0; n<6 && c!=0; n++){ + c=format[i++]; + switch (c){ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + colorUnix=colorUnix*10+c-'0'; + break; + case 'm': ; + WORD colorWin=unixColorToWin(colorUnix); + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(hConsole, colorWin); + goto end_iteration; + default: + goto end_iteration; + } + } + } + }), + /* UNIX */ + putc(c,stdout); + ); + } + // common characters + else { + putc(c,stdout); + } + #if defined(_WIN64) || defined(_WIN32) + end_iteration:; + #endif + } + va_end(vl); +} diff --git a/kerep-headers/kprint/kprintf.h b/kerep/src/kprint/kprintf.h similarity index 100% rename from kerep-headers/kprint/kprintf.h rename to kerep/src/kprint/kprintf.h diff --git a/kerep-headers/random/krandom.h b/kerep/src/random/krandom.h similarity index 100% rename from kerep-headers/random/krandom.h rename to kerep/src/random/krandom.h diff --git a/kerep/src/random/splitmix64/splitmix64.c b/kerep/src/random/splitmix64/splitmix64.c new file mode 100644 index 0000000..c267b9e --- /dev/null +++ b/kerep/src/random/splitmix64/splitmix64.c @@ -0,0 +1,34 @@ +#include "splitmix64.h" + +/* +This is a fixed-increment version of Java 8's SplittableRandom generator +See http://dx.doi.org/10.1145/2714064.2660195 and +http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html +It is a very fast generator passing BigCrush, and it can be useful if +for some reason you absolutely want 64 bits of state; otherwise, we +rather suggest to use a xoroshiro128+ (for moderately parallel +computations) or xorshift1024* (for massively parallel computations) +generator. +*/ + +// The state can be seeded with any (upto) 64 bit integer value. + +void* splitmix64_init(u64 seed){ + splitmix64_state* state=malloc(sizeof(splitmix64_state)); + *state=seed; + return state; +} + +u64 splitmix64_next(void* _state) { + splitmix64_state* state=_state; + // increment the state variable + *state += 0x9e3779b97f4a7c15; + // copy the state to a working variable + u64 z = *state; + // xor the variable with the variable right bit shifted 30 then multiply by a constant + z = (z ^ (z>>30)) * 0xbf58476d1ce4e5b9; + // xor the variable with the variable right bit shifted 27 then multiply by a constant + z = (z ^ (z>>27)) * 0x94d049bb133111eb; + // return the variable xored with itself right bit shifted 31 + return z ^ (z>>31); +} diff --git a/kerep-headers/random/splitmix64/splitmix64.h b/kerep/src/random/splitmix64/splitmix64.h similarity index 100% rename from kerep-headers/random/splitmix64/splitmix64.h rename to kerep/src/random/splitmix64/splitmix64.h diff --git a/kerep-headers/random/xoroshiro/32bitValue/xoroshiro64.h b/kerep/src/random/xoroshiro/32bitValue/xoroshiro64.h similarity index 100% rename from kerep-headers/random/xoroshiro/32bitValue/xoroshiro64.h rename to kerep/src/random/xoroshiro/32bitValue/xoroshiro64.h diff --git a/kerep/src/random/xoroshiro/32bitValue/xoroshiro64star.c b/kerep/src/random/xoroshiro/32bitValue/xoroshiro64star.c new file mode 100644 index 0000000..ca57d35 --- /dev/null +++ b/kerep/src/random/xoroshiro/32bitValue/xoroshiro64star.c @@ -0,0 +1,49 @@ +/* Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoroshiro64.h" + +/* +This is xoroshiro64* 1.0, our best and fastest 32-bit small-state +generator for 32-bit floating-poi32 numbers. We suggest to use its +upper bits for floating-poi32 generation, as it is slightly faster than +xoroshiro64**. It passes all tests we are aware of except for linearity +tests, as the lowest six bits have low linear complexity, so if low +linear complexity is not considered an issue (as it is usually the +case) it can be used to generate 32-bit outputs, too. + +We suggest to use a sign test to extract a random Boolean value, and +right shifts to extract subsets of bits. + +The state must be seeded so that it is not everywhere zero. +*/ + +static inline u32 rotl(const u32 x, i32 k) { + return (x << k) | (x >> (32 - k)); +} + +u32 xoroshiro64star_next(void* _state) { + xoroshiro64_state* state=_state; + const u32 s0 = state->s[0]; + u32 s1 = state->s[1]; + const u32 result = s0 * 0x9E3779BB; + + s1 ^= s0; + state->s[0] = rotl(s0, 26) ^ s1 ^ (s1 << 9); // a, b + state->s[1] = rotl(s1, 13); // c + + return result; +} + +void* xoroshiro64_init(u64 seed){ + xoroshiro64_state* state=malloc(sizeof(xoroshiro64_state)); + splitmix64_state* splitmix=splitmix64_init(seed); + state->merged=splitmix64_next(splitmix); + splitmix64_free(splitmix); + return state; +} diff --git a/kerep/src/random/xoroshiro/32bitValue/xoroshiro64starstar.c b/kerep/src/random/xoroshiro/32bitValue/xoroshiro64starstar.c new file mode 100644 index 0000000..40d93fc --- /dev/null +++ b/kerep/src/random/xoroshiro/32bitValue/xoroshiro64starstar.c @@ -0,0 +1,37 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoroshiro64.h" + +/* This is xoroshiro64** 1.0, our 32-bit all-purpose, rock-solid, + small-state generator. It is extremely fast and it passes all tests we + are aware of, but its state space is not large enough for any parallel + application. + + For generating just single-precision (i.e., 32-bit) floating-point + numbers, xoroshiro64* is even faster. + + The state must be seeded so that it is not everywhere zero. */ + + +static inline u32 rotl(const u32 x, i32 k) { + return (x << k) | (x >> (32 - k)); +} + +u32 xoroshiro64starstar_next(void* _state) { + xoroshiro64_state* state=_state; + const u32 s0 = state->s[0]; + u32 s1 = state->s[1]; + const u32 result = rotl(s0 * 0x9E3779BB, 5) * 5; + + s1 ^= s0; + state->s[0] = rotl(s0, 26) ^ s1 ^ (s1 << 9); // a, b + state->s[1] = rotl(s1, 13); // c + + return result; +} diff --git a/kerep-headers/random/xoroshiro/64bitValue/xoroshiro128.h b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128.h similarity index 100% rename from kerep-headers/random/xoroshiro/64bitValue/xoroshiro128.h rename to kerep/src/random/xoroshiro/64bitValue/xoroshiro128.h diff --git a/kerep/src/random/xoroshiro/64bitValue/xoroshiro128plus.c b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128plus.c new file mode 100644 index 0000000..05760ec --- /dev/null +++ b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128plus.c @@ -0,0 +1,59 @@ +/* Written in 2016-2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoroshiro128.h" + +/* This is xoroshiro128+ 1.0, our best and fastest small-state generator + for floating-poi32 numbers, but its state space is large enough only + for mild parallelism. We suggest to use its upper bits for + floating-poi32 generation, as it is slightly faster than + xoroshiro128++/xoroshiro128**. It passes all tests we are aware of + except for the four lower bits, which might fail linearity tests (and + just those), so if low linear complexity is not considered an issue (as + it is usually the case) it can be used to generate 64-bit outputs, too; + moreover, this generator has a very mild Hamming-weight dependency + making our test (http://prng.di.unimi.it/hwd.php) fail after 5 TB of + output; we believe this slight bias cannot affect any application. If + you are concerned, use xoroshiro128++, xoroshiro128** or xoshiro256+. + + We suggest to use a sign test to extract a random Boolean value, and + right shifts to extract subsets of bits. + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. + + NOTE: the parameters (a=24, b=16, b=37) of this version give slightly + better results in our test than the 2016 version (a=55, b=14, c=36). +*/ + +static inline u64 rotl(const u64 x, i32 k) { + return (x << k) | (x >> (64 - k)); +} + +u64 xoroshiro128plus_next(void* _state){ + xoroshiro128_state* state=_state; + const u64 s0 = state->s[0]; + u64 s1 = state->s[1]; + const u64 result = s0 + s1; + + s1 ^= s0; + state->s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b + state->s[1] = rotl(s1, 37); // c + + return result; +} + +void* xoroshiro128_init(u64 seed){ + xoroshiro128_state* state=malloc(sizeof(xoroshiro128_state)); + splitmix64_state* splitmix=splitmix64_init(seed); + state->s[0]=splitmix64_next(splitmix); + state->s[1]=splitmix64_next(splitmix); + splitmix64_free(splitmix); + return state; +} diff --git a/kerep/src/random/xoroshiro/64bitValue/xoroshiro128plusplus.c b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128plusplus.c new file mode 100644 index 0000000..43e110e --- /dev/null +++ b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128plusplus.c @@ -0,0 +1,39 @@ +/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoroshiro128.h" + +/* This is xoroshiro128++ 1.0, one of our all-purpose, rock-solid, + small-state generators. It is extremely (sub-ns) fast and it passes all + tests we are aware of, but its state space is large enough only for + mild parallelism. + + For generating just floating-poi32 numbers, xoroshiro128+ is even + faster (but it has a very mild bias, see notes in the comments). + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + + +static inline u64 rotl(const u64 x, i32 k) { + return (x << k) | (x >> (64 - k)); +} + +u64 xoroshiro128plusplus_next(void* _state){ + xoroshiro128_state* state=_state; + const u64 s0 = state->s[0]; + u64 s1 = state->s[1]; + const u64 result = rotl(s0 + s1, 17) + s0; + + s1 ^= s0; + state->s[0] = rotl(s0, 49) ^ s1 ^ (s1 << 21); // a, b + state->s[1] = rotl(s1, 28); // c + + return result; +} diff --git a/kerep/src/random/xoroshiro/64bitValue/xoroshiro128starstar.c b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128starstar.c new file mode 100644 index 0000000..5e25851 --- /dev/null +++ b/kerep/src/random/xoroshiro/64bitValue/xoroshiro128starstar.c @@ -0,0 +1,39 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoroshiro128.h" + +/* This is xoroshiro128** 1.0, one of our all-purpose, rock-solid, + small-state generators. It is extremely (sub-ns) fast and it passes all + tests we are aware of, but its state space is large enough only for + mild parallelism. + + For generating just floating-poi32 numbers, xoroshiro128+ is even + faster (but it has a very mild bias, see notes in the comments). + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + + +static inline u64 rotl(const u64 x, i32 k) { + return (x << k) | (x >> (64 - k)); +} + +u64 xoroshiro128starstar_next(void* _state){ + xoroshiro128_state* state=_state; + const u64 s0 = state->s[0]; + u64 s1 = state->s[1]; + const u64 result = rotl(s0 * 5, 7) * 9; + + s1 ^= s0; + state->s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b + state->s[1] = rotl(s1, 37); // c + + return result; +} diff --git a/kerep-headers/random/xoroshiro/xoroshiro.h b/kerep/src/random/xoroshiro/xoroshiro.h similarity index 100% rename from kerep-headers/random/xoroshiro/xoroshiro.h rename to kerep/src/random/xoroshiro/xoroshiro.h diff --git a/kerep-headers/random/xoshiro-xoroshiro.md b/kerep/src/random/xoshiro-xoroshiro.md similarity index 100% rename from kerep-headers/random/xoshiro-xoroshiro.md rename to kerep/src/random/xoshiro-xoroshiro.md diff --git a/kerep-headers/random/xoshiro/32bitValue/xoshiro128.h b/kerep/src/random/xoshiro/32bitValue/xoshiro128.h similarity index 100% rename from kerep-headers/random/xoshiro/32bitValue/xoshiro128.h rename to kerep/src/random/xoshiro/32bitValue/xoshiro128.h diff --git a/kerep/src/random/xoshiro/32bitValue/xoshiro128plus.c b/kerep/src/random/xoshiro/32bitValue/xoshiro128plus.c new file mode 100644 index 0000000..f68b3d5 --- /dev/null +++ b/kerep/src/random/xoshiro/32bitValue/xoshiro128plus.c @@ -0,0 +1,54 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoshiro128.h" + +/* This is xoshiro128+ 1.0, our best and fastest 32-bit generator for 32-bit + floating-poi32 numbers. We suggest to use its upper bits for + floating-poi32 generation, as it is slightly faster than xoshiro128**. + It passes all tests we are aware of except for + linearity tests, as the lowest four bits have low linear complexity, so + if low linear complexity is not considered an issue (as it is usually + the case) it can be used to generate 32-bit outputs, too. + + We suggest to use a sign test to extract a random Boolean value, and + right shifts to extract subsets of bits. + + The state must be seeded so that it is not everywhere zero. */ + + +static inline u32 rotl(const u32 x, i32 k) { + return (x << k) | (x >> (32 - k)); +} + +u32 xoshiro128plus_next(void* _state){ + xoshiro128_state* state=_state; + const u32 result = state->s[0] + state->s[3]; + + const u32 t = state->s[1] << 9; + + state->s[2] ^= state->s[0]; + state->s[3] ^= state->s[1]; + state->s[1] ^= state->s[2]; + state->s[0] ^= state->s[3]; + + state->s[2] ^= t; + + state->s[3] = rotl(state->s[3], 11); + + return result; +} + +void* xoshiro128_init(u64 seed){ + xoshiro128_state* state=malloc(sizeof(xoshiro128_state)); + splitmix64_state* splitmix=splitmix64_init(seed); + state->merged[0]=splitmix64_next(splitmix); + state->merged[1]=splitmix64_next(splitmix); + splitmix64_free(splitmix); + return state; +} diff --git a/kerep/src/random/xoshiro/32bitValue/xoshiro128plusplus.c b/kerep/src/random/xoshiro/32bitValue/xoshiro128plusplus.c new file mode 100644 index 0000000..8fea492 --- /dev/null +++ b/kerep/src/random/xoshiro/32bitValue/xoshiro128plusplus.c @@ -0,0 +1,42 @@ +/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoshiro128.h" + +/* This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid + generators. It has excellent speed, a state size (128 bits) that is + large enough for mild parallelism, and it passes all tests we are aware + of. + + For generating just single-precision (i.e., 32-bit) floating-point + numbers, xoshiro128+ is even faster. + + The state must be seeded so that it is not everywhere zero. */ + + +static inline u32 rotl(const u32 x, i32 k) { + return (x << k) | (x >> (32 - k)); +} + +u32 xoshiro128plusplus_next(void* _state){ + xoshiro128_state* state=_state; + const u32 result = rotl(state->s[0] + state->s[3], 7) + state->s[0]; + + const u32 t = state->s[1] << 9; + + state->s[2] ^= state->s[0]; + state->s[3] ^= state->s[1]; + state->s[1] ^= state->s[2]; + state->s[0] ^= state->s[3]; + + state->s[2] ^= t; + + state->s[3] = rotl(state->s[3], 11); + + return result; +} diff --git a/kerep/src/random/xoshiro/32bitValue/xoshiro128starstar.c b/kerep/src/random/xoshiro/32bitValue/xoshiro128starstar.c new file mode 100644 index 0000000..87a20bc --- /dev/null +++ b/kerep/src/random/xoshiro/32bitValue/xoshiro128starstar.c @@ -0,0 +1,45 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoshiro128.h" + +/* This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid + generators. It has excellent speed, a state size (128 bits) that is + large enough for mild parallelism, and it passes all tests we are aware + of. + + Note that version 1.0 had mistakenly state->s[0] instead of state->s[1] as state + word passed to the scrambler. + + For generating just single-precision (i.e., 32-bit) floating-point + numbers, xoshiro128+ is even faster. + + The state must be seeded so that it is not everywhere zero. */ + + +static inline u32 rotl(const u32 x, i32 k) { + return (x << k) | (x >> (32 - k)); +} + +u32 xoshiro128starstar_next(void* _state){ + xoshiro128_state* state=_state; + const u32 result = rotl(state->s[1] * 5, 7) * 9; + + const u32 t = state->s[1] << 9; + + state->s[2] ^= state->s[0]; + state->s[3] ^= state->s[1]; + state->s[1] ^= state->s[2]; + state->s[0] ^= state->s[3]; + + state->s[2] ^= t; + + state->s[3] = rotl(state->s[3], 11); + + return result; +} diff --git a/kerep-headers/random/xoshiro/64bitValue/xoshiro256.h b/kerep/src/random/xoshiro/64bitValue/xoshiro256.h similarity index 100% rename from kerep-headers/random/xoshiro/64bitValue/xoshiro256.h rename to kerep/src/random/xoshiro/64bitValue/xoshiro256.h diff --git a/kerep/src/random/xoshiro/64bitValue/xoshiro256plus.c b/kerep/src/random/xoshiro/64bitValue/xoshiro256plus.c new file mode 100644 index 0000000..ee7ae32 --- /dev/null +++ b/kerep/src/random/xoshiro/64bitValue/xoshiro256plus.c @@ -0,0 +1,58 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoshiro256.h" + +/* This is xoshiro256+ 1.0, our best and fastest generator for floating-point + numbers. We suggest to use its upper bits for floating-point + generation, as it is slightly faster than xoshiro256++/xoshiro256**. It + passes all tests we are aware of except for the lowest three bits, + which might fail linearity tests (and just those), so if low linear + complexity is not considered an issue (as it is usually the case) it + can be used to generate 64-bit outputs, too. + + We suggest to use a sign test to extract a random Boolean value, and + right shifts to extract subsets of bits. + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + + +static inline u64 rotl(const u64 x, i32 k) { + return (x << k) | (x >> (64 - k)); +} + +u64 xoshiro256plus_next(void* _state){ + xoshiro256_state* state=_state; + const u64 result = state->s[0] + state->s[3]; + + const u64 t = state->s[1] << 17; + + state->s[2] ^= state->s[0]; + state->s[3] ^= state->s[1]; + state->s[1] ^= state->s[2]; + state->s[0] ^= state->s[3]; + + state->s[2] ^= t; + + state->s[3] = rotl(state->s[3], 45); + + return result; +} + +void* xoshiro256_init(u64 seed){ + xoshiro256_state* state=malloc(sizeof(xoshiro256_state)); + splitmix64_state* splitmix=splitmix64_init(seed); + state->s[0]=splitmix64_next(splitmix); + state->s[1]=splitmix64_next(splitmix); + state->s[2]=splitmix64_next(splitmix); + state->s[3]=splitmix64_next(splitmix); + splitmix64_free(splitmix); + return state; +} diff --git a/kerep/src/random/xoshiro/64bitValue/xoshiro256plusplus.c b/kerep/src/random/xoshiro/64bitValue/xoshiro256plusplus.c new file mode 100644 index 0000000..d28eb7c --- /dev/null +++ b/kerep/src/random/xoshiro/64bitValue/xoshiro256plusplus.c @@ -0,0 +1,37 @@ +/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoshiro256.h" + +/* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators. + It has excellent (sub-ns) speed, a state (256 bits) that is large + enough for any parallel application, and it passes all tests we are + aware of. + + For generating just floating-poi32 numbers, xoshiro256+ is even faster. + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + +static inline u64 rotl(const u64 x, i32 k) { + return (x << k) | (x>>(64 - k)); +} + +u64 xoshiro256plusplus_next(void* _state) { + xoshiro256_state* state=_state; + const u64 result=rotl(state->s[0] + state->s[3], 23) + state->s[0]; + const u64 t=state->s[1] << 17; + state->s[2] ^= state->s[0]; + state->s[3] ^= state->s[1]; + state->s[1] ^= state->s[2]; + state->s[0] ^= state->s[3]; + state->s[2] ^= t; + state->s[3]=rotl(state->s[3], 45); + return result; +} diff --git a/kerep/src/random/xoshiro/64bitValue/xoshiro256starstar.c b/kerep/src/random/xoshiro/64bitValue/xoshiro256starstar.c new file mode 100644 index 0000000..804007c --- /dev/null +++ b/kerep/src/random/xoshiro/64bitValue/xoshiro256starstar.c @@ -0,0 +1,42 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include "xoshiro256.h" + +/* This is xoshiro256** 1.0, one of our all-purpose, rock-solid + generators. It has excellent (sub-ns) speed, a state (256 bits) that is + large enough for any parallel application, and it passes all tests we + are aware of. + + For generating just floating-poi32 numbers, xoshiro256+ is even faster. + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + +static inline u64 rotl(const u64 x, i32 k) { + return (x << k) | (x >> (64 - k)); +} + +u64 xoshiro256starstar_next(void* _state){ + xoshiro256_state* state=_state; + const u64 result = rotl(state->s[1] * 5, 7) * 9; + + const u64 t = state->s[1] << 17; + + state->s[2] ^= state->s[0]; + state->s[3] ^= state->s[1]; + state->s[1] ^= state->s[2]; + state->s[0] ^= state->s[3]; + + state->s[2] ^= t; + + state->s[3] = rotl(state->s[3], 45); + + return result; +} diff --git a/kerep-headers/random/xoshiro/xoshiro.h b/kerep/src/random/xoshiro/xoshiro.h similarity index 100% rename from kerep-headers/random/xoshiro/xoshiro.h rename to kerep/src/random/xoshiro/xoshiro.h diff --git a/libkerep.a.tar.xz b/libkerep.a.tar.xz deleted file mode 100644 index 06af52d44ca97b0a22d9be29f2d96a821fe38df8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48248 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