Repository: r-lyeh/tinybits Branch: master Commit: 2a735d9717c9 Files: 145 Total size: 497.8 KB Directory structure: gitextract_m65kpms5/ ├── README.md ├── UNLICENSE.md ├── tinyarc4.hpp ├── tinyassert.c ├── tinyatoi.c ├── tinybenchmark.hpp ├── tinybsearch.c ├── tinybsearch.cc ├── tinybuild.h ├── tinydebug.h ├── tinydefer.cc ├── tinydir.cc ├── tinydixy.c ├── tinydual.sh.bat ├── tinyendian.c ├── tinyerror.c ├── tinyfsm.c ├── tinygc.cc ├── tinyhexbase.c ├── tinyhexdump.c ├── tinyhuman.hpp ├── tinyini.c ├── tinyjson5.c ├── tinylog.c ├── tinylog.h ├── tinylogger.h ├── tinylogger.hpp ├── tinymatch.c ├── tinymime.c ├── tinypipe.hpp ├── tinyprint.cc ├── tinypulse.c ├── tinyroman.cc ├── tinystring.c ├── tinystring.cc ├── tinytga.c ├── tinytime.cc ├── tinytodo.c ├── tinytty.c ├── tinyuniso.cc ├── tinyunit.c ├── tinyuntar.cc ├── tinyunzip.cc ├── tinyvariant.cc ├── tinyvbyte.h ├── tinywav.c ├── tinywtf.h ├── tinyzlib.cpp └── vault/ ├── ; ds ├── _test.c ├── all.c ├── bin.c ├── bin_dbkv.c ├── bin_json5.c ├── buf.c ├── buf_arc4.c ├── buf_base64.c ├── buf_base92.c ├── buf_cobs.c ├── buf_crc.c ├── buf_endian.c ├── buf_interleave.c ├── buf_netstring.c ├── buf_pack.c ├── buf_pack754.h ├── buf_packhalf.h ├── buf_packint.h ├── buf_packvli.h ├── buf_zigzag.c ├── c.c ├── c_alignas.c ├── c_benchmark.c ├── c_cc4.c ├── c_checkva.c ├── c_countof.c ├── c_ifdef.c ├── c_incbin.h ├── c_once.c ├── c_overload.c ├── c_plan.c ├── c_section.c ├── c_thread.c ├── c_unreachable.c ├── c_with.c ├── ds.c ├── ds_alloc.c ├── ds_array.c ├── ds_format.c ├── ds_hash.c ├── ds_map.c ├── ds_quark.c ├── ds_rope.c ├── ds_set.c ├── ds_sort.c ├── ds_stream.c ├── ds_string.c ├── net.c ├── net_ecdh.h ├── net_fragment.c ├── net_tunnel.c ├── net_webserver.c ├── os.c ├── os_ansi.c ├── os_assert.c ├── os_breakpoint.c ├── os_cpu.c ├── os_date.c ├── os_dialog.c ├── os_die.c ├── os_entropy.c ├── os_env.c ├── os_exec.c ├── os_exit.c ├── os_file.c ├── os_icon.c ├── os_ini.c ├── os_locale.c ├── os_logger.c ├── os_logger2.c ├── os_logger3.c ├── os_memory.c ├── os_mime.c ├── os_singleton.c ├── os_test.c ├── os_trap.c ├── os_tray.c ├── syn.c ├── syn_atomic.c ├── syn_channel.c ├── syn_condv.c ├── syn_coro.c ├── syn_fiber.c ├── syn_fiber_amd64.h ├── syn_fiber_arm.h ├── syn_fiber_ppc.h ├── syn_fiber_sjlj.h ├── syn_fiber_ucontext.h ├── syn_fiber_win32.h ├── syn_fiber_x86.h ├── syn_mcmp.c ├── syn_mutex.c ├── syn_semaphore.c ├── syn_sleep.c ├── syn_thread.c └── syn_tls.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # tinybits - [x] Tiny bits and useful snippets that I keep using everywhere. - [x] Too simple to become libraries. Just cut & paste. - [x] Cross-platform C/C++. - [x] Public Domain. |Snippet|Language|Domain| |:------|:-------|:-----| |[tinyarc4.hpp](tinyarc4.hpp)|C++|ARC4 stream cypher| |[tinyassert.c](tinyassert.c)|C/C++|Old assert() macro with new tricks| |[tinyatoi.c](tinyatoi.c)|C|atoi() implementation| |[tinybenchmark.hpp](tinybenchmark.hpp)|C++|Benchmark code| |[tinybsearch.c](tinybsearch.c)|C|Dichotomic binary search| |[tinybsearch.cc](tinybsearch.cc)|C++|Dichotomic binary search| |[tinybuild.h](tinybuild.h)|C|Build macros| |[tinydebug.h](tinydebug.h)|C|Debug macros| |[tinydefer.cc](tinydefer.cc)|C++|Defer macro, Go style| |[tinydir.cc](tinydir.cc)|C++|Directory listing| |[tinydixy.c](tinydixy.c)|C|Small YAML-subset config file parser| |[tinydual.sh.bat](tinydual.sh.bat)|Bash|Dual bash/batch file| |[tinyendian.c](tinyendian.c)|C|Endianness conversions| |[tinyerror.c](tinyerror.c)|C|Error handling| |[tinyfsm.c](tinyfsm.c)|C|Tight FSM| |[tinygc.cc](tinygc.cc)|C++|Garbage collector (C++)| |[tinyhexbase.c](tinyhexbase.c)|C|Simple binary to ascii encoder| |[tinyhexdump.c](tinyhexdump.c)|C|Hexdump viewer| |[tinyhuman.hpp](tinyhuman.hpp)|C++|De/humanized numbers| |[tinyini.c](tinyini.c)|C|Config parser (ini+)| |[tinyjson5.c](tinyjson5.c)|C|JSON5/SJSON/JSON parser/writer| |[tinylog.h](tinylog.h)|C|Logging utilities| |[tinylogger.h](tinylogger.h)|C|Simplest colorful logger| |[tinylogger.hpp](tinylogger.hpp)|C++|Session logger| |[tinymatch.c](tinymatch.c)|C|Wildcard/pattern matching| |[tinymime.c](tinymime.c)|C|MIME/file-type detection| |[tinypipe.hpp](tinypipe.hpp)|C++11|Chainable pipes| |[tinyprint.cc](tinyprint.cc)|C++|Comma-based printer| |[tinypulse.c](tinypulse.c)|C|Digital pulses| |[tinyroman.cc](tinyroman.cc)|C++|Integer to roman literals| |[tinystring.c](tinystring.c)|C|C string library| |[tinystring.cc](tinystring.cc)|C++|C++ string utilities| |[tinytga.c](tinytga.c)|C|TGA writer (fork)| |[tinytime.cc](tinytime.cc)|C++|Timing utilities| |[tinytodo.c](tinytodo.c)|C|TODO() macro| |[tinytty.c](tinytty.c)|C|Terminal utilities| |[tinyunit.c](tinyunit.c)|C|Unit-testing| |[tinyuniso.cc](tinyuniso.cc)|C++|.iso/9960 unarchiver| |[tinyuntar.cc](tinyuntar.cc)|C++|.tar unarchiver| |[tinyunzip.cc](tinyunzip.cc)|C++|.zip unarchiver| |[tinyvariant.cc](tinyvariant.cc)|C++|Variant class| |[tinyvbyte.h](tinyvbyte.h)|C|vbyte encoder/decoder (VLE)| |[tinywav.c](tinywav.c)|C|WAV writer (fork)| |[tinywtf.h](tinywtf.h)|C/C++|Portable host macros| |[tinyzlib.cpp](tinyzlib.cpp)|C++|zlib inflater| ================================================ FILE: UNLICENSE.md ================================================ This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ================================================ FILE: tinyarc4.hpp ================================================ // tinyARC4, ARC4 stream cypher. based on code by Mike Shaffer. // - rlyeh, public domain ~~ listening to Black Belt - Leeds | wtrmrkrlyeh #pragma once #include static std::string tinyARC4( const std::string &text, const std::string &passkey ) { int sbox[256], key[256]; std::string output; size_t plen = passkey.size(), tlen = text.size(); if( plen ) { output.resize( text.size() ); for( size_t a = 0; a < 256; a++ ) { key[ a ] = passkey[ a % plen ]; sbox[ a ] = a; } for( size_t a = 0, b = 0; a < 256; a++ ) { b = (b + sbox[ a ] + key[ a ]) % 256; int swap = sbox[ a ]; sbox[ a ] = sbox[ b ]; sbox[ b ] = swap; } for( size_t a = 0, i = 0, j = 0, k; a < tlen; a++ ) { i = (i + 1) % 256; j = (j + sbox[ i ]) % 256; int swap = sbox[ i ]; sbox[ i ] = sbox[ j ]; sbox[ j ] = swap; k = sbox[(sbox[ i ] + sbox[j]) % 256]; output[ a ] = text[ a ] ^ k; } } else output = text; return output; } /* #include #include int main( int argc, const char **argv ) { // sample std::string encrypted = tinyARC4( "Hello world.", "my-password" ); std::string decrypted = tinyARC4( encrypted, "my-password" ); std::cout << "ARC4 Encrypted text: " << encrypted << std::endl; std::cout << "ARC4 Decrypted text: " << decrypted << std::endl; // tests assert( tinyARC4("hello world", "my key") != "hello world" ); assert( tinyARC4(tinyARC4("hello world", "my key"), "my key") == "hello world" ); } */ ================================================ FILE: tinyassert.c ================================================ // old assert() macro with new tricks. // - rlyeh, public domain. // // - [x] log failures always. break debugger only once per failure. // - [x] enabled always. even in optimized builds. unless ASSERT_LEVEL is 0. // - [x] allow messages. ie, assert(my_var != 5, "failed my_var!=%d", my_var). // - [x] break debugger only if present (hassle developers; dont hassle users). // - [x] overridable logger & debugger break. tip: may define ASSERT_BREAK as abort(), or leave it blank as well. // - [x] filter out by configurable range (to speed up things when having thousand of expensive asserts). see ASSERT_LEVEL. // configuration: percentage of random checked asserts: [0%(none)..50%(half)..100%(all)]. default: 100% #ifndef ASSERT_LEVEL #define ASSERT_LEVEL 100 #endif // impl below ------------------------------------------------------------------ #include #include #include #include #undef assert // default logger: dump to stderr #ifndef ASSERT_LOG #define ASSERT_LOG(x) do { fprintf(stderr, "%s\n", (x)); } while(0) #endif // default break behavior: break if debugged. # if !defined ASSERT_BREAK && defined _WIN32 #define ASSERT_BREAK() do{ if(IsDebuggerPresent()) __debugbreak(); } while(0) #include // needed for __cplusplus #elif !defined ASSERT_BREAK // && defined __unix__ #define ASSERT_BREAK() do { signal(SIGTRAP, break_handler_); raise(SIGTRAP); } while(0) #include static void break_handler_(int signum) { signal(SIGTRAP, SIG_DFL); } #endif #if (ASSERT_LEVEL <= 0) || defined SHIPPING #define assert(EXPR, ...) (void)0 #else #define assert(EXPR, ...) do { \ static int64_t maybe = -1; if(maybe < 0) { \ /* original splitmix64 by Sebastiano Vigna (CC0)*/ \ uint64_t z = (__LINE__ + __COUNTER__ + UINT64_C(0x9E3779B97F4A7C15)); \ z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); \ maybe = (unsigned)((z ^ (z >> 31)) % 100) < ASSERT_LEVEL; } \ if( maybe ) { \ int msvc_trick[] = {0,}; \ if( !(EXPR) ) { \ char text[4096], *ptr = text; \ ptr += sprintf( ptr, "!" __VA_ARGS__ ); \ ptr += text[1] ? 0 : sprintf( ptr, "Assertion failed: %s", #EXPR ); \ ptr += sprintf( ptr, " (unexpected) %s:%d", __FILE__, __LINE__); \ ASSERT_LOG((1+text)); \ msvc_trick[0]++; /* fool clang -Wunused-variable */ \ static int once = 1; for(;once;once=0) ASSERT_BREAK(); \ } \ } \ } while(0) #endif #if 0 // demo int main() { for( int i = 0; i < 3; ++i ) { assert(i < 2); assert(i < 2, "Error! %d should be smaller than %d", i, 2); } puts("Program continues over here unless debugger is attached..."); } #endif ================================================ FILE: tinyatoi.c ================================================ // Tiny atoi() replacement. rlyeh, public domain | wtrmrkrlyeh #pragma once static int tinyatoi( const char *s ) { int v = 0, n = 1; if( s ) { while( *s == '-' ) n *= -1, s++; while( *s >= '0' && *s <= '9') v = (v * 10) + *s++ - '0'; } return n * v; } /* #include int main() { assert( 1230 == tinyatoi("01230") ); assert( -1230 == tinyatoi("-01230") ); assert( 1230 == tinyatoi("--01230") ); assert( -1230 == tinyatoi("---01230") ); } */ ================================================ FILE: tinybenchmark.hpp ================================================ // tiny benchmarks. OpenMP required. // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include struct bench { double line, time; operator bool() const { return true; } ~bench() { printf("L%d %gms\n", (int)line, (omp_get_wtime() - time) * 1000); } }; #define bench if( const bench x = { __LINE__, omp_get_wtime() } ) /* int main() { bench { for( int i = 0; i < 100000000; ++i ); puts("hello stdio"); } } */ ================================================ FILE: tinybsearch.c ================================================ // Tiny binary search (dichotomic): array must be sorted && supporting sequential access. // - rlyeh, public domain | wtrmrkrlyeh #include unsigned bsearchint( const int *array, int numelems, int key ) { int min = 0, max = numelems; while( min <= max ) { int mid = min + ( max - min ) / 2; /**/ if( key == array[mid] ) return mid; else if( key < array[mid] ) max = mid - 1; else min = mid + 1; } return ~0u; } unsigned bsearchsz( const size_t *array, int numelems, size_t key ) { int min = 0, max = numelems; while( min <= max ) { int mid = min + ( max - min ) / 2; /**/ if( key == array[mid] ) return mid; else if( key < array[mid] ) max = mid - 1; else min = mid + 1; } return ~0u; } unsigned bsearchstr( const char **array, int numelems, const char *key ) { int min = 0, max = numelems; while( min <= max ) { int mid = min + ( max - min ) / 2; int search = strcmp(key, array[mid]); /**/ if( 0 ==search ) return mid; else if( search < 0 ) max = mid - 1; else min = mid + 1; } return ~0u; } /* #include int main() { // @ [0] [1] [2] [3] const char *dict[] = { "abc", "abracadabra", "ale hop", "all your base"}; assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "abc") == 0 ); // @ [0] assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "abracadabra") == 1 ); // @ [1] assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "ale hop") == 2 ); // @ [2] assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "all your base") == 3 ); // @ [3] assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "are belong to us") == ~0u ); // not found assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "move") == ~0u ); // not found assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "every") == ~0u ); // not found assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "zig") == ~0u ); // not found assert( bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), "") == ~0u ); // not found for( int i = 0; i < sizeof(dict) / sizeof(dict[0]); ++i ) { assert( i == bsearchstr(dict, sizeof(dict) / sizeof(dict[0]), dict[i]) ); } } */ ================================================ FILE: tinybsearch.cc ================================================ // Tiny binary search (dichotomic): container must be sorted && supporting sequential access. // - rlyeh, public domain | wtrmrkrlyeh template unsigned bsearch( const T &x, const container &v ) { int min = 0, max = int(v.size()); while( min <= max ) { int mid = min + ( max - min ) / 2; /**/ if( x == v[mid] ) return mid; else if( x < v[mid] ) max = mid - 1; else min = mid + 1; } return ~0u; } /* #include #include int main() { // @ [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] std::vector v { 0, 1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 15, 16 }; assert( bsearch( 0, v) == 0 ); // @ [ 0] assert( bsearch(10, v) == 7 ); // @ [ 7] assert( bsearch(11, v) == 8 ); // @ [ 8] assert( bsearch(15, v) == 11 ); // @ [11] assert( bsearch(16, v) == 12 ); // @ [12] assert( bsearch(-3, v) == ~0u ); // not found assert( bsearch(18, v) == ~0u ); // not found assert( bsearch( 8, v) == ~0u ); // not found assert( bsearch( 9, v) == ~0u ); // not found for( const auto &i : v ) { assert( v[bsearch(i,v)] == i ); } } */ ================================================ FILE: tinybuild.h ================================================ // Tiny buildinfo macros // - rlyeh, public domain | wtrmrkrlyeh #pragma once #ifndef BUILD_GIT_BRANCH #define BUILD_GIT_BRANCH "n/a" #endif #ifndef BUILD_GIT_REVISION #define BUILD_GIT_REVISION "NaN" #endif #ifndef BUILD_BITS #if _WIN64 || __x86_64__ || __ppc64__ #define BUILD_BITS 64 #elif _WIN32 || __GNUC__ #define BUILD_BITS 32 #else #define BUILD_BITS 00 #endif #endif #ifndef BUILD_PROJECT #define BUILD_PROJECT "UNNAMED" #endif #ifndef BUILD_VERSION #define BUILD_VERSION "0.0.0" #endif #ifndef BUILD_URL #define BUILD_URL "https://" #endif #ifndef BUILD_STAMP #define BUILD_STAMP __DATE__ " " __TIME__ #endif #ifndef BUILD_STR #define BUILD_S7R(a) #a #define BUILD_STR(a) BUILD_S7R(a) #endif #ifndef BUILD_ARCH #define BUILD_ARCH BUILD_STR(BUILD_BITS) "-bit" #endif #ifndef BUILD_TYPE #define BUILD_TYPE "DEBUG" #endif #ifndef BUILD_INFO #define BUILD_INFO BUILD_PROJECT " " BUILD_VERSION " (" BUILD_ARCH " " BUILD_TYPE ") (" BUILD_STAMP ") (git:" BUILD_GIT_BRANCH " rev:" BUILD_GIT_REVISION ")" #endif /* #include int main() { puts( BUILD_INFO ); } */ ================================================ FILE: tinydebug.h ================================================ // Tiny debug macros // - rlyeh, public domain | wtrmrkrlyeh // // Build cube of 3 dimensions, 5 levels each: // // . PUBLIC AXIS: 0 STUDIO/INTERNAL, 1 QA, 2 USER-TESTING, 3 SHOWCASE, 4 SHIPPING/MASTER // / // / // +--------> DEBUG AXIS: 0 FATAL, 1 ERROR, 2 WARN, 3 INFO, 4 VERBOSE // | // | // | // V OPTIMIZATION AXIS: 0 DEBUG, 1 DEBUGOPT, 2 OPTSYM, 3 OPTMAX, 4 STRIPPED // // Example: a RETAIL build might be PUB>=3 DBG<=1 OPT>=3 #pragma once #if defined(DEBUG) && (defined(NDEBUG) || defined(_NDEBUG)) #undef DEBUG // NDEBUG has precedence #endif #ifndef DBGLVL #define DBGLVL (0) #endif #ifndef OPTLVL #define OPTLVL (0) #endif #ifndef PUBLVL #define PUBLVL (0) #endif #if DBGLVL #define REL if(0) #define DBG if(1) #define DBG0 if(DBGLVL >= 0) #define DBG1 if(DBGLVL >= 1) #define DBG2 if(DBGLVL >= 2) #define DBG3 if(DBGLVL >= 3) #define DBG4 if(DBGLVL >= 4) #else #define REL if(1) #define DBG if(0) #define DBG0 if(0) #define DBG1 if(0) #define DBG2 if(0) #define DBG3 if(0) #define DBG4 if(0) #endif #if OPTLVL #define OPT if(1) #define OPT0 if(OPTLVL == 0) #define OPT1 if(OPTLVL >= 1) #define OPT2 if(OPTLVL >= 2) #define OPT3 if(OPTLVL >= 3) #define OPT4 if(OPTLVL >= 4) #else #define OPT if(0) #define OPT0 if(1) #define OPT1 if(0) #define OPT2 if(0) #define OPT3 if(0) #define OPT4 if(0) #endif #if PUBLVL #define DEV if(0) #define PUB if(1) #define PUB0 if(PUBLVL >= 0) #define PUB1 if(PUBLVL >= 1) #define PUB2 if(PUBLVL >= 2) #define PUB3 if(PUBLVL >= 3) #define PUB4 if(PUBLVL >= 4) #else #define DEV if(1) #define PUB if(0) #define PUB0 if(0) #define PUB1 if(0) #define PUB2 if(0) #define PUB3 if(0) #define PUB4 if(0) #endif // aliases #define DEBUGSYM DEV DBG OPT0 #define DEBUGOPT DEV DBG OPT2 #define DEVELSYM DEV REL OPT0 #define DEVELOPT DEV REL OPT2 #define SHIPPING PUB REL OPT3 /* #include #include int main() { DBG puts("shown in debug builds"); REL puts("shown in release builds"); DEV puts("shown in internal development builds"); DEV OPT0 puts("shown in internal development builds, with no optimization level"); PUB OPT3 puts("shown in public builds with optimization level >= 3"); SHIPPING puts("shown in final builds"); char collected_flags[128] = {0}; char *buf = collected_flags; DBG strcat(buf, "DEBUG,"); REL strcat(buf, "RELEASE,"); DBG0 strcat(buf, "DEBUG >= 0,"); DBG1 strcat(buf, "DEBUG >= 1,"); DBG2 strcat(buf, "DEBUG >= 2,"); DBG3 strcat(buf, "DEBUG >= 3,"); DBG4 strcat(buf, "DEBUG >= 4,"); OPT0 strcat(buf, "OPTIM == 0,"); OPT1 strcat(buf, "OPTIM == 1,"); OPT2 strcat(buf, "OPTIM == 2,"); OPT3 strcat(buf, "OPTIM == 3,"); OPT4 strcat(buf, "OPTIM == 4,"); OPT0 DBG strcat(buf, "DEVELDBG (OPT0 && DBG && DEV),"); OPT2 DBG strcat(buf, "DEVELOPT (OPT2 && DBG && DEV),"); OPT2 REL strcat(buf, "OPTIMSYM (OPT2 && REL && DEV),"); SHIPPING strcat(buf, "SHIPPING (OPT3 && REL && PUB),"); puts( collected_flags ); } */ ================================================ FILE: tinydefer.cc ================================================ // tinydefer, Go style // - rlyeh, public domain. #include struct defer { std::function fn; ~defer() { fn(); } }; #define DEFER_MERGE_(a,b) a##b #define DEFER_LABEL_(a) DEFER_MERGE_(unique_name_, a) #define DEFER_UNIQUE_NAME DEFER_LABEL_(__LINE__) #define defer defer DEFER_UNIQUE_NAME; DEFER_UNIQUE_NAME.fn = [&] /* #include int main() { puts("1"); defer { puts("2"); puts("3"); }; defer { puts("4"); puts("5"); }; puts("6"); } */ ================================================ FILE: tinydir.cc ================================================ // tiny directory listing // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #ifdef _WIN32 #include #else #include #endif template bool tinydir( const char *directory, const FN &yield ) { std::string src( directory ); while( !src.empty() && (src.back() == '/' || src.back() == '\\') ) src.pop_back(); #ifdef _WIN32 WIN32_FIND_DATA fdata; for( HANDLE h = FindFirstFileA( (src + "/*").c_str(), &fdata ); h != INVALID_HANDLE_VALUE; ) { for( bool next = true; next; next = FindNextFileA( h, &fdata ) != 0 ) { if( fdata.cFileName[0] != '.' ) { yield( (src + "/" + fdata.cFileName).c_str(), (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0 ); } } return FindClose( h ), true; } #else for( DIR *dir = opendir( (src + "/").c_str() ); dir; ) { for( struct dirent *ep; ep = readdir( dir ); ) { if( ep->d_name[0] != '.' ) { DIR *tmp = opendir( ep->d_name ); yield( (src + "/" + ep->d_name).c_str(), tmp ? (closedir( tmp ), 1) : 0 ); } } return closedir( dir ), true; } #endif return false; } /* #include #include int main() { std::function callback = [&]( const char *name, bool is_dir ) { printf( "%5s %s\n", is_dir ? "" : "", name ); //if( is_dir ) tinydir( name, callback ); // <-- uncomment for recursive listing }; return tinydir( "./", callback ); } */ ================================================ FILE: tinydixy.c ================================================ // tinydixy, small hierarchical config file format (subset of YAML, spec in https://github.com/kuyawa/Dixy) // - rlyeh, public domain // api, returns number of pairs found int tinydixy( const char *buffer, int (*yield)(const char *key, const char *val) ); // implementation #include #include int tinydixy( const char *s, int (*yield)(const char *key, const char *val) ) { char *map = 0; int mapcap = 0, maplen = 0, num_pairs_found = 0; enum { DEL, REM, KEY, SUB, VAL } fsm = DEL; const char *cut[5] = {0}, *end[5] = {0}; while( *s ) { while( *s && (*s == '\r' || *s == '\n') ) ++s; /**/ if( *s == '#' ) cut[fsm = REM] = ++s; else if( *s == ' ' || *s == '\t' ) cut[fsm = SUB] = ++s; else if( *s == ':' ) cut[fsm = VAL] = ++s; else if( *s > ' ' && *s <= 'z' ) cut[fsm = KEY] = cut[SUB] = end[SUB] = s, free(map), map = 0, mapcap = 0, maplen = 0; else { ++s; continue; } /**/ if( fsm == REM ) { while(*s && *s != '\r'&& *s != '\n') ++s; } else if( fsm == KEY ) { while(*s && *s > ' ' && *s <= 'z' && *s != ':') ++s; end[fsm] = s; } else if( fsm == SUB ) { while(*s && *s > ' ' && *s <= 'z' && *s != ':') ++s; end[fsm] = s; } else if( fsm == VAL ) { while(*s && *s >= ' ' && *s <= 'z' && *s != '\r' && *s != '\n') ++s; end[fsm] = s; while( end[fsm][-1] == ' ' ) --end[fsm]; char buf[256] = {0}, *key = buf, *val = ""; if( end[KEY] - cut[KEY] ) key += sprintf(key, "%.*s", end[KEY] - cut[KEY], cut[KEY] ); if( end[SUB] - cut[SUB] ) key += sprintf(key, ".%.*s", end[SUB] - cut[SUB], cut[SUB] ); int reqlen = (key - buf) + 1 + (end[VAL] - cut[VAL]) + 1 + 1; if( (reqlen + maplen) >= mapcap ) map = realloc( map, mapcap += reqlen + 512 ); sprintf( map + maplen, "%.*s%c%.*s%c%c", key - buf, buf, 0, end[VAL] - cut[VAL], cut[VAL], 0, 0 ); val = map + maplen + (key - buf) + 2, key = map + maplen; if( val[0] ) { yield( key, val ); num_pairs_found++; } maplen += reqlen - 1; } } free( map ); return num_pairs_found; } // sample /* int puts2( const char *key, const char *val ) { printf("%s:'%s'\n", key, val); return 0; } int main() { const char *sample = "# Dixy 1.0\n" "\n" "name: Taylor Swift\n" "age: 27\n" "phones:\n" " 0: 555-SWIFT\n" " 1: 900-SWIFT\n" " 2: 800-TAYLOR\n" "body:\n" " height: 6 ft\n" " weight: 120 lbs\n" "pets:\n" " 0:\n" " name: Fido\n" " breed: chihuahua\n" " 1:\n" " name: Tinkerbell\n" " breed: bulldog\n"; printf("%d keys found\n", tinydixy( sample, puts2 )); } */ ================================================ FILE: tinydual.sh.bat ================================================ #/bin/bash 2>nul || goto :windows # bash echo hello Bash ls exit :windows @echo off echo hello Windows ver exit /b ================================================ FILE: tinyendian.c ================================================ // Tiny endianness. rlyeh, public domain | wtrmrkrlyeh #pragma once #include #define IS_BIG_ENDIAN (*(uint16_t *)"\0\1" == 1) static uint16_t swap16( uint16_t x ) { return (x << 8) | (x >> 8); } static uint32_t swap32( uint32_t x ) { return (x << 24) | (x >> 24) | ((x & 0xff00) << 8) | ((x >> 8) & 0xff00); } static uint64_t swap64( uint64_t x ) { return (x << 56) | (x >> 56) | ((x & 0xff00) << 40) | ((x >> 40) & 0xff00) | ((x & 0xff0000) << 24) | ((x >> 24) & 0xff0000) | ((x & 0xff000000) << 8) | ((x >> 8) & 0xff000000); } static uint16_t tobe16( uint16_t x ) { return IS_BIG_ENDIAN ? x : swap16(x); } static uint32_t tobe32( uint32_t x ) { return IS_BIG_ENDIAN ? x : swap32(x); } static uint64_t tobe64( uint64_t x ) { return IS_BIG_ENDIAN ? x : swap64(x); } static uint16_t tole16( uint16_t x ) { return IS_BIG_ENDIAN ? swap16(x) : x; } static uint32_t tole32( uint32_t x ) { return IS_BIG_ENDIAN ? swap32(x) : x; } static uint64_t tole64( uint64_t x ) { return IS_BIG_ENDIAN ? swap64(x) : x; } /* #include int main() { printf("%x\n", swap32(0x12345678) ); } */ ================================================ FILE: tinyerror.c ================================================ // simple error handling api. non-intrusive version. // - rlyeh, public domain. // // Errors automatically printed in debug builds. // Usage: use return OK(retvalue) or return ERROR(retvalue, "error"...); as desired. // Usage: to check for errors: if(ERROR) { /* do something here */ } // Good habit: system errorcode first, then human explanation when reporting errors. Ie, ERROR(rc, "404 File not found %s\n", file); #pragma once #include # if !defined ERROR_LOG && defined NDEBUG #define ERROR_LOG(...) (void)0 #elif !defined ERROR_LOG #define ERROR_LOG(...) (fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")) #endif #define OK(retval) (ERROR = (char*)0, (retval)) #define ERROR(retval, ...) (ERROR_LOG("" __VA_ARGS__), ERROR = (char*)"Error: " #__VA_ARGS__ " (@" __FUNCTION__ " " __FILE__ ":" ERR0R(__LINE__) ") ", (retval)) #define ERR0R(rc) ERRoR(rc) #define ERRoR(rc) #rc # ifdef __GNUC__ #define __FUNCTION__ "" static __thread char* ERROR = 0; #else // _MSC_VER static __declspec(thread) char* ERROR = 0; #endif #if 0 // demo int derefence(int *ptr) { if(ptr) return OK(*ptr); return ERROR(0, "404: Cannot deference pointer [%p]", ptr); // errorcode + variable message (ideal) return ERROR(0, "Cannot deference pointer [%p]", ptr); // dynamic message (no errorcode) return ERROR(0, "Cannot deference pointer"); // fixed message (no errorcode) return ERROR(0); // (no message) (no errorcode) } int main(int arg, char **argv) { int a = derefence(&arg); // pass int b = derefence(NULL); // fail if( ERROR ) { /* do something here */ } } #endif ================================================ FILE: tinyfsm.c ================================================ // Tiny FSM. rlyeh, public domain | wtrmrkrlyeh #pragma once #define with(st) for(int i=1;i--;st[1]=st[0]) switch(((st[0])<<16)|(st[1])) #define when(a) break; case (((a)<<16)|(a)) #define transition(a,b) break; case (((b)<<16)|(a)) typedef int fsm[2]; /* #include enum { IDLE, WALKING, RUNNING, }; void update( fsm state ) { with(state) { when(IDLE): puts("idle"); transition(IDLE,WALKING): puts("idle --> walking"); transition(IDLE,RUNNING): puts("idle --> running"); when(WALKING): puts("walking"); transition(WALKING,IDLE): puts("walking --> idle"); transition(WALKING,RUNNING): puts("walking --> running"); when(RUNNING): puts("running"); transition(RUNNING,IDLE): puts("running --> idle"); transition(RUNNING,WALKING): puts("running --> walking"); } } int main() { fsm state = {0}; *state = IDLE; update(state); *state = WALKING; update(state); *state = WALKING; update(state); *state = RUNNING; update(state); *state = IDLE; update(state); } */ ================================================ FILE: tinygc.cc ================================================ // tiny garbage collector (<100 LOCs). Genius code tricks by @orangeduck (see: https://github.com/orangeduck/tgc README) // - rlyeh, public domain. void gc_init(void *argc, int initial_mbytes_reserved); // pointer to argc (from main), and initial MiB reserved void gc_run(void); // mark & sweep void gc_stop(void); // sweep void* gc_malloc(int sz); // allocator char* gc_strdup(const char *str); // util // macro that forbids pointer arithmetic (enables fixed pointer addresses) #define GC(type) type const // --- #include // setjmp, jmp_buf #include // realloc #include // memcpy, strlen #include // printf #include // uintptr_t, UINTPTR_MAX #include // std::set<> #include // std::vector<> #ifndef GC_REALLOC #define GC_REALLOC realloc #endif static std::set gc_inuse; static std::vector gc_spawned; static void *gc_top = 0, *gc_min = 0, *gc_max = (void*)UINTPTR_MAX; static void gc_mark_stack(void) { void *bot = gc_top, *top = &bot, *last = 0; for( void *p = bot gc_max ) continue; // out of gc_spawned bounds. if( (uintptr_t)ptr & 0x7 ) continue; // 64-bit unaligned (not a pointer). gc_inuse.insert(last = ptr); } } static void gc_mark() { // mark reachable stack pointers jmp_buf env = {0}; void (*volatile check)(void) = gc_mark_stack; setjmp(env); check(); } static void gc_sweep() { // sweep unreachable stack pointers gc_min = (void*)UINTPTR_MAX, gc_max = 0; size_t back = gc_spawned.size(); for( size_t i = 0; i < back; ++i) { void *ptr = gc_spawned[i]; if( ptr > gc_max ) gc_max = ptr; if( ptr < gc_min ) gc_min = ptr; bool used = gc_inuse.find(ptr) != gc_inuse.end(); if( !used ) { GC_REALLOC(gc_spawned[i], 0); //free void *swap = gc_spawned[--back]; // vector erase gc_spawned[back] = gc_spawned[i]; gc_spawned[i--] = swap; } } size_t collected = gc_spawned.size() - back; if( collected ) printf("gc: %9d objects collected\n", (int)collected); gc_spawned.resize( back ); gc_inuse.clear(); } void gc_init(void *argc, int MiB) { gc_top = argc; gc_spawned.reserve((MiB > 0) * MiB * 1024 * 1024 / sizeof(void*)); } void gc_run() { gc_mark(); gc_sweep(); } void gc_stop() { gc_sweep(); } void *gc_malloc( int sz ) { void *ptr = GC_REALLOC(0, sz); // malloc if( ptr ) gc_spawned.push_back(ptr); return ptr; } char *gc_strdup( const char *s ) { int bytes = (int)strlen(s)+1; return (char *)memcpy(gc_malloc(bytes), s, bytes); } /* #include #define benchmark(t,...) for(clock_t beg=clock(), end=beg-beg; !end; printf("" __VA_ARGS__), printf(" %5.2fs\n", t=((end=clock()-beg) / (double)CLOCKS_PER_SEC))) void bench() { enum { FRAMES = 30, COUNT = 1000000 }; double baseline; benchmark(baseline, "%2.1fM allocs+frees (baseline; regular malloc)", FRAMES * COUNT * 2 / 1000000.0) { for( int frame = 0; frame < FRAMES; ++frame ) { for( int n = 0; n < COUNT; ++n ) { free( malloc(16) ); } } } double gctime; benchmark(gctime, "%2.1fM allocs+frees (gc)", FRAMES * COUNT * 2 / 1000000.0) { for( int frame = 0; frame < FRAMES; ++frame ) { for( int n = 0; n < COUNT; ++n ) { (void)gc_malloc(16); } } gc_run(); } if(baseline<=gctime) printf("gc is x%.2f times slower\n", gctime/baseline); else if(baseline >gctime) printf("gc is x%.2f times faster!\n", baseline/gctime); } void demo() { GC(void*) memory = gc_malloc(1024); (void)memory; // will be collected GC(char*) string = gc_strdup("hello world"); // will be collected GC(char*) x = gc_strdup("Hi"); x[0] |= 32; // will be collected. note: indexing is ok; pointer arithmetic is forbidden. gc_run(); puts(string); gc_run(); } int main(int argc, char **argv) { gc_init(&argc, 256); (void)argv; demo(); bench(); gc_stop(); } */ ================================================ FILE: tinyhexbase.c ================================================ // hexBASE // Very simple binary to ascii encoding. 0 to 2+100% bytes overhead (worst case) // // Specification: // if char in [32..126] range then print "%c", char ('~' escaped as "~~"), // else print "~%02x[...]~", for all remaining bytes. // // - rlyeh, public domain. #pragma once #include static inline void hexbasenl( FILE *fp, const void *ptr, int len ) { const unsigned char *buf = (const unsigned char*)ptr; for( ; len-- > 0; buf++ ) { unsigned char chr = (unsigned char)*buf; /**/ if( chr >= 32 && chr < 126 ) fprintf(fp, "%c", chr); else if( chr == 126 ) fprintf(fp, "%s", "~~"); else { fprintf(fp, "%c", '~'); do fprintf(fp, "%02x", *buf++); while( len-- > 0 ); fprintf(fp, "%c", '~'); } } } static inline void hexbase( FILE *fp, const void *ptr, int len ) { hexbasenl(fp, ptr, len); fprintf(fp, "%s\n", ""); } /* #include int main() { hexbase( stdout, "hello world", strlen("hello world") ); // --> hello world hexbase( stdout, "hello world \x1\x2\x3\xff", strlen("hello world") + 5 ); // --> hello world ~010203ff~ hexbase( stdout, "hello~world", strlen("hello~world") ); // --> hello~~world hexbase( stdout, "\xa1\2\3", 3 ); // --> ~a10203~ hexbase( stdout, "\1\2\3hello world", 3 + strlen("hello world") ); // --> ~01020368656c6c6f20776f726c64~ hexbase( stdout, "\1\2\3hello world\xf0", 4 + strlen("hello world") ); // --> ~01020368656c6c6f20776f726c64f0~ } */ ================================================ FILE: tinyhexdump.c ================================================ // Tiny hexdump viewer. rlyeh, public domain | wtrmrkrlyeh #include void hexdump( FILE *fp, const void *ptr, unsigned len, int width ) { unsigned char *data = (unsigned char*)ptr; for( unsigned jt = 0; jt < len; jt += width ) { fprintf( fp, "; %05d ", jt ); for( unsigned it = jt, next = it + width; it < len && it < next; ++it ) { fprintf( fp, "%02x %s", (unsigned char)data[it], &" \n\0...\n"[ (1+it) < len ? 2 * !!((1+it) % width) : 3 ] ); } fprintf( fp, "; %05d ", jt ); for( unsigned it = jt, next = it + width; it < len && it < next; ++it ) { fprintf( fp, " %c %s", (signed char)data[it] >= 32 ? (signed char)data[it] : (signed char)'.', &" \n\0...\n"[ (1+it) < len ? 2 * !!((1+it) % width) : 3 ] ); } } } /* #include int main() { const char *sample = __FILE__ "/" __TIME__ "/" __DATE__; hexdump( stdout, sample, strlen(sample), 16 ); } */ ================================================ FILE: tinyhuman.hpp ================================================ // tiny de/humanized numbers. based on freebsd implementation. // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include inline std::string humanize( uint64_t num, const char *suffix = " " ) { const char prefixes[] = " KMGTPE"; const char* prefixp = prefixes; uint64_t i = num, d = 0; while( (i > 1024) && *prefixp++ ) { d = (i % 1024) / 10, i /= 1024; } if (d > 0) return std::to_string(i) + '.' + std::to_string(d) + suffix + *prefixp; else return std::to_string(i) + suffix + *prefixp; } inline uint64_t dehumanize( const std::string &str ) { size_t sz = 1, mul = 0; double num = std::stof(str, &sz); while( str[sz] && str[sz] == ' ' ) sz++; switch( str[sz] ) { default: case 'B': case 'b': mul = 0; break; case 'K': case 'k': mul = 1; break; case 'M': case 'm': mul = 2; break; case 'G': case 'g': mul = 3; break; case 'T': case 't': mul = 4; break; case 'P': case 'p': mul = 5; break; case 'E': case 'e': mul = 6; // may overflow } while( mul-- ) if( num * 1024 < num ) return 0; else num *= 1024; return num; } /* #include int main() { std::cout << __LINE__ << " " << humanize(1238) << "m" << std::endl; std::cout << __LINE__ << " " << humanize(123823) << "l" << std::endl; std::cout << __LINE__ << " " << humanize(123828328) << "bytes" << std::endl; std::cout << "---" << std::endl; std::cout << __LINE__ << " " << dehumanize("118 km") << std::endl; std::cout << __LINE__ << " " << dehumanize("118.9M") << std::endl; std::cout << __LINE__ << " " << dehumanize("118M") << std::endl; std::cout << __LINE__ << " " << dehumanize("118b") << std::endl; std::cout << __LINE__ << " " << dehumanize("118k") << std::endl; std::cout << __LINE__ << " " << dehumanize("118mb") << std::endl; std::cout << __LINE__ << " " << dehumanize("118gb") << std::endl; std::cout << __LINE__ << " " << dehumanize("118tb") << std::endl; std::cout << __LINE__ << " " << dehumanize("118pb") << std::endl; } */ ================================================ FILE: tinyini.c ================================================ // ini+, extended ini format // - rlyeh, public domain // // # spec // // ; line comment // [details] ; map section name (optional) // user=john ; key and value (mapped here as details.user=john) // +surname=doe jr. ; sub-key and value (mapped here as details.user.surname=doe jr.) // color=240 ; key and value \ // color=253 ; key and value |> array: color[0], color[1] and color[2] // color=255 ; key and value / // color= ; remove key/value(s) // color=white ; recreate key; color[1] and color[2] no longer exist // [] ; unmap section // -note=keys may start with symbols (except plus and semicolon) // -note=linefeeds are either \r, \n or \r\n. // -note=utf8 everywhere. // #pragma once // api char *ini( const char *text ); // api, alternate callback version void ini_cb( const char *text, void (*yield)( const char *key, const char *value, void *userdata ), void *userdata ); // impl #include #include static char *ini( const char *s ) { char *map = 0; int mapcap = 0, maplen = 0; enum { DEL, REM, TAG, KEY, SUB, VAL } fsm = DEL; const char *cut[6] = {0}, *end[6] = {0}; while( *s ) { while( *s && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') ) ++s; /**/ if( *s == ';' ) cut[fsm = REM] = ++s; else if( *s == '[' ) cut[fsm = TAG] = ++s; else if( *s == '+' ) cut[fsm = SUB] = ++s; else if( *s == '=' ) cut[fsm = VAL] = ++s; else if( *s > ' ' && *s <= 'z' && *s != ']' ) cut[fsm = KEY] = cut[SUB] = end[SUB] = s; else { ++s; continue; } /**/ if( fsm == REM ) { while(*s && *s != '\r'&& *s != '\n') ++s; } else if( fsm == TAG ) { while(*s && *s != '\r'&& *s != '\n'&& *s != ']') ++s; end[fsm] = s; } else if( fsm == KEY ) { while(*s && *s > ' ' && *s <= 'z' && *s != '=') ++s; end[fsm] = s; } else if( fsm == SUB ) { while(*s && *s > ' ' && *s <= 'z' && *s != '=') ++s; end[fsm] = s; } else if( fsm == VAL ) { while(*s && *s >= ' ' && *s <= 'z' && *s != ';') ++s; end[fsm] = s; while( end[fsm][-1] == ' ' ) --end[fsm]; char buf[256] = {0}, *key = buf; if( end[TAG] - cut[TAG] ) key += sprintf(key, "%.*s.", (int)(end[TAG] - cut[TAG]), cut[TAG] ); if( end[KEY] - cut[KEY] ) key += sprintf(key, "%.*s", (int)(end[KEY] - cut[KEY]), cut[KEY] ); if( end[SUB] - cut[SUB] ) key += sprintf(key, ".%.*s", (int)(end[SUB] - cut[SUB]), cut[SUB] ); int reqlen = (key - buf) + 1 + (end[VAL] - cut[VAL]) + 1 + 1; if( (reqlen + maplen) >= mapcap ) map = realloc( map, mapcap += reqlen + 512 ); sprintf( map + maplen, "%.*s%c%.*s%c%c", (int)(key - buf), buf, 0, (int)(end[VAL] - cut[VAL]), cut[VAL], 0, 0 ); maplen += reqlen - 1; } } return map; } static void ini_cb( const char *text, void (*yield)( const char *key, const char *value, void *userdata ), void *userdata ) { char *kv = ini( text ); if( kv ) { for( char *iter = kv; iter[0]; ) { const char *key = iter; while( *iter++ ); const char *val = iter; while( *iter++ ); yield( key, val, userdata ); } free( kv ); } } /* int main() { char *kv = ini( "; line comment\n" "[details] ; map section name (optional)\n" "user=john ; key and value (mapped here as details.user=john)\n" "+surname=doe jr. ; sub-key and value (mapped here as details.user.surname=doe jr.)\n" "color=240 ; key and value \\\n" "color=253 ; key and value |> array: color[0], color[1] and color[2]\n" "color=255 ; key and value /\n" "color= ; remove key/value(s)\n" "color=white ; recreate key; color[1] and color[2] no longer exist\n" "[] ; unmap section\n" "-note=keys may start with symbols (except plus and semicolon)\n" "-note=linefeeds are either \\r, \\n or \\r\\n.\n" "-note=utf8 everywhere.\n" ); if( kv ) { for( char *iter = kv; iter[0]; ) { printf("key: '%s', ", iter); while( *iter++ ); printf("val: '%s'\n", iter); while( *iter++ ); } free( kv ); } } */ ================================================ FILE: tinyjson5.c ================================================ // JSON5 + SJSON parser module // // License: // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, // publish, and distribute this file as you see fit. // No warranty is implied, use at your own risk. // // Credits: // Dominik Madarasz (original code) (GitHub: zaklaus) // r-lyeh (fork) #ifndef JSON5_H #define JSON5_H #ifndef JSON5_ASSERT #define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0) #endif #ifndef JSON5_REALLOC #define JSON5_REALLOC realloc #endif #include #include typedef enum json5_type { json5_undefined, json5_null, json5_bool, json5_object, json5_string, json5_array, json5_integer, json5_real, } json5_type; typedef struct json5 { char* name; unsigned type : 3; unsigned count : 29; union { struct json5* array; struct json5* nodes; int64_t integer; double real; char* string; int boolean; }; } json5; char* json5_parse(json5 *root, char *source, int flags); void json5_write(FILE *fp, const json5 *root); void json5_free(json5 *root); #endif // JSON5_H #ifdef JSON5_C #pragma once #include #include #include #include #include // vector library ------------------------------------------------------------- size_t vsize( void *p ) { return p ? 0[ (size_t*)p - 2 ] : 0; } void *vresize( void *p, size_t sz ) { size_t *ret = (size_t*)p - 2; if( !p ) { ret = (size_t*)JSON5_REALLOC( 0, sizeof(size_t) * 2 + sz ); ret[0] = sz; ret[1] = 0; } else { size_t osz = ret[0]; size_t ocp = ret[1]; if( sz <= (osz + ocp) ) { ret[0] = sz; ret[1] = ocp - (sz - osz); } else { ret = (size_t*)JSON5_REALLOC( ret, sizeof(size_t) * 2 + sz * 1.75 ); ret[0] = sz; ret[1] = (size_t)(sz * 1.75) - sz; } } return &ret[2]; } void *vrealloc( void *p, size_t sz ) { if( sz ) { return vresize( p, sz ); } else { if( p ) { size_t *ret = (size_t*)p - 2; ret[0] = 0; ret[1] = 0; JSON5_REALLOC( ret, 0 ); } return 0; } } // array library -------------------------------------------------------------- #ifndef array_cast #ifdef __cplusplus #define array_cast(x) (decltype(x)) #else #define array_cast(x) (void *) #endif #define array_push(t, ...) ( (t) = array_cast(t) vrealloc((t), (array_count(t) + 1) * sizeof(0[t]) ), (t)[ array_count(t) - 1 ] = (__VA_ARGS__) ) #define array_count(t) (int)( (t) ? vsize(t) / sizeof(0[t]) : 0u ) #define array_free(t) ( array_cast(t) vrealloc((t), 0), (t) = 0 ) #endif // json5 ---------------------------------------------------------------------- char *json5__trim(char *p) { while (*p) { /**/ if( isspace(*p) ) ++p; else if( p[0] == '/' && p[1] == '*' ) { // skip C comment for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {} if( *p ) p += 2; } else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment for( p += 2; *p && p[0] != '\n'; ++p) {} if( *p ) ++p; } else break; } return p; } char *json5__parse_value(json5 *obj, char *p, char **err_code); char *json5__parse_string(json5 *obj, char *p, char **err_code) { assert(obj && p); if( *p == '"' || *p == '\'' || *p == '`' ) { obj->type = json5_string; obj->string = p + 1; char eos_char = *p, *b = obj->string, *e = b; while (*e) { /**/ if( *e == '\\' && (e[1] == eos_char) ) ++e; else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' '; else if( *e == eos_char ) break; ++e; } *e = '\0'; return p = e + 1; } //JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } char *json5__parse_object(json5 *obj, char *p, char **err_code) { assert(obj && p); if( 1 /* *p == '{' */ ) { /* <-- for SJSON */ int skip = *p == '{'; /* <-- for SJSON */ obj->type = json5_object; while (*p) { json5 node = { 0 }; do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' ); if( *p == '}' ) { ++p; break; } // @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) { else if( isalpha(*p) || *p == '_' || *p == '$' ) { // also || is_unicode(p) node.name = p; do { ++p; } while (*p && (*p == '_' || isalpha(*p) || isdigit(*p)) ); // also || is_unicode(p) char *e = p; p = json5__trim(p); *e = '\0'; } else { //if( *p == '"' || *p == '\'' || *p == '`' ) { char *ps = json5__parse_string(&node, p, err_code); if( !ps ) { return NULL; } p = ps; node.name = node.string; p = json5__trim(p); } // @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6 if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) { JSON5_ASSERT; *err_code = "json5_error_invalid_name"; return NULL; } if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) { JSON5_ASSERT; *err_code = "json5_error_invalid_name"; return NULL; } p = json5__trim(p + 1); p = json5__parse_value(&node, p, err_code); if( *err_code[0] ) { return NULL; } if( node.type != json5_undefined ) { array_push(obj->nodes, node); ++obj->count; } if( *p == '}') { ++p; break; } } return p; } JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } char *json5__parse_value(json5 *obj, char *p, char **err_code) { assert(obj && p); p = json5__trim(p); char *is_string = json5__parse_string(obj, p, err_code); if( is_string ) { p = is_string; if( *err_code[0] ) { return NULL; } } else if( *p == '{' ) { p = json5__parse_object( obj, p, err_code ); if( *err_code[0] ) { return NULL; } } else if( *p == '[' ) { obj->type = json5_array; while (*p) { json5 elem = { 0 }; do { p = json5__trim(p + 1); } while( *p == ',' ); if( *p == ']') { ++p; break; } p = json5__parse_value(&elem, p, err_code); if( *err_code[0] ) { return NULL; } if( elem.type != json5_undefined ) { array_push(obj->array, elem); ++obj->count; } if (*p == ']') { ++p; break; } } } else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) { const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity" }; const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 }; for( int i = 0; labels[i]; ++i ) { if( !strncmp(p, labels[i], lenghts[i] ) ) { p += lenghts[i]; #ifdef _MSC_VER // somehow, NaN is apparently signed in MSC /**/ if( i >= 5 ) obj->type = json5_real, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN; #else /**/ if( i >= 5 ) obj->type = json5_real, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN; #endif else if( i >= 1 ) obj->type = json5_bool, obj->boolean = i <= 2; else obj->type = json5_null; break; } } if( obj->type == json5_undefined ) { JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } } else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) { char buffer[16] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0; while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) { is_hex |= (*p | 32) == 'x'; is_dbl |= *p == '.'; *buf++ = *p++; } obj->type = is_dbl ? json5_real : json5_integer; /**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real ); else if( is_hex ) sscanf( buffer, "%llx", &obj->integer ); // SCNx64 -> inttypes.h else sscanf( buffer, "%lld", &obj->integer ); // SCNd64 -> inttypes.h } else { return NULL; } return p; } char *json5_parse(json5 *root, char *p, int flags) { assert(root && p); char *err_code = ""; *root = (json5) {0}; p = json5__trim(p); if( *p == '[' ) { /* <-- for SJSON */ json5__parse_value(root, p, &err_code); } else { json5__parse_object(root, p, &err_code); /* <-- for SJSON */ } return err_code[0] ? err_code : 0; } void json5_free(json5 *root) { if( root->type == json5_array && root->array ) { for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) { json5_free(root->array + i); } array_free(root->array); } if( root->type == json5_object && root->nodes ) { for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) { json5_free(root->nodes + i); } array_free(root->nodes); } *root = (json5) {0}; // needed? } void json5_write(FILE *fp, const json5 *o) { #ifdef _MSC_VER static __declspec(thread) int indent = 0; #else static THREAD_LOCAL int indent = 0; #endif static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; if( o->name ) { fprintf(fp, "%.*s\"%s\": ", indent, tabs, o->name); } /**/ if( o->type == json5_null ) fprintf(fp, "%s", "null"); else if( o->type == json5_bool ) fprintf(fp, "%s", o->boolean ? "true" : "false"); else if( o->type == json5_integer ) fprintf(fp, "%lld", o->integer); else if( o->type == json5_real ) { /**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" ); else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" ); else fprintf(fp, "%.4llf", o->real); } else if( o->type == json5_string ) { // write (escaped) string char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256]; for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i]; const char *b = o->string, *e = strpbrk(b, chars), *sep = "\""; while( e ) { fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] ); e = strpbrk( b = e + 1, chars); sep = ""; } fprintf(fp, "%s%s\"", sep, b); } else if( o->type == json5_array ) { const char *sep = ""; fprintf(fp, "%s", "[ "); for( int i = 0, cnt = o->count; i < cnt; ++i ) { fprintf(fp, "%s", sep); sep = ", "; json5_write(fp, o->array + i); } fprintf(fp, "%s", " ]"); } else if( o->type == json5_object ) { const char *sep = ""; fprintf(fp, "%.*s{\n", 0 * (++indent), tabs); for( int i = 0, cnt = o->count; i < cnt; ++i ) { fprintf(fp, "%s", sep); sep = ",\n"; json5_write(fp, o->nodes + i); } fprintf(fp, "\n%.*s}", --indent, tabs); } else { char p[16] = {0}; JSON5_ASSERT; /* "json5_error_invalid_value"; */ } } #endif // JSON5_C #ifdef JSON5_BENCH #include int main() { // https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/ char *content = 0; for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) { fseek(fp, 0L, SEEK_END); size_t pos = ftell(fp); fseek(fp, 0L, SEEK_SET); content = (char*)malloc( pos + 1 ); fread(content, 1, pos, fp); content[pos] = 0; } if( content ) { clock_t start = clock(); json5 root = {0}; char *error = json5_parse(&root, content, 0); clock_t end = clock(); double delta = ( end - start ) / (double)CLOCKS_PER_SEC; if( !error ) { printf("Parsing time: %.3fms\n", delta*1000); printf("Total nodes: %d\n", array_count(root.array)); printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string, root.array[0].nodes[1].string, root.array[0].nodes[2].string); } else { printf("Error: %s\n", error); } json5_free(&root); free(content); } } #endif #ifdef JSON5_DEMO int main() { char source5[] = " // comments\n" /* json5 sample */ " unquoted: 'and you can quote me on that',\n" " singleQuotes: 'I can use \"double quotes\" here',\n" " lineBreaks : \"Look, Mom! \\\n" "No \\n's!\",\n" " hexadecimal: 0x100,\n" " leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n" " positiveSign: +1,\n" " trailingComma: 'in objects', andIn: ['arrays', ],\n" " \"backwardsCompatible\": \"with JSON\",\n" "" " ip = \"127.0.0.1\"\n" /* sjson sample */ " port = 8888\n" "" " /* comment //nested comment*/\n" /* tests */ " // comment /*nested comment*/\n" " nil: null," " \"+lšctžýáíé=:\": true,,,," " huge: 2.2239333e5, " " array: [+1,2,-3,4,5], " " hello: 'world /*comment in string*/ //again', " " abc: 42.67, def: false, " " children : { a: 1, b: 2, }," " invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ]," "" "}\n"; json5 root = { 0 }; char *error = json5_parse(&root, source5, 0); if( error ) { printf("Error: %s\n", error); } else { json5_write(stdout, &root); } json5_free(&root); } #endif ================================================ FILE: tinylog.c ================================================ // Tiny logging utilities. rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include #ifdef _WIN32 #define TTY(ansi) "" #else #define TTY(ansi) ansi #endif #define TRACE(...) do { time_t t = time(0); printf("%s[TRACE %.8s]%s %s:%d ", TTY("\27[34m"), 11+ctime(&t), TTY("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define DEBUG(...) do { time_t t = time(0); printf("%s[DEBUG %.8s]%s %s:%d ", TTY("\27[36m"), 11+ctime(&t), TTY("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define INFO(...) do { time_t t = time(0); printf("%s[INFO %.8s]%s %s:%d ", TTY("\27[32m"), 11+ctime(&t), TTY("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define WARN(...) do { time_t t = time(0); printf("%s[WARN %.8s]%s %s:%d ", TTY("\27[33m"), 11+ctime(&t), TTY("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define ERROR(...) do { time_t t = time(0); printf("%s[ERROR %.8s]%s %s:%d ", TTY("\27[31m"), 11+ctime(&t), TTY("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define FATAL(...) do { time_t t = time(0); printf("%s[FATAL %.8s]%s %s:%d ", TTY("\27[35m"), 11+ctime(&t), TTY("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) /* int main() { FATAL("Hello %d\n", 123); ERROR("Hello %d\n", 123); WARN("Hello %d\n", 123); INFO("Hello %d\n", 123); DEBUG("Hello %d\n", 123); TRACE("Hello %d\n", 123); } */ ================================================ FILE: tinylog.h ================================================ // Tiny logging utilities. rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include #ifdef _WIN32 #define ANSI(code) "" #else #define ANSI(code) code #endif #define TRACE(...) do { time_t t = time(0); printf("%s[TRACE %.8s]%s %s:%d ", ANSI("\27[34m"), 11+ctime(&t), ANSI("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define DEBUG(...) do { time_t t = time(0); printf("%s[DEBUG %.8s]%s %s:%d ", ANSI("\27[36m"), 11+ctime(&t), ANSI("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define INFO(...) do { time_t t = time(0); printf("%s[INFO %.8s]%s %s:%d ", ANSI("\27[32m"), 11+ctime(&t), ANSI("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define WARN(...) do { time_t t = time(0); printf("%s[WARN %.8s]%s %s:%d ", ANSI("\27[33m"), 11+ctime(&t), ANSI("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define ERROR(...) do { time_t t = time(0); printf("%s[ERROR %.8s]%s %s:%d ", ANSI("\27[31m"), 11+ctime(&t), ANSI("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) #define FATAL(...) do { time_t t = time(0); printf("%s[FATAL %.8s]%s %s:%d ", ANSI("\27[35m"), 11+ctime(&t), ANSI("\27[0m"), __FILE__, __LINE__); printf(__VA_ARGS__); } while(0) /* int main() { FATAL("Hello %d\n", 123); ERROR("Hello %d\n", 123); WARN("Hello %d\n", 123); INFO("Hello %d\n", 123); DEBUG("Hello %d\n", 123); TRACE("Hello %d\n", 123); } */ ================================================ FILE: tinylogger.h ================================================ // simple logger. likely slow, though. // - rlyeh, public domain. // // - [x] colors based on content. // - [x] no logging categories. throughput flood configurable by percentage (see LOG_LEVEL var). // - [x] print fatal errors always. with optional callstack (see LOG_PRINT_STACK macro). #pragma once #include #include #include # if !defined LOG_LEVEL && (defined NDEBUG || defined SHIPPING) #define LOG_LEVEL 0 // 0% - no messages logged (only fatal errors will) #elif !defined LOG_LEVEL #define LOG_LEVEL 100 // 100% - all messages logged #endif #ifndef LOG_TIMER #define LOG_TIMER() 0.0 /* user-defined apptime clock here */ #endif #ifndef LOG_PRINT_STACK #define LOG_PRINT_STACK() /* user-defined callstack printer here */ #endif # if !defined LOG_ENABLE_ANSI && defined _WIN32 #include #define LOG_ENABLE_ANSI() for(unsigned mode=0;!mode;mode=1) SetConsoleMode(GetStdHandle(-11), (GetConsoleMode(GetStdHandle(-11), &mode), mode|4)) #elif !defined LOG_ENABLE_ANSI #define LOG_ENABLE_ANSI() #endif #define LOG(...) do { \ static char must_log = -1; \ static enum { undefined=-1, restore=0, r=31,g,y,b,p,t,w,inv=0x80 } color = undefined; \ char buf[2048]; snprintf(buf, 2048, "" __VA_ARGS__); \ if( color == undefined ) { \ LOG_ENABLE_ANSI(); \ char low[2048]; int i; for(i=0;buf[i];++i) low[i] = 32|buf[i]; low[i]='\0'; \ /**/ if( strstr(low,"fatal")|| strstr(low,"panic") || strstr(low,"assert") ) color=r|inv,must_log=1; \ else if( strstr(low,"fail") || strstr(low,"error") ) color=r; \ else if( strstr(low,"warn") || strstr(low,"alert") ) color=y; /*beware,caution*/ \ else if( strstr(low,"info") || strstr(low,"succe") ) color=g; /*ok, no error*/ \ else if( strstr(low,"debug") ) color=t; \ else if( strstr(low,"trace") ) color=p; else color=restore; \ if( must_log < 0 ) { /* original splitmix64 by Sebastiano Vigna (CC0)*/ \ uint64_t z = (__LINE__ + __COUNTER__ + UINT64_C(0x9E3779B97F4A7C15)); \ z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); \ must_log = (unsigned)((z ^ (z >> 31)) % 100) < LOG_LEVEL; } } \ if( must_log ) { \ double timer = LOG_TIMER(); \ if(color&0x80) fprintf(stderr, "\033[7m"); \ if(timer>0)fprintf(stderr, "\033[%dm%07.3fs %s (%s:%d)", color&0x7f, timer, buf, __FILE__, __LINE__); \ else fprintf(stderr, "\033[%dm%s (%s:%d)", color&0x7f, buf, __FILE__, __LINE__); \ fprintf(stderr, "\033[%dm\n", restore); \ if(color&0x7f == r) LOG_PRINT_STACK(); } \ } while(0) #if 0 // demo int main() { LOG("Test 1 - normal message: hello world %d", 123); LOG("Test 2 - trace message"); LOG("Test 3 - debug message"); LOG("Test 4 - info message"); LOG("Test 5 - warning message"); LOG("Test 6 - error message"); LOG("Test 7 - fatal error message (fatal errors are always printed, despite LOG_LEVEL var)"); } #endif ================================================ FILE: tinylogger.hpp ================================================ // Tiny session logger. rlyeh, public domain | wtrmrkrlyeh #pragma once #include #ifdef SHIPPING struct logger { logger() { fclose(stdout); } }; #else struct logger { logger() { # if defined(PSVITA) freopen("host0://log_vita.txt", "a+t", stdout); # elif defined(PS3) freopen("/app_home/log_ps3.txt", "a+t", stdout); # elif defined(PS4) freopen("/hostapp/log_ps4.txt", "a+t", stdout); # else freopen("log_desktop.txt", "a+t", stdout); # endif // Flush automatically every 16 KiB from now setvbuf(stdout, NULL, _IOFBF, 16 * 1024); // Header puts(";; New session"); fflush(stdout); } ~logger() { fflush(stdout); } }; #endif /* int main() { logger resident; puts("hello world"); } */ ================================================ FILE: tinymatch.c ================================================ // tiny wildcard/pattern matching. Based on anonymous souce code (Rob Pike's ?). // - rlyeh. public domain | wtrmrkrlyeh static int match( const char *pattern, const char *str ) { if( *pattern=='\0' ) return !*str; if( *pattern=='*' ) return match(pattern+1, str) || (*str && match(pattern, str+1)); if( *pattern=='?' ) return *str && (*str != '.') && match(pattern+1, str+1); return (*str == *pattern) && match(pattern+1, str+1); } /* #include int main() { printf("%s\n", match("abc", "abc") ? "match!" : "not found" ); printf("%s\n", match("abc*", "abc") ? "match!" : "not found" ); printf("%s\n", match("*bc", "abc") ? "match!" : "not found" ); printf("%s\n", match("*bc*", "abc") ? "match!" : "not found" ); printf("%s\n", match("*b?d*", "abcdef") ? "match!" : "not found" ); } */ ================================================ FILE: tinymime.c ================================================ // tinymime. ported from https://github.com/sindresorhus/file-type (source is mit licensed) // - rlyeh, public domain #pragma once static const char *tinymime( const unsigned char *buf, size_t len ) { if( !(buf && len > 60) ) { return ""; // invalid } if (buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF) { return "jpg"; } if (buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47) { return "png"; } if (buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46) { return "gif"; } if (buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50) { return "webp"; } // needs to be before `tif` check if (((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) && buf[8] == 0x43 && buf[9] == 0x52) { return "cr2"; } if ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) { return "tif"; } if (buf[0] == 0x42 && buf[1] == 0x4D) { return "bmp"; } if (buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0xBC) { return "jxr"; } if (buf[0] == 0x38 && buf[1] == 0x42 && buf[2] == 0x50 && buf[3] == 0x53) { return "psd"; } // needs to be before `zip` check if (buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x3 && buf[3] == 0x4 && buf[30] == 0x6D && buf[31] == 0x69 && buf[32] == 0x6D && buf[33] == 0x65 && buf[34] == 0x74 && buf[35] == 0x79 && buf[36] == 0x70 && buf[37] == 0x65 && buf[38] == 0x61 && buf[39] == 0x70 && buf[40] == 0x70 && buf[41] == 0x6C && buf[42] == 0x69 && buf[43] == 0x63 && buf[44] == 0x61 && buf[45] == 0x74 && buf[46] == 0x69 && buf[47] == 0x6F && buf[48] == 0x6E && buf[49] == 0x2F && buf[50] == 0x65 && buf[51] == 0x70 && buf[52] == 0x75 && buf[53] == 0x62 && buf[54] == 0x2B && buf[55] == 0x7A && buf[56] == 0x69 && buf[57] == 0x70) { return "epub"; } // needs to be before `zip` check // assumes signed .xpi from addons.mozilla.org if (buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x3 && buf[3] == 0x4 && buf[30] == 0x4D && buf[31] == 0x45 && buf[32] == 0x54 && buf[33] == 0x41 && buf[34] == 0x2D && buf[35] == 0x49 && buf[36] == 0x4E && buf[37] == 0x46 && buf[38] == 0x2F && buf[39] == 0x6D && buf[40] == 0x6F && buf[41] == 0x7A && buf[42] == 0x69 && buf[43] == 0x6C && buf[44] == 0x6C && buf[45] == 0x61 && buf[46] == 0x2E && buf[47] == 0x72 && buf[48] == 0x73 && buf[49] == 0x61) { return "xpi"; } if (buf[0] == 0x50 && buf[1] == 0x4B && (buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) && (buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)) { return "zip"; } if( len > 261 ) if (buf[257] == 0x75 && buf[258] == 0x73 && buf[259] == 0x74 && buf[260] == 0x61 && buf[261] == 0x72) { return "tar"; } if (buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 && buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 && (buf[6] == 0x0 || buf[6] == 0x1)) { return "rar"; } if (buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x8) { return "gz"; } if (buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68) { return "bz2"; } if (buf[0] == 0x37 && buf[1] == 0x7A && buf[2] == 0xBC && buf[3] == 0xAF && buf[4] == 0x27 && buf[5] == 0x1C) { return "7z"; } if (buf[0] == 0x78 && buf[1] == 0x01) { return "dmg"; } if ( (buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && (buf[3] == 0x18 || buf[3] == 0x20) && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70) || (buf[0] == 0x33 && buf[1] == 0x67 && buf[2] == 0x70 && buf[3] == 0x35) || (buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x1C && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 && buf[8] == 0x6D && buf[9] == 0x70 && buf[10] == 0x34 && buf[11] == 0x32 && buf[16] == 0x6D && buf[17] == 0x70 && buf[18] == 0x34 && buf[19] == 0x31 && buf[20] == 0x6D && buf[21] == 0x70 && buf[22] == 0x34 && buf[23] == 0x32 && buf[24] == 0x69 && buf[25] == 0x73 && buf[26] == 0x6F && buf[27] == 0x6D) || (buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x1C && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 && buf[8] == 0x69 && buf[9] == 0x73 && buf[10] == 0x6F && buf[11] == 0x6D) || (buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x1c && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 && buf[8] == 0x6D && buf[9] == 0x70 && buf[10] == 0x34 && buf[11] == 0x32 && buf[12] == 0x0 && buf[13] == 0x0 && buf[14] == 0x0 && buf[15] == 0x0) ) { return "mp4"; } if ((buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x1C && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 && buf[8] == 0x4D && buf[9] == 0x34 && buf[10] == 0x56)) { return "m4v"; } if (buf[0] == 0x4D && buf[1] == 0x54 && buf[2] == 0x68 && buf[3] == 0x64) { return "mid"; } // needs to be before the `webm` check if (buf[31] == 0x6D && buf[32] == 0x61 && buf[33] == 0x74 && buf[34] == 0x72 && buf[35] == 0x6f && buf[36] == 0x73 && buf[37] == 0x6B && buf[38] == 0x61) { return "mkv"; } if (buf[0] == 0x1A && buf[1] == 0x45 && buf[2] == 0xDF && buf[3] == 0xA3) { return "webm"; } if (buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x14 && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70) { return "mov"; } if (buf[0] == 0x52 && buf[1] == 0x49 && buf[2] == 0x46 && buf[3] == 0x46 && buf[8] == 0x41 && buf[9] == 0x56 && buf[10] == 0x49) { return "avi"; } if (buf[0] == 0x30 && buf[1] == 0x26 && buf[2] == 0xB2 && buf[3] == 0x75 && buf[4] == 0x8E && buf[5] == 0x66 && buf[6] == 0xCF && buf[7] == 0x11 && buf[8] == 0xA6 && buf[9] == 0xD9) { return "wmv"; } if (buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x1 && (buf[3] >> 4) == 0xb ) { // buf[3].toString(16)[0] === 'b') { return "mpg"; } if ((buf[0] == 0x49 && buf[1] == 0x44 && buf[2] == 0x33) || (buf[0] == 0xFF && buf[1] == 0xfb)) { return "mp3"; } if ((buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 && buf[8] == 0x4D && buf[9] == 0x34 && buf[10] == 0x41) || (buf[0] == 0x4D && buf[1] == 0x34 && buf[2] == 0x41 && buf[3] == 0x20)) { return "m4a"; } // needs to be before `ogg` check if (buf[28] == 0x4F && buf[29] == 0x70 && buf[30] == 0x75 && buf[31] == 0x73 && buf[32] == 0x48 && buf[33] == 0x65 && buf[34] == 0x61 && buf[35] == 0x64) { return "opus"; } if (buf[0] == 0x4F && buf[1] == 0x67 && buf[2] == 0x67 && buf[3] == 0x53) { return "ogg"; } if (buf[0] == 0x66 && buf[1] == 0x4C && buf[2] == 0x61 && buf[3] == 0x43) { return "flac"; } if (buf[0] == 0x52 && buf[1] == 0x49 && buf[2] == 0x46 && buf[3] == 0x46 && buf[8] == 0x57 && buf[9] == 0x41 && buf[10] == 0x56 && buf[11] == 0x45) { return "wav"; } if (buf[0] == 0x23 && buf[1] == 0x21 && buf[2] == 0x41 && buf[3] == 0x4D && buf[4] == 0x52 && buf[5] == 0x0A) { return "amr"; } if (buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46) { return "pdf"; } if (buf[0] == 0x4D && buf[1] == 0x5A) { return "exe"; } if ((buf[0] == 0x43 || buf[0] == 0x46) && buf[1] == 0x57 && buf[2] == 0x53) { return "swf"; } if (buf[0] == 0x7B && buf[1] == 0x5C && buf[2] == 0x72 && buf[3] == 0x74 && buf[4] == 0x66) { return "rtf"; } if ( (buf[0] == 0x77 && buf[1] == 0x4F && buf[2] == 0x46 && buf[3] == 0x46) && ( (buf[4] == 0x00 && buf[5] == 0x01 && buf[6] == 0x00 && buf[7] == 0x00) || (buf[4] == 0x4F && buf[5] == 0x54 && buf[6] == 0x54 && buf[7] == 0x4F) ) ) { return "woff"; } if ( (buf[0] == 0x77 && buf[1] == 0x4F && buf[2] == 0x46 && buf[3] == 0x32) && ( (buf[4] == 0x00 && buf[5] == 0x01 && buf[6] == 0x00 && buf[7] == 0x00) || (buf[4] == 0x4F && buf[5] == 0x54 && buf[6] == 0x54 && buf[7] == 0x4F) ) ) { return "woff2"; } if ( (buf[34] == 0x4C && buf[35] == 0x50) && ( (buf[8] == 0x00 && buf[9] == 0x00 && buf[10] == 0x01) || (buf[8] == 0x01 && buf[9] == 0x00 && buf[10] == 0x02) || (buf[8] == 0x02 && buf[9] == 0x00 && buf[10] == 0x02) ) ) { return "eot"; } if (buf[0] == 0x00 && buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x00 && buf[4] == 0x00) { return "ttf"; } if (buf[0] == 0x4F && buf[1] == 0x54 && buf[2] == 0x54 && buf[3] == 0x4F && buf[4] == 0x00) { return "otf"; } if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01 && buf[3] == 0x00) { return "ico"; } if (buf[0] == 0x46 && buf[1] == 0x4C && buf[2] == 0x56 && buf[3] == 0x01) { return "flv"; } if (buf[0] == 0x25 && buf[1] == 0x21) { return "ps"; } if (buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00) { return "xz"; } if (buf[0] == 0x53 && buf[1] == 0x51 && buf[2] == 0x4C && buf[3] == 0x69) { return "sqlite"; } if (buf[0] == 0x4E && buf[1] == 0x45 && buf[2] == 0x53 && buf[3] == 0x1A) { return "nes"; } if (buf[0] == 0x43 && buf[1] == 0x72 && buf[2] == 0x32 && buf[3] == 0x34) { return "crx"; } if ( (buf[0] == 0x4D && buf[1] == 0x53 && buf[2] == 0x43 && buf[3] == 0x46) || (buf[0] == 0x49 && buf[1] == 0x53 && buf[2] == 0x63 && buf[3] == 0x28) ) { return "cab"; } // needs to be before `ar` check if (buf[0] == 0x21 && buf[1] == 0x3C && buf[2] == 0x61 && buf[3] == 0x72 && buf[4] == 0x63 && buf[5] == 0x68 && buf[6] == 0x3E && buf[7] == 0x0A && buf[8] == 0x64 && buf[9] == 0x65 && buf[10] == 0x62 && buf[11] == 0x69 && buf[12] == 0x61 && buf[13] == 0x6E && buf[14] == 0x2D && buf[15] == 0x62 && buf[16] == 0x69 && buf[17] == 0x6E && buf[18] == 0x61 && buf[19] == 0x72 && buf[20] == 0x79) { return "deb"; } if (buf[0] == 0x21 && buf[1] == 0x3C && buf[2] == 0x61 && buf[3] == 0x72 && buf[4] == 0x63 && buf[5] == 0x68 && buf[6] == 0x3E) { return "ar"; } if (buf[0] == 0xED && buf[1] == 0xAB && buf[2] == 0xEE && buf[3] == 0xDB) { return "rpm"; } if ( (buf[0] == 0x1F && buf[1] == 0xA0) || (buf[0] == 0x1F && buf[1] == 0x9D) ) { return "z"; } if (buf[0] == 0x4C && buf[1] == 0x5A && buf[2] == 0x49 && buf[3] == 0x50) { return "lz"; } if (buf[0] == 0xD0 && buf[1] == 0xCF && buf[2] == 0x11 && buf[3] == 0xE0 && buf[4] == 0xA1 && buf[5] == 0xB1 && buf[6] == 0x1A && buf[7] == 0xE1) { return "msi"; } return ""; // invalid }; /* #include int main( int argc, const char **argv ) { if( argc == 2 ) { FILE *fp = fopen( argv[1], "rb" ); if( fp ) { char buf[512]; int len = fread(buf, 1, 512, fp); puts( tinymime(buf, len) ); fclose(fp); } } } */ ================================================ FILE: tinypipe.hpp ================================================ // tiny chainable pipes (C++11) // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include template< typename T, typename istream > void copy( T &s, const istream &is ) { std::stringstream ss; std::streamsize at = is.rdbuf()->pubseekoff(0,is.cur); ss << is.rdbuf(); is.rdbuf()->pubseekpos(at); auto x = ss.str(); s.assign( x.begin(), x.end() ); } template< typename istream, typename ostream, typename container = std::vector > bool pipe( const istream &is, ostream &os, const std::vector< int (*)(const char *, int, char *, int) > &vec = std::vector< int (*)(const char *, int, char *, int) >() ) { container src, dst; container *A = &src, *B = &dst, *C; copy( src, is ); if( !is.good() ) { return false; } for( auto &fn : vec ) { auto bounds = fn( (const char *)&(*A)[0],A->size(),0,B->size() ); B->resize( bounds ); int wlen = fn( (const char *)&(*A)[0], A->size(), &(*B)[0], B->size() ); if( wlen >= 0 ) { B->resize( wlen ); C = B, B = A, A = C; } else return false; } os.write( &(*A)[0], (*A).size() ); return os.good(); } /* int rot13ish( const char *src, int slen, char *dst, int dlen ) { if( !dst ) return slen * 2; // bounds char *bak = dst; const char *p = src, *e = src + slen; while( p < e ) *dst++ = (*p & 0xf0) | (*p & 0x0f) ^ 0x7, ++p; return dst - bak; } int upper( const char *src, int slen, char *dst, int dlen ) { if( !dst ) return slen * 2; // bounds char *bak = dst; const char *p = src, *e = src + slen; size_t len = e - p; while( p < e ) *dst++ = (*p >= 'a' ? *p - 'a' + 'A' : *p), ++p; return dst - bak; } int lower( const char *src, int slen, char *dst, int dlen ) { if( !dst ) return slen * 2; // bounds char *bak = dst; const char *p = src, *e = src + slen; size_t len = e - p; while( p < e ) *dst++ = (*p >= 'A' ? *p - 'A' + 'a' : *p), ++p; return dst - bak; } int noparens( const char *src, int slen, char *dst, int dlen ) { if( !dst ) return slen * 2; // bounds char *bak = dst; const char *p = src, *e = src + slen; while( p < e ) { if( *p != '(' && *p != ')' ) *dst++ = *p++; else *p++; } return dst - bak; } int numberx2( const char *src, int slen, char *dst, int dlen ) { if( !dst ) return slen * 2; // bounds char *bak = dst; const char *p = src, *e = src + slen; while( p < e ) { bool n = *p >= '0' && *p <= '9'; dst[0]=dst[n]=*p++, dst+=n+1; } return dst - bak; } int l33t( const char *src, int slen, char *dst, int dlen ) { if( !dst ) return slen * 2; // bounds char *bak = dst; const char *p = src, *e = src + slen; while(p < e) switch(*p++) { default: *dst++ = p[-1]; break; case 'O': *dst++ = '0'; break; case 'T': *dst++ = '7'; break; case 'E': *dst++ = '3'; break; case 'L': *dst++ = '1'; break; case 'I': *dst++ = '1'; break; case 'A': *dst++ = '4'; } return dst - bak; } #include #include #include int main() { std::stringstream is; is << "hello"; pipe( is, std::cout ); std::ifstream ifs{__FILE__}; pipe( ifs, std::cout, { rot13ish, rot13ish } ); pipe( ifs, std::cout, { upper, l33t, lower, numberx2, noparens } ); } */ ================================================ FILE: tinyprint.cc ================================================ // Tiny printer. Original code by Evan Wallace. rlyeh, public domain | wtrmrkrlyeh #include struct print { const char *sep = ""; ~print() { std::cout << std::endl; } template print& operator,( const T &t ) { return std::cout << sep << t, sep = " ", *this; } }; #define print print(), /* int main() { print "hello", 123, "world"; print "yeah"; } */ ================================================ FILE: tinypulse.c ================================================ // Tiny digital pulses/signals. rlyeh, public domain | wtrmrkrlyeh #include void pulse( int *state ) { switch( state[1] * 2 + state[0] ) { default: break; case 0: puts("pulse OFF"); break; case 1: puts("pulse UP"); break; case 2: puts("pulse DOWN"); break; case 3: puts("pulse ON"); } state[1] = state[0]; } /* int main() { int state[2] = {0}; *state = 0; pulse(state); *state = 1; pulse(state); *state = 1; pulse(state); *state = 0; pulse(state); *state = 0; pulse(state); } */ ================================================ FILE: tinyroman.cc ================================================ // Tiny integer to roman numerals converter (roughly tested). rlyeh, public domain | wtrmrkrlyeh #include #include std::string romanize( int i ) { static std::string table[] = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M", "MM", "MMM", "MMMM", }; std::string out; for( int base = 0; i > 0; base++, i /= 10 ) { int mod = i % 10; if( mod > 0 ) { out = table[(mod - 1) + base * 9] + out; } } return out; } /* #include int main() { assert( romanize(0) == "" ); assert( romanize(10) == "X" ); assert( romanize(1990) == "MCMXC" ); assert( romanize(2008) == "MMVIII" ); assert( romanize(99) == "XCIX" ); assert( romanize(47) == "XLVII" ); } */ ================================================ FILE: tinystring.c ================================================ // C string library // - rlyeh, public domain // temporary strings api (stack) char* strtmp(const char *fmt, ...); int stristmp(const char *s); // allocated strings api (heap) #define strnew(fmt, ...) strdup(strtmp(fmt,__VA_ARGS__)) #define strdel(s) ((stristmp(s) ? (void)0 : free(s)), (s)=0) // implementation -------------------------------------------------------------- #if defined _MSC_VER && !defined __thread #define __thread __declspec(thread) #endif #include #include #include #include #include #include int stristmp(const char *s) { // is_va() return (1&(uintptr_t)s) && s[-1] == 0; } char* strtmp(const char *fmt, ...) { // va() va_list vl; va_start(vl, fmt); int sz = vsnprintf( 0, 0, fmt, vl ) + 1; va_end(vl); int reqlen = sz + 1; // 1 for even padding char* ptr; enum { STACK_ALLOC = 16384 }; if( reqlen < STACK_ALLOC ) { // fit stack? static __thread char buf[STACK_ALLOC+1]; static __thread int cur = 1, len = STACK_ALLOC; ptr = buf + ((cur+reqlen) > len ? cur = 1 : (cur += reqlen) - reqlen); } else { // else use heap (@fixme: single memleak per invoking thread) static __thread char *buf = 0; static __thread int cur = 1, len = -STACK_ALLOC; if( reqlen >= len ) buf = realloc(buf, len = abs(len) * 1.75 + reqlen); ptr = buf + ((cur+reqlen) > len ? cur = 1 : (cur += reqlen) - reqlen); } ptr += !(1&(uintptr_t)ptr); // align to even address only when already odd ptr[-1] = 0; // add header assert(stristmp(ptr)); va_start(vl,fmt); vsnprintf( ptr, sz, fmt, vl ); va_end(vl); return (char *)ptr; } /* int main() { // creation char *x = strtmp("hello %d", 123); puts(x); char *y = strnew("hello %d", 123); puts(y); assert(strcmp(x,y)==0); // destruction // strdel(x); assert(x == 0); // optional free, since x is a temporary string (strtmp) strdel(y); assert(y == 0); // required free, since y was explicitly allocated (with strnew) // test concat char *z = strtmp("%d",6); z = strtmp("%s%s%s",z,z,z); assert( 0 == strcmp(z,"666") ); // test memory is never exhausted for(int i = 0; i < 10000; ++i) assert(strtmp("hello %d",123)); // test asserts are enabled assert(~puts("Ok")); } */ ================================================ FILE: tinystring.cc ================================================ // tiny C++ string utilities // - rlyeh, public domain | wtrmrkrlyeh // @toadd: pad, left, right, center, triml, trimr, trim, [-1] #include #include std::deque< std::string > tokenize( const std::string &self, const char *delimiters ) { char map[256] = {}; while( *delimiters++ ) map[ (unsigned char) delimiters[-1] ] = '\1'; std::deque< std::string > tokens(1); for( auto &ch : self ) { /**/ if( !map[(unsigned char)ch] ) tokens.back().push_back( ch ); else if( tokens.back().size() ) tokens.push_back( std::string() ); } while( tokens.size() && !tokens.back().size() ) tokens.pop_back(); return tokens; } std::deque< std::string > split( const std::string &self, const std::string &delimiters ) { std::string str; std::deque< std::string > tokens; for( auto &ch : self ) { if( delimiters.find_first_of( ch ) != std::string::npos ) { if( str.size() ) tokens.push_back( str ), str = ""; tokens.push_back( std::string() + ch ); } else str += ch; } return str.empty() ? tokens : ( tokens.push_back( str ), tokens ); } std::string left_of( const std::string &substring, const std::string &self ) { std::string::size_type pos = self.find( substring ); return pos == std::string::npos ? self : self.substr(0, pos); } std::string right_of( const std::string &substring, const std::string &self ) { std::string::size_type pos = self.find( substring ); return pos == std::string::npos ? self : self.substr(pos + substring.size() ); } std::string replace_one( const std::string &self, const std::string &target, const std::string &replacement ) { std::string str = self; auto found = str.find(target); return found == std::string::npos ? str : (str.replace(found, target.length(), replacement), str); } std::string replace_all( const std::string &self, const std::string &target, const std::string &replacement ) { size_t found = 0; std::string s = self; while( ( found = s.find( target, found ) ) != std::string::npos ) { s.replace( found, target.length(), replacement ); found += replacement.length(); } return s; } /* #include int main() { assert( tokenize("a/b/c/\\d\\e,f,g,", "/\\,") == (std::deque { "a", "b", "c", "d", "e", "f", "g" }) ); assert( split("a/b/c/\\d\\e,f,g,", "/\\,") == (std::deque { "a", "/", "b", "/", "c", "/", "\\", "d", "\\", "e", ",", "f", ",", "g", "," }) ); assert( left_of("beginning", "in the beginning") == "in the " ); assert( right_of("in the ", "in the beginning") == "beginning" ); assert( replace_all( "0cad0", "0", "abra" ) == "abracadabra" ); assert( replace_one( "0cad0", "0", "abra" ) == "abracad0" ); } */ ================================================ FILE: tinytga.c ================================================ // Tiny TGA writer: original code by jon olick, public domain // Elder C version by rlyeh, public domain | wtrmrkrlyeh #include static void tinytga(FILE *fp, void *rgba, int width, int height, int numChannels) { // Swap RGBA to BGRA if using 3 or more channels int x, i, y, j, bpc = numChannels * 8; // 8 bits per channel int remap[4] = {numChannels >= 3 ? 2 : 0, 1, numChannels >= 3 ? 0 : 2, 3}; char *s = (char *)rgba; // Header fwrite("\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, 1, fp); fwrite(&width, 2, 1, fp); fwrite(&height, 2, 1, fp); fwrite(&bpc, 2, 1, fp); for(y = height-1; y >= 0; --y) { i = (y * width) * numChannels; for(x = i; x < i+width*numChannels; x += numChannels) { for(j = 0; j < numChannels; ++j) { fputc(s[x+remap[j]], fp); } } } } ================================================ FILE: tinytime.cc ================================================ // Tiny timing utilities. rlyeh, public domain | wtrmrkrlyeh #include #include #if !defined(TIMING_USE_OMP) && ( defined(USE_OMP) || defined(_MSC_VER) /*|| defined(__ANDROID_API__)*/ ) # define TIMING_USE_OMP # include #endif double now() { # ifdef TIMING_USE_OMP static auto const epoch = omp_get_wtime(); return omp_get_wtime() - epoch; # else static auto const epoch = std::chrono::steady_clock::now(); // milli ms > micro us > nano ns return std::chrono::duration_cast< std::chrono::microseconds >( std::chrono::steady_clock::now() - epoch ).count() / 1000000.0; # endif } double bench( void (*fn)() ) { double took = -now(); return ( fn(), took + now() ); } void sleep( double secs ) { std::chrono::microseconds duration( (int)(secs * 1000000) ); std::this_thread::sleep_for( duration ); } /* #include int main() { double t0 = now(); printf("%g s.\n", bench( []{ sleep(0.1234); } ) ); double t1 = now(); printf("%g s.\n", t1 - t0 ); } */ ================================================ FILE: tinytodo.c ================================================ // Tiny todo() static assert macro. based on code by https://github.com/andyw8/do_by // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include static inline int TODO(const char *DT) { return /*D*/ ( DT[4] == ' ' ? 0 : (DT[4] - '0') * 10 ) + (DT[5] - '0') + /*M*/ ( DT[2] == 'n' ? 1 : DT[2] == 'b' ? 2 : DT[2] == 'r' && DT[0] == 'M' ? 3 : DT[2] == 'r' && DT[0] != 'M' ? 4 : DT[2] == 'y' ? 5 : DT[2] == 'n' ? 6 : DT[2] == 'l' ? 7 : DT[2] == 'g' ? 8 : DT[2] == 'p' ? 9 : DT[2] == 't' ? 10 : DT[2] == 'v' ? 11 : 12 ) * 100 + /*Y*/ (DT[7] - '0') * 1e7 + (DT[8] - '0') * 1e6 + (DT[9] - '0') * 1e5 + (DT[10] - '0') * 1e4; } #define TODO(DT) do { static int ever = 0; if(!ever) { ever = 1; if( TODO(__DATE__) >= TODO(DT) ) fprintf(stderr, "TODO date expired! %s\n", DT); } } while(0) /* int main() { TODO( "Aug 11 2011: Finish prototype. Warn if current date >= Aug 11 2011" ); TODO( "May 19 2014: Finish gameplay. Warn if current date >= May 19 2014" ); TODO( "Sep 26 2015: Finish QA. Warn if current date >= Sep 26 2015" ); } */ ================================================ FILE: tinytty.c ================================================ // Tiny terminal utilities. rlyeh, public domain | wtrmrkrlyeh #include #ifdef _WIN32 #define ANSI(ansi, win) win #else #define ANSI(ansi, win) ansi #endif // A few unicode characters and ANSI colors static const char *tick = ANSI("\u2713", "[v]"), *cross = ANSI("\u2717", "[x]"), *arrow = ANSI("\u2794", "[>]"); static const char *red = ANSI("\27[31m",""), *green = ANSI("\27[32m",""), *blue = ANSI("\27[34m",""); static const char *yellow = ANSI("\27[33m",""), *magenta = ANSI("\27[35m",""), *cyan = ANSI("\27[36m",""); static const char *end = ANSI("\27[0m",""); // 256-color terminal void tty256( unsigned char r, unsigned char g, unsigned char b ) { ANSI(printf("\033[38;5;%dm", (r/51)*36+(g/51)*6+(b/51)+16), (void)0); } // terminal writer w/ console width clamping #ifdef _WIN32 #include #include #endif void tty( const char *txt ) { #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO c; if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &c) ) { int len = strlen(txt), w = c.srWindow.Right-c.srWindow.Left-c.dwCursorPosition.X; printf("%.*s%s\n", len > w ? w - 3 : w, txt, len > w ? "..." : "" ); return; } #endif puts( txt ); } /* int main() { // usage: printf("%s%s%s%s%s\n", green, tick, yellow, " passed", end); // or: tty256( 255, 192, 0 ); printf("%s\n", "256 colors"); // also: tty( "DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! " ); // // more tests: // tty( "hey" ); // printf("%s", "[test] "); // tty( "DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! DECREASE WINDOW WIDTH AND RUN THE APP AGAIN! " ); } */ ================================================ FILE: tinyuniso.cc ================================================ // tiny iso/9660 unarchiver. [ref] http://wiki.osdev.org/ISO_9660 // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include #include #include template bool tinyuniso( istream &is, const FN &yield ) { // directory record struct dir_t { uint8_t flags; uint64_t offset; uint64_t length; uint16_t parent; union { uint8_t st[7]; struct { uint8_t Y,M,D,h,m,s,GMT; }; }; std::string name; } root = {}; // unpack data (raw) auto unpack_raw = [&]( int l, void *ptr ) { if( !ptr ) is.ignore( l ); else is.read( (char *)ptr, l ); }; // unpack directory record auto unpack_record = [&]( dir_t *out ) -> uint8_t { auto get_max_of = []( int a, int b ) { return a < b ? b : a; }; auto unpack_16L = [&]( uint32_t *t ) { unpack_raw(4, t); if(t) *t &= (uint16_t)(-1); }; auto unpack_32L = [&]( uint64_t *t ) { unpack_raw(8, t); if(t) *t &= (uint32_t)(-1); }; auto unpack_str = [&]( int l, std::string *s ) { s->resize( l ); unpack_raw( l, &(*s)[0] ); while( !s->empty() && s->back() == ' ' ) s->pop_back(); // rstrip(' ') *s = s->substr( 0, s->find_first_of(';') ); // left_of(';') for( auto &ch : *s ) ch -= 32 * ( ch >= 'a' && ch <= 'z' ); // upper() }; uint8_t len0, len1; unpack_raw( 1, &len0 ); if( len0 ) { unpack_raw( 1, 0 ); unpack_32L( &(out->offset) ); unpack_32L( &(out->length) ); unpack_raw( 7, &(out->st) ); unpack_raw( 1, &(out->flags) ); unpack_raw( 1+1+4, 0 ); unpack_raw( 1, &len1 ); unpack_str( len1, &(out->name) ); unpack_raw( !(len1 % 2), 0); unpack_raw( get_max_of(0, len0 - (34 + len1 - (len1 % 2))), 0 ); } else len0 = 1, *out = dir_t(); return len0; }; // retrieve partial contents auto get_sector = [&]( int64_t sector, int64_t length ) { return is.seekg( sector*2048, is.beg ), is.good(); }; // retrieve whole file contents auto get_file = [&]( char *ptr, const dir_t &f ) { get_sector( f.offset, f.length ); unpack_raw( f.length, ptr ); }; // parse volume descriptors int sector = 0x10; while( get_sector(sector++, 2048) ) { uint8_t typecode; unpack_raw(1, &typecode); /**/ if( typecode == 255 ) break; else if( typecode == 0x1 ) { // skip most of primary volume descriptor std::string id(5, '\0'); unpack_raw(5, &id[0]); if(id != "CD001") return false; unpack_raw(1+1+32+32+8+8+32+4+4+4+8+4+4+4+4, 0); unpack_record( &root ); break; } else return false; } // recurse directory structure using fn = std::function; fn tree = [&] ( const dir_t &node, const char *parent ) { std::vector list; // unpack dir auto sector = node.offset; auto read = 0; get_sector(sector, 2048); dir_t self; read += unpack_record( &self ); dir_t prev; read += unpack_record( &prev ); // iterate children while( read < self.length ) { if( read % 2048 == 0) { get_sector(++sector, 2048); } dir_t data; read += unpack_record( &data ); if( data.name.empty() ) { // if end of directory auto skip = 2048 - (read % 2048); unpack_raw(skip, 0); read += skip; } else { list.push_back( data ); } } // iterate listing: recurse if dir, else yield file for( auto &c : list ) { if( c.flags & 2 ) { tree( c, (node.name + "/").c_str() ); } else { std::string fname = std::string("/") + parent + node.name + "/" + c.name; char buffer[128] = {}; auto offset = (int)(c.GMT) * 15 * 60; // offset in seconds from GMT in 15min intervals sprintf(buffer, "%d/%02d/%02d %02d:%02d:%02d%c%d", 1900+c.Y, c.M, c.D, c.h, c.m, c.s, offset >= 0 ? '+':'-', offset ); if( char *ptr = yield( fname, c.length, buffer, c.offset ) ) { get_file( ptr, c ); } } } }; tree(root, ""); return is.good(); } /* #include #include int main( int argc, const char **argv ) { if( argc <= 1 ) { return std::cout << "Usage: " << argv[0] << " file.iso [path]" << std::endl, -1; } std::string read; std::ifstream ifs( argv[1], std::ios::binary ); bool ok = ifs.good() && tinyuniso( ifs, [&]( const std::string &path, uint64_t size, const char *stamp, uint64_t offset ) { // .csv list std::cout << "'" << path << "'," << size << ",'" << stamp << "'" << std::endl; // read file (if provided) if( argc > 2 && path == argv[2] ) { read.resize( size ); return &read[0]; } return (char *)0; } ); if( !read.empty() ) std::cout << read << std::endl; return ok; } */ ================================================ FILE: tinyunit.c ================================================ // Tiny unittest suite. rlyeh, public domain | wtrmrkrlyeh #include #include #include #define suite(...) if(printf("------ " __VA_ARGS__),puts(""),1) #define test(...) (errno=0,++tst,err+=!(ok=!!(__VA_ARGS__))),printf("[%s] L%d %s (%s)\n",ok?" OK ":"FAIL",__LINE__,#__VA_ARGS__,strerror(errno)) static unsigned tst=0,err=0,ok=1; static void summary(void){ suite("summary"){ printf("[%s] %d tests = %d passed + %d errors\n",err?"FAIL":" OK ",tst,tst-err,err); }; } /* int main() { atexit( summary ); // orphan test test(1<2); // grouped tests suite("grouped tests %d/%d", 1,1) { test(1<2); test(1<2); } suite("grouped tests %d/%d", 1,2) { test(1<2); test(1<2); } } */ ================================================ FILE: tinyuntar.cc ================================================ // portable gnu tar and ustar extraction // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include template bool tinyuntar( istream &is, const FN &yield ) { enum { name = 0, // (null terminated) mode = 100, // (octal) uid = 108, // (octal) gid = 116, // (octal) size = 124, // (octal) modtime = 136, // (octal) checksum = 148, // (octal) type = 156, // \0|'0':file,1:hardlink,2:symlink,3:chardev,4:blockdev,5:dir,6:fifo,L:longnameblocknext linkname = 157, // if !ustar link indicator magic = 257, // if ustar "ustar" -- 6th character may be space or null, else zero version = 263, // if ustar "00", else zero uname = 265, // if ustar owner username, else zero gname = 297, // if ustar owner groupname, else zero devmajor = 329, // if ustar device major number, else zero devminor = 337, // if ustar device minor number , else zero path = 345, // if ustar filename prefix, else zero padding = 500, // if ustar relevant for checksum, else zero total = 512 }; // equivalent to sscanf(buf, 8, "%.7o", &size); or (12, "%.11o", &modtime) // ignores everything after first null or space, including trailing bytes struct octal { uint64_t operator()( const char *src, const char *eof ) const { uint64_t sum = 0, mul = 1; const char *ptr = eof; while( ptr-- >= src ) eof = ( 0 != ptr[1] && 32 != ptr[1] ) ? eof : ptr; while( eof-- >= src ) sum += (uint8_t)(eof[1] - '0') * mul, mul *= 8; return sum; } }; // handle both regular tar and ustar tar filenames until end of tar is found for( char header[512], blank[512] = {}; is.good(); ) { is.read( header, 512 ); if( memcmp( header, blank, 512 ) ) { // if not end of tar if( !memcmp( header+magic, "ustar", 5 ) ) { // if valid ustar int namelen = strlen(header+name), pathlen = strlen(header+path); // read filename std::string entry = std::string(header+path, pathlen < 155 ? pathlen : 155 ) + (pathlen ? "/" : "") + std::string(header+name, namelen < 100 ? namelen : 100 ); switch( header[type] ) { default: // unsupported file type break; case '5': //yield(entry.back()!='/'?entry+'/':entry,0);// directory break; case 'L': entry = header+name; is.read( header, 512 ); // gnu tar long filename break; case '0': case 0: { // regular file uint64_t len = octal()(header+size, header+modtime); // decode octal size char *dst = (len ? yield(entry, len) : 0); // read block if processed if( dst ) is.read( dst, len ); else is.ignore( len ); // skip block if unprocessed is.ignore( (512 - (len & 511)) & 511 ); // skip padding } } } else return false; } else return is.good(); } return false; } /* #include #include #include int main( int argc, const char **argv ) { if( argc != 2 ) return std::cerr << "Usage: " << argv[0] << " archive.tar" << std::endl, -1; std::map dir; std::ifstream in(argv[1], std::ios_base::binary); return tinyuntar( in, [&]( const std::string &filename, uint64_t size ) { std::cout << filename << " (" << size << " bytes)" << std::endl; return dir[ filename ] = (char *)malloc( size ); // processed if valid ptr, skipped if null } ); } */ ================================================ FILE: tinyunzip.cc ================================================ // tiny zip unarchiver. based on junzip by Joonas Pihlajamaa // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include #include #define STBI_NO_WRITE #define STBI_NO_HDR #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_STATIC #include "stb_image.h" template bool tinyunzip( istream &is, const FN &yield ) { bool swaple = 0; auto swap16 = [&]( uint16_t t ) { return !swaple ? t : (t >> 8) | (t << 8); }; auto swap32 = [&]( uint32_t t ) { return !swaple ? t : (t >> 24) | (t << 24) | ((t >> 8) & 0xff00) | ((t & 0xff00) << 8); }; #pragma pack( push, 1 ) struct global_file_header { uint32_t magic; // <-- uint16_t version; uint16_t versionreq; uint16_t flags; uint16_t zmethod; // <-- uint16_t modftme; uint16_t modfdte; uint32_t crc32; uint32_t size; uint32_t unsize; uint16_t namelen; // <-- filename length uint16_t xtralen; // <-- extra field length uint16_t commlen; // <-- comment filed length uint16_t beginvol; uint16_t attrint; uint32_t attrext; uint32_t offsrel; // <-- } gfh = {}; struct file_header { uint16_t zmethod; // <-- uint16_t modftme; uint16_t modfdte; uint32_t crc32; uint32_t size; // <-- uint32_t unsize; // <-- union { uint32_t offset; // <-- struct { uint16_t namelen; // <-- uint16_t xtralen; // <-- }; }; } fh = {}; struct local_file_header { uint32_t magic; // <-- uint16_t versionreq; uint16_t flags; file_header fh; } lfh; struct end_record { uint32_t magic; // <-- 0x06054b50 uint16_t volno; // <-- uint16_t volnodir; // <-- uint16_t volentries; // <-- uint16_t totentries; // <-- uint32_t dirsize; uint32_t diroffs; // <-- uint16_t commlen; // after this field, zip file comment follows (variable size) } er = {}; #pragma pack( pop ) enum { BUFFER_SIZE = 65536 }; // max name len std::vector buffer( BUFFER_SIZE ), cdata; auto read_end_record = [&] { is.seekg( 0, is.end ); size_t total = is.tellg(); if( total >= sizeof(end_record) ) { size_t to_read = (total < BUFFER_SIZE) ? total : BUFFER_SIZE; is.seekg( total - to_read, is.beg ); is.read( &buffer[0], to_read ); for( int at = to_read - sizeof(end_record); at >= 0; at-- ) { er = *(end_record *) &buffer[at]; if( er.magic == 0x06054B50 || er.magic == 0x504B5460 ) { swaple = er.magic != 0x06054B50; return !swap16(er.volno) && !swap16(er.volnodir) && swap16(er.totentries) == swap16(er.volentries); } } } return false; }; auto read_central_dir = [&] { is.seekg( swap32(er.diroffs), is.beg ); if( is.good() ) for( int it = 0; it < swap16(er.totentries); it++ ) { is.read( (char *)&gfh, sizeof(global_file_header) ); if( is.good() && gfh.magic == swap32(0x02014B50) && swap16(gfh.namelen) + 1 < BUFFER_SIZE ) { is.read( (char *)&buffer[0], swap16(gfh.namelen) ); buffer[ swap16(gfh.namelen) ] = '\0'; is.seekg( swap16(gfh.xtralen) + swap16(gfh.commlen), is.cur ); fh = *(file_header *)&gfh.zmethod; fh.offset = gfh.offsrel; if( swap32(fh.size) != 0 || buffer[ swap16(gfh.namelen) - 1 ] != '/' ) // if not dir if( swap16(fh.zmethod) == 0 || swap16(fh.zmethod) == 8 ) { // if supported char *ptr = yield( &buffer[0], swap32(fh.unsize), swap32(fh.size) ); // reserve if( ptr ) { auto pos = is.tellg(); is.seekg( swap32(fh.offset), is.beg ); is.read( (char *)&lfh, sizeof(local_file_header) ); if( lfh.magic != swap32(0x04034B50) ) return false; is.seekg(swap16(lfh.fh.namelen) + swap16(lfh.fh.xtralen), is.cur); if( swap16(fh.zmethod) ) { cdata.resize( swap32(fh.size) ); is.read( &cdata[0], cdata.size() ); if( stbi_zlib_decode_noheader_buffer( ptr, swap32(fh.unsize), &cdata[0], swap32(fh.size) ) < 0 ) { return false; } } else { is.read( ptr, swap32(fh.size) ); } is.seekg( pos, is.beg ); } } } else return false; } else return false; return true; }; return read_end_record() && read_central_dir() && is.good(); } /* #include #include #include int main( int argc, const char **argv ) { std::map toc; std::ifstream is( argc > 1 ? argv[1] : argv[0], std::ios::binary ); return tinyunzip( is, [&]( const char *filename, int dlen, int zlen ) { std::cout << filename << " " << zlen << " -> " << dlen << std::endl; return toc[ filename ] = (char *)malloc(dlen); } ); } */ ================================================ FILE: tinyvariant.cc ================================================ // Tiny variant class. rlyeh, public domain | wtrmrkrlyeh #include #include struct var { int type; union { int integer; double real; std::string *string; std::function *callback; }; var() : type(0), integer(0) {} var( const int &i ) : type(0), integer(i) {} var( const double &r ) : type(1), real(r) {} var( const std::string &r ) : type(2), string( new std::string(r) ) {} template var( const char (&s)[N]) : type(2), string( new std::string(s) ) {} template var( const T &fn ) : type(3), callback( new std::function(fn) ) {} var( const var &other ) { operator=( other ); } ~var() { cleanup(); } void cleanup() { /**/ if( type == 3 ) delete callback, callback = 0; else if( type == 2 ) delete string, string = 0; type = 0; } var &operator=( const var &other ) { if( &other != this ) { cleanup(); type = other.type; /**/ if( type == 0 ) integer = other.integer; else if( type == 1 ) real = other.real; else if( type == 2 ) string = new std::string( *other.string ); else if( type == 3 ) callback = new std::function( *other.callback ); } return *this; } void operator()() const { if( type == 3 ) if( *callback ) (*callback)(); } template inline friend ostream &operator <<( ostream &os, const var &self ) { /**/ if( self.type == 0 ) return os << self.integer, os; else if( self.type == 1 ) return os << self.real, os; else if( self.type == 2 ) return os << *self.string, os; else if( self.type == 3 ) return os << '[' << self.callback << ']', os; return os; } }; template inline bool is(const var &v) { return false; } template<> inline bool is(const var &v) { return v.type == 0; } template<> inline bool is(const var &v) { return v.type == 1; } template<> inline bool is(const var &v) { return v.type == 2; } template<> inline bool is>(const var &v) { return v.type == 3; } template inline const T& cast(const var &v) { static T t; return t = T(); } template<> inline const int& cast(const var &v) { return v.integer; } template<> inline const double& cast(const var &v) { return v.real; } template<> inline const std::string& cast(const var &v) { return *v.string; } template<> inline const std::function& cast>(const var &v) { return *v.callback; } /* #include #include int main() { std::cout << "sizeof(var)=" << sizeof(var) << std::endl; var digit = 42, other = "hello world"; std::cout << digit << std::endl; assert( is(digit) ); std::cout << other << std::endl; assert( is(other) ); other = digit; std::cout << other << std::endl; assert( is(other) ); other = []{ std::cout << "callback!" << std::endl; }; std::cout << other << std::endl; other(); } */ ================================================ FILE: tinyvbyte.h ================================================ // tiny variable byte length encoder/decoder (vbyte) // - rlyeh, public domain | wtrmrkrlyeh #pragma once #include enum { VBYTE_MIN_REQ_BYTES = 1, VBYTE_MAX_REQ_BYTES = 10 }; static uint64_t vbuencode( uint8_t *buffer, uint64_t value ) { /* 7-bit packing. MSB terminates stream */ const uint8_t *buffer0 = buffer; do { *buffer++ = (uint8_t)( 0x80 | (value & 0x7f) ); value >>= 7; } while( value > 0 ); *(buffer-1) ^= 0x80; return buffer - buffer0; } static uint64_t vbudecode( uint64_t *value, const uint8_t *buffer ) { /* 7-bit unpacking. MSB terminates stream */ const uint8_t *buffer0 = buffer; uint64_t out = 0, j = -7; do { out |= (( ((uint64_t)(*buffer)) & 0x7f) << (j += 7) ); } while( ((uint64_t)(*buffer++)) & 0x80 ); *value = out; return buffer - buffer0; } static uint64_t vbiencode( uint8_t *buffer, int64_t value ) { /* convert sign|magnitude to magnitude|sign */ uint64_t nv = (uint64_t)((value >> 63) ^ (value << 1)); /* encode unsigned */ return vbuencode( buffer, nv ); } static uint64_t vbidecode( int64_t *value, const uint8_t *buffer ) { /* decode unsigned */ uint64_t nv, ret = vbudecode( &nv, buffer ); /* convert magnitude|sign to sign|magnitude */ *value = ((nv >> 1) ^ -(nv & 1)); return ret; } /* #include #define test(type, encfunc, decfunc, number) do { \ type copy; \ char buf[16]; \ int written = encfunc( buf, number ), i = 0; \ printf("[ ] %s: ", #type); \ while( i < written ) printf("%02x,", (uint8_t)buf[i++] ); \ decfunc( ©, buf ); \ printf("\r%s\n", copy == number ? "[ OK ]" : "[FAIL]"); \ } while(0) int main() { test( int64_t, vbiencode, vbidecode, 0); test( int64_t, vbiencode, vbidecode, -1); test( int64_t, vbiencode, vbidecode, +1); test( int64_t, vbiencode, vbidecode, -2); test( int64_t, vbiencode, vbidecode, +2); test( int64_t, vbiencode, vbidecode, INT8_MIN); test( int64_t, vbiencode, vbidecode, INT8_MAX); test( int64_t, vbiencode, vbidecode, INT16_MIN); test( int64_t, vbiencode, vbidecode, INT16_MAX); test( int64_t, vbiencode, vbidecode, INT32_MIN); test( int64_t, vbiencode, vbidecode, INT32_MAX); test( int64_t, vbiencode, vbidecode, INT64_MIN); test( int64_t, vbiencode, vbidecode, INT64_MAX); test(uint64_t, vbuencode, vbudecode, 0); test(uint64_t, vbuencode, vbudecode, 1); test(uint64_t, vbuencode, vbudecode, 2); test(uint64_t, vbuencode, vbudecode, 3); test(uint64_t, vbuencode, vbudecode, 4); test(uint64_t, vbuencode, vbudecode, UINT8_MAX); test(uint64_t, vbuencode, vbudecode, UINT16_MAX); test(uint64_t, vbuencode, vbudecode, UINT32_MAX); test(uint64_t, vbuencode, vbudecode, UINT64_MAX); } */ ================================================ FILE: tinywav.c ================================================ // Tiny WAV writer: original code by jon olick, public domain // Floating point support + pure C version by rlyeh, public domain | wtrmrkrlyeh #include static void tinywav(FILE *fp, short numChannels, short bitsPerSample, int sampleRateHz, const void *data, int size, int is_floating) { short bpsamp; int length, bpsec; fwrite("RIFF", 1, 4, fp); length = size + 44 - 8; fwrite(&length, 1, 4, fp); fwrite(is_floating ? "WAVEfmt \x10\x00\x00\x00\x03\x00" : "WAVEfmt \x10\x00\x00\x00\x01\x00", 1, 14, fp); fwrite(&numChannels, 1, 2, fp); fwrite(&sampleRateHz, 1, 4, fp); bpsec = numChannels * sampleRateHz * bitsPerSample/8; fwrite(&bpsec, 1, 4, fp); bpsamp = numChannels * bitsPerSample/8; fwrite(&bpsamp, 1, 2, fp); fwrite(&bitsPerSample, 1, 2, fp); fwrite("data", 1, 4, fp); fwrite(&size, 1, 4, fp); fwrite(data, 1, size, fp); } ================================================ FILE: tinywtf.h ================================================ // tiny portable host macros (C/C++ language features, compilers, os, arch, tls...) // used to avoid #if/n/def hell. // - rlyeh, public domain | wtrmrkrlyeh #ifndef $no // Core #define $no(...) #define $yes(...) __VA_ARGS__ // Directive modifiers #define $on(v) (0 v(+1)) // usage: #if $on($cl) #define $is (0 v(+1)) // usage: #if $is($debug) #define $has(...) $clang(__has_feature(__VA_ARGS__)) $nclang(__VA_ARGS__) // usage: #if $has(cxx_exceptions) // Text/code modifiers #define $quote(...) #__VA_ARGS__ #define $comment $no #define $uncomment $yes // Compiler utils #include #if INTPTR_MAX == INT64_MAX # define $bits64 $yes # define $bits32 $no #elif INTPTR_MAX == INT32_MAX # define $bits64 $no # define $bits32 $yes #else # define $bits64 $no # define $bits32 $no #endif #if defined(NDEBUG) || defined(_NDEBUG) || defined(RELEASE) # define $release $yes # define $debug $no #else # define $release $no # define $debug $yes #endif #if defined(NDEVEL) || defined(_NDEVEL) || defined(PUBLIC) # define $public $yes # define $devel $no #else # define $public $no # define $devel $yes #endif #if defined(__GNUC__) || defined(__MINGW32__) # define $gcc $yes # define $ngcc $no #else # define $gcc $no # define $ngcc $yes #endif #ifdef _MSC_VER # define $cl $yes # define $ncl $no #else # define $cl $no # define $ncl $yes #endif #ifdef __clang__ # define $clang $yes # define $nclang $no #else # define $clang $no # define $nclang $yes #endif #if $on($cl) || $on($gcc) || $on($clang) # define $supported_compiler $yes # define $unsupported_compiler $no #else # define $supported_compiler $no # define $unsupported_compiler $yes #endif // usage: if $likely (expr) { ... } #if $on($gcc) || $on($clang) # define $likely(expr) (__builtin_expect(!!(expr), 1)) # define $unlikely(expr) (__builtin_expect(!!(expr), 0)) #else # define $likely(expr) ((expr)) # define $unlikely(expr) ((expr)) #endif // create a $warning(...) macro // usage: $warning("this is shown at compile time") #if $on($gcc) || $on($clang) # define $w4rning(msg) _Pragma(#msg) # define $warning(msg) $w4rning( message( msg ) ) #elif $on($cl) # define $warning(msg) __pragma( message( msg ) ) #else # define $warning(msg) #endif // create a $todo(...) macro // usage: $todo("this is shown at compile time") #define $t0d0(X) #X #define $tod0(X) $t0d0(X) #define $todo(...) $warning( __FILE__ "(" $tod0(__LINE__)") : $todo - " #__VA_ARGS__ " - [ " $cl(__FUNCTION__) $ncl(__func__) " ]" ) // C++ detect and version #ifdef __cplusplus # define $c $no # define $cpp $yes # if (__cplusplus < 201103L && !defined(_MSC_VER)) || (defined(_MSC_VER) && (_MSC_VER < 1700)) || (defined(__GLIBCXX__) && __GLIBCXX__ < 20130322L) # define $cpp11 $no # define $cpp03 $yes # else # define $cpp11 $yes # define $cpp03 $no # endif #else # define $c $yes # define $cpp $no # define $cpp11 $no # define $cpp03 $no #endif // C++ exceptions #if defined(__cplusplus) && ( \ (defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS > 0)) || \ (defined(_STLP_USE_EXCEPTIONS) && (_STLP_USE_EXCEPTIONS > 0)) || \ (defined(HAVE_EXCEPTIONS)) || \ (defined(__EXCEPTIONS)) || \ (defined(_CPPUNWIND)) || \ ($has(cxx_exceptions)) ) /*(__has_feature(cxx_exceptions))*/ # define $exceptions $yes # define $nexceptions $no #else # define $exceptions $no # define $nexceptions $yes #endif // Thread Local Storage #if defined(__MINGW32__) || defined(__SUNPRO_C) || defined(__xlc__) || defined(__GNUC__) || defined(__clang__) || defined(__GNUC__) // __INTEL_COMPILER on linux // MingW, Solaris Studio C/C++, IBM XL C/C++,[3] GNU C,[4] Clang[5] and Intel C++ Compiler (Linux systems) # define $tls(x) __thread x #else // Visual C++,[7] Intel C/C++ (Windows systems),[8] C++Builder, and Digital Mars C++ # define $tls(x) __declspec(thread) x #endif // OS utils #if defined(_WIN32) # define $windows $yes # define $nwindows $no #else # define $windows $no # define $nwindows $yes #endif #ifdef __linux__ # define $linux $yes # define $nlinux $no #else # define $linux $no # define $nlinux $yes #endif #ifdef __APPLE__ # define $apple $yes # define $napple $no #else # define $apple $no # define $napple $yes #endif #if defined(__APPLE__) && defined(TARGET_OS_MAC) # define $osx $yes # define $nosx $no #else # define $osx $no # define $nosx $yes #endif #if defined(__APPLE__) && (defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)) # define $ios $yes # define $nios $no #else # define $ios $no # define $nios $yes #endif #if defined(__ANDROID_API__) # define $android $yes # define $nandroid $no #else # define $android $no # define $nandroid $yes #endif #if $on($windows) || $on($apple) || $on($linux) || $on($osx) || $on($ios) || $on($android) # define $supported_os $yes # define $unsupported_os $no #else # define $supported_os $no # define $unsupported_os $yes #endif #endif #ifdef UNDEFINE #undef UNDEFINE #undef $android #undef $apple #undef $bits32 #undef $bits64 #undef $c #undef $cl #undef $clang #undef $comment #undef $cpp #undef $cpp03 #undef $cpp11 #undef $debug #undef $devel #undef $exceptions #undef $gcc #undef $has #undef $ios #undef $is #undef $likely #undef $linux #undef $nandroid #undef $napple #undef $ncl #undef $nclang #undef $nexceptions #undef $ngcc #undef $nios #undef $nlinux #undef $no #undef $nosx #undef $nwindows #undef $on #undef $osx #undef $public #undef $quote #undef $release #undef $supported_compiler #undef $supported_os #undef $tls #undef $todo #undef $uncomment #undef $unlikely #undef $unsupported_compiler #undef $unsupported_os #undef $warning #undef $windows #undef $yes #ifdef $tod0 #undef $tod0 #endif #ifdef $t0d0 #undef $t0d0 #endif #ifdef $w4rning #undef $w4rning #endif #endif /* #include int main() { $c( puts("C"); ) $cpp( puts("C++"); $cpp03( puts("C++03"); ) $cpp11( puts("C++11"); ) $exceptions( puts("exceptions enabled"); ) $nexceptions( puts("exceptions disabled"); ) ) $bits32( puts("32-bit"); ) $bits64( puts("64-bit"); ) $debug( puts("unoptimized"); ) $release( puts("optimized"); ) $windows( puts("windows"); ) $ios( puts("ios"); ) $osx( puts("osx"); ) $warning("this line emits a warning"); $todo("this line needs a better comment"); const char *src = $quote(multi line); $comment( puts("never compiled"); ); static $tls(int) thread_local_integer = 1; // etc... } */ ================================================ FILE: tinyzlib.cpp ================================================ // tiny zlib inflater. extracted from tigr (credits to Richard Mitton). @todo: remove exceptions // - rlyeh, public domain | wtrmrkrlyeh static bool tinyzlib(void *out, unsigned outlen, const void *in, unsigned inlen) { struct State { unsigned bits, count; const unsigned char *in, *inend; unsigned char *out, *outend; unsigned litcodes[288], distcodes[32], lencodes[19]; int tlit, tdist, tlen; }; // Built-in DEFLATE standard tables. const char order[] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; const char lenBits[29+2] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0, 0,0 }; const int lenBase[29+2] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 0,0 }; const char distBits[30+2] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13, 0,0 }; const int distBase[30+2] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 }; auto emit = [](State *s, int len) -> unsigned char * { s->out += len; if(!(s->out <= s->outend)) throw; return s->out-len; }; auto bits = [](State *s, int n) { int v = s->bits & ((1 << n)-1); s->bits >>= n; s->count -= n; while (s->count < 16) { if(!(s->in != s->inend)) throw; s->bits |= (*s->in++) << s->count; s->count += 8; } return v; }; auto copy = [&](State *s, const unsigned char *src, int len) { unsigned char *dest = emit(s, len); while (len--) *dest++ = *src++; }; auto decode = [&](State *s, unsigned tree[], int max) { auto rev16_src = [](unsigned n) -> unsigned { // Table to . static const unsigned char reverseTable[256] = { #define R2(n) n, n + 128, n + 64, n + 192 #define R4(n) R2(n), R2(n + 32), R2(n + 16), R2(n + 48) #define R6(n) R4(n), R4(n + 8), R4(n + 4), R4(n + 12) R6(0), R6(2), R6(1), R6(3) }; return (reverseTable[n&0xff] << 8) | reverseTable[(n>>8)&0xff]; }; auto rev16 = [](unsigned n) -> unsigned { // bit-reverse two bytes return (((((n )&0xff) * 0x0202020202ULL & 0x010884422010ULL) % 1023) << 8) | ((((n>>8)&0xff) * 0x0202020202ULL & 0x010884422010ULL) % 1023); }; // Find the next prefix code. unsigned lo = 0, hi = max, key; unsigned search = (rev16(s->bits) << 16) | 0xffff; while (lo < hi) { unsigned guess = (lo + hi) / 2; if (search < tree[guess]) hi = guess; else lo = guess + 1; } // Pull out the key and check it. key = tree[lo-1]; if(!(((search^key) >> (32-(key&0xf))) == 0)) throw; bits(s, key & 0xf); return (key >> 4) & 0xfff; }; auto build = [&](State *s, unsigned *tree, unsigned char *lens, int symcount) -> int { int n, codes[16], first[16], counts[16]={0}; // Frequency count. for (n=0;ndistcodes, s->tdist); int offs = bits(s, distBits[dsym]) + distBase[dsym]; copy(s, s->out - offs, length); }; for (;;) { int sym = decode(s, s->litcodes, s->tlit); if (sym < 256) *emit(s, 1) = (unsigned char)sym; else if (sym > 256) run(s, sym-257); else break; } }; auto stored = [&](State *s) { // Uncompressed data block int len; bits(s, s->count & 7); len = bits(s, 16); if(!(((len^s->bits)&0xffff) == 0xffff)) throw; if(!(s->in + len <= s->inend)) throw; copy(s, s->in, len); s->in += len; bits(s, 16); }; auto fixed = [&](State *s) { // Fixed set of Huffman codes int n; unsigned char lens[288+32]; for (n= 0;n<=143;n++) lens[n] = 8; for (n=144;n<=255;n++) lens[n] = 9; for (n=256;n<=279;n++) lens[n] = 7; for (n=280;n<=287;n++) lens[n] = 8; for (n=0;n<32;n++) lens[288+n] = 5; // Build lit/dist trees. s->tlit = build(s, s->litcodes, lens, 288); s->tdist = build(s, s->distcodes, lens+288, 32); }; auto dynamic = [&](State *s) { int n, i, nlit, ndist, nlen; unsigned char lenlens[19] = {0}, lens[288+32]; nlit = 257 + bits(s, 5); ndist = 1 + bits(s, 5); nlen = 4 + bits(s, 4); for (n=0;ntlen = build(s, s->lencodes, lenlens, 19); // Decode code lengths. for (n=0;nlencodes, s->tlen); switch (sym) { case 16: for (i = 3+bits(s,2); i; i--,n++) lens[n] = lens[n-1]; break; case 17: for (i = 3+bits(s,3); i; i--,n++) lens[n] = 0; break; case 18: for (i = 11+bits(s,7); i; i--,n++) lens[n] = 0; break; default: lens[n++] = (unsigned char)sym; break; } } // Build lit/dist trees. s->tlit = build(s, s->litcodes, lens, nlit); s->tdist = build(s, s->distcodes, lens+nlit, ndist); }; struct State s0 = {}, *s = &s0; // We assume we can buffer 2 extra bytes from off the end of 'in'. s->in = (unsigned char *)in; s->inend = s->in + inlen + 2; s->out = (unsigned char *)out; s->outend = s->out + outlen; s->bits = 0; s->count = 0; bits(s, 0); try { for( int last = 0; !last; ) { last = bits(s, 1); switch (bits(s, 2)) { case 0: stored(s); break; case 1: fixed(s); block(s); break; case 2: dynamic(s); block(s); break; case 3: return 0; } } return true; } catch(...) {} return false; } ================================================ FILE: vault/; ds ================================================ ================================================ FILE: vault/_test.c ================================================ // compilation test // - rlyeh, public domain #define ALL_C #include "all.c" int main() {} ================================================ FILE: vault/all.c ================================================ // - rlyeh, public domain #ifdef ALL_C #define C_C #define OS_C #define DS_C #define SYN_C #define BIN_C #define BUF_C #define NET_C #endif #include "c.c" #include "os.c" #include "ds.c" #include "syn.c" #include "bin.c" #include "buf.c" #include "net.c" ================================================ FILE: vault/bin.c ================================================ // - rlyeh, public domain #ifdef BIN_C #define DBKV_C #define JSON5_C #endif #include "os.c" #include "bin_dbkv.c" #include "bin_json5.c" ================================================ FILE: vault/bin_dbkv.c ================================================ // KISSDB written by Adam Ierymenko // KISSDB is in the public domain and is distributed with NO WARRANTY. // http://creativecommons.org/publicdomain/zero/1.0/ #ifndef DBKV_H #define DBKV_H #include #if 0 #if !defined(DBKV_LEN_KEY) && !defined(DBKV_LEN_VALUE) // UDP packet size 576 (IPv4) = UDP payload 508 + UDP header 8 + IP header 60 // UDP packet size 1280 (IPv6) = UDP payload 1212 + UDP header 8 + IP header 60 enum { DBKV_HASH_BUCKET = 508 /*1024*/, DBKV_LEN_KEY = 31+1, /*8*/ DBKV_LEN_VALUE = 508 - DBKV_LEN_KEY /*64*/ }; #endif #else enum { DBKV_HASH_BUCKET = 1024, DBKV_LEN_KEY = 8, DBKV_LEN_VALUE = 64 }; #endif typedef char dbkv_key[DBKV_LEN_KEY]; typedef char dbkv_val[DBKV_LEN_VALUE]; bool dbkv_read( const char *dbfile, const char *keystr, char *val ); bool dbkv_write( const char *dbfile, const char *keystr, const char *val ); #endif #ifdef DBKV_C #pragma once #include //#include "detect/detect_memory.c" // realloc #if 1 //ndef $ #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif #include # if !defined fseeko && defined _MSC_VER // _MSC_VER # define fseeko _fseeki64 # define ftello _ftelli64 # elif !defined fseeko # define fseeko fseek # define ftello ftell # endif # if !defined fopen_s && !defined _MSC_VER # define fopen_s(fp,path,mode) ( (*(fp) = fopen( path, mode ) ) ) #endif #endif /* (Keep It) Simple Stupid Database * * Written by Adam Ierymenko * KISSDB is in the public domain and is distributed with NO WARRANTY. * http://creativecommons.org/publicdomain/zero/1.0/ * * Note: big-endian systems will need changes to implement byte swapping * on hash table file I/O. Or you could just use it as-is if you don't care * that your database files will be unreadable on little-endian systems. * * ----- * * KISSDB file format (version 2) * Author: Adam Ierymenko * http://creativecommons.org/publicdomain/zero/1.0/ * * In keeping with the goal of minimalism the file format is very simple, the * sort of thing that would be given as an example in an introductory course in * data structures. It's a basic hash table that adds additional pages of hash * table entries on collision. * * It consists of a 28 byte header followed by a series of hash tables and data. * All integer values are stored in the native word order of the target * architecture (in the future the code might be fixed to make everything * little-endian if anyone cares about that). * * The header consists of the following fields: * * [0-3] magic numbers: (ASCII) 'K', 'd', 'B', KISSDB_VERSION (currently 2) * [4-11] 64-bit hash table size in entries * [12-19] 64-bit key size in bytes * [20-27] 64-bit value size in bytes * * Hash tables are arrays of [hash table size + 1] 64-bit integers. The extra * entry, if nonzero, is the offset in the file of the next hash table, forming * a linked list of hash tables across the file. * * Immediately following the header, the first hash table will be written when * the first key/value is added. The algorithm for adding new entries is as * follows: * * (1) The key is hashed using a 64-bit variant of the DJB2 hash function, and * this is taken modulo hash table size to get a bucket number. * (2) Hash tables are checked in order, starting with the first hash table, * until a zero (empty) bucket is found. If one is found, skip to step (4). * (3) If no empty buckets are found in any hash table, a new table is appended * to the file and the final pointer in the previous hash table is set to * its offset. (In the code the update of the next hash table pointer in * the previous hash table happens last, after the whole write is complete, * to avoid corruption on power loss.) * (4) The key and value are appended, in order with no additional meta-data, * to the database file. Before appending the offset in the file stream * where they will be stored is saved. After appending, this offset is * written to the empty hash table bucket we chose in steps 2/3. Hash table * updates happen last to avoid corruption if the write does not complete. * * Lookup of a key/value pair occurs as follows: * * (1) The key is hashed and taken modulo hash table size to get a bucket * number. * (2) If this bucket's entry in the hash table is nonzero, the key at the * offset specified by this bucket is compared to the key being looked up. * If they are equal, the value is read and returned. * (3) If the keys are not equal, the next hash table is checked and step (2) * is repeated. If an empty bucket is encountered or if we run out of hash * tables, the key was not found. * * To update an existing value, its location is looked up and the value portion * of the entry is rewritten. */ // --- kissdb.h --- /* (Keep It) Simple Stupid Database * * Written by Adam Ierymenko * KISSDB is in the public domain and is distributed with NO WARRANTY. * * http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Version: 2 * * This is the file format identifier, and changes any time the file * format changes. The code version will be this dot something, and can * be seen in tags in the git repository. */ #define KISSDB_VERSION 2 /** * KISSDB database state * * These fields can be read by a user, e.g. to look up key_size and * value_size, but should never be changed. */ typedef struct { unsigned long hash_table_size; unsigned long key_size; unsigned long value_size; unsigned long hash_table_size_bytes; unsigned long num_hash_tables; uint64_t *hash_tables; FILE *f; } KISSDB; enum { KISSDB_ERROR_IO = -1 }; // I/O error or file not found enum { KISSDB_ERROR_MALLOC = -2 }; // Out of memory enum { KISSDB_ERROR_INVALID_PARAMETERS = -3 }; // Invalid paramters (e.g. missing _size paramters on init to create database) enum { KISSDB_ERROR_CORRUPT_DBFILE = -4 }; // Database file appears corrupt enum { KISSDB_OPEN_MODE_RDONLY = 1 }; // Open mode: read only enum { KISSDB_OPEN_MODE_RDWR = 2 }; // Open mode: read/write enum { KISSDB_OPEN_MODE_RWCREAT = 3 }; // Open mode: read/write, create if doesn't exist enum { KISSDB_OPEN_MODE_RWREPLACE = 4 }; // Open mode: truncate database, open for reading and writing /** * Open database * * The three _size parameters must be specified if the database could * be created or re-created. Otherwise an error will occur. If the * database already exists, these parameters are ignored and are read * from the database. You can check the struture afterwords to see what * they were. * * @param db Database struct * @param path Path to file * @param mode One of the KISSDB_OPEN_MODE constants * @param hash_table_size Size of hash table in 64-bit entries (must be >0) * @param key_size Size of keys in bytes * @param value_size Size of values in bytes * @return 0 on success, nonzero on error */ static int KISSDB_open( KISSDB *db, const char *path, int mode, unsigned long hash_table_size, unsigned long key_size, unsigned long value_size); /** * Close database * * @param db Database struct */ static void KISSDB_close(KISSDB *db); /** * Get an entry * * @param db Database struct * @param key Key (key_size bytes) * @param vbuf Value buffer (value_size bytes capacity) * @return -1 on I/O error, 0 on success, 1 on not found */ static int KISSDB_get(KISSDB *db,const void *key,void *vbuf); /** * Put an entry (overwriting it if it already exists) * * In the already-exists case the size of the database file does not * change. * * @param db Database struct * @param key Key (key_size bytes) * @param value Value (value_size bytes) * @return -1 on I/O error, 0 on success */ static int KISSDB_put(KISSDB *db,const void *key,const void *value); /** * Cursor used for iterating over all entries in database */ typedef struct { KISSDB *db; unsigned long h_no; unsigned long h_idx; } KISSDB_Iterator; /** * Initialize an iterator * * @param db Database struct * @param i Iterator to initialize */ static void KISSDB_Iterator_init(KISSDB *db,KISSDB_Iterator *dbi); /** * Get the next entry * * The order of entries returned by iterator is undefined. It depends on * how keys hash. * * @param Database iterator * @param kbuf Buffer to fill with next key (key_size bytes) * @param vbuf Buffer to fill with next value (value_size bytes) * @return 0 if there are no more entries, negative on error, positive if an kbuf/vbuf have been filled */ static int KISSDB_Iterator_next(KISSDB_Iterator *dbi,void *kbuf,void *vbuf); // --- kissdb.c --- #include #include #include #define KISSDB_HEADER_SIZE ((sizeof(uint64_t) * 3) + 4) /* djb2 hash function */ static uint64_t KISSDB_hash(const void *b,unsigned long len) { unsigned long i; uint64_t hash = 5381; for(i=0;if = (FILE *)0; fopen_s(&db->f,path,((mode == KISSDB_OPEN_MODE_RWREPLACE) ? "w+b" : (((mode == KISSDB_OPEN_MODE_RDWR)||(mode == KISSDB_OPEN_MODE_RWCREAT)) ? "r+b" : "rb"))); if (!db->f) { if (mode == KISSDB_OPEN_MODE_RWCREAT) { db->f = (FILE *)0; fopen_s(&db->f,path,"w+b"); } if (!db->f) return KISSDB_ERROR_IO; } if (fseeko(db->f,0,SEEK_END)) { fclose(db->f); return KISSDB_ERROR_IO; } if (ftello(db->f) < KISSDB_HEADER_SIZE) { /* write header if not already present */ if ((hash_table_size)&&(key_size)&&(value_size)) { if (fseeko(db->f,0,SEEK_SET)) { fclose(db->f); return KISSDB_ERROR_IO; } tmp2[0] = 'K'; tmp2[1] = 'd'; tmp2[2] = 'B'; tmp2[3] = KISSDB_VERSION; if (fwrite(tmp2,4,1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } tmp = hash_table_size; if (fwrite(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } tmp = key_size; if (fwrite(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } tmp = value_size; if (fwrite(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } fflush(db->f); } else { fclose(db->f); return KISSDB_ERROR_INVALID_PARAMETERS; } } else { if (fseeko(db->f,0,SEEK_SET)) { fclose(db->f); return KISSDB_ERROR_IO; } if (fread(tmp2,4,1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } if ((tmp2[0] != 'K')||(tmp2[1] != 'd')||(tmp2[2] != 'B')||(tmp2[3] != KISSDB_VERSION)) { fclose(db->f); return KISSDB_ERROR_CORRUPT_DBFILE; } if (fread(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } if (!tmp) { fclose(db->f); return KISSDB_ERROR_CORRUPT_DBFILE; } hash_table_size = (unsigned long)tmp; if (fread(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } if (!tmp) { fclose(db->f); return KISSDB_ERROR_CORRUPT_DBFILE; } key_size = (unsigned long)tmp; if (fread(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; } if (!tmp) { fclose(db->f); return KISSDB_ERROR_CORRUPT_DBFILE; } value_size = (unsigned long)tmp; } db->hash_table_size = hash_table_size; db->key_size = key_size; db->value_size = value_size; db->hash_table_size_bytes = sizeof(uint64_t) * (hash_table_size + 1); /* [hash_table_size] == next table */ httmp = MALLOC(db->hash_table_size_bytes); if (!httmp) { fclose(db->f); return KISSDB_ERROR_MALLOC; } db->num_hash_tables = 0; db->hash_tables = (uint64_t *)0; while (fread(httmp,db->hash_table_size_bytes,1,db->f) == 1) { hash_tables_rea = REALLOC(db->hash_tables,db->hash_table_size_bytes * (db->num_hash_tables + 1)); if (!hash_tables_rea) { KISSDB_close(db); FREE(httmp); return KISSDB_ERROR_MALLOC; } db->hash_tables = hash_tables_rea; memcpy(((uint8_t *)db->hash_tables) + (db->hash_table_size_bytes * db->num_hash_tables),httmp,db->hash_table_size_bytes); ++db->num_hash_tables; if (httmp[db->hash_table_size]) { if (fseeko(db->f,httmp[db->hash_table_size],SEEK_SET)) { KISSDB_close(db); FREE(httmp); return KISSDB_ERROR_IO; } } else break; } FREE(httmp); return 0; } static void KISSDB_close(KISSDB *db) { if (db->hash_tables) FREE(db->hash_tables); if (db->f) fclose(db->f); memset(db,0,sizeof(KISSDB)); } static int KISSDB_get(KISSDB *db,const void *key,void *vbuf) { uint8_t tmp[4096]; const uint8_t *kptr; unsigned long klen,i; uint64_t hash = KISSDB_hash(key,db->key_size) % (uint64_t)db->hash_table_size; uint64_t offset; uint64_t *cur_hash_table; long n; cur_hash_table = db->hash_tables; for(i=0;inum_hash_tables;++i) { offset = cur_hash_table[hash]; if (offset) { if (fseeko(db->f,offset,SEEK_SET)) return KISSDB_ERROR_IO; kptr = (const uint8_t *)key; klen = db->key_size; while (klen) { n = (long)fread(tmp,1,(klen > sizeof(tmp)) ? sizeof(tmp) : klen,db->f); if (n > 0) { if (memcmp(kptr,tmp,n)) goto get_no_match_next_hash_table; kptr += n; klen -= (unsigned long)n; } else return 1; /* not found */ } if (fread(vbuf,db->value_size,1,db->f) == 1) return 0; /* success */ else return KISSDB_ERROR_IO; } else return 1; /* not found */ get_no_match_next_hash_table: cur_hash_table += db->hash_table_size + 1; } return 1; /* not found */ } static int KISSDB_put(KISSDB *db,const void *key,const void *value) { uint8_t tmp[4096]; const uint8_t *kptr; unsigned long klen,i; uint64_t hash = KISSDB_hash(key,db->key_size) % (uint64_t)db->hash_table_size; uint64_t offset; uint64_t htoffset,lasthtoffset; uint64_t endoffset; uint64_t *cur_hash_table; uint64_t *hash_tables_rea; long n; lasthtoffset = htoffset = KISSDB_HEADER_SIZE; cur_hash_table = db->hash_tables; for(i=0;inum_hash_tables;++i) { offset = cur_hash_table[hash]; if (offset) { /* rewrite if already exists */ if (fseeko(db->f,offset,SEEK_SET)) return KISSDB_ERROR_IO; kptr = (const uint8_t *)key; klen = db->key_size; while (klen) { n = (long)fread(tmp,1,(klen > sizeof(tmp)) ? sizeof(tmp) : klen,db->f); if (n > 0) { if (memcmp(kptr,tmp,n)) goto put_no_match_next_hash_table; kptr += n; klen -= (unsigned long)n; } } /* C99 spec demands seek after fread(), required for Windows */ fseeko(db->f,0,SEEK_CUR); if (fwrite(value,db->value_size,1,db->f) == 1) { fflush(db->f); return 0; /* success */ } else return KISSDB_ERROR_IO; } else { /* add if an empty hash table slot is discovered */ if (fseeko(db->f,0,SEEK_END)) return KISSDB_ERROR_IO; endoffset = ftello(db->f); if (fwrite(key,db->key_size,1,db->f) != 1) return KISSDB_ERROR_IO; if (fwrite(value,db->value_size,1,db->f) != 1) return KISSDB_ERROR_IO; if (fseeko(db->f,htoffset + (sizeof(uint64_t) * hash),SEEK_SET)) return KISSDB_ERROR_IO; if (fwrite(&endoffset,sizeof(uint64_t),1,db->f) != 1) return KISSDB_ERROR_IO; cur_hash_table[hash] = endoffset; fflush(db->f); return 0; /* success */ } put_no_match_next_hash_table: lasthtoffset = htoffset; htoffset = cur_hash_table[db->hash_table_size]; cur_hash_table += (db->hash_table_size + 1); } /* if no existing slots, add a new page of hash table entries */ if (fseeko(db->f,0,SEEK_END)) return KISSDB_ERROR_IO; endoffset = ftello(db->f); hash_tables_rea = REALLOC(db->hash_tables,db->hash_table_size_bytes * (db->num_hash_tables + 1)); if (!hash_tables_rea) return KISSDB_ERROR_MALLOC; db->hash_tables = hash_tables_rea; cur_hash_table = &(db->hash_tables[(db->hash_table_size + 1) * db->num_hash_tables]); memset(cur_hash_table,0,db->hash_table_size_bytes); cur_hash_table[hash] = endoffset + db->hash_table_size_bytes; /* where new entry will go */ if (fwrite(cur_hash_table,db->hash_table_size_bytes,1,db->f) != 1) return KISSDB_ERROR_IO; if (fwrite(key,db->key_size,1,db->f) != 1) return KISSDB_ERROR_IO; if (fwrite(value,db->value_size,1,db->f) != 1) return KISSDB_ERROR_IO; if (db->num_hash_tables) { if (fseeko(db->f,lasthtoffset + (sizeof(uint64_t) * db->hash_table_size),SEEK_SET)) return KISSDB_ERROR_IO; if (fwrite(&endoffset,sizeof(uint64_t),1,db->f) != 1) return KISSDB_ERROR_IO; db->hash_tables[((db->hash_table_size + 1) * (db->num_hash_tables - 1)) + db->hash_table_size] = endoffset; } ++db->num_hash_tables; fflush(db->f); return 0; /* success */ } static void KISSDB_Iterator_init(KISSDB *db,KISSDB_Iterator *dbi) { dbi->db = db; dbi->h_no = 0; dbi->h_idx = 0; } static int KISSDB_Iterator_next(KISSDB_Iterator *dbi,void *kbuf,void *vbuf) { uint64_t offset; if ((dbi->h_no < dbi->db->num_hash_tables)&&(dbi->h_idx < dbi->db->hash_table_size)) { while (!(offset = dbi->db->hash_tables[((dbi->db->hash_table_size + 1) * dbi->h_no) + dbi->h_idx])) { if (++dbi->h_idx >= dbi->db->hash_table_size) { dbi->h_idx = 0; if (++dbi->h_no >= dbi->db->num_hash_tables) return 0; } } if (fseeko(dbi->db->f,offset,SEEK_SET)) return KISSDB_ERROR_IO; if (fread(kbuf,dbi->db->key_size,1,dbi->db->f) != 1) return KISSDB_ERROR_IO; if (fread(vbuf,dbi->db->value_size,1,dbi->db->f) != 1) return KISSDB_ERROR_IO; if (++dbi->h_idx >= dbi->db->hash_table_size) { dbi->h_idx = 0; ++dbi->h_no; } return 1; } return 0; } #endif #ifdef DBKV_BENCH #include #include #ifdef _MSC_VER #include #define clock_t double #define clock omp_get_wtime #undef CLOCKS_PER_SEC #define CLOCKS_PER_SEC 1.0 #endif int main(int argc,char **argv) { #ifndef N enum { N = 100000 }; #endif uint64_t i,j; uint64_t v[8]; KISSDB db; KISSDB_Iterator dbi; char *got_all_values = malloc(N); int q; clock_t begin, end; #ifdef STDIO2 if( argc < 2 ) { printf("%s [file.db]\n", argv[0]); printf("%s [mem://file.db]\n", argv[0]); exit(0); } const char *dbfile = argv[1]; #else const char *dbfile = "test.db"; #endif begin = clock(); printf("Opening new empty database %s...\n", dbfile); if (KISSDB_open(&db,dbfile,KISSDB_OPEN_MODE_RWREPLACE,1024,8,sizeof(v))) { printf("KISSDB_open failed\n"); return 1; } end = clock(); { double sec = (end - begin)/(double)CLOCKS_PER_SEC; printf("OK! %5.2fs\n", sec); } begin = clock(); printf("Putting %d 64-byte values...\n", N); for(i=0;i 0) { if (i < N) got_all_values[i] = 1; else { printf("KISSDB_Iterator_next failed, bad data (%"PRIu64")\n",i); return 1; } } for(i=0;i DBKV_LEN_KEY ? DBKV_LEN_KEY : strlen(keystr), keystr); int result = KISSDB_get( &kdb, key, val ), not_found = result > 0; // -1:I/O error, 0:ok, 1:not found ok = result >= 0; //printf("rd %s=%s (found: %d) (rc: %d)\n", key, val, !not_found, result); KISSDB_close( &kdb ); } return !!ok; } bool dbkv_write( const char *dbfile, const char *keystr, const char *val ) { KISSDB kdb = {0}; int ok = 0 == KISSDB_open( &kdb, dbfile, KISSDB_OPEN_MODE_RWCREAT, DBKV_HASH_BUCKET, DBKV_LEN_KEY, DBKV_LEN_VALUE); if( ok ) { dbkv_key key = {0}; strcpy( key, keystr ); //sprintf(key, "%.*s", strlen(keystr) > DBKV_LEN_KEY ? DBKV_LEN_KEY : strlen(keystr), keystr); int result = KISSDB_put( &kdb, key, val ); // -1:I/O error, 0:ok ok = result >= 0; //printf("wr (rc: %d)\n", result); KISSDB_close( &kdb ); } return !!ok; } #ifdef DBKV_DEMO #include int main( int argc, char **argv ) { dbkv_val val = "0"; // default value, if not found dbkv_read( "db.kvs", "/hello", val ); printf("/hello=%s\n", val); val[0] ++; if( val[0] > '9' ) val[0] = '0'; dbkv_write( "db.kvs", "/hello", val ); } #define main main__ #endif // KBDV_DEMO #endif // KBDV_C ================================================ FILE: vault/bin_json5.c ================================================ // JSON5 + SJSON parser module // // License: // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, // publish, and distribute this file as you see fit. // No warranty is implied, use at your own risk. // // Credits: // Dominik Madarasz (original code) (GitHub: zaklaus) // r-lyeh (fork) #ifndef JSON5_H #define JSON5_H #ifndef JSON5_ASSERT #define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0) #endif #include #include typedef enum json5_type { json5_undefined, // 0 json5_null, // 1 json5_bool, // 2 json5_object, // 3 json5_string, // 4 json5_array, // 5 json5_integer, // 6 json5_real, // 7 } json5_type; typedef struct json5 { char* name; #ifdef NDEBUG unsigned type : 3; #else json5_type type; #endif unsigned count : 29; union { struct json5* array; struct json5* nodes; int64_t integer; double real; char* string; int boolean; }; } json5; char* json5_parse(json5 *root, char *source, int flags); void json5_write(FILE *fp, const json5 *root); void json5_free(json5 *root); #endif // JSON5_H // json5 ---------------------------------------------------------------------- #ifdef JSON5_C #pragma once #include #include #include #include #include char *json5__trim(char *p) { while (*p) { /**/ if( isspace(*p) ) ++p; else if( p[0] == '/' && p[1] == '*' ) { // skip C comment for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {} if( *p ) p += 2; } else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment for( p += 2; *p && p[0] != '\n'; ++p) {} if( *p ) ++p; } else break; } return p; } char *json5__parse_value(json5 *obj, char *p, char **err_code); char *json5__parse_string(json5 *obj, char *p, char **err_code) { assert(obj && p); if( *p == '"' || *p == '\'' || *p == '`' ) { obj->type = json5_string; obj->string = p + 1; char eos_char = *p, *b = obj->string, *e = b; while (*e) { /**/ if( *e == '\\' && (e[1] == eos_char) ) ++e; else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' '; else if( *e == eos_char ) break; ++e; } *e = '\0'; return p = e + 1; } //JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } char *json5__parse_object(json5 *obj, char *p, char **err_code) { assert(obj && p); if( 1 /* *p == '{' */ ) { /* <-- for SJSON */ int skip = *p == '{'; /* <-- for SJSON */ obj->type = json5_object; obj->nodes = 0; obj->count = 0; while (*p) { json5 node = { 0 }; do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' ); if( *p == '}' ) { ++p; break; } // @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) { else if( isalpha(*p) || *p == '_' || *p == '$' ) { // also || is_unicode(p) node.name = p; do { ++p; } while (*p && (*p == '_' || isalpha(*p) || isdigit(*p)) ); // also || is_unicode(p) char *e = p; p = json5__trim(p); *e = '\0'; } else { //if( *p == '"' || *p == '\'' || *p == '`' ) { char *ps = json5__parse_string(&node, p, err_code); if( !ps ) { return NULL; } p = ps; node.name = node.string; p = json5__trim(p); } // @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6 if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) { JSON5_ASSERT; *err_code = "json5_error_invalid_name"; return NULL; } if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) { JSON5_ASSERT; *err_code = "json5_error_invalid_name"; return NULL; } p = json5__trim(p + 1); p = json5__parse_value(&node, p, err_code); if( *err_code[0] ) { return NULL; } if( node.type != json5_undefined ) { array_push(obj->nodes, node); ++obj->count; } if( *p == '}') { ++p; break; } } return p; } JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } char *json5__parse_value(json5 *obj, char *p, char **err_code) { assert(obj && p); p = json5__trim(p); char *is_string = json5__parse_string(obj, p, err_code); if( is_string ) { p = is_string; if( *err_code[0] ) { return NULL; } } else if( *p == '{' ) { p = json5__parse_object( obj, p, err_code ); if( *err_code[0] ) { return NULL; } } else if( *p == '[' ) { obj->type = json5_array; obj->array = 0; obj->count = 0; while (*p) { json5 elem = { 0 }; do { p = json5__trim(p + 1); } while( *p == ',' ); if( *p == ']') { ++p; break; } p = json5__parse_value(&elem, p, err_code); if( *err_code[0] ) { return NULL; } if( elem.type != json5_undefined ) { array_push(obj->array, elem); ++obj->count; } if (*p == ']') { ++p; break; } } } else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) { const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity" }; const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 }; for( int i = 0; labels[i]; ++i ) { if( !strncmp(p, labels[i], lenghts[i] ) ) { p += lenghts[i]; #ifdef _MSC_VER // somehow, NaN is apparently signed in MSC /**/ if( i >= 5 ) obj->type = json5_real, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN; #else /**/ if( i >= 5 ) obj->type = json5_real, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN; #endif else if( i >= 1 ) obj->type = json5_bool, obj->boolean = i <= 2; else obj->type = json5_null; break; } } if( obj->type == json5_undefined ) { JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } } else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) { char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0; while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) { is_hex |= (*p | 32) == 'x'; is_dbl |= *p == '.'; *buf++ = *p++; } obj->type = is_dbl ? json5_real : json5_integer; /**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real ); else if( is_hex ) sscanf( buffer, "%llx", &obj->integer ); // SCNx64 -> inttypes.h else sscanf( buffer, "%lld", &obj->integer ); // SCNd64 -> inttypes.h } else { return NULL; } return p; } char *json5_parse(json5 *root, char *p, int flags) { char *err_code = ""; *root = (json5) {0}; if( p && p[0] ) { p = json5__trim(p); if( *p == '[' ) { /* <-- for SJSON */ json5__parse_value(root, p, &err_code); } else { json5__parse_object(root, p, &err_code); /* <-- for SJSON */ } } else { root->type = json5_object; } return err_code[0] ? err_code : 0; } void json5_free(json5 *root) { if( root->type == json5_array && root->array ) { for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) { json5_free(root->array + i); } array_free(root->array); } if( root->type == json5_object && root->nodes ) { for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) { json5_free(root->nodes + i); } array_free(root->nodes); } *root = (json5) {0}; // needed? } void json5_write(FILE *fp, const json5 *o) { #ifdef _MSC_VER static __declspec(thread) int indent = 0; #else static __thread int indent = 0; #endif int tabs = 1; // 0,1,2,4,8 if( o->name ) { fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":"); } /**/ if( o->type == json5_null ) fprintf(fp, "%s", "null"); else if( o->type == json5_bool ) fprintf(fp, "%s", o->boolean ? "true" : "false"); else if( o->type == json5_integer ) fprintf(fp, "%lld", o->integer); else if( o->type == json5_real ) { /**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" ); else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" ); else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness } #if 0 else if( o->type == json5_string ) { // write (escaped) string char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256]; for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i]; const char *b = o->string, *e = strpbrk(b, chars), *sep = "\""; while( e ) { fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] ); e = strpbrk( b = e + 1, chars); sep = ""; } fprintf(fp, "%s%s\"", sep, b); } #else else if( o->type == json5_string ) { // write string fprintf(fp, "\"%s\"", o->string); } #endif else if( o->type == json5_array ) { const char *sep = ""; fprintf(fp, "%s", tabs ? "[ " : "["); for( int i = 0, cnt = o->count; i < cnt; ++i ) { fprintf(fp, "%s", sep); sep = tabs ? ", " : ","; json5_write(fp, o->array + i); } fprintf(fp, "%s", tabs ? " ]" : "]"); } else if( o->type == json5_object ) { const char *sep = ""; fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":""); for( int i = 0, cnt = o->count; i < cnt; ++i ) { fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ","; json5_write(fp, o->nodes + i); } fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, ""); } else { char p[16] = {0}; JSON5_ASSERT; /* "json5_error_invalid_value"; */ } } #endif // JSON5_C #ifdef JSON5_BENCH #include int main() { // https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/ char *content = 0; for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) { fseek(fp, 0L, SEEK_END); size_t pos = ftell(fp); fseek(fp, 0L, SEEK_SET); content = (char*)malloc( pos + 1 ); fread(content, 1, pos, fp); content[pos] = 0; } if( content ) { clock_t start = clock(); json5 root = {0}; char *error = json5_parse(&root, content, 0); clock_t end = clock(); double delta = ( end - start ) / (double)CLOCKS_PER_SEC; if( !error ) { printf("Parsing time: %.3fms\n", delta*1000); printf("Total nodes: %d\n", array_count(root.array)); printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string, root.array[0].nodes[1].string, root.array[0].nodes[2].string); } else { printf("Error: %s\n", error); } json5_free(&root); free(content); } } #endif #ifdef JSON5_DEMO int main() { char source5[] = " // comments\n" /* json5 sample */ " unquoted: 'and you can quote me on that',\n" " singleQuotes: 'I can use \"double quotes\" here',\n" " lineBreaks : \"Look, Mom! \\\n" "No \\n's!\",\n" " hexadecimal: 0x100,\n" " leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n" " positiveSign: +1,\n" " trailingComma: 'in objects', andIn: ['arrays', ],\n" " \"backwardsCompatible\": \"with JSON\",\n" "" " ip = \"127.0.0.1\"\n" /* sjson sample */ " port = 8888\n" "" " /* comment //nested comment*/\n" /* tests */ " // comment /*nested comment*/\n" " nil: null," " \"+lšctžýáíé=:\": true,,,," " huge: 2.2239333e5, " " array: [+1,2,-3,4,5], " " hello: 'world /*comment in string*/ //again', " " abc: 42.67, def: false, " " children : { a: 1, b: 2, }," " invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ]," "" " multiline: `this is\n" "a multiline string\n" "yeah`" "}\n"; json5 root = { 0 }; char *error = json5_parse(&root, source5, 0); if( error ) { printf("Error: %s\n", error); } else { json5_write(stdout, &root); } json5_free(&root); } #endif ================================================ FILE: vault/buf.c ================================================ // - rlyeh, public domain #ifdef BUF_C #define ARC4_C #define BASE64_C #define BASE92_C #define COBS_C #define CRC_C #define ENDIAN_C #define INTERLEAVE_C #define NETSTRING_C #define ZIGZAG_C #define PACK_C #define HALF_C #endif #include "os.c" #include "buf_arc4.c" // buffer_crypt_arc4 #include "buf_base64.c" // buffer_rebase_base64 #include "buf_base92.c" // buffer_rebase_base92 #include "buf_cobs.c" // buffer_rebase_cobs #include "buf_crc.c" // buffer_checksum_crc #include "buf_endian.c" // buffer_utils_endianness #include "buf_interleave.c" // buffer_utils_interleave #include "buf_netstring.c" // buffer_escape_netstring #include "buf_zigzag.c" // buffer_utils_zigzag #include "buf_packint.h" // integer packing #include "buf_packvli.h" // variable int packing #include "buf_pack754.h" // float754 packing #include "buf_packhalf.h" // floathalf packing #include "buf_pack.c" // data packer ================================================ FILE: vault/buf_arc4.c ================================================ // ARC4 de/cryptor. Based on code by Mike Shaffer. // - rlyeh, public domain. void *arc4( void *buffer, unsigned buflen, const void *pass, unsigned passlen ); #ifdef ARC4_C #pragma once #include void *arc4( void *buf_, unsigned buflen, const void *pass_, unsigned passlen ) { // [ref] http://www.4guysfromrolla.com/webtech/code/rc4.inc.html assert(passlen); int sbox[256], key[256]; char *buf = (char*)buf_; const char *pass = (const char*)pass_; for( unsigned a = 0; a < 256; a++ ) { key[a] = pass[a % passlen]; sbox[a] = a; } for( unsigned a = 0, b = 0; a < 256; a++ ) { b = (b + sbox[a] + key[a]) % 256; int swap = sbox[a]; sbox[a] = sbox[b]; sbox[b] = swap; } for( unsigned a = 0, b = 0, i = 0; i < buflen; ++i ) { a = (a + 1) % 256; b = (b + sbox[a]) % 256; int swap = sbox[a]; sbox[a] = sbox[b]; sbox[b] = swap; buf[i] ^= sbox[(sbox[a] + sbox[b]) % 256]; } return buf_; } #ifdef ARC4_DEMO #include #include int main( int argc, char **argv ) { char buffer[] = "Hello world."; int buflen = strlen(buffer); char *password = argc > 1 ? argv[1] : "abc123"; int passlen = strlen(password); printf("Original: %s\n", buffer); printf("Password: %s\n", password); char *encrypted = arc4( buffer, buflen, password, passlen ); printf("ARC4 Encrypted text: '%s'\n", encrypted); char *decrypted = arc4( buffer, buflen, password, passlen ); printf("ARC4 Decrypted text: '%s'\n", decrypted); } #endif // ARC4_DEMO #endif // ARC4_C ================================================ FILE: vault/buf_base64.c ================================================ // base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN). // - rlyeh, public domain // // note: functions return 0 on error // note: base64("")=="" #ifndef BASE64_H #define BASE64_H unsigned base64_bounds(unsigned inlen); unsigned base64_encode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned base64_decode(const void *in, unsigned inlen, void *out, unsigned outlen); #endif #ifdef BASE64_C #pragma once #include #include #include #include #include unsigned base64_bounds(unsigned size) { return 4 * ((size + 2) / 3); } unsigned base64_encode(const void *in_, unsigned inlen, void *out_, unsigned outlen) { uint_least32_t v; unsigned ii, io, rem; char *out = (char *)out_; const unsigned char *in = (const unsigned char *)in_; const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) { unsigned char ch; ch = in[ii]; v = (v << 8) | ch; rem += 8; while (rem >= 6) { rem -= 6; if (io >= outlen) return 0; /* truncation is failure */ out[io ++] = base64enc_tab[(v >> rem) & 63]; } } if (rem) { v <<= (6 - rem); if (io >= outlen) return 0; /* truncation is failure */ out[io ++] = base64enc_tab[v & 63]; } if (io < outlen) out[io] = 0; return io; } unsigned base64_decode(const void *in_, unsigned inlen, void *out_, unsigned outlen) { uint_least32_t v; unsigned ii, io, rem; char *out = (char *)out_; const unsigned char *in = (const unsigned char *)in_; const uint8_t base64dec_tab[256]= { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255, 62,255,255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, }; for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) { unsigned char ch; if (isspace(in[ii])) continue; if ((in[ii]=='=') || (!in[ii])) break; /* stop at = or null character*/ ch = base64dec_tab[(unsigned char)in[ii]]; if (ch == 255) break; /* stop at a parse error */ v = (v<<6) | ch; rem += 6; if (rem >= 8) { rem -= 8; if (io >= outlen) return 0; /* truncation is failure */ out[io ++] = (v >> rem) & 255; } } if (rem >= 8) { rem -= 8; if (io >= outlen) return 0; /* truncation is failure */ out[io ++] = (v >> rem) & 255; } return io; } #endif // BASE64_C ================================================ FILE: vault/buf_base92.c ================================================ // THE BEERWARE LICENSE (Revision 42): // wrote this file. As long as you retain this notice you // can do whatever you want with this stuff. If we meet some day, and you // think this stuff is worth it, you can buy me a beer in return // - Nathan Hwang (thenoviceoof) #ifndef BASE92_H #define BASE92_H unsigned base92_encode(const void *in, unsigned inlen, void* out, unsigned outlen); unsigned base92_decode(const void *in, unsigned inlen, void* out, unsigned outlen); unsigned base92_bounds(unsigned inlen); #endif #ifdef BASE92_C #pragma once #include #include unsigned base92_bounds(unsigned inlen) { unsigned size = (inlen * 8) % 13, extra_null = 1; if(size == 0) return 2 * ((inlen * 8) / 13) + extra_null; if(size < 7) return 2 * ((inlen * 8) / 13) + extra_null + 1; return 2 * ((inlen * 8) / 13) + extra_null + 2; } unsigned base92_encode(const void* in, unsigned inlen, void *out, unsigned size) { char *res = (char *)out; const unsigned char *str = (const unsigned char *)in; unsigned int j = 0; // j for encoded unsigned long workspace = 0; // bits holding bin unsigned short wssize = 0; // number of good bits in workspace unsigned char c; const unsigned char ENCODE_MAPPING[256] = { 33, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (inlen) { for (unsigned i = 0; i < inlen; i++) { workspace = workspace << 8 | str[i]; wssize += 8; if (wssize >= 13) { int tmp = (workspace >> (wssize - 13)) & 8191; c = ENCODE_MAPPING[(tmp / 91)]; if (c == 0) return 0; // illegal char res[j++] = c; c = ENCODE_MAPPING[(tmp % 91)]; if (c == 0) return 0; // illegal char res[j++] = c; wssize -= 13; } } // encode a last byte if (0 < wssize && wssize < 7) { int tmp = (workspace << (6 - wssize)) & 63; // pad the right side c = ENCODE_MAPPING[(tmp)]; if (c == 0) return 0; // illegal char res[j++] = c; } else if (7 <= wssize) { int tmp = (workspace << (13 - wssize)) & 8191; // pad the right side c = ENCODE_MAPPING[(tmp / 91)]; if (c == 0) return 0; // illegal char res[j++] = c; c = ENCODE_MAPPING[(tmp % 91)]; if (c == 0) return 0; // illegal char res[j++] = c; } } else { res[j++] = '~'; } // add null byte res[j] = 0; return j; } // this guy expects a null-terminated string // gives back a non-null terminated string, and properly filled len unsigned base92_decode(const void* in, unsigned size, void *out, unsigned outlen_unused) { const char* str = (const char*)in; unsigned char *res = (unsigned char *)out; int i, j = 0, b1, b2; unsigned long workspace = 0; unsigned short wssize = 0; const unsigned char DECODE_MAPPING[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; // handle small cases first if (size == 0 || (str[0] == '~' && str[1] == '\0')) { res[0] = 0; return 1; } // calculate size int len = ((size/2 * 13) + (size%2 * 6)) / 8; // handle pairs of chars for (i = 0; i + 1 < size; i += 2) { b1 = DECODE_MAPPING[(str[i])]; b2 = DECODE_MAPPING[(str[i+1])]; workspace = (workspace << 13) | (b1 * 91 + b2); wssize += 13; while (wssize >= 8) { res[j++] = (workspace >> (wssize - 8)) & 255; wssize -= 8; } } // handle single char if (size % 2 == 1) { workspace = (workspace << 6) | DECODE_MAPPING[(str[size - 1])]; wssize += 6; while (wssize >= 8) { res[j++] = (workspace >> (wssize - 8)) & 255; wssize -= 8; } } //assert(j == len); return j; } #endif ================================================ FILE: vault/buf_cobs.c ================================================ // Based on code by Jacques Fortier. // "Redistribution and use in source and binary forms are permitted, with or without modification." // // Consistent Overhead Byte Stuffing is an encoding that removes all 0 bytes from arbitrary binary data. // The encoded data consists only of bytes with values from 0x01 to 0xFF. This is useful for preparing data for // transmission over a serial link (RS-232 or RS-485 for example), as the 0 byte can be used to unambiguously indicate // packet boundaries. COBS also has the advantage of adding very little overhead (at least 1 byte, plus up to an // additional byte per 254 bytes of data). For messages smaller than 254 bytes, the overhead is constant. // // This implementation is designed to be both efficient and robust. // The decoder is designed to detect malformed input data and report an error upon detection. // #ifndef COBS_H #define COBS_H unsigned cobs_bounds(unsigned len); unsigned cobs_encode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned cobs_decode(const void *in, unsigned inlen, void *out, unsigned outlen); #endif #ifdef COBS_C #pragma once #include #include #include unsigned cobs_bounds( unsigned len ) { return ceil(len / 254.0) + 1; } unsigned cobs_encode(const void *in, unsigned inlen, void *out, unsigned outlen) { const uint8_t *src = (const uint8_t *)in; uint8_t *dst = (uint8_t*)out; size_t srclen = inlen; uint8_t code = 1; size_t read_index = 0, write_index = 1, code_index = 0; while( read_index < srclen ) { if( src[ read_index ] == 0) { dst[ code_index ] = code; code = 1; code_index = write_index++; read_index++; } else { dst[ write_index++ ] = src[ read_index++ ]; code++; if( code == 0xFF ) { dst[ code_index ] = code; code = 1; code_index = write_index++; } } } dst[ code_index ] = code; return write_index; } unsigned cobs_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { const uint8_t *src = (const uint8_t *)in; uint8_t *dst = (uint8_t*)out; size_t srclen = inlen; uint8_t code, i; size_t read_index = 0, write_index = 0; while( read_index < srclen ) { code = src[ read_index ]; if( ((read_index + code) > srclen) && (code != 1) ) { return 0; } read_index++; for( i = 1; i < code; i++ ) { dst[ write_index++ ] = src[ read_index++ ]; } if( (code != 0xFF) && (read_index != srclen) ) { dst[ write_index++ ] = '\0'; } } return write_index; } #ifdef COBS_DEMO #include #include #include void test( const char *buffer, int buflen ) { char enc[4096]; int enclen = cobs_encode( buffer, buflen, enc, 4096 ); char dec[4096]; int declen = cobs_decode( enc, enclen, dec, 4096 ); assert( enclen >= buflen ); assert( declen == buflen ); assert( memcmp(dec, buffer, buflen) == 0 ); printf("%d->%d->%d (+%d extra bytes)\n", declen, enclen, declen, enclen - declen); } int main() { const char *null = 0; test( null, 0 ); const char empty[] = ""; test( empty, sizeof(empty) ); const char text[] = "hello world\n"; test( text, sizeof(text) ); const char bintext[] = "hello\0\0\0world\n"; test( bintext, sizeof(bintext) ); const char blank[512] = {0}; test( blank, sizeof(blank) ); char longbintext[1024]; for( int i = 0; i < 1024; ++i ) longbintext[i] = (unsigned char)i; test( longbintext, sizeof(longbintext) ); assert(~puts("Ok")); } #define main main__ #endif // COBS_DEMO #endif // COBS_C ================================================ FILE: vault/buf_crc.c ================================================ // - rlyeh, public domain unsigned crc32(unsigned h, const void *ptr, unsigned len); uint64_t crc64(uint64_t h, const void *ptr, uint64_t len); #ifdef CRC_C #pragma once unsigned crc32(unsigned h, const void *ptr_, unsigned len) { // based on public domain code by Karl Malbrain const uint8_t *ptr = (const uint8_t *)ptr_; if (!ptr) return 0; const unsigned tbl[16] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; for(h = ~h; len--; ) { uint8_t b = *ptr++; h = (h >> 4) ^ tbl[(h & 15) ^ (b & 15)]; h = (h >> 4) ^ tbl[(h & 15) ^ (b >> 4)]; } return ~h; } uint64_t crc64(uint64_t h, const void *ptr, uint64_t len) { // based on public domain code by Lasse Collin // also, use poly64 0xC96C5795D7870F42 for crc64-ecma static uint64_t crc64_table[256]; static uint64_t poly64 = UINT64_C(0x95AC9329AC4BC9B5); if( poly64 ) { for( int b = 0; b < 256; ++b ) { uint64_t r = b; for( int i = 0; i < 8; ++i ) { r = r & 1 ? (r >> 1) ^ poly64 : r >> 1; } crc64_table[ b ] = r; //printf("%016llx\n", crc64_table[b]); } poly64 = 0; } const uint8_t *buf = (const uint8_t *)ptr; uint64_t crc = ~h; // ~crc; while( len != 0 ) { crc = crc64_table[(uint8_t)crc ^ *buf++] ^ (crc >> 8); --len; } return ~crc; } #endif ================================================ FILE: vault/buf_endian.c ================================================ // binary endianness ---------------------------------------------------------- // - rlyeh, public domain #pragma once #include static uint16_t bswap16( uint16_t x ) { return (x << 8) | (x >> 8); } static uint32_t bswap32( uint32_t x ) { x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); return (x << 16) | (x >> 16); } static uint64_t bswap64( uint64_t x ) { x = ((x << 8) & 0xff00ff00ff00ff00ULL) | ((x >> 8) & 0x00ff00ff00ff00ffULL); x = ((x << 16) & 0xffff0000ffff0000ULL) | ((x >> 16) & 0x0000ffff0000ffffULL); return (x << 32) | (x >> 32); } static int is_big() { return (*(uint16_t *)"\0\1") == 1; } static int is_little() { return (*(uint16_t *)"\0\1") != 1; } #if defined(_MSC_VER) #include #define bswap16 _byteswap_ushort #define bswap32 _byteswap_ulong #define bswap64 _byteswap_uint64 #elif defined __GNUC__ #define bswap16 __builtin_bswap16 #define bswap32 __builtin_bswap32 #define bswap64 __builtin_bswap64 #endif ================================================ FILE: vault/buf_interleave.c ================================================ // array de/interleaving // - rlyeh, public domain. // // results: // R0G0B0 R1G1B1 R2G2B2... -> R0R1R2... B0B1B2... G0G1G2... // R0G0B0A0 R1G1B1A1 R2G2B2A2... -> R0R1R2... A0A1A2... B0B1B2... G0G1G2... #ifndef INTERLEAVE_H #define INTERLEAVE_H void *interleave( void *out, const void *list, int list_count, int sizeof_item, unsigned columns ); #endif // ----------------------------------------------------------------------------- #ifdef INTERLEAVE_C #pragma once #include #include void *interleave( void *out, const void *list, int list_count, int sizeof_item, unsigned columns ) { void *bak = out; assert( columns < list_count ); // required int row_count = list_count / columns; for( int offset = 0; offset < columns; offset++ ) { for( int row = 0; row < row_count; row++ ) { memcpy( out, &((char*)list)[ (offset + row * columns) * sizeof_item ], sizeof_item ); out = ((char*)out) + sizeof_item; } } return bak; } #ifdef INTERLEAVE_DEMO #include void test( const char *name, int interleaving, int deinterleaving, const char *original ) { char interleaved[128] = {0}; interleave( interleaved, original, strlen(original)/2, 2, interleaving ); char deinterleaved[128] = {0}; interleave( deinterleaved, interleaved, strlen(original)/2, 2, deinterleaving ); printf( "\n%s\n", name ); printf( "original:\t%s\n", original ); printf( "interleaved:\t%s\n", interleaved ); printf( "deinterleaved:\t%s\n", deinterleaved ); assert( 0 == strcmp(original, deinterleaved) ); } int main() { test("audio 2ch", 2, 3, "L0R0" "L1R1" "L2R2" ); test("image 3ch", 3, 3, "R0G0B0" "R1G1B1" "R2G2B2" ); test("image 4ch", 4, 3, "R0G0B0A0" "R1G1B1A1" "R2G2B2A2" ); test("audio 5ch", 5, 3, "A0B0C0L0R0" "A1B1C1L1R1" "A2B2C2L2R2" ); test("audio 5.1ch", 6, 3, "A0B0C0L0R0S0" "A1B1C1L1R1S1" "A2B2C2L2R2S2" ); test("opengl material 9ch", 9, 3, "X0Y0Z0q0w0e0r0u0v0" "X1Y1Z1q1w1e1r1u1v1" "X2Y2Z2q2w2e2r2u2v2" ); test("opengl material 10ch", 10, 3, "X0Y0Z0q0w0e0r0s0u0v0" "X1Y1Z1q1w1e1r1s1u1v1" "X2Y2Z2q2w2e2r2s2u2v2" ); assert(~puts("Ok")); } #endif // INTERLEAVE_DEMO #endif // INTERLEAVE_C ================================================ FILE: vault/buf_netstring.c ================================================ // netstring en/decoder // - rlyeh, public domain. unsigned netstring_bounds(unsigned inlen); unsigned netstring_encode(const char *in, unsigned inlen, char *out, unsigned outlen); unsigned netstring_decode(const char *in, unsigned inlen, char *out, unsigned outlen); #ifdef NETSTRING_C #pragma once #include #include unsigned netstring_bounds(unsigned inlen) { return 5 + inlen + 3; // 3 for ;,\0 + 5 if inlen < 100k ; else (unsigned)ceil(log10(inlen + 1)) } unsigned netstring_encode(const char *in, unsigned inlen, char *out, unsigned outlen) { // if(outlen < netstring_bounds(inlen)) return 0; sprintf(out, "%u:%.*s,", inlen, inlen, in); return strlen(out); } unsigned netstring_decode(const char *in, unsigned inlen, char *out, unsigned outlen) { // if(outlen < inlen) return 0; const char *bak = in; sscanf(in, "%u", &outlen); while( *++in != ':' ); memcpy(out, in+1, outlen), out[outlen-1] = 0; // return outlen; // number of written bytes return (outlen + (in+2 - bak)); // number of read bytes } #ifdef NETSTRING_DEMO #include int main() { // encode const char text1[] = "hello world!", text2[] = "abc123"; unsigned buflen = netstring_bounds(strlen(text1) + strlen(text2)); char *buf = malloc(buflen), *ptr = buf; ptr += netstring_encode(text1, strlen(text1), ptr, buflen -= (ptr - buf)); ptr += netstring_encode(text2, strlen(text2), ptr, buflen -= (ptr - buf)); printf("%s -> ", buf); // decode char out[12]; unsigned plen = strlen(ptr = buf); while(plen > 0) { int written = netstring_decode(ptr, plen, out, 12); ptr += written; plen -= written; printf("'%s'(%s)(%d), ", out, ptr, plen ); } puts(""); } #define main main__ #endif // NETSTRING_DEMO #endif // NETSTRING_C ================================================ FILE: vault/buf_pack.c ================================================ // Based on code by Brian "Beej Jorgensen" Hall (public domain) [1]. // Based on code by Ginger Bill's half<->float (public domain) [2]. // - rlyeh, public domain. // // pack.c -- perl/python-ish pack/unpack functions // like printf and scanf, but for binary data. // // format flags: // (<) little endian (>) big endian (! also) (=) native endian // (c) 8-bit char (b) 8-bit byte // (h) 16-bit half (w) 16-bit word // (i) 32-bit integer (u) 32-bit unsigned (f) 32-bit float // (l) 64-bit long (q) 64-bit quad (d) 64-bit double // (v) varint // (s) string (64-bit varint length prepended) // (S) string (32-bit fixed length prepended) // (m) memblock (64-bit varint length prepended) // (M) memblock (32-bit fixed length prepended) // (z) memblock (zeroed) // (#) number of arguments processed (only when unpacking) // // @todo: // - (x) document & test flag // @totest: // - (s) string (64-bit variable length automatically prepended) // - (S) string (32-bit fixed length automatically prepended) // - (m) memblock (64-bit variable length automatically prepended) // - (M) memblock (32-bit fixed length automatically prepended) // - (z) memblock (zeroed) // - (#) number of arguments processed (only when unpacking) // // @refs: // [1] http://beej.us/guide/bgnet/output/html/multipage/advanced.html#serialization (Modified to encode NaN and Infinity as well.) // [2] https://github.com/gingerBill/gb // [3] http://www.mrob.com/pub/math/floatformats.html#minifloat // [4] microfloat: [0.002 to 240] range. // [5] half float: can approximate any 16-bit unsigned integer or its reciprocal to 3 decimal places. #ifndef PACK_H #define PACK_H #include #include #include // - save data dictated by the format string from the buffer. return: number of bytes written, or 0 if error. // if first argument is zero, returns number of bytes required for packing. int savef(FILE *file, const char *format, ...); int saveb(unsigned char *buf, const char *format, ...); // - load data dictated by the format string into the buffer. return: number of bytes read, or 0 if error. // if first argument is zero, returns number of bytes required for unpacking. int loadf(FILE *file, const char *format, ...); int loadb(const unsigned char *buf, const char *format, ...); #endif #ifdef PACK_C #pragma once // #include "packendian.c" // endianness // #include "pack754.c" // float754 packing -------------------------------------- // #include "packint.c" // integer packing --------------------------------------- // #include "packvli.c" // variable int packing ---------------------------------- // b/f packing ----------------------------------------------------------------- #include #include #include #include #include #include #include int loadb_(const uint8_t *buf, const char *fmt, va_list ap) { uint64_t args = 0; const uint8_t *buf0 = buf; char tmp[16+1]; //uint64_t size = 0, len; int32_t len, count, maxstrlen=0; int le = 0; if(!buf) // buffer estimation for(; *fmt != '\0'; fmt++) { switch(*fmt) { default: if (!isdigit(*fmt)) return 0; break; case '!': case '>': case '<': case '=': case ' ': // 0-bit endianness break; case 'c': case 'b': { int8_t c = (int8_t)va_arg(ap, int); buf += 1; } // 8-bit promoted break; case 'h': case 'w': { int16_t h = (int16_t)va_arg(ap, int); buf += 2; } // 16-bit promoted break; case 'i': case 'u': { int32_t l = va_arg(ap, int32_t); buf += 4; } // 32-bit break; case 'l': case 'q': { int64_t L = va_arg(ap, int64_t); buf += 8; } // 64-bit break; case 'f': { float f = (float)va_arg(ap, double); buf += 4; } // 32-bit float promoted break; case 'd': { double F = (double)va_arg(ap, double); buf += 8; } // 64-bit float (double) break; case 'v': { int64_t L = va_arg(ap, int64_t); buf += pack64iv(tmp, L); } // varint (8,16,32,64 ...) break; case 's': { char* s = va_arg(ap, char*); len = strlen(s); buf += pack64iv(tmp, len) + len; } // string, 64-bit variable length prepended break; case 'S': { char* s = va_arg(ap, char*); len = strlen(s); buf += 4 + len; } // string, 32-bit fixed length prepended break; case 'm': { int len = va_arg(ap, int); char *s = va_arg(ap, char*); buf += pack64iv(tmp, len) + len; } // memblock, 64-bit variable length prepended break; case 'M': { int len = va_arg(ap, int); char *s = va_arg(ap, char*); buf += 4 + len; } // memblock, 32-bit fixed length prepended break; case 'z': { int len = va_arg(ap, int); buf += len; } // memblock (zeroed) } } if(buf) // buffer unpacking for(; *fmt != '\0'; fmt++) { switch(*fmt) { default: if (isdigit(*fmt)) { // track max str len maxstrlen = maxstrlen * 10 + (*fmt-'0'); } else { return 0; } break; case ' ': break; case '!': le = 0; break; case '>': le = 0; break; case '<': le = 1; break; case '=': le = is_little() ? 1 : 0; break; case 'c': case 'b': ++args; { // 8-bit int8_t *v = va_arg(ap, int8_t*); *v = *buf <= 0x7f ? (int8_t)*buf : -1 -(uint8_t)(0xffu - *buf); buf += 1; } break; case 'h': case 'w': ++args; { // 16-bit int16_t *v = va_arg(ap, int16_t*); *v = unpack16i(buf, le); buf += 2; } break; case 'i': case 'u': ++args; { // 32-bit int32_t *v = va_arg(ap, int32_t*); *v = unpack32i(buf, le); buf += 4; } break; case 'l': case 'q': ++args; { // 64-bit int64_t *v = va_arg(ap, int64_t*); *v = unpack64i(buf, le); buf += 8; } break; case 'v': ++args; { // varint (8,16,32,64 ...) int64_t *L = va_arg(ap, int64_t*); buf += unpack64iv(buf, L); } break; case 'f': ++args; { // 32-bit float float *v = va_arg(ap, float*); int32_t i = unpack32i(buf, le); *v = unpack754_32(i); buf += 4; } break; case 'd': ++args; { // 64-bit float (double) double *v = va_arg(ap, double*); int64_t i = unpack64i(buf, le); *v = unpack754_64(i); buf += 8; } break; case 'S': ++args; { // string, 32-bit fixed length prepended char *s = va_arg(ap, char*); int64_t vlen = unpack32i(buf, le), read = 4; count = (maxstrlen > 0 && vlen >= maxstrlen ? maxstrlen - 1 : vlen); memcpy(s, buf + read, count); s[count] = '\0'; buf += read + vlen; } break; case 's': ++args; { // string, 64-bit variable length prepended char *s = va_arg(ap, char*); int64_t vlen, read = unpack64iv(buf, &vlen); count = (maxstrlen > 0 && vlen >= maxstrlen ? maxstrlen - 1 : vlen); memcpy(s, buf + read, count); s[count] = '\0'; buf += read + vlen; } break; case 'M': ++args; { // memblock, 32-bit fixed length prepended char *s = va_arg(ap, char*); int64_t vlen = unpack64iv(buf, &vlen), read = 4; count = vlen; //(maxstrlen > 0 && vlen >= maxstrlen ? maxstrlen - 1 : vlen); memcpy(s, buf + read, count); //s[count] = '\0'; buf += read + vlen; } break; case 'm': ++args; { // memblock, 64-bit variable length prepended char *s = va_arg(ap, char*); int64_t vlen, read = unpack64iv(buf, &vlen); count = vlen; //(maxstrlen > 0 && vlen >= maxstrlen ? maxstrlen - 1 : vlen); memcpy(s, buf + read, count); //s[count] = '\0'; buf += read + vlen; } break; case 'z': ++args; { // zero-init mem block int *l = va_arg(ap, int*); const uint8_t *prev = buf; while( *buf == 0 ) ++buf; *l = buf - prev; } break; case '#': { int *l = va_arg(ap, int*); *l = args; } } if (!isdigit(*fmt)) { maxstrlen = 0; } } return (int)( buf - buf0 ); } int saveb_(uint8_t *buf, const char *fmt, va_list ap) { uint64_t size = 0, len; int le = 0; // buffer estimation if( !buf ) { return loadb_(buf, fmt, ap); // + strlen(buf) * 17; // worse (v)arint estimation for 128-bit ints (17 bytes each) } // buffer packing for(; *fmt != '\0'; fmt++) { switch(*fmt) { default: size = 0; // error break; case '!': le = 0; break; case '>': le = 0; break; case '<': le = 1; break; case ' ': le = le; break; case '=': le = is_little() ? 1 : 0; break; case 'c': case 'b': { // 8-bit int v = (int8_t)va_arg(ap, int /*promoted*/ ); *buf++ = (v>>0)&0xff; size += 1; } break; case 'h': case 'w': { // 16-bit int v = (int16_t)va_arg(ap, int /*promoted*/ ); pack16i(buf, v, le); buf += 2; size += 2; } break; case 'i': case 'u': { // 32-bit int32_t v = va_arg(ap, int32_t); pack32i(buf, v, le); buf += 4; size += 4; } break; case 'l': case 'q': { // 64-bit int64_t v = va_arg(ap, int64_t); pack64i(buf, v, le); buf += 8; size += 8; } break; case 'v': { // varint (8,16,32,64 ...) int64_t v = va_arg(ap, int64_t); int64_t L = pack64iv(buf, v); buf += L; size += L; } break; case 'f': { // 32-bit float double v = (float)va_arg(ap, double /*promoted*/ ); int32_t i = pack754_32(v); // convert to IEEE 754 pack32i(buf, i, le); buf += 4; size += 4; } break; case 'd': { // 64-bit float (double) double v = (double)va_arg(ap, double); int64_t i = pack754_64(v); // convert to IEEE 754 pack64i(buf, i, le); buf += 8; size += 8; } break; case 'S': { // string, 32-bit fixed length prepended char* s = va_arg(ap, char*); int len = strlen(s); pack32i(buf, len, le); memcpy(buf + 4, s, len); buf += 4 + len; size += 4 + len; } break; case 's': { // string, 64-bit variable length prepended char* s = va_arg(ap, char*); int len = strlen(s); int64_t L = pack64iv(buf, len); memcpy(buf + L, s, len); buf += L + len; size += L + len; } break; case 'M': { // memblock, 32-bit fixed length prepended int len = va_arg(ap, int); char* s = va_arg(ap, char*); pack32i(buf, len, le); memcpy(buf + 4, s, len); buf += 4 + len; size += 4 + len; } break; case 'm': { // memblock, 64-bit variable length prepended int len = va_arg(ap, int); char* s = va_arg(ap, char*); int64_t L = pack64iv(buf, len); memcpy(buf + L, s, len); buf += L + len; size += L + len; } break; case 'z': { // memblock (zeroed) int len = va_arg(ap, int); memset(buf, 0, len); buf += len; size += len; } } } return (int)size; } int saveb(uint8_t *buf, const char *fmt, ...) { va_list ap; va_start(ap, fmt); int rc = saveb_( buf, fmt, ap); va_end(ap); return rc; } int loadb(const uint8_t *buf, const char *fmt, ...) { va_list ap; va_start(ap, fmt); int rc = loadb_( buf, fmt, ap); va_end(ap); return rc; } int savef(FILE *fp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); // estimate bytes int req = saveb_( 0, fmt, ap); char stack[4096]; char *buf = req < 4096 ? stack : (char*)calloc(1, req + 1 ); int rc = saveb_(buf, fmt, ap); fwrite(buf, req,1, fp); if( !(req < 4096) ) free(buf); va_end(ap); return rc; } int loadf(FILE *fp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); // estimate bytes int req = loadb_( 0, fmt, ap) * 2; // *2 in case it is underestimated char stack[4096]; char *buf = req < 4096 ? stack : (char*)calloc(1, req + 1 ); fread(buf, req,1, fp); int rc = loadb_(buf, fmt, ap); if( !(req < 4096) ) free(buf); va_end(ap); return rc; } #endif #ifdef PACK_DEMO typedef struct bootsector { uint8_t jump_instruction[3]; uint8_t oem_name[8]; uint16_t bytes_per_sector; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t fat_copies; uint16_t max_dirs; uint16_t sector_count; uint8_t media_descriptor; uint16_t sectors_per_fat; uint16_t sectors_per_head; uint16_t heads; uint32_t hidden_sectors; uint32_t sector_countz; } bootsector; int main() { const char *dna = "3b8bhbhbbhhbhhhuu"; // "1c1h212122122233"; "i3c8chchchhchhhdd" bootsector fat = { {0,1,2},{3,4,5,6,7,8,9,10},11,12,13,14,15,16,17,18,19,20,21,22 }; hexdump(&fat, sizeof(bootsector)); FILE *fp = fopen("test.mbr", "wb"); savef(fp, dna, &fat); // fclose(fp); memset(&fat, 0, sizeof(bootsector)); fp = fopen("test.mbr", "rb"); loadf(fp, dna, &fat); fclose(fp); hexdump(&fat, sizeof(bootsector)); } #endif ================================================ FILE: vault/buf_pack754.h ================================================ // float754.c packing ---------------------------------------------------------- // [1] http://www.mrob.com/pub/math/floatformats.html#minifloat // [2] microfloat: [0.002 to 240] range. // [3] half float: can approximate any 16-bit unsigned integer or its reciprocal to 3 decimal places. // - rlyeh, public domain #pragma once #include #include #define pack754_8(f) ( pack754((f), 8, 4)) //@r-lyeh, add microfloat [1][3] #define pack754_16(f) ( pack754((f), 16, 5)) //@r-lyeh, add half [1][4] #define pack754_32(f) ( pack754((f), 32, 8)) #define pack754_64(f) ( pack754((f), 64, 11)) #define unpack754_8(u) (unpack754((u), 8, 4)) //@r-lyeh, add microfloat #define unpack754_16(u) (unpack754((u), 16, 5)) //@r-lyeh, add half #define unpack754_32(u) (unpack754((u), 32, 8)) #define unpack754_64(u) (unpack754((u), 64, 11)) #if 1 // [src] http://beej.us/guide/bgnet/output/html/multipage/advanced.html#serialization // Modified to encode NaN and Infinity as well. #pragma once #include #include static uint64_t pack754(long double f, unsigned bits, unsigned expbits) { long double fnorm; int shift; long long sign, exp, significand; unsigned significandbits = bits - expbits - 1; // -1 for sign bit if (f == 0.0) return 0; // get this special case out of the way //@r{ beware! works for 32/64 only else if (f == INFINITY) return 0x7f800000ULL << (bits - 32); // 0111 1111 1000 else if (f == -INFINITY) return 0xff800000ULL << (bits - 32); else if (f != f) return 0x7fc00000ULL << (bits - 32); // 0111 1111 1100 NaN //@r} // check sign and begin normalization if (f < 0) { sign = 1; fnorm = -f; } else { sign = 0; fnorm = f; } // get the normalized form of f and track the exponent shift = 0; while(fnorm >= 2.0) { fnorm /= 2.0; shift++; } while(fnorm < 1.0) { fnorm *= 2.0; shift--; } fnorm = fnorm - 1.0; // calculate the binary form (non-float) of the significand data significand = fnorm * ((1LL<>significandbits)&((1LL< 0) { result *= 2.0; shift--; } while(shift < 0) { result /= 2.0; shift++; } // sign it result *= (i>>(bits-1))&1? -1.0: 1.0; return result; } #else static uint64_t pack754(long double f, unsigned bits, unsigned expbits) { long double fnorm; int shift; long long sign, exp, significand; unsigned significandbits = bits - expbits - 1; // -1 for sign bit //@r-lyeh{ #define FLOAT_NINF (((0xFFFFffffFFFFffffULL) >> significandbits) << significandbits) #define FLOAT_PINF (((0x7FFFffffFFFFffffULL) >> significandbits) << significandbits) #define FLOAT_NANF (FLOAT_PINF | (1ULL << (significandbits-1))) #define FLOAT_NEG0 (1ULL << (bits-1)) /**/ if(f != f) return FLOAT_NANF; else if(f == 0.0f) return signbit(f) ? FLOAT_NEG0 : 0; else if(f/f != f/f) return f > 0 ? FLOAT_PINF : FLOAT_NINF; //@r-lyeh} // check sign and begin normalization if (f < 0) { sign = 1; fnorm = -f; } else { sign = 0; fnorm = f; } // get the normalized form of f and track the exponent shift = 0; while(fnorm >= 2.0) { fnorm /= 2.0; shift++; } while(fnorm < 1.0) { fnorm *= 2.0; shift--; } fnorm = fnorm - 1.0; // calculate the binary form (non-float) of the significand data significand = fnorm * ((1LL<>significandbits)&((1LL< 0) { result *= 2.0; shift--; } while(shift < 0) { result /= 2.0; shift++; } // sign it result *= (i>>(bits-1))&1? -1.0: 1.0; return result; } #endif typedef int static_assert_flt[ sizeof(float) == 4 ]; typedef int static_assert_dbl[ sizeof(double) == 8 ]; ================================================ FILE: vault/buf_packhalf.h ================================================ // from ginger bill's gbmath.h (public domain) #ifndef HALF_H #define HALF_H typedef uint16_t half; float half_to_float(half value); half float_to_half(float value); #endif #ifdef HALF_C #pragma once float half_to_float(half value) { union { unsigned int i; float f; } result; int s = (value >> 15) & 0x001; int e = (value >> 10) & 0x01f; int m = value & 0x3ff; if (e == 0) { if (m == 0) { /* Plus or minus zero */ result.i = (unsigned int)(s << 31); return result.f; } else { /* Denormalized number */ while (!(m & 0x00000400)) { m <<= 1; e -= 1; } e += 1; m &= ~0x00000400; } } else if (e == 31) { if (m == 0) { /* Positive or negative infinity */ result.i = (unsigned int)((s << 31) | 0x7f800000); return result.f; } else { /* Nan */ result.i = (unsigned int)((s << 31) | 0x7f800000 | (m << 13)); return result.f; } } e = e + (127 - 15); m = m << 13; result.i = (unsigned int)((s << 31) | (e << 23) | m); return result.f; } half float_to_half(float value) { union { unsigned int i; float f; } v; int i, s, e, m; v.f = value; i = (int)v.i; s = (i >> 16) & 0x00008000; e = ((i >> 23) & 0x000000ff) - (127 - 15); m = i & 0x007fffff; if (e <= 0) { if (e < -10) return (half)s; m = (m | 0x00800000) >> (1 - e); if (m & 0x00001000) m += 0x00002000; return (half)(s | (m >> 13)); } else if (e == 0xff - (127 - 15)) { if (m == 0) { return (half)(s | 0x7c00); /* NOTE(bill): infinity */ } else { /* NOTE(bill): NAN */ m >>= 13; return (half)(s | 0x7c00 | m | (m == 0)); } } else { if (m & 0x00001000) { m += 0x00002000; if (m & 0x00800000) { m = 0; e += 1; } } if (e > 30) { float volatile f = 1e12f; int j; for (j = 0; j < 10; j++) f *= f; /* NOTE(bill): Cause overflow */ return (half)(s | 0x7c00); } return (half)(s | (e << 10) | (m >> 13)); } } #endif ================================================ FILE: vault/buf_packint.h ================================================ // int packing ----------------------------------------------------------------- // - rlyeh, public domain #pragma once #include // pack16i() -- store a 16-bit int into a char buffer (like htons()) // pack32i() -- store a 32-bit int into a char buffer (like htonl()) // pack64i() -- store a 64-bit int into a char buffer (like htonl()) static void pack16i(uint8_t *buf, uint16_t i, int swap) { if( swap ) i = bswap16(i); memcpy( buf, &i, sizeof(i) ); } static void pack32i(uint8_t *buf, uint32_t i, int swap) { if( swap ) i = bswap32(i); memcpy( buf, &i, sizeof(i) ); } static void pack64i(uint8_t *buf, uint64_t i, int swap) { if( swap ) i = bswap64(i); memcpy( buf, &i, sizeof(i) ); } // unpack16i() -- unpack a 16-bit int from a char buffer (like ntohs()) // unpack32i() -- unpack a 32-bit int from a char buffer (like ntohl()) // unpack64i() -- unpack a 64-bit int from a char buffer (like ntohl()) // changes unsigned numbers to signed if needed. static int16_t unpack16i(const uint8_t *buf, int swap) { uint16_t i; memcpy(&i, buf, sizeof(i)); if( swap ) i = bswap16(i); return i <= 0x7fffu ? (int16_t)i : -1 -(uint16_t)(0xffffu - i); } static int32_t unpack32i(const uint8_t *buf, int swap) { uint32_t i; memcpy(&i, buf, sizeof(i)); if( swap ) i = bswap32(i); return i <= 0x7fffffffu ? (int32_t)i : -1 -(int32_t)(0xffffffffu - i); } static int64_t unpack64i(const uint8_t *buf, int swap) { uint64_t i; memcpy(&i, buf, sizeof(i)); if( swap ) i = bswap64(i); return i <= 0x7fffffffffffffffull ? (int64_t)i : -1 -(int64_t)(0xffffffffffffffffull - i); } ================================================ FILE: vault/buf_packvli.h ================================================ // vli varint spec (licensed under public domain, unlicense, CC0 and MIT-0: pick one). // - rlyeh. // 7 [0 xxxx xxx] 7-bit value in 1 byte (0- 127) // 7 [10 xxx xxx] [yyyyyyyy] 14-bit value in 2 bytes (0- 16,383) // 6 [110 xx xxx] [yyyyyyyy] [zzzzzzzz] 21-bit value in 3 bytes (0- 2,097,151) // 8 [111 00 xxx] [ 3 bytes] 27-bit value in 4 bytes (0- 134,217,727) // 8 [111 01 xxx] [ 4 bytes] 35-bit value in 5 bytes (0- 34,359,738,367) // 5 [111 10 xxx] [ 5 bytes] 43-bit value in 6 bytes (0- 8,796,093,022,207) // 8 [111 11 000] [ 6 bytes] 48-bit value in 7 bytes (0-281,474,976,710,655) // 8 [111 11 001] [ 7 bytes] 56-bit value in 8 bytes (...) // 8 [111 11 010] [ 8 bytes] 64-bit value in 9 bytes // 8 [111 11 011] [ 9 bytes] 72-bit value in 10 bytes // 8 [111 11 100] [10 bytes] 80-bit value in 11 bytes // A [111 11 101] [12 bytes] 96-bit value in 13 bytes // A [111 11 110] [14 bytes] 112-bit value in 15 bytes // A [111 11 111] [16 bytes] 128-bit value in 17 bytes // 1 1 2 3 = 7 #pragma once #if 0 // try // 7 [0 xxxx xxx] 7-bit value in 1 byte (0- 127) // 7 [10 xxx xxx] [yyyyyyyy] 14-bit value in 2 bytes (0- 16,383) // 6 [110 xx xxx] [yyyyyyyy] [zzzzzzzz] 21-bit value in 3 bytes (0- 2,097,151) // 8 [111 00 xxx] [ 3 bytes] 27-bit value in 4 bytes (0- 134,217,727) // 7 [111 01 xxx] [ 4 bytes] 35-bit value in 5 bytes (0- 34,359,738,367) // 8 [111 10 0xx] [ 5 bytes] 42-bit value in 6 bytes (0- 4,398,046,511,103) // 6 [111 10 1xx] [ 6 bytes] 50-bit value in 7 bytes (0-1,125,899,906,842,623) // 8 [111 11 000] [ 7 bytes] 56-bit value in 8 bytes (...) // 8 [111 11 001] [ 8 bytes] 64-bit value in 9 bytes // 8 [111 11 010] [ 9 bytes] 72-bit value in 10 bytes // 8 [111 11 011] [10 bytes] 80-bit value in 11 bytes // A [111 11 100] [12 bytes] 96-bit value in 13 bytes // A [111 11 101] [14 bytes] 112-bit value in 15 bytes // A [111 11 110] [16 bytes] 128-bit value in 17 bytes // N [111 11 111] [nn bytes] custom bit value in N+1 bytes // 1 1 2 1 2 = 7 #endif #include #include static uint64_t pack64uv( uint8_t *buffer, uint64_t value ) { #define ADD(bits) *buffer++ = (value >>= bits) if( value < (1ull<< 7) ) return *buffer = value, 1; if( value < (1ull<<14) ) return *buffer++ = 0x80|(value&0x3f), ADD(6), 2; if( value < (1ull<<21) ) return *buffer++ = 0xc0|(value&0x1f), ADD(5), ADD(8), 3; if( value < (1ull<<27) ) return *buffer++ = 0xe0|(value&0x07), ADD(3), ADD(8), ADD(8), 4; if( value < (1ull<<35) ) return *buffer++ = 0xe8|(value&0x07), ADD(3), ADD(8), ADD(8), ADD(8), 5; if( value < (1ull<<43) ) return *buffer++ = 0xf0|(value&0x07), ADD(3), ADD(8), ADD(8), ADD(8), ADD(8), 6; if( value < (1ull<<48) ) return *buffer++ = 0xf8|(value&0x00), ADD(0), ADD(8), ADD(8), ADD(8), ADD(8), ADD(8), 7; if( value < (1ull<<56) ) return *buffer++ = 0xf9|(value&0x00), ADD(0), ADD(8), ADD(8), ADD(8), ADD(8), ADD(8), ADD(8), 8; /*if( value < (1ull<<64))*/return *buffer++ = 0xfa|(value&0x00), ADD(0), ADD(8), ADD(8), ADD(8), ADD(8), ADD(8), ADD(8), ADD(8), 9; /*...*/ #undef ADD } static uint64_t unpack64uv( const uint8_t *buffer, uint64_t *value ) { uint64_t bytes, out = 0, shift = 0; const int table[] = { 6,7,8,9,10,12,14,16 }; /**/ if( *buffer >= 0xf8 ) bytes = table[*buffer - 0xf8]; else if( *buffer >= 0xe0 ) bytes = 3 + ((*buffer>>3) & 3); else if( *buffer >= 0xc0 ) bytes = 2; else bytes = *buffer >= 0x80; #define POP(bits) out = out | (uint64_t)*buffer++ << (shift += bits); switch( bytes ) { default: break; case 0: out = *buffer++; break; case 1: out = *buffer++ & 0x3f; POP(6); break; case 2: out = *buffer++ & 0x1f; POP(5); POP(8); break; case 3: out = *buffer++ & 0x07; POP(3); POP(8); POP(8); break; case 4: out = *buffer++ & 0x07; POP(3); POP(8); POP(8); POP(8); break; case 5: out = *buffer++ & 0x07; POP(3); POP(8); POP(8); POP(8); POP(8); break; case 6: ++buffer; shift = -8; POP(8); POP(8); POP(8); POP(8); POP(8); POP(8); break; case 7: ++buffer; shift = -8; POP(8); POP(8); POP(8); POP(8); POP(8); POP(8); POP(8); break; case 8: ++buffer; shift = -8; POP(8); POP(8); POP(8); POP(8); POP(8); POP(8); POP(8); POP(8); } #undef POP return *value = out, bytes+1; } static uint64_t pack64iv( uint8_t *buffer, int64_t value_ ) { uint64_t value = (uint64_t)((value_ >> 63) ^ (value_ << 1)); return pack64uv(buffer, value); /* convert sign|magnitude to magnitude|sign */ } static uint64_t unpack64iv( const uint8_t *buffer, int64_t *value ) { uint64_t out = 0, ret = unpack64uv( buffer, &out ); *value = ((out >> 1) ^ -(out & 1)); /* convert magnitude|sign to sign|magnitude */ return ret; } #if 0 // variable int packing -------------------------------------------------------- // vbyte, varint (signed) static uint64_t pack_LEB128( uint8_t *buffer, int64_t value_ ) { /* convert sign|magnitude to magnitude|sign */ // uint64_t value = (uint64_t)value_; value = value & (1ull << 63) ? ~(value << 1) : (value << 1); uint64_t value = (uint64_t)((value_ >> 63) ^ (value_ << 1)); // (branchless) /* encode unsigned : 7-bit pack. MSB terminates stream */ const uint8_t *buffer0 = buffer; while( value > 127 ) { *buffer++ = value | 0x80; // (uint8_t)(( value & 0xFF ) | 0x80 ); value >>= 7; } *buffer++ = value; return buffer - buffer0; } static uint64_t unpack_LEB128( const uint8_t *buffer, int64_t *value ) { /* decode unsigned : 7-bit unpack. MSB terminates stream */ const uint8_t *buffer0 = buffer; uint64_t out = 0, j = -7; do { out |= ( ((uint64_t)*buffer) & 0x7f) << (j += 7); } while( (*buffer++) & 0x80 ); /* convert magnitude|sign to sign|magnitude */ // *value = out & (1) ? ~(out >> 1) : (out >> 1); *value = ((out >> 1) ^ -(out & 1)); // (branchless) return buffer - buffer0; } #endif #ifdef VLI_DEMO int main() { int tests = 0, passes = 0; #define testi(v) do { \ int64_t val = v; \ char out[16]; \ int len = pack64iv(out, val); \ int64_t in = ~val; \ unpack64iv(out, &in); \ int ok = val == in; ++tests; passes += ok; \ printf("%c %02d/%02d (-) %#llx (%llu) <-> %d bytes <-> %#llx (%llu)\n", "NY"[ok], passes, tests, val, val, len, in, in); \ } while(0) #define testu(v) do { \ uint64_t val = (v); \ char out[16]; \ int len = pack64uv(out, val); \ uint64_t in = ~val; \ unpack64uv(out, &in); \ int ok = val == in; ++tests; passes += ok; \ printf("%c %02d/%02d (+) %#llx (%llu) <-> %d bytes <-> %#llx (%llu)\n", "NY"[ok], passes, tests, val, val, len, in, in); \ } while(0) #define test(v) do { testi(v); testu(v); } while(0) test(0); test((1ull<<7)-1); test( 1ull<<7); test((1ull<<14)-1); test( 1ull<<14); test((1ull<<21)-1); test( 1ull<<21); test((1ull<<27)-1); test( 1ull<<27); test((1ull<<35)-1); test( 1ull<<35); test((1ull<<48)-1); test( 1ull<<48); test(~0ull-1); test(~0ull); printf("%d tests, %d errors\n", tests, tests - passes); } #endif ================================================ FILE: vault/buf_zigzag.c ================================================ // zigzag de/encoder // - rlyeh, public domain #ifndef ZIGZAG_H #define ZIGZAG_H #include uint64_t zig64( int64_t value ); // convert sign|magnitude to magnitude|sign int64_t zag64( uint64_t value ); // convert magnitude|sign to sign|magnitude #endif // ----------------------------------------------------------------------------- #ifdef ZIGZAG_C #pragma once uint64_t zig64( int64_t value ) { // convert sign|magnitude to magnitude|sign return (value >> 63) ^ (value << 1); } int64_t zag64( uint64_t value ) { // convert magnitude|sign to sign|magnitude return (value >> 1) ^ -(value & 1); } #ifdef ZIGZAG_DEMO #include int main() { int16_t x = -1000; printf("%d -> %llu %llx -> %lld\n", x, zig64(x), zig64(x), zag64(zig64(x))); } #define main main__ #endif // ZIGZAG_DEMO #endif // ZIGZAG_C ================================================ FILE: vault/c.c ================================================ // - rlyeh, public domain #ifdef C_C #pragma once #define BENCHMARK_C #define IFDEF_C #define OVERLOAD_C #define WITH_C #endif #include "c_benchmark.c" #include "c_ifdef.c" #include "c_overload.c" #include "c_with.c" #include "c_incbin.h" #include "c_section.c" #include "c_alignas.c" #include "c_thread.c" #include "c_plan.c" #include "c_once.c" #include "c_checkva.c" #include "c_cc4.c" // after c_ifdef ================================================ FILE: vault/c_alignas.c ================================================ // - rlyeh, public domain #pragma once #if __STDC_VERSION__ >= 201112L # ifndef _MSC_VER # include # endif # define ALIGNAS(bytes) alignas(bytes) #else # define ALIGNAS(bytes) #endif ================================================ FILE: vault/c_benchmark.c ================================================ // - rlyeh, public domain #ifndef BENCHMARK_H #define BENCHMARK_H #include #ifdef _MSC_VER #include #define benchmark(t,...) \ for(double b_=omp_get_wtime(), e_=b_; e_==b_; t=(((e_=omp_get_wtime())-b_)*1.0), \ (void)(!0["" #__VA_ARGS__] ? 0 : (printf("" __VA_ARGS__), printf(" (%.4fs)\n",t)))) #else #include #define benchmark(t,...) \ for(clock_t b_=clock(), e_=b_; e_==b_; t=(((e_=clock())-b_)*1.0/CLOCKS_PER_SEC), \ (void)(!0["" #__VA_ARGS__] ? 0 : (printf("" __VA_ARGS__), printf(" (%.3fs)\n",t)))) #endif #endif // BENCHMARK_H #ifdef BENCHMARK_DEMO #pragma once int main() { double t; benchmark(t, "benchmark i/o, %d times", 1000) { for(int i = 0; i < 1000; ++i) { fclose(fopen(__FILE__,"rb")); } } benchmark(t) { // benchmark title (as seen above) is optional for(int i = 0; i < 1000; ++i) { fclose(fopen(__FILE__,"rb")); } } } #define main __main #endif // BENCHMARK_DEMO ================================================ FILE: vault/c_cc4.c ================================================ #pragma once #define CC4(abcd) ((0[#abcd "\0\0\0\0"] << 24) | (1[#abcd "\0\0\0\0"] << 16) | (2[#abcd "\0\0\0\0"] << 8) | (3[#abcd "\0\0\0\0"] << 0)) #if LITTLE #define CC4_FMT(i) ((i)>>24)&255,((i)>>16)&255,((i)>> 8)&255,((i)>> 0)&255 #else #define CC4_FMT(i) ((i)>> 0)&255,((i)>> 8)&255,((i)>>16)&255,((i)>>24)&255 #endif /* int main() { int x = CC4(user); printf("%#x %c%c%c%c\n", x, CC4_FMT(x)); } */ ================================================ FILE: vault/c_checkva.c ================================================ #pragma once // MSVC2015 trick by doynax (check+dead code elimination) // see: https://stackoverflow.com/a/42313782 #define CHECK_VA(...) \ (printf || printf(__VA_ARGS__), (__VA_ARGS__)) // usage: // #define my_printf(...) my_printf(CHECK_VA(__VA_ARGS__)) ================================================ FILE: vault/c_countof.c ================================================ // - rlyeh, public domain #pragma once #ifndef COUNTOF #define COUNTOF(x) (int)(sizeof(x)/sizeof(0[x])) #endif ================================================ FILE: vault/c_ifdef.c ================================================ // - rlyeh, public domain #ifndef IFDEF_H #define IFDEF_H #define IF(x,T,...) IF_(x)(T,__VA_ARGS__/*F*/) #define IFN(x,T,...) IF_(x)(__VA_ARGS__/*F*/,T) #define IF_(x) IF_DETECT(x) #define IF_DETECT(...) IF_DETECT_##__VA_ARGS__ #define IF_DETECT_0(t,...) __VA_ARGS__ #define IF_DETECT_1(t,...) t #define IFDEF IF #define IFNDEF IFN // ----------------------------------------------------------------------------- // build type #define DEBUG 0 #define DEBUGOPT 0 #define RELEASE 0 // endianness symbols #define BIG 0 #define LITTLE 0 // arch wordsize and cacheline symbols #define X32 0 #define X64 0 #define L64 0 #define L128 0 // architecture symbols #define ARM 0 #define PPC 0 #define INTEL 0 #define JIT 0 // language symbols #define C89 0 #define C99 0 #define C11 0 #define CPP 0 #define CPP03 0 #define CPP11 0 #define EXCEPTIONS 0 // language features #define COMPUTED_GOTO 0 #define FLEXIBLE_ARRAY 0 // compiler symbols #define CLANG 0 #define GCC 0 #define ICC 0 #define MINGW 0 #define MSC 0 #define SNC 0 #define GCC_COMPAT 0 // platform symbols #define ANDROID 0 #define BSD 0 #define HTML5 0 #define IOS 0 #define LINUX 0 #define OSX 0 #define PS4 0 #define RASPBERRYPI 0 #define STEAMLINK 0 #define UNIX 0 #define WINDOWS 0 #define XBOX1 0 // detect build type (@todo: check _DEBUG compatibility in non-msvc compilers) // we assume that we are on shipping build if asserts are removed (ie, NDEBUG is defined) #if defined NDEBUG && defined _DEBUG # undef DEBUGOPT # define DEBUGOPT 1 #elif defined NDEBUG # undef RELEASE # define RELEASE 1 #else # undef DEBUG # define DEBUG 1 #endif // detect compiler // also compiler arm __CC_ARM, ibm __xlc__ || __xlC__, sun __SUNPRO_C || __SUNPRO_CC #if defined __ICC // __ICL, __INTEL_COMPILER # undef ICC # define ICC 1 #elif defined __SNC__ # undef SNC # define SNC 1 #elif defined __clang__ // before gcc # undef CLANG # define CLANG 1 #elif defined __GNUC__ # undef GCC # define GCC 1 #elif defined _MSC_VER # undef MSC # define MSC 1 #elif defined __MINGW32__ # undef MINGW # define MINGW 1 #endif // detect compilers in gcc compatibility mode (clang, icc, snc) #if defined __GNUC__ && !defined (__GNUC_PATCHLEVEL__) # undef GCC # define GCC 1 #endif // detect C++ language and version #ifdef __cplusplus # undef CPP # define CPP 1 # if (__cplusplus < 201103L && !defined _MSC_VER) || (defined _MSC_VER && _MSC_VER < 1700) # undef CPP03 # define CPP03 1 # else # undef CPP11 # define CPP11 1 # endif #else // detect C language and version #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L # undef C11 # define C11 1 #elif defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L # undef C99 # define C99 1 #else # undef C89 # define C89 1 #endif #endif // detect language features #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L // C99 # undef FLEXIBLE_ARRAY # define FLEXIBLE_ARRAY // char data[FLEXIBLE_ARRAY]; -> char data[]; #endif #ifdef __GNUC__ # undef COMPUTED_GOTO # define COMPUTED_GOTO 1 #endif // detect c++ exceptions #if defined __cplusplus && ( \ (defined _HAS_EXCEPTIONS && _HAS_EXCEPTIONS) || \ (defined _STLP_USE_EXCEPTIONS && _STLP_USE_EXCEPTIONS) || \ (defined HAVE_EXCEPTIONS) || \ (defined __EXCEPTIONS) || \ (defined _CPPUNWIND) ) # undef EXCEPTIONS # define EXCEPTIONS 1 #endif #ifdef __has_feature #if __has_feature(cxx_exceptions) # undef EXCEPTIONS # define EXCEPTIONS 1 #endif #endif // detect endianness // also BE if (*(uint16_t *)"\0\xff" < 0x100) #if defined __BIG_ENDIAN__ || defined __ARMEB__ || defined __THUMBEB__ || defined __MIPSEB__ || (defined __BYTE_ORDER__ && __BYTE_ORDER__==__BIG_ENDIAN) # undef BIG # define BIG 1 #else # undef LITTLE # define LITTLE 1 #endif // detect architecture // also __mips__ __mips (L64), __ia64, __sparc, __alpha, __hppa, __avr32__, __sh__, __itanium__|_M_IA64 #if defined _M_ARM // __aarch64__ __arm__ # undef ARM # define ARM 1 #elif defined _M_PPC // __ppc64__ __ppc__ __powerpc__ __powerpc64__ __powerpc # undef PPC # define PPC 1 #elif defined _M_IX86 || defined _M_X64 # undef INTEL # define INTEL 1 #else // likely VM/JIT # undef JIT # define JIT 1 #endif // detect wordsize #include # if defined INTPTR_MAX && INTPTR_MAX == INT32_MAX # undef X32 # define X32 1 #elif defined INTPTR_MAX && INTPTR_MAX == INT64_MAX // __x86_64__ _M_X64 __aarch64__ __powerpc64__ __ppc64__ __itanium__ _M_IA64 # undef X64 # define X64 1 #endif // detect cache line size #if PPC # undef L128 # define L128 1 #else # undef L64 # define L64 1 #endif // detect platform // also CYG __CYGWIN__, QNX __QNXNTO__, SOL __sun, __hpux, _AIX, __WINDOWS__ (watcom), _WINDOWS # if defined __EMSCRIPTEN__ # undef HTML5 # define HTML5 1 #elif defined _XBOX_ONE || defined _DURANGO # undef XBOX1 # define XBOX1 1 // before windows #elif defined _WIN32 || defined __MINGW32__ # undef WINDOWS # define WINDOWS 1 #elif defined __ANDROID__ # undef ANDROID # define ANDROID 1 // before linux #elif defined __ORBIS__ # undef PS4 # define PS4 1 // before linux #elif defined __STEAMLINK__ # undef STEAMLINK # define STEAMLINK 1 // before linux #elif defined __VCCOREVER__ # undef RASPBERRYPI # define RASPBERRYPI 1 // before linux #elif defined __linux__ # undef LINUX # define LINUX 1 // before unix #elif defined __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ || defined __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ // TARGET_OS_IPHONE == 1 # undef IOS # define IOS 1 // before bsd #elif defined __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ || defined __APPLE__ // TARGET_OS_MAC == 1 # undef OSX # define OSX 1 // before bsd #elif defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__ # undef BSD # define BSD 1 // before unix #elif defined __unix__ # undef UNIX # define UNIX 1 #endif #ifdef IFDEF_DEMO #pragma once #include int main() { // compile-time if int debug = 0; IF(DEBUG, debug = 1); // compile-time if-else const char *arch = IF(X64, "64", "32") "-bit arch"; // compile-time if-else-if const char *cpu = "CPU-" IF(INTEL, "Intel", IF(ARM, "ARM", "Unknown")); // symbols are boolean preprocessor directives as well #if DEBUG debug = 1; #endif // debug all symbols puts( 1 + IF(X32, "+X32") IF(X64, "+X64") IF(L64, "+L64") IF(L128, "+L128") IF(BIG, "+BIG") IF(LITTLE, "+LITTLE") IF(ARM, "+ARM") IF(PPC, "+PPC") IF(INTEL, "+INTEL") IF(JIT, "+JIT") IF(DEBUG, "+DEBUG") IF(DEBUGOPT, "+DEBUGOPT") IF(RELEASE, "+RELEASE") IF(C89, "+C89") IF(C99, "+C99") IF(C11, "+C11") IF(CPP, "+CPP") IF(CPP03, "+CPP03") IF(CPP11, "+CPP11") IF(EXCEPTIONS, "+EXCEPT") IF(MSC, "+MSC") IF(SNC, "+SNC") IF(ICC, "+ICC") IF(GCC, "+GCC") IF(CLANG, "+CLANG") IF(MINGW, "+MINGW") IF(GCC_COMPAT, "+GCC_COMPAT") IF(HTML5, "+HTML5") IF(BSD, "+BSD") IF(UNIX, "+UNIX") IF(IOS, "+IOS") IF(ANDROID, "+ANDROID") IF(XBOX1, "+XBOX1") IF(PS4, "+PS4") IF(WINDOWS, "+WINDOWS") IF(LINUX, "+LINUX") IF(OSX, "+OSX") IF(RASPBERRYPI, "+RASPBERRYPI") IF(STEAMLINK, "+STEAMLINK") ); } #define main __main #endif // IFDEF_DEMO #endif // IFDEF_H ================================================ FILE: vault/c_incbin.h ================================================ /** * @file c_incbin.h * @author Dale Weiler * @brief Utility for including binary files * * Facilities for including binary files into the current translation unit and * making use from them externally in other translation units. * * --- * * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to */ #if 1 #elif 0 // Usage: #include #include #include "c_incbin.h" INCBIN(Lorem, "loremipsum.txt"); INCBIN(Onebyte, "onebyte.txt"); INCBIN(Sevenbytes, "sevenbytes.txt"); int main(int argc, char **argv) { assert(gLoremSize==962); assert(&gLoremData[gLoremSize] == (const unsigned char*) &gLoremEnd); assert(gOnebyteSize == 1); assert(&gOnebyteData[gOnebyteSize] == (const unsigned char*) &gOnebyteEnd); assert(gSevenbytesSize==7); assert(&gSevenbytesData[gSevenbytesSize] == (const unsigned char*) &gSevenbytesEnd); exit(0); } #endif #ifndef INCBIN_H #define INCBIN_H #include #if defined(__AVX512BW__) || \ defined(__AVX512CD__) || \ defined(__AVX512DQ__) || \ defined(__AVX512ER__) || \ defined(__AVX512PF__) || \ defined(__AVX512VL__) || \ defined(__AVX512F__) # define INCBIN_ALIGNMENT_INDEX 6 #elif defined(__AVX__) || \ defined(__AVX2__) # define INCBIN_ALIGNMENT_INDEX 5 #elif defined(__SSE__) || \ defined(__SSE2__) || \ defined(__SSE3__) || \ defined(__SSSE3__) || \ defined(__SSE4_1__) || \ defined(__SSE4_2__) || \ defined(__neon__) # define INCBIN_ALIGNMENT_INDEX 4 #elif ULONG_MAX != 0xffffffffu # define INCBIN_ALIGNMENT_INDEX 3 # else # define INCBIN_ALIGNMENT_INDEX 2 #endif /* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */ #define INCBIN_ALIGN_SHIFT_0 1 #define INCBIN_ALIGN_SHIFT_1 2 #define INCBIN_ALIGN_SHIFT_2 4 #define INCBIN_ALIGN_SHIFT_3 8 #define INCBIN_ALIGN_SHIFT_4 16 #define INCBIN_ALIGN_SHIFT_5 32 #define INCBIN_ALIGN_SHIFT_6 64 /* Actual alignment value */ #define INCBIN_ALIGNMENT \ INCBIN_CONCATENATE( \ INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \ INCBIN_ALIGNMENT_INDEX) /* Stringize */ #define INCBIN_STR(X) \ #X #define INCBIN_STRINGIZE(X) \ INCBIN_STR(X) /* Concatenate */ #define INCBIN_CAT(X, Y) \ X ## Y #define INCBIN_CONCATENATE(X, Y) \ INCBIN_CAT(X, Y) /* Deferred macro expansion */ #define INCBIN_EVAL(X) \ X #define INCBIN_INVOKE(N, ...) \ INCBIN_EVAL(N(__VA_ARGS__)) /* Green Hills uses a different directive for including binary data */ #if defined(__ghs__) # if (__ghs_asm == 2) # define INCBIN_MACRO ".file" /* Or consider the ".myrawdata" entry in the ld file */ # else # define INCBIN_MACRO "\tINCBIN" # endif #else # define INCBIN_MACRO ".incbin" #endif #ifndef _MSC_VER # define INCBIN_ALIGN \ __attribute__((aligned(INCBIN_ALIGNMENT))) #else # define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT)) #endif #if defined(__arm__) || /* GNU C and RealView */ \ defined(__arm) || /* Diab */ \ defined(_ARM) /* ImageCraft */ # define INCBIN_ARM #endif #ifdef __GNUC__ /* Utilize .balign where supported */ # define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" # define INCBIN_ALIGN_BYTE ".balign 1\n" #elif defined(INCBIN_ARM) /* * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is * the shift count. This is the value passed to `.align' */ # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n" # define INCBIN_ALIGN_BYTE ".align 0\n" #else /* We assume other inline assembler's treat `.align' as `.balign' */ # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" # define INCBIN_ALIGN_BYTE ".align 1\n" #endif /* INCBIN_CONST is used by incbin.c generated files */ #if defined(__cplusplus) # define INCBIN_EXTERNAL extern "C" # define INCBIN_CONST extern const #else # define INCBIN_EXTERNAL extern # define INCBIN_CONST const #endif /** * @brief Optionally override the linker section into which data is emitted. * * @warning If you use this facility, you'll have to deal with platform-specific linker output * section naming on your own * * Overriding the default linker output section, e.g for esp8266/Arduino: * @code * #define INCBIN_OUTPUT_SECTION ".irom.text" * #include "incbin.h" * INCBIN(Foo, "foo.txt"); * // Data is emitted into program memory that never gets copied to RAM * @endcode */ #if !defined(INCBIN_OUTPUT_SECTION) # if defined(__APPLE__) # define INCBIN_OUTPUT_SECTION ".const_data" # else # define INCBIN_OUTPUT_SECTION ".rodata" # endif #endif #if defined(__APPLE__) /* The directives are different for Apple branded compilers */ # define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" # define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" # define INCBIN_INT ".long " # define INCBIN_MANGLE "_" # define INCBIN_BYTE ".byte " # define INCBIN_TYPE(...) #else # define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n" # define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" # if defined(__ghs__) # define INCBIN_INT ".word " # else # define INCBIN_INT ".int " # endif # if defined(__USER_LABEL_PREFIX__) # define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__) # else # define INCBIN_MANGLE "" # endif # if defined(INCBIN_ARM) /* On arm assemblers, `@' is used as a line comment token */ # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n" # elif defined(__MINGW32__) || defined(__MINGW64__) /* Mingw doesn't support this directive either */ # define INCBIN_TYPE(NAME) # else /* It's safe to use `@' on other architectures */ # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n" # endif # define INCBIN_BYTE ".byte " #endif /* List of style types used for symbol names */ #define INCBIN_STYLE_CAMEL 0 #define INCBIN_STYLE_SNAKE 1 /** * @brief Specify the prefix to use for symbol names. * * By default this is `g', producing symbols of the form: * @code * #include "incbin.h" * INCBIN(Foo, "foo.txt"); * * // Now you have the following symbols: * // const unsigned char gFooData[]; * // const unsigned char *const gFooEnd; * // const unsigned int gFooSize; * @endcode * * If however you specify a prefix before including: e.g: * @code * #define INCBIN_PREFIX incbin * #include "incbin.h" * INCBIN(Foo, "foo.txt"); * * // Now you have the following symbols instead: * // const unsigned char incbinFooData[]; * // const unsigned char *const incbinFooEnd; * // const unsigned int incbinFooSize; * @endcode */ #if !defined(INCBIN_PREFIX) # define INCBIN_PREFIX g #endif /** * @brief Specify the style used for symbol names. * * Possible options are * - INCBIN_STYLE_CAMEL "CamelCase" * - INCBIN_STYLE_SNAKE "snake_case" * * Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form: * @code * #include "incbin.h" * INCBIN(Foo, "foo.txt"); * * // Now you have the following symbols: * // const unsigned char FooData[]; * // const unsigned char *const FooEnd; * // const unsigned int FooSize; * @endcode * * If however you specify a style before including: e.g: * @code * #define INCBIN_STYLE INCBIN_STYLE_SNAKE * #include "incbin.h" * INCBIN(foo, "foo.txt"); * * // Now you have the following symbols: * // const unsigned char foo_data[]; * // const unsigned char *const foo_end; * // const unsigned int foo_size; * @endcode */ #if !defined(INCBIN_STYLE) # define INCBIN_STYLE INCBIN_STYLE_CAMEL #endif /* Style lookup tables */ #define INCBIN_STYLE_0_DATA Data #define INCBIN_STYLE_0_END End #define INCBIN_STYLE_0_SIZE Size #define INCBIN_STYLE_1_DATA _data #define INCBIN_STYLE_1_END _end #define INCBIN_STYLE_1_SIZE _size /* Style lookup: returning identifier */ #define INCBIN_STYLE_IDENT(TYPE) \ INCBIN_CONCATENATE( \ INCBIN_STYLE_, \ INCBIN_CONCATENATE( \ INCBIN_EVAL(INCBIN_STYLE), \ INCBIN_CONCATENATE(_, TYPE))) /* Style lookup: returning string literal */ #define INCBIN_STYLE_STRING(TYPE) \ INCBIN_STRINGIZE( \ INCBIN_STYLE_IDENT(TYPE)) \ /* Generate the global labels by indirectly invoking the macro with our style * type and concatenating the name against them. */ #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ INCBIN_INVOKE( \ INCBIN_GLOBAL, \ INCBIN_CONCATENATE( \ NAME, \ INCBIN_INVOKE( \ INCBIN_STYLE_IDENT, \ TYPE))) \ INCBIN_INVOKE( \ INCBIN_TYPE, \ INCBIN_CONCATENATE( \ NAME, \ INCBIN_INVOKE( \ INCBIN_STYLE_IDENT, \ TYPE))) /** * @brief Externally reference binary data included in another translation unit. * * Produces three external symbols that reference the binary data included in * another translation unit. * * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with * "Data", as well as "End" and "Size" after. An example is provided below. * * @param NAME The name given for the binary data * * @code * INCBIN_EXTERN(Foo); * * // Now you have the following symbols: * // extern const unsigned char FooData[]; * // extern const unsigned char *const FooEnd; * // extern const unsigned int FooSize; * @endcode */ #define INCBIN_EXTERN(NAME) \ INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \ INCBIN_CONCATENATE( \ INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ INCBIN_STYLE_IDENT(DATA))[]; \ INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \ INCBIN_CONCATENATE( \ INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ INCBIN_STYLE_IDENT(END)); \ INCBIN_EXTERNAL const unsigned int \ INCBIN_CONCATENATE( \ INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ INCBIN_STYLE_IDENT(SIZE)) /** * @brief Include a binary file into the current translation unit. * * Includes a binary file into the current translation unit, producing three symbols * for objects that encode the data and size respectively. * * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with * "Data", as well as "End" and "Size" after. An example is provided below. * * @param NAME The name to associate with this binary data (as an identifier.) * @param FILENAME The file to include (as a string literal.) * * @code * INCBIN(Icon, "icon.png"); * * // Now you have the following symbols: * // const unsigned char IconData[]; * // const unsigned char *const IconEnd; * // const unsigned int IconSize; * @endcode * * @warning This must be used in global scope * @warning The identifiers may be different if INCBIN_STYLE is not default * * To externally reference the data included by this in another translation unit * please @see INCBIN_EXTERN. */ #ifdef _MSC_VER #define INCBIN(NAME, FILENAME) \ INCBIN_EXTERN(NAME) #else #define INCBIN(NAME, FILENAME) \ __asm__(INCBIN_SECTION \ INCBIN_GLOBAL_LABELS(NAME, DATA) \ INCBIN_ALIGN_HOST \ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \ INCBIN_MACRO " \"" FILENAME "\"\n" \ INCBIN_GLOBAL_LABELS(NAME, END) \ INCBIN_ALIGN_BYTE \ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \ INCBIN_BYTE "1\n" \ INCBIN_GLOBAL_LABELS(NAME, SIZE) \ INCBIN_ALIGN_HOST \ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \ INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \ INCBIN_ALIGN_HOST \ ".text\n" \ ); \ INCBIN_EXTERN(NAME) #endif #endif // ----------------------------------------------------------------------------- ================================================ FILE: vault/c_once.c ================================================ #pragma once #define ONCE static int once##__LINE__=1; for(;once##__LINE__;once##__LINE__=0) /* #define once_unique(a) once_join(a, __LINE__) #define once_join(a,b) once_jo1n(a,b) #define once_jo1n(a,b) a##b #define ONCE \ static int once_unique(once) = 0; for(; !once_unique(once); once_unique(once) = 1) */ /* int main() { for( int i = 0; i < 10; ++i ) { ONCE { puts("hi"); } } } */ ================================================ FILE: vault/c_overload.c ================================================ // msvc trick to expand vargs properly by Braden Steffaniak // - see https://stackoverflow.com/a/24028231 #pragma once #define GLUE(x, y) x y #define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count #define EXPAND_ARGS(args) RETURN_ARG_COUNT args #define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) #define OVERLOAD_MACRO2(name, count) name##count #define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) #define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) #define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) /* usage: #define ASSERT1(expr) assert1(expr) #define ASSERT2(expr, errormsg) assert2(expr, errormsg) #define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__) */ ================================================ FILE: vault/c_plan.c ================================================ // PLAN is usually a TODO taskfile, like: AUTORUN { puts("[x] task1"); puts("[ ] task2"); } // Usage: cl test.c -DPLAN="^" or -DPLAN="\\\"my_plan.h\\\"" #pragma once #if defined __has_include # if __has_include("plan.c") # include "plan.c" # endif #elif defined PLAN # include PLAN #endif ================================================ FILE: vault/c_section.c ================================================ // - rlyeh, public domain #pragma once #ifndef _MSC_VER #define SECTION(name) __attribute__((section("." #name "#"))) #else #define SECTION(name) __declspec(allocate("." #name)) #endif ================================================ FILE: vault/c_thread.c ================================================ // - rlyeh, public domain #pragma once #if defined _MSC_VER || defined __TINYC__ #define THREAD __declspec(thread) #else #define THREAD __thread #endif ================================================ FILE: vault/c_unreachable.c ================================================ // - rlyeh, public domain #pragma once #ifdef DEBUG #define UNREACHABLE() assert(!"This line was not meant to be reached") #elif defined( _MSC_VER ) #define UNREACHABLE() __assume(0) #elif defined __GNUC__ // && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) #define UNREACHABLE() __builtin_unreachable() #else #define UNREACHABLE() #endif ================================================ FILE: vault/c_with.c ================================================ // - rlyeh, public domain #pragma once #define ON 1 #define OFF 0 #define WITH(x) ((x)+0) /* // cl c_with.c // cl c_with.c -DALSA // cl c_with.c -DALSA=ON // cl c_with.c -DALSA=OFF // cl c_with.c -DALSA=0 // cl c_with.c -DALSA=1 int main() { #if WITH(_MSC_VER) puts("msc"); #endif #if WITH(__GNUC__) puts("gcc"); #endif #if WITH(ALSA) puts("alsa audio"); #endif #if WITH(_WIN32) puts("win32"); #endif } */ ================================================ FILE: vault/ds.c ================================================ // [x] data structures: array, hash, map @todo: set, lfqueue, sort, comp, // - rlyeh, public domain #ifdef DS_C #define ALLOC_C #define HASH_C #define MAP_C #define QUARK_C #define STREAM_C #define FORMAT_C #define STRING_C #define SORT_C #define SET_C #define ROPE_C #endif #include "os.c" #include "ds_hash.c" #include "ds_sort.c" #include "ds_array.c" #include "ds_alloc.c" #include "ds_map.c" #include "ds_set.c" #include "ds_stream.c" #include "ds_format.c" #include "ds_string.c" #include "ds_quark.c" #include "ds_rope.c" ================================================ FILE: vault/ds_alloc.c ================================================ // [x] memory: realloc, vrealloc, stack // [ ] @todo: gc, pool, game mark/rewind+game stack |level|game|>--- stack_max ) stack_max = stack_ptr; return (stack_ptr = 0), NULL; } if( !stack_mem ) stack_mem = xrealloc(stack_mem, xlen(stack_mem) + 4 * 1024 * 1024); return &stack_mem[ (stack_ptr += bytes) - bytes ]; } #endif ================================================ FILE: vault/ds_array.c ================================================ // array library -------------------------------------------------------------- // - rlyeh, public domain #pragma once #ifdef __cplusplus #define array_cast(x) (decltype(x)) #else #define array_cast(x) (void *) #endif #define array(t) t* #define array_init(t) ( (t) = 0 ) #define array_resize(t, n) ( array_c_ = array_count(t), array_realloc_((t),(n)), ((n)>array_c_? memset(array_c_+(t),0,((n)-array_c_)*sizeof(0[t])) : (void*)0), (t) ) #define array_push(t, ...) ( array_realloc_((t),array_count(t)+1), (t)[ array_count(t) - 1 ] = (__VA_ARGS__) ) #define array_pop(t) ( array_realloc_((t), array_count(t)-1) ) #define array_back(t) ( (t)[ array_count(t)-1 ] ) // ( (t) ? &(t)[ array_count(t)-1 ] : NULL ) #define array_data(t) (t) #define array_at(t,i) (t[i]) #define array_count(t) (int)( (t) ? array_vlen_(t) / sizeof(0[t]) : 0u ) #define array_bytes(t) (int)( (t) ? array_vlen_(t) : 0u ) #define array_sort(t, cmpfunc) qsort( t, array_count(t), sizeof(0[t]), cmpfunc ) #define array_empty(t) ( !array_count(t) ) #define array_free(t) array_clear(t) static __thread unsigned array_c_; #if 0 // original: no reserve support #define array_reserve(t, n) ((void)0) // not implemented #define array_clear(t) ( array_realloc_((t), 0), (t) = 0 ) #define array_vlen_(t) ( vlen(t) - 0 ) #define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+0) * sizeof(0[t])) ) #else // new: with reserve support (buggy still?) #define array_reserve(t, n) ( array_realloc_((t),(n)), array_realloc_((t),0) ) #define array_clear(t) ( array_realloc_((t), -1), (t) = 0 ) // -1 #define array_vlen_(t) ( vlen(t) - sizeof(0[t]) ) // -1 #define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+1) * sizeof(0[t])) ) // +1 #endif #define array_reverse(t) \ do if( array_count(t) ) { \ for(int l = array_count(t), e = l-1, i = (array_push(t, 0[t]), 0); i <= e/2; ++i ) \ { l[t] = i[t]; i[t] = (e-i)[t]; (e-i)[t] = l[t]; } \ array_pop(t); \ } while(0) //#define array_foreach2(t,val_t,v) \ // for( val_t *v = &0[t]; v < (&0[t] + array_count(t)); ++v ) //#define array_foreach(t, it) \ // for( void *end__ = (it = &0[t]) + array_count(t); it != end__; ++it ) #define array_foreach(t,val_t,v) \ for( val_t *it__ = &0[t]; it__ < (&0[t] + array_count(t)); ++it__ ) \ for( val_t v = *it__, *on__ = &v; on__; on__ = 0 ) #define array_search(t, key, cmpfn) /* requires sorted array beforehand */ \ bsearch(&key, t, array_count(t), sizeof(t[0]), cmpfn ) #define array_insert(t, i, n) do { \ int ac = array_count(t); \ if( i >= ac ) { \ array_push(t, n); \ } else { \ array_push(t, array_back(t)); \ memmove( &(t)[(i)+1], &(t)[i], (ac - (i)) * sizeof(t[0]) ); \ (t)[ i ] = (n); \ } \ } while(0) #define array_copy(t, src) do { /*todo: review old vrealloc call!*/ \ array_free(t); \ (t) = vrealloc( (t), array_count(src) * sizeof(0[t])); \ memcpy( (t), src, array_count(src) * sizeof(0[t])); \ } while(0) #define array_erase(t, i) do { \ memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \ array_pop(t); \ } while(0) #define array_unique(t, cmpfunc) do { /*todo: review old vrealloc call!*/ \ int cnt = array_count(t), dupes = 0; \ if( cnt > 1 ) { \ const void *prev = &(t)[0]; \ array_sort(t, cmpfunc); \ for( int i = 1; i < cnt; ) { \ if( cmpfunc(&t[i], prev) == 0 ) { \ memmove( &t[i], &t[i+1], (cnt - 1 - i) * sizeof(t[0]) ) ; \ --cnt; \ ++dupes; \ } else { \ prev = &(t)[i]; \ ++i; \ } \ } \ if( dupes ) { \ (t) = vrealloc((t), (array_count(t) - dupes) * sizeof(0[t])); \ } \ } \ } while(0) ================================================ FILE: vault/ds_format.c ================================================ // format library // - rlyeh, public domain #ifndef FORMAT_H #define FORMAT_H #include char* vl(const char *fmt, va_list); char* va(const char *fmt, ...); int is_va(const char *s); #define va(...) \ (printf || printf(__VA_ARGS__), va(__VA_ARGS__)) // vs2015 check va trick #define vl(...) \ (printf || vprintf(__VA_ARGS__), vl(__VA_ARGS__)) // this doesnt work afaik #endif // ----------------------------------------------------------------------------- #ifdef FORMAT_C #pragma once #if defined _MSC_VER && !defined __thread #define __thread __declspec(thread) #endif #include #include #include #include #include #include int is_va(const char *s) { return (1&(uintptr_t)s) && s[-1] == 0; } char* (vl)(const char *fmt, va_list vl) { va_list copy; va_copy(copy, vl); int sz = vsnprintf( 0, 0, fmt, copy ) + 1; va_end(copy); int reqlen = sz + 1; // 1 for even padding char* ptr; enum { STACK_ALLOC = 16384 }; if( reqlen < STACK_ALLOC ) { // fit stack? static __thread char buf[STACK_ALLOC+1]; static __thread int cur = 1, len = STACK_ALLOC; ptr = buf + ((cur+reqlen) > len ? cur = 1 : (cur += reqlen) - reqlen); } else { // else use heap (@fixme: single memleak per invoking thread) static __thread char *buf = 0; static __thread int cur = 1, len = -STACK_ALLOC; if( reqlen >= len ) buf = realloc(buf, len = abs(len) * 1.75 + reqlen); ptr = buf + ((cur+reqlen) > len ? cur = 1 : (cur += reqlen) - reqlen); } ptr += !(1&(uintptr_t)ptr); // align to even address only when already odd ptr[-1] = 0; // add header assert(is_va(ptr)); vsnprintf( ptr, sz, fmt, vl ); return (char *)ptr; } char* (va)(const char *fmt, ...) { va_list list; va_start(list, fmt); char *s = vl(fmt, list); va_end(list); return s; } // ----------------------------------------------------------------------------- #ifdef FORMAT_DEMO #pragma once int main() { char *x = va("hello %d", 123); puts(x);} #define main main__ #endif // FORMAT_DEMO #endif // FORMAT_C ================================================ FILE: vault/ds_hash.c ================================================ // hash ------------------------------------------------------------------------ // - rlyeh, public domain uint64_t hash_int(int key); uint64_t hash_flt(double dbl); uint64_t hash_u64(uint64_t key); uint64_t hash_str(const char* str); uint64_t hash_ptr(const void* ptr); uint64_t hash_vec2(const float pt[2]); uint64_t hash_vec3(const float pt[3]); uint64_t hash_vec4(const float pt[4]); // compile time string hash (pure C) ------------------------------------------- // Based on code by http://lolengine.net/blog/2011/12/20/cpp-constant-string-hash // This macro is not always inlined. Depends on compilers and optimization flags. // For example g++ requires -O3 and msvc requires /O2 (verify output assembly with /Fa). // About the hash function, it is using my own and free hashing algorithm: // a few more collisions than FNV1A64 (faster though). // - rlyeh, public domain. #ifndef HASH_STR #define HASH_STR(str) HASH_STR64(0,131ull,str,0) #define HASH_STR64(hsh,mul,str,idx) HASH_STR16(HASH_STR16(HASH_STR16(HASH_STR16(hsh,mul,str,idx+48),mul,str,idx+32),mul,str,idx+16),mul,str,idx) #define HASH_STR16(hsh,mul,str,idx) HASH_STR04(HASH_STR04(HASH_STR04(HASH_STR04(hsh,mul,str,idx+12),mul,str,idx+ 8),mul,str,idx+ 4),mul,str,idx) #define HASH_STR04(hsh,mul,str,idx) HASH_STR01(HASH_STR01(HASH_STR01(HASH_STR01(hsh,mul,str,idx+ 3),mul,str,idx+ 2),mul,str,idx+ 1),mul,str,idx) #define HASH_STR01(hsh,mul,str,idx) ((HASH_STRCHR(str,idx) ^ hsh) * mul) #define HASH_STRCHR(str,idx) ((unsigned)str[(idx)> 16) ^ x) * 0x45d9f3b; x = ((x >> 16) ^ x) * 0x45d9f3b; x = (x >> 16) ^ x; return x; } uint32_t unhash32(uint32_t x) { x = ((x >> 16) ^ x) * 0x119de1f3; x = ((x >> 16) ^ x) * 0x119de1f3; x = (x >> 16) ^ x; return x; } uint64_t hash64(uint64_t x) { x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); x = x ^ (x >> 31); return x; } /* uint32_t hash64(uint64_t v) { uint32_t hi = v >> 32; uint32_t lo = v & 0xFFFFFFFF; // (uint32_t)-1; return triple32(hi) ^ triple32(lo); } */ uint64_t unhash64(uint64_t x) { x = (x ^ (x >> 31) ^ (x >> 62)) * UINT64_C(0x319642b2d24d8ec3); x = (x ^ (x >> 27) ^ (x >> 54)) * UINT64_C(0x96de1b173f119089); x = x ^ (x >> 30) ^ (x >> 60); return x; } // } Thomas Mueller uint64_t hash_triple32(uint32_t x) { // triple32 hashing (unlicensed) https://github.com/skeeto/hash-prospector // exact bias: 0.020888578919738908 x ^= x >> 17; x *= UINT32_C(0xed5ad4bb); x ^= x >> 11; x *= UINT32_C(0xac4c1b51); x ^= x >> 15; x *= UINT32_C(0x31848bab); x ^= x >> 14; return x; } uint64_t hash_untriple32(uint32_t x) { x ^= x >> 14 ^ x >> 28; x *= UINT32_C(0x32b21703); x ^= x >> 15 ^ x >> 30; x *= UINT32_C(0x469e0db1); x ^= x >> 11 ^ x >> 22; x *= UINT32_C(0x79a85073); x ^= x >> 17; return x; } uint64_t hash_mix64(uint64_t key) { // Thomas Wang's 64bit Mix Function (public domain) http://www.cris.com/~Ttwang/tech/inthash.htm key += ~(key << 32); key ^= (key >> 22); key += ~(key << 13); key ^= (key >> 8); key += (key << 3); key ^= (key >> 15); key += ~(key << 27); key ^= (key >> 31); return key; } uint64_t hash_int(int key) { // return hash64((uint64_t)key); // return hash_mix64((uint64_t)key); // return hash_triple32((uint32_t)key); return hash32((uint32_t)key); } uint64_t hash_u64(uint64_t key) { // return hash_mix64(key); return hash64(key); } // --- uint64_t hash_ptr(const void *ptr) { uint64_t key = (uint64_t)(uintptr_t)ptr; return hash_u64(key >> 3); // >> 4? needed? } uint64_t hash_flt(double dbl) { union { uint64_t i; double d; } u; u.d = dbl; return hash_u64( u.i ); } uint64_t hash_vec2(const float v2[2]) { return hash_flt(v2[0]) ^ hash_flt(v2[1]); } uint64_t hash_vec3(const float v3[3]) { return hash_flt(v3[0]) ^ hash_flt(v3[1]) ^ hash_flt(v3[2]); } uint64_t hash_vec4(const float v4[4]) { return hash_flt(v4[0]) ^ hash_flt(v4[1]) ^ hash_flt(v4[2]) ^ hash_flt(v4[3]); } uint64_t hash_str(const char* str) { // faster than fnv1a, a few more collisions though. uint64_t hash = 0; // fnv1a: 14695981039346656037ULL; while( *str ) { hash = ( (unsigned char)*str++ ^ hash ) * 131; // fnv1a: 0x100000001b3ULL; } return hash; } #ifdef HASH_DEMO #include int main() { uint64_t literal = (uint64_t)0xf0e28e9bdecd6646ull; printf("%016llx\n", literal); uint64_t precomp = (uint64_t)STRHASH("/hello/world.txt"); printf("%016llx\n", precomp); uint64_t runtime = (uint64_t)strhash("/hello/world.txt"); printf("%016llx\n", runtime); } #define main __main #endif // HASH_DEMO #endif // HASH_C ================================================ FILE: vault/ds_map.c ================================================ // generic map container. // ideas from: https://en.wikipedia.org/wiki/Hash_table // ideas from: https://probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable/ // ideas from: http://www.idryman.org/blog/2017/05/03/writing-a-damn-fast-hash-table-with-tiny-memory-footprints/ // - rlyeh, public domain. #ifndef MAP_H #define MAP_H #include #include // config #ifndef MAP_REALLOC #define MAP_REALLOC REALLOC #endif #ifndef MAP_HASHSIZE #define MAP_HASHSIZE (4096 << 4) #endif #ifndef MAP_DONT_ERASE #define MAP_DONT_ERASE 1 #endif // public api #define map(K,V) \ struct { map base; struct { pair p; K key; V val; } tmp, *ptr; V* tmpval; \ int (*typed_cmp)(K, K); uint64_t (*typed_hash)(K); } * #define map_init(m, cmpfn, hashfn) ( \ (m) = map_cast((m)) MAP_REALLOC(0, sizeof(*(m))), \ map_init(&(m)->base), \ (m)->base.cmp = (int(*)(void*,void*))( (m)->typed_cmp = map_cast((m)->typed_cmp) cmpfn), \ (m)->base.hash = (uint64_t(*)(void*))( (m)->typed_hash = map_cast((m)->typed_hash) hashfn ) \ ) #define map_free(m) ( \ map_free(&(m)->base), \ MAP_REALLOC((m), sizeof(*(m))), (m) = 0 \ ) #define map_insert(m, k, v) ( \ (m)->ptr = map_cast((m)->ptr) MAP_REALLOC(0, sizeof((m)->tmp)), \ (m)->ptr->val = (v), \ (m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \ map_insert(&(m)->base, &(m)->ptr->p, &(m)->ptr->key, &(m)->ptr->val, (m)->ptr->p.keyhash, (m)->ptr), \ &(m)->ptr->val \ ) #define map_find(m, k) ( \ (m)->ptr = &(m)->tmp, \ (m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \ (m)->ptr = map_cast((m)->ptr) map_find(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash), \ (m)->ptr ? &(m)->ptr->val : 0 \ ) #define map_erase(m, k) ( \ (m)->ptr = &(m)->tmp, \ (m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \ map_erase(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash) \ ) #define map_foreach(m,key_t,k,val_t,v) \ for( int i_ = 0; i_ < MAP_HASHSIZE; ++i_) \ for( pair *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \ for( key_t k = *(key_t *)cur_->key; on_; ) \ for( val_t v = *(val_t *)cur_->value; on_; on_ = 0 ) #define map_clear(m) ( \ map_clear(&(m)->base) \ ) #define map_count(m) map_count(&(m)->base) #define map_gc(m) map_gc(&(m)->base) // private: #ifdef __cplusplus #define map_cast(t) (decltype(t)) #else #define map_cast(t) (void *) #endif typedef struct pair { struct pair *next; uint64_t keyhash; void *key; void *value; void *super; } pair; typedef struct map { array(pair*) array; int (*cmp)(void *, void *); uint64_t (*hash)(void *); } map; void (map_init)(map *m); void (map_free)(map *m); void (map_insert)(map *m, pair *p, void *key, void *value, uint64_t keyhash, void *super); void (map_erase)(map *m, void *key, uint64_t keyhash); void* (map_find)(map *m, void *key, uint64_t keyhash); int (map_count)(map *m); void (map_gc)(map *m); // only if using MAP_DONT_ERASE #endif // ------------------------------- #if defined MAP_C || defined MAP_DEMO #pragma once #include #include #include #include #include #include #include enum { MAP_GC_SLOT = MAP_HASHSIZE }; typedef int map_is_pow2_assert[ !(MAP_HASHSIZE & (MAP_HASHSIZE - 1)) ]; static int map_get_index(uint64_t hkey1) { return hkey1 & (MAP_HASHSIZE-1); } void (map_init)(map* m) { map c = {0}; *m = c; array_resize(m->array, (MAP_HASHSIZE+1)); memset(m->array, 0, (MAP_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset() } void (map_insert)(map* m, pair *p, void *key, void *value, uint64_t keyhash, void *super) { p->keyhash = keyhash; p->key = key; p->value = value; p->super = super; /* Insert onto the beginning of the list */ int index = map_get_index(p->keyhash); p->next = m->array[index]; m->array[index] = p; } void* (map_find)(map* m, void *key, uint64_t keyhash) { int index = map_get_index(keyhash); for( pair *cur = m->array[index]; cur; cur = cur->next ) { if( cur->keyhash == keyhash ) { char **c = (char **)cur->key; char **k = (char **)key; if( !m->cmp(c[0], k[0]) ) { return cur->super; } } } return 0; } void (map_erase)(map* m, void *key, uint64_t keyhash) { int index = map_get_index(keyhash); for( pair *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) { if( cur->keyhash == keyhash ) { char **c = (char **)cur->key; char **k = (char **)key; if( !m->cmp(c[0], k[0]) ) { if( prev ) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0; #if MAP_DONT_ERASE /* Insert onto the beginning of the GC list */ cur->next = m->array[MAP_GC_SLOT]; m->array[MAP_GC_SLOT] = cur; #else MAP_REALLOC(cur,0); #endif return; } } } } int (map_count)(map* m) { int counter = 0; for( int i = 0; i < MAP_HASHSIZE; ++i) { for( pair *cur = m->array[i]; cur; cur = cur->next ) { ++counter; } } return counter; } void (map_gc)(map* m) { #if MAP_DONT_ERASE for( pair *next, *cur = m->array[MAP_GC_SLOT]; cur; cur = next ) { next = cur->next; MAP_REALLOC(cur,0); } m->array[MAP_GC_SLOT] = 0; #endif } void (map_clear)(map* m) { for( int i = 0; i <= MAP_HASHSIZE; ++i) { for( pair *next, *cur = m->array[i]; cur; cur = next ) { next = cur->next; MAP_REALLOC(cur,0); } m->array[i] = 0; } } void (map_free)(map* m) { (map_clear)(m); array_free(m->array); m->array = 0; map c = {0}; *m = c; } // ------------------------------- #ifdef MAP_DEMO #include #include #ifdef __cplusplus #include #include #endif #ifdef NDEBUG #undef NDEBUG #include #define NDEBUG #else #include #endif void map_benchmark() { #ifndef M #define M 100 #endif #ifndef N #define N 50000 #endif #define BENCH(CREATE,COUNT,INSERT,FIND,ITERATE,ERASE,DESTROY) do { \ static char **bufs = 0; \ if(!bufs) { \ bufs = (char **)MAP_REALLOC(0, sizeof(char*) * N ); \ for( int i = 0; i < N; ++i ) { \ bufs[i] = (char*)MAP_REALLOC(0, 16); \ sprintf(bufs[i], "%d", i); \ } \ } \ clock_t t0 = clock(); \ for( int i = 0; i < M; ++i ) { \ CREATE; \ if(i==0) printf("CREATE:%d ", (int)COUNT), fflush(stdout); \ for( int j = 0; j < N; ++j ) { \ char *buf = bufs[j]; \ INSERT; \ } \ if(i==0) printf("INSERT:%d ", (int)COUNT), fflush(stdout); \ for( int j = 0; j < N; ++j ) { \ char *buf = bufs[j]; \ FIND; \ } \ if(i==0) printf("FIND:%d ", (int)COUNT), fflush(stdout); \ ITERATE; \ if(i==0) printf("ITERATE:%d ", (int)COUNT), fflush(stdout); \ for( int j = 0; j < N; ++j ) { \ char *buf = bufs[j]; \ ERASE; \ } \ if(i==0) printf("REMOVE:%d ", (int)COUNT), fflush(stdout); \ DESTROY; \ if(i==0) printf("DESTROY%s", " "); \ } \ clock_t t1 = clock(); \ double t = (t1 - t0) / (double)CLOCKS_PER_SEC; \ int ops = (M*N)*6; \ printf("%d ops in %fs = %.2fM ops/s (" #CREATE ")\n", ops, t, ops / (t * 1e6)); \ } while(0) map(char*,int) m = 0; BENCH( map_init(m, sort_str, hash_str), map_count(m), map_insert(m, buf, i), map_find(m, buf), map_foreach(m,char*,k,int,v) {}, map_erase(m, buf), map_free(m) ); #ifdef __cplusplus using std_map = std::map; BENCH( std_map v, v.size(), v.insert(std::make_pair(buf, i)), v.find(buf), for( auto &kv : v ) {}, v.erase(buf), {} ); using std_unordered_map = std::unordered_map; BENCH( std_unordered_map v, v.size(), v.insert(std::make_pair(buf, i)), v.find(buf), for( auto &kv : v ) {}, v.erase(buf), {} ); #endif #undef N #undef M } void map_tests() { { map(int,char*) m = 0; map_init(m, sort_int, hash_int); assert( 0 == map_count(m) ); map_insert(m, 123, "123"); map_insert(m, 456, "456"); assert( 2 == map_count(m) ); assert( map_find(m, 123) ); assert( map_find(m, 456) ); assert(!map_find(m, 789) ); assert( 0 == strcmp("123", *map_find(m, 123)) ); assert( 0 == strcmp("456", *map_find(m, 456)) ); map_foreach(m,const int,k,char*,v) { printf("%d->%s\n", k, v); } map_erase(m, 123); assert(!map_find(m, 123) ); assert( 1 == map_count(m) ); map_erase(m, 456); assert(!map_find(m, 456) ); assert( 0 == map_count(m) ); map_free(m); } { map(char*,int) m = 0; map_init(m, sort_str, hash_str); assert( map_count(m) == 0 ); map_insert(m, "123", 123); map_insert(m, "456", 456); assert( map_count(m) == 2 ); assert( map_find(m,"123") ); assert( map_find(m,"456") ); assert(!map_find(m,"789") ); map_foreach(m,const char *,k,int,v) { printf("%s->%d\n", k, v); } map_erase(m, "123"); assert( 456 == *map_find(m,"456") ); assert( map_count(m) == 1 ); map_erase(m, "456"); assert( map_count(m) == 0 ); map_free(m); assert(!puts("Ok")); } } void map_tests2() { map(char*,double) m = 0; map_init(m, sort_str, hash_str); map_insert(m, "hello", 101.1); map_insert(m, "world", 102.2); map_insert(m, "nice", 103.3); map_insert(m, "hash", 104.4); assert(!map_find(m, "random")); assert(map_find(m, "hello")); assert(map_find(m, "world")); assert(map_find(m, "nice") ); assert(map_find(m, "hash") ); assert( 101.1 == *map_find(m, "hello")); assert( 102.2 == *map_find(m, "world")); assert( 103.3 == *map_find(m, "nice")); assert( 104.4 == *map_find(m, "hash")); // reinsertion assert(map_insert(m, "hello", -101.1)); assert(-101.1 == *map_find(m, "hello")); map_foreach(m,char*,k,double,v) { printf("%s -> %f\n", k, v); } map_free(m); assert( !puts("Ok") ); } void map_benchmark2() { #ifndef NUM #define NUM 2000000 #endif map(int,int) m = 0; map_init(m, sort_int, hash_int); clock_t t0 = clock(); for( int i = 0; i < NUM; ++i ) { map_insert(m, i, i+1); } for( int i = 0; i < NUM; ++i ) { uint32_t *v = map_find(m, i); assert( v && *v == i + 1 ); } double t = (clock() - t0) / (double)CLOCKS_PER_SEC; printf("[0]=%d\n", *map_find(m, 0)); printf("[N-1]=%d\n", *map_find(m, NUM-1)); printf("%d ops in %5.3fs = %fM ops/s\n", (NUM*2), t, (NUM*2) / (1e6 * t) ); map_free(m); } int main() { map_tests(); puts("---"); map_tests2(); puts("---"); map_benchmark(); puts("---"); map_benchmark2(); assert(~puts("Ok")); } #define main main__ #endif // MAP_DEMO #endif // MAP_C ================================================ FILE: vault/ds_quark.c ================================================ // quarks ---------------------------------------------------------------------- // - rlyeh, public domain #ifndef QUARK_H #define QUARK_H typedef unsigned quark; quark quark_intern(char *s); char* quark_str(quark q); int quark_len(quark q); int quark_hash(quark q); int quark_cmp(quark a, quark b); #define quark_intern(__VA_ARGS__) quark_intern(va(__VA_ARGS__)) #endif // ----------------------------------------------------------------------------- #ifdef QUARK_C #pragma once typedef struct quark_dictionary { char *block; uint64_t block_used; array(char*) strings; unsigned index; } quark_dictionary; static __thread quark_dictionary qd = {0}; quark (quark_intern)(char *s) { if( !qd.block ) qd.block = REALLOC(0, 4 * 1024 * 1024 ); // uint64_t n = strlen(s) + 1; array_push(qd.strings, qd.block + qd.block_used + 4); memcpy(qd.block + qd.block_used, s - 4, n + 4); qd.block_used += n + 4; return ++qd.index; } char *quark_str(quark q) { return q > 0 && q <= qd.index ? qd.strings[ q - 1 ] : ""; } int quark_len(quark q) { return strlen(quark_str(q)); } int quark_hash(quark q) { return strhash(quark_str(q)); } int quark_cmp(quark q1, quark q2) { return strcmp(quark_str(q1), quark_str(q2)); } #endif ================================================ FILE: vault/ds_rope.c ================================================ #ifndef ROPE_H #define ROPE_H typedef struct rope rope; void rope_init( rope *s, int reserve, float grow ); void rope_append( rope *s, const char *buf, int len ); void rope_dump( rope *s ); int rope_len( rope *s ); #endif #ifdef ROPE_C #pragma once #include #include #include #include struct rope { struct rope *root; struct rope *next; int len, cap; float grow; unsigned char *data; // unsigned char data[rope_BLOCKSIZE - sizeof(rope*)*2 - sizeof(int)*2 )]; }; void rope_init( rope *s, int reserve, float grow ) { memset(s, 0, sizeof(rope)); s->grow = grow; s->data = realloc( s->data, (s->len = 0, s->cap = reserve) ); } void rope_append( rope *s, const char *buf, int len ) { while( s->next ) s = s->next; if( len > 0 && s->cap > 0 ) { int max = len > s->cap ? s->cap : len; memcpy(s->data + s->len, buf, max); s->len += max; s->cap -= max; buf += max; len -= max; } if( len > 0 ) { s->next = malloc( sizeof(rope) ); // linear (no decimals) or incremental (decimals) rope_init( s->next, (s->grow - (int)s->grow) > 0 ? (len+1) * s->grow : s->grow, s->grow ); rope_append( s->next, buf, len ); } } void rope_dump( rope *s ) { puts("--"); for( ; s ; s = s->next ) { printf("%03d/%03d bytes: \"%.*s\"\n", s->len, s->cap + s->len, s->len, s->data); } } int rope_len( rope *s ) { int len = 0; for( ; s ; s = s->next ) { len += s->len; } return len; } #ifdef ROPE_DEMO int main() { rope s; rope_init(&s, 5, 5); // 1.75); // 1.75f); rope_dump(&s); rope_append(&s, "hello world.", 12); rope_dump(&s); rope_append(&s, "there's a lady who's sure all that glitter's gold.", 50); rope_dump(&s); printf("%d bytes\n", rope_len(&s)); } #define main main__ #endif // ROPE_DEMO #endif // ROPE_C ================================================ FILE: vault/ds_set.c ================================================ // generic set container. // ideas from: https://en.wikipedia.org/wiki/Hash_table // ideas from: https://probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable/ // ideas from: http://www.idryman.org/blog/2017/05/03/writing-a-damn-fast-hash-table-with-tiny-memory-footprints/ // - rlyeh, public domain. #ifndef SET_H #define SET_H #include #include // config #ifndef SET_REALLOC #define SET_REALLOC REALLOC #endif #ifndef SET_HASHSIZE #define SET_HASHSIZE (4096 << 4) #endif #ifndef SET_DONT_ERASE #define SET_DONT_ERASE 1 #endif // public api #define set(K) \ struct { set base; struct { set_item p; K key; } tmp, *ptr; \ int (*typed_cmp)(K, K); uint64_t (*typed_hash)(K); } * #define set_init(m, cmpfn, hashfn) ( \ (m) = REALLOC(0, sizeof(*m)), \ set_init(&(m)->base), \ (m)->base.cmp = (int(*)(void*,void*))( (m)->typed_cmp = cmpfn), \ (m)->base.hash = (uint64_t(*)(void*))( (m)->typed_hash = hashfn ) \ ) #define set_free(m) ( \ set_clear(m), \ set_free(&(m)->base), \ (m) = REALLOC((m), 0), \ (m) = 0 \ ) #define set_insert(m, k) ( \ (m)->ptr = set_cast((m)->ptr) SET_REALLOC(0, sizeof((m)->tmp)), \ (m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \ set_insert(&(m)->base, &(m)->ptr->p, &(m)->ptr->key, (m)->ptr->p.keyhash, (m)->ptr), \ &(m)->ptr->key \ ) #define set_find(m, k) ( \ (m)->ptr = &(m)->tmp, \ (m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \ (m)->ptr = set_cast((m)->ptr) set_find(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash), \ (m)->ptr ? &(m)->ptr->key : 0 \ ) #define set_erase(m, k) ( \ (m)->ptr = &(m)->tmp, \ (m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \ set_erase(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash) \ ) #define set_foreach(m,key_t,k) \ for( int i_ = 0; i_ < SET_HASHSIZE; ++i_) \ for( set_item *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \ for( key_t k = *(key_t *)cur_->key; on_; on_ = 0 ) #define set_clear(m) ( \ set_clear(&(m)->base) \ ) #define set_count(m) set_count(&(m)->base) #define set_gc(m) set_gc(&(m)->base) // private: #ifdef __cplusplus #define set_cast(t) (decltype(t)) #else #define set_cast(t) (void *) #endif typedef struct set_item { struct set_item *next; uint64_t keyhash; void *key; void *super; } set_item; typedef struct set { array(set_item*) array; int (*cmp)(void *, void *); uint64_t (*hash)(void *); } set; void (set_init)(set *m); void (set_free)(set *m); void (set_insert)(set *m, set_item *p, void *key, uint64_t keyhash, void *super); void (set_erase)(set *m, void *key, uint64_t keyhash); void* (set_find)(const set *m, void *key, uint64_t keyhash); int (set_count)(const set *m); void (set_gc)(set *m); // only if using SET_DONT_ERASE #endif // ------------------------------- #ifdef SET_C #pragma once #include #include #include #include #include #include #include enum { set_GC_SLOT = SET_HASHSIZE }; typedef int set_is_pow2_assert[ !(SET_HASHSIZE & (SET_HASHSIZE - 1)) ]; static int set_get_index(uint64_t hkey1) { return hkey1 & (SET_HASHSIZE-1); } void (set_init)(set* m) { set zero = {0}; *m = zero; array_resize(m->array, (SET_HASHSIZE+1)); memset(m->array, 0, (SET_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset() } void (set_insert)(set* m, set_item *p, void *key, uint64_t keyhash, void *super) { p->keyhash = keyhash; p->key = key; p->super = super; /* Insert onto the beginning of the list */ int index = set_get_index(p->keyhash); p->next = m->array[index]; m->array[index] = p; } void* (set_find)(const set* m, void *key, uint64_t keyhash) { int index = set_get_index(keyhash); for( const set_item *cur = m->array[index]; cur; cur = cur->next ) { if( cur->keyhash == keyhash ) { char **c = (char **)cur->key; char **k = (char **)key; if( !m->cmp(c[0], k[0]) ) { return cur->super; } } } return 0; } void (set_erase)(set* m, void *key, uint64_t keyhash) { int index = set_get_index(keyhash); for( set_item *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) { if( cur->keyhash == keyhash ) { char **c = (char **)cur->key; char **k = (char **)key; if( !m->cmp(c[0], k[0]) ) { if (prev) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0; #if SET_DONT_ERASE /* Insert onto the beginning of the GC list */ cur->next = m->array[set_GC_SLOT]; m->array[set_GC_SLOT] = cur; #else SET_REALLOC(cur,0); #endif return; } } } } int (set_count)(const set* m) { // does not include GC_SLOT int counter = 0; for( int i = 0; i < SET_HASHSIZE; ++i) { for( const set_item *cur = m->array[i]; cur; cur = cur->next ) { ++counter; } } return counter; } void (set_gc)(set* m) { // clean deferred GC_SLOT only #if SET_DONT_ERASE for( set_item *next, *cur = m->array[set_GC_SLOT]; cur; cur = next ) { next = cur->next; SET_REALLOC(cur,0); } m->array[set_GC_SLOT] = 0; #endif } void (set_clear)(set* m) { // include GC_SLOT for( int i = 0; i <= SET_HASHSIZE; ++i) { for( set_item *next, *cur = m->array[i]; cur; cur = next ) { next = cur->next; SET_REALLOC(cur,0); } m->array[i] = 0; } } void (set_free)(set* m) { (set_clear)(m); array_free(m->array); m->array = 0; set zero = {0}; *m = zero; } // ------------------------------- #ifdef SET_DEMO #include #include #ifdef NDEBUG #undef NDEBUG #include #define NDEBUG #else #include #endif void set_tests() { { set(int) m = {0}; set_init(&m, cmp_int, hash_int); assert( 0 == set_count(&m) ); set_insert(&m, 123); set_insert(&m, 456); assert( 2 == set_count(&m) ); assert( set_find(&m, 123) ); assert( set_find(&m, 456) ); assert(!set_find(&m, 789) ); assert( 123 == *set_find(&m, 123) ); assert( 456 == *set_find(&m, 456) ); set_foreach(&m,const int,k) { printf("%d\n", k); } set_erase(&m, 123); assert(!set_find(&m, 123) ); assert( 1 == set_count(&m) ); set_erase(&m, 456); assert(!set_find(&m, 456) ); assert( 0 == set_count(&m) ); set_free(&m); } { set(char*) m = {0}; set_init(&m, cmp_str, hash_str); assert( set_count(&m) == 0 ); set_insert(&m, "123"); set_insert(&m, "456"); assert( set_count(&m) == 2 ); assert( set_find(&m,"123") ); assert( set_find(&m,"456") ); assert(!set_find(&m,"789") ); set_foreach(&m,const char *,k) { printf("%s\n", k); } set_erase(&m, "123"); assert( 0 == strcmp("456", *set_find(&m,"456")) ); assert( set_count(&m) == 1 ); set_erase(&m, "456"); assert( set_count(&m) == 0 ); set_free(&m); assert(!puts("Ok")); } } void set_tests2() { set(char*) m; set_init(&m, cmp_str, hash_str); set_insert(&m, "hello"); set_insert(&m, "world"); set_insert(&m, "nice"); set_insert(&m, "hash"); assert(!set_find(&m, "random")); assert(set_find(&m, "hello")); assert(set_find(&m, "world")); assert(set_find(&m, "nice")); assert(set_find(&m, "hash")); assert( 0 == strcmp("hello", *set_find(&m, "hello"))); assert( 0 == strcmp("world", *set_find(&m, "world"))); assert( 0 == strcmp("nice", *set_find(&m, "nice"))); assert( 0 == strcmp("hash", *set_find(&m, "hash"))); // reinsertion assert(set_insert(&m, "hello")); assert(0 == strcmp("hello", *set_find(&m, "hello"))); set_free(&m); assert( !puts("Ok") ); } void set_benchmark() { #ifndef NUM #define NUM 2000000 #endif set(int) m; set_init(&m, cmp_int, hash_int); clock_t t0 = clock(); for( int i = 0; i < NUM; ++i ) { set_insert(&m, i); } for( int i = 0; i < NUM; ++i ) { int *v = set_find(&m, i); assert( v && *v == i ); } double t = (clock() - t0) / (double)CLOCKS_PER_SEC; printf("[0]=%d\n", *set_find(&m, 0)); printf("[N-1]=%d\n", *set_find(&m, NUM-1)); printf("%d ops in %5.3fs = %fM ops/s\n", (NUM*2), t, (NUM*2) / (1e6 * t) ); set_free(&m); } int main() { set_tests(); puts("---"); set_tests2(); puts("---"); set_benchmark(); assert(~puts("Ok")); } #define main main__ #endif // SET_DEMO #endif // SET_C ================================================ FILE: vault/ds_sort.c ================================================ // compare (<0:less,0:equal,>0:greater) ---------------------------------------- // - rlyeh, public domain // typed int sort_int(int a, int b); int sort_u64(uint64_t a, uint64_t b); int sort_str(const char *a, const char *b); int sort_ptr(void *a, void *b); // untyped (generic, qsort style). single indirection. int sort_int_qs(const void *a, const void *b); int sort_uns_qs(const void *a, const void *b); int sort_i16_qs(const void *a, const void *b); int sort_u16_qs(const void *a, const void *b); int sort_i32_qs(const void *a, const void *b); int sort_u32_qs(const void *a, const void *b); int sort_f32_qs(const void *a, const void *b); int sort_i64_qs(const void *a, const void *b); int sort_u64_qs(const void *a, const void *b); int sort_f64_qs(const void *a, const void *b); int sort_ptr_qs(const void *a, const void *b); int sort_str_qs(const void *a, const void *b); int sort_stri_qs(const void *a, const void *b); int sort_stra_qs(const void *a, const void *b); // untyped (generic, qsort style). double indirection. int sort_int_qs2(const void *a, const void *b); int sort_uns_qs2(const void *a, const void *b); int sort_i16_qs2(const void *a, const void *b); int sort_u16_qs2(const void *a, const void *b); int sort_i32_qs2(const void *a, const void *b); int sort_u32_qs2(const void *a, const void *b); int sort_f32_qs2(const void *a, const void *b); int sort_i64_qs2(const void *a, const void *b); int sort_u64_qs2(const void *a, const void *b); int sort_f64_qs2(const void *a, const void *b); int sort_ptr_qs2(const void *a, const void *b); int sort_str_qs2(const void *a, const void *b); int sort_stri_qs2(const void *a, const void *b); int sort_stra_qs2(const void *a, const void *b); // ----------------------------------------------------------------------------- #ifdef SORT_C #pragma once // typed. no indirection. int sort_int(int a, int b) { return a - b; } int sort_u64(uint64_t a, uint64_t b) { return a > b ? +1 : -!!(a - b); } int sort_ptr(void *a, void *b) { return (uintptr_t)a > (uintptr_t)b ? +1 : -!!((uintptr_t)a - (uintptr_t)b); } int sort_str(const char *a, const char *b) { #if 0 // useful? int sa = strlen((const char*)a); int sb = strlen((const char*)b); return sa>sb ? +1 : sa *b ? +1 : -!!(*a-*b); } int sort_i64_qs(const void *p, const void *q) { const int64_t*a = (const int64_t*)p; const int64_t*b = (const int64_t*)q; return *a > *b ? +1 : -!!(*a-*b); } int sort_u64_qs(const void *p, const void *q) { const uint64_t*a = (const uint64_t*)p; const uint64_t*b = (const uint64_t*)q; return *a > *b ? +1 : -!!(*a-*b); } int sort_f64_qs(const void *p, const void *q) { const double*a = (const double*)p; const double*b = (const double*)q; return *a > *b ? +1 : -!!(*a-*b); } int sort_ptr_qs(const void *p, const void *q) { const uintptr_t*a = (const uintptr_t*)p; const uintptr_t*b = (const uintptr_t*)q; return *a > *b ? +1 : -!!(*a-*b); } int sort_str_qs(const void *a, const void *b) { // str const char *sa = *(const char **)a; const char *sb = *(const char **)b; return strcmp(sa, sb); } int sort_stri_qs(const void *a, const void *b) { // str const char *sa = *(const char **)a; const char *sb = *(const char **)b; for( ; *sa && *sb ; ++sa, ++sb ) { if( ((*sa)|32) != ((*sb)|32)) { break; } } return (*sa) - (*sb); } int sort_stra_qs(const void *a, const void *b) { // str const char *sa = *(const char **)a; const char *sb = *(const char **)b; int sort__alphanum(const char *l, const char *r); return sort__alphanum(sa, sb); } #include int sort__alphanum(const char *l, const char *r) { /* The Alphanum Algorithm is an improved sorting algorithm for strings containing numbers. Instead of sorting numbers in ASCII order like a standard sort, this algorithm sorts numbers in numeric order. The Alphanum Algorithm is discussed at http://www.DaveKoelle.com This implementation is Copyright (c) 2008 Dirk Jagdmann . It is a cleanroom implementation of the algorithm and not derived by other's works. In contrast to the versions written by Dave Koelle this source code is distributed with the libpng/zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ int is_string = 1; while(*l && *r) { if(is_string) { char l_char, r_char; while((l_char=*l) && (r_char=*r)) { // check if this are digit characters const bool l_digit=isdigit(l_char), r_digit=isdigit(r_char); // if both characters are digits, we continue in NUMBER mode if(l_digit && r_digit) { is_string = 0; break; } // if only the left character is a digit, we have a result if(l_digit) return -1; // if only the right character is a digit, we have a result if(r_digit) return +1; // compute the difference of both characters const int diff=l_char - r_char; // if they differ we have a result if(diff != 0) return diff; // otherwise process the next characters ++l; ++r; } } else { // mode==NUMBER // get the left number unsigned long l_int=0; while(*l && isdigit(*l)) { // TODO: this can overflow l_int=l_int*10 + *l-'0'; ++l; } // get the right number unsigned long r_int=0; while(*r && isdigit(*r)) { // TODO: this can overflow r_int=r_int*10 + *r-'0'; ++r; } // if the difference is not equal to zero, we have a comparison result const long diff=l_int-r_int; if(diff != 0) return diff; // otherwise we process the next substring in STRING mode is_string = 1; } } if(*r) return -1; if(*l) return +1; return 0; } #ifdef SORT_DEMO #include #include int main() { uint64_t a64 = 0, b64 = 0; assert( 0 == sort_u64(a64, b64)); const char *astr = "hello", *bstr = "Hello"; assert( 0 < sort_str(astr, bstr)); assert( 0 == sort_stri_qs(&astr, &bstr)); assert( 0 < sort_stra_qs(&astr, &bstr)); assert(~puts("Ok")); } #define main main__ #endif // SORT_DEMO #endif // SORT_C ================================================ FILE: vault/ds_stream.c ================================================ // stream: read/write/sync, stats, sim // implementations: mem/list(str), file/dir, tcp/udp, stdin/stdout, sql/kissdb // - rlyeh, public domain #ifndef STREAM_MEM_ENLARGE_FACTOR #define STREAM_MEM_ENLARGE_FACTOR 1.5 // 2 // 1.5 #endif // hacky solution to an old problem: hook stdio.h to new tech, so old code gets free upgrades. // this is only enabled if STDIO2 is defined #ifdef STDIO2 #define _FILE_OFFSET_BITS 64 #ifdef __cplusplus #include #endif #include #ifndef STDIO2_H #define STDIO2_H typedef FILE STREAM_FILE; #define FILE STREAM #define fopen(uri,...) stream_open((uri),__VA_ARGS__) #define fread(ptr,c,sz,fp) (stream_read((fp),(ptr),(c)*(sz)) == (c)*(sz) ? (sz) : 0) #define fwrite(ptr,c,sz,fp) (stream_puts((fp),(ptr),(c)*(sz)) == (c)*(sz) ? (sz) : 0) #define fclose(fp) stream_shut((fp)) #define ftell(fp) stream_tell((fp)) #define fputs(s,fp) stream_puts((fp),(s),strlen(s)) // @todo: optimize #define fprintf #error:not_supported // stream_puts((fp),va(__VA_ARGS__),strlen(va(__VA_ARGS__))) // @todo: optimize #define fseek(fp,p,m) stream_seek((fp),(p),(m)) #define fflush(fp) stream_sync((fp)) #define fgets #error:not_supported #define fgetc #error:not_supported #define fputc #error:not_supported #define fscanf #error:not_supported #define feof #error:not_supported #define ferror #error:not_supported #define fgetpos #error:not_supported #define fsetpos #error:not_supported #define freopen #error:not_supported #define funopen #error:not_supported #define fopen_s(pfp,n,m) ((*pfp) = fopen((n),(m)), -!(*pfp)) #define fseeko fseek #define _fseeki64 fseek #define ftello ftell #define _ftelli64 ftell #endif #endif #ifndef STREAM_H #define STREAM_H #include // stream is a virtual interface that reads and writes bytes, with seeking support. // could have used funopen() instead, but it is not available everywhere. // --- // [x] transports: null://, file://, mem://. // [ ] extra transports: udp://, tcp://, http://, ftp://, ipc://, rpc:// // [x] pipes: chain of transforms to apply after reading/before writing. // [ ] transactions: txbegin(id) + content + txend(id) // [x] linked streams -[hub]< (multicast write) // [ ] linked streams: >[sink]- >[multi]<) (multicast read, multicast read/write) typedef struct STREAM STREAM; // basic usage //int stream_stat(const char *spec); // check for presence STREAM* stream_open(const char *spec, ...); int stream_read(STREAM *s, void *ptr, int sz); int stream_puts(STREAM *s, const void *ptr, int sz); int stream_seek(STREAM *s, int offs, int mode); int stream_tell(STREAM *s); int stream_sync(STREAM *s); int stream_shut(STREAM *s); int stream_link(STREAM *s, STREAM *other); int stream_pipe(STREAM *s, int(*pipe)(void *ptr, int sz) ); char *stream_info(STREAM *s); // create and register new stream protocol int stream_make( const char *spec, int (*open)(STREAM *self, const char *spec, va_list vl), int (*read)(STREAM *self, void *ptr, int sz), int (*puts)(STREAM *self, const void *ptr, int sz), int (*seek)(STREAM *self, int offs, int mode), int (*tell)(STREAM *self), int (*sync)(STREAM *self), int (*shut)(STREAM *self) ); #endif // ----------------------------------------------------------------------------- #ifdef STREAM_C #pragma once // null:// interface ---------------------------------------------------------- static int nil_open(STREAM *s, const char *spec, va_list vl) { return 0; } static int nil_read(STREAM *s, void *ptr, int sz) { return sz; } static int nil_write(STREAM *s, const void *ptr, int sz) { return sz; } static int nil_seek(STREAM *s, int offs, int mode) { return 0; } static int nil_tell(STREAM *s) { return 0; } static int nil_sync(STREAM *s) { return 0; } static int nil_close(STREAM *s) { return 0; } // file:// interface ---------------------------------------------------------- typedef struct file { #ifdef STDIO2 #undef FILE #endif FILE *fp; #ifdef STDIO2 #define FILE STREAM #endif } file; static int file_open(STREAM *s, const char *spec, va_list vl) { return (((file*)s)->fp = (fopen)(spec, va_arg(vl, const char *))) ? 0 : -1; } static int file_read_(STREAM *s, void *ptr, int sz) { return (fread)(ptr, 1, sz, ((file*)s)->fp); } static int file_write_(STREAM *s, const void *ptr, int sz) { return (fwrite)(ptr, 1, sz, ((file*)s)->fp); } static int file_seek(STREAM *s, int offs, int mode) { return (fseek)(((file*)s)->fp, offs, mode); } static int file_tell(STREAM *s) { return (int)(ftell)(((file*)s)->fp); } static int file_sync(STREAM *s) { return (fflush)(((file*)s)->fp), 0; } static int file_close(STREAM *s) { return (fclose)(((file*)s)->fp), 0; } #if 1 // not used static int file_access( const char *filename ) { #ifdef _WIN return _access( filename, 0 ) != -1 ? 0 : -1; #else return access( filename, F_OK ) != -1 ? 0 : -1; #endif } #endif // mem:// interface ----------------------------------------------------------- // @works: wb, rb // @todo: w+b, r+b, a+b, ab typedef struct membuffer { char *begin, *cur, *end, *bottom; int owned; // true if using own allocated mem; false if using external mem char *filename; int mode; } membuffer; typedef struct ramdisk { char *filename; char *begin, *end; struct ramdisk *next; } ramdisk; ramdisk *top = 0; ramdisk *ramnew(void) { if( !top ) return top = CALLOC(1,sizeof(ramdisk)); while(top->next) top = top->next; return top->next = CALLOC(1,sizeof(ramdisk)); } ramdisk *ramfind(const char *filename) { for(ramdisk *t = top; t; t = t->next) { if(!strcmp(t->filename, filename)) return t; } return 0; } static int mem_open(STREAM *s, const char *spec, va_list vl) { #if 0 int external = 1; char* begin = (char*)va_arg(vl, void*); char* end = (char*)va_arg(vl, void*); #else int external = 0; char* begin = MALLOC(1), *end = begin+1; const char *mode = va_arg(vl, const char *); if( mode[0] == 'r' ) { ramdisk *found = ramfind(spec); if( found ) { FREE(begin); begin = found->begin; end = found->end; } } #endif membuffer *mb = (membuffer*)s; mb->owned = !external; // !begin; mb->begin = begin; mb->cur = mb->begin; mb->end = end; mb->bottom = mb->end; mb->filename = STRDUP(spec); mb->mode = mode[0]; return 0; } static int mem_read(STREAM *s, void *ptr, int sz) { membuffer *mb = (membuffer*)s; int avail = mb->end - mb->cur; if( avail < sz ) { sz = avail; } memcpy(ptr, mb->cur, sz); mb->cur += sz; return sz; } static unsigned round_next_pow2(unsigned v) { // compute the next highest power of 2 of 32-bit v v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } static int mem_write(STREAM *s, const void *ptr, int sz) { membuffer *mb = (membuffer*)s; int avail = mb->bottom - mb->cur; if( mb->owned && sz >= avail ) { /* used to be > before adding extra zero comment below */ int pointer = mb->cur - mb->begin; int oldlen = (mb->end - mb->begin); int oldcap = (mb->bottom - mb->begin); int newcap = (oldcap + sz) * STREAM_MEM_ENLARGE_FACTOR; // newcap = round_next_pow2(newcap); mb->begin = REALLOC(mb->begin, newcap); mb->cur = mb->begin + pointer; mb->end = mb->begin + oldcap; mb->bottom = mb->begin + newcap; } /* <-- check below removed because it does not work with streams of unknown size (mb->avail == 0) if( mb->avail < sz ) { sz = mb->avail; } */ memcpy(mb->cur, ptr, sz); mb->cur += sz; if(mb->cur > mb->end) mb->end = mb->cur + 1; return sz; } static int mem_seek(STREAM *s, int offs, int mode) { membuffer *mb = (membuffer*)s; char *dst; /**/ if( mode == SEEK_SET ) dst = mb->begin + offs; else if( mode == SEEK_CUR ) dst = mb->cur + offs; else if( mode == SEEK_END ) dst = mb->end - 1 + offs; else return -1; if( dst >= mb->begin && dst < mb->end) { mb->cur = dst; return 0; } return -1; } static int mem_tell(STREAM *s) { membuffer *mb = (membuffer*)s; return mb->cur - mb->begin; } static int mem_sync(STREAM *s) { membuffer *mb = (membuffer*)s; return 0; } static int mem_close(STREAM *s) { membuffer *mb = (membuffer*)s; #if 0 if( mb->owned ) REALLOC( mb->begin, 0 ); #else if( mb->mode == 'w' ) { ramdisk *found = ramfind(mb->filename); if( !found ) found = ramnew(); if( found ) { FREE(found->begin); FREE(found->filename); found->filename = STRDUP(mb->filename); found->begin = mb->begin; found->end = mb->end; } } #endif return 0; } // stream --------------------------------------------------------------------- typedef struct STREAM { // opaque data always first member in struct. // implementations can cast directly to their types as long as their sizes are less than sizeof(dummy). char dummy[ 256 - sizeof(const char *) - sizeof(int(*)()) * (7+8) - sizeof(STREAM*) - 8*4 ]; // members const char *spec; int (*open)(void *self, const char *spec, ...); int (*read)(void *self, void *ptr, int sz); int (*puts)(void *self, const void *ptr, int sz); int (*seek)(void *self, int offs, int mode); int (*tell)(void *self); int (*sync)(void *self); int (*shut)(void *self); int (*pipe[8])(void *ptr, int sz); STREAM *next; uint64_t rd, wr, rdb, wrb; // rd/wr hits, rd/wr bytes } STREAM; typedef int sizeof_stream[ sizeof(STREAM) == 256 ]; static STREAM sprotocols[32] = {0}; static int sprotocols_count = 0; int stream_make( const char *spec, int (*open)(STREAM *self, const char *spec, va_list), int (*read)(STREAM *self, void *ptr, int sz), int (*puts)(STREAM *self, const void *ptr, int sz), int (*seek)(STREAM *self, int offs, int mode), int (*tell)(STREAM *self), int (*sync)(STREAM *self), int (*shut)(STREAM *self) ) { STREAM protocol = { {0}, spec, open, read, puts, seek, tell, sync, shut }; sprotocols[sprotocols_count++] = protocol; // default to file:// (slot #0) if no explicit protocol is provided int special_case = !strcmp(protocol.spec, "file://"); if( special_case ) { STREAM swapped = sprotocols[ 0 ]; sprotocols[ 0 ] = sprotocols[ sprotocols_count - 1 ]; sprotocols[ sprotocols_count - 1 ] = swapped; } return 0; } STREAM *stream_open(const char *spec, ...) { static int registered = 0; if( !registered ) { // auto-register provided interfaces registered = 1; stream_make( "file://", file_open, file_read_, file_write_, file_seek, file_tell, file_sync, file_close ); stream_make( "mem://", mem_open, mem_read, mem_write, mem_seek, mem_tell, mem_sync, mem_close ); stream_make( "null://", nil_open, nil_read, nil_write, nil_seek, nil_tell, nil_sync, nil_close ); } STREAM *self = (STREAM*)REALLOC(0, sizeof(STREAM)); *self = sprotocols[0]; // default file:// interface if( strstr(spec, "://") ) { for( int i = 0; i < sprotocols_count; ++i ) { if( strstr(spec, sprotocols[i].spec) ) { *self = sprotocols[i]; spec += strlen(sprotocols[i].spec); break; } } } va_list vl; va_start(vl, spec); if( 0 != self->open( self, spec, vl ) ) { REALLOC(self, 0); self = 0; } va_end(vl); return self; } int stream_pipe(STREAM *self, int(*pipe)(void*,int)) { int it = 0; for(;it <= 8 && self->pipe[it]; ++it); return it == 8 ? -1 : (self->pipe[it] = pipe, 0); } static int read_loop(STREAM *self, void *buffer, int count) { int offset = 0; while( count > 0 ) { int block = self->read(self, (char *)buffer + offset, count); if( block <= 0 ) { // < 0 for blocking sockets! return block; } if( block ) { offset += block; count -= block; ++self->rd; self->rdb += block; } } return offset; } static int write_loop(STREAM *self, const void *buffer, int count) { int offset = 0; while( count > 0 ) { int block = self->puts(self, (const char *)buffer + offset, count); if (block <= 0) { // < 0 for blocking sockets! return block; } if( block ) { offset += block; count -= block; ++self->wr; self->wrb += block; } } return offset; } int stream_read(STREAM *self, void *ptr, int sz) { int rc = 0; while( self ) { rc = read_loop(self, ptr, sz); for(int it=0;it<8 && rc>=0;++it) rc = self->pipe[it] ? self->pipe[it]((void*)ptr, sz) : rc; if( rc < 0 ) break; self = self->next; } return rc; } int stream_puts(STREAM *self, const void *ptr, int sz) { int rc = 0; while( self ) { for(int it=0;it<8 && rc>=0;++it) rc = self->pipe[it] ? self->pipe[it]((void*)ptr, sz) : rc; rc = write_loop(self, ptr, sz); if( rc < 0 ) break; self = self->next; } return rc; } int stream_seek(STREAM *self, int offs, int mode) { int errors = 0; while( self ) { STREAM *next = self->next; errors |= !!self->seek(self, offs, mode); self = next; } return -errors; } int stream_tell(STREAM *self) { int minimum = INT_MAX; while( self ) { STREAM *next = self->next; int at = self->tell(self); if( at < minimum ) minimum = at; self = next; } return minimum; } int stream_sync(STREAM *self) { while( self ) { STREAM *next = self->next; self->sync(self); self = next; } return 0; } int stream_shut(STREAM *self) { while( self ) { STREAM *next = self->next; self->sync(self); self->shut(self); free(self); self = next; } return 0; } int stream_link(STREAM *self, STREAM *other) { if( !self->next ) return !!(self->next = other); return stream_link(self->next, other); } char* stream_info(STREAM *self) { static __declspec(thread) char buf[128]; snprintf(buf, 128, "[STREAM:%p]: puts/pull %llu/%llu bytes (%llu/%llu ops)", self, self->wrb, self->rdb, self->wr, self->rd); return buf; } #endif ================================================ FILE: vault/ds_string.c ================================================ // string library // - rlyeh, public domain #ifndef STRING_H #define STRING_H // temporary strings api (stack) #define strtmp(fmt, ...) va(fmt, __VA_ARGS__) // allocated strings api (heap) #define strnew(fmt, ...) STRDUP(va(fmt,__VA_ARGS__)) #define strdel(s) ((is_va(s) ? (void)0 : REALLOC((s), 0)), (s)=0) #define stradd(s,fmt,...) stradd((s), va(fmt, __VA_ARGS__)) // string utils (beware! destructive operations mostly) char* strlower(char *s); char* strupper(char *s); char* strcamel(char *s); char* strtrim(char *s); char* strswap(char *s, const char *src, const char *dst); int strmatch(const char *s, const char *wildcard); uint64_t strhash(const char *s); array(uint32_t) strutf8(const char *utf8); const char* strrstr(const char *s1, const char *s2); char* strmap(char *inout, const char *src, const char *dst); int strhead( const char *string, const char *substr ); int strtail( const char *s, const char *e ); char* strflip(char *s); const char* strsub( const char *str, int pos ); int streq( const char *string, const char *substr ); int streqi( const char *string, const char *substr ); const char* strskip(const char *s, const char *chars); const char* strfind(const char *s, const char *chars); char* (stradd)(char **s, const char *s2); // tokenizer utils char** strsplit(const char *string, const char *delimiters); // adds additional eos char* strjoin(int num, char **list, const char *separator); // if num<=0, check for eos int strchop(const char *string, const char *substr, char **left, char **right); // tokenizer utils (array version; without trailing null string) array(char*) strsplit2(const char *string, const char *delimiters); char* strjoin2(array(char*) list, const char *separator); // aliases typedef char* string; #endif // ----------------------------------------------------------------------------- #ifdef STRING_C #pragma once #if defined _MSC_VER && !defined __thread #define __thread __declspec(thread) #endif #include #include #include #include #include #include // tokenizer ------------------------------------------------------------------- array(char*) strsplit(const char *text_, const char *delimiters) { static __thread char *mems[16] = {0}; static __thread array(char*) list[16] = {0}; static __thread int list_count = 0; int len = strlen(text_); int slot = (list_count = (list_count+1) % 16); mems[slot] = REALLOC(mems[slot], len+1); char *text = mems[slot]; memcpy(text, text_, len+1); array(char *) *out = &list[slot]; array_clear(*out); int found[256] = {1,0}, i = 0; while( *delimiters ) found[(unsigned char)*delimiters++] = 1; while( text[i] ) { int begin = i; while(text[i] && !found[(unsigned char)text[i]]) ++i; int end = i; while(text[i] && found[(unsigned char)text[i]]) ++i; if (end > begin) { array_push(*out, (text + begin)); (text + begin)[ end - begin ] = 0; } } array_push(*out, 0); //(end of array) return *out; } char* strjoin(int num_list, char **list, const char *separator) { #if 1 static __thread char* mems[16] = {0}; static __thread int num = 0; int slot = (num = (num+1) % 16); int len = 0, inc = 0, seplen = strlen(separator); for( int i = 0; (num_list > 0 ? i < num_list : !!list[i]); ++i ) { len += strlen(list[i]) + inc; inc = seplen; } mems[slot] = REALLOC(mems[slot], len+1); char *p = mems[slot]; *p = 0; const char *sep = ""; for( int i = 0; (num_list > 0 ? i < num_list : !!list[i]); ++i ) { p += sprintf(p, "%s%s", sep, list[i]); sep = separator; } return mems[slot]; #else char *x = 0, *sep = ""; for( int i = 0; (num_list > 0 ? i < num_list : !!list[i]); ++i ) { stradd(&x, "%s%s", sep, list[i]); sep = separator; } char *ret = va("%s", x); FREE(x); return ret; #endif } #include array(char*) strsplit2(const char *str, const char *separator) { // @todo: replaces strsplit? static __thread int slot = 0; static __thread char *buf[16] = {0}; static __thread array(char*) list[16] = {0}; slot = (slot+1) % 16; array_resize(list[slot], 0); buf[slot] = REALLOC(buf[slot], strlen(str)+1); for(char *dst = buf[slot]; str && *str; ) { // find && skip separator const char *sep = strpbrk(str, separator); const char *src = str; int slen = (int)(sep - src); str = sep + (sep ? strspn(sep, separator) : 0); // append & update content array_push(list[slot], dst); memcpy((dst += slen) - slen, src, slen); *dst++ = '\0'; } return list[slot]; } char *strjoin2(array(char*) list, const char *separator) { return strjoin( array_count(list), list, separator ); } // string utils ---------------------------------------------------------------- const char *strsub( const char *str, int pos ) { int size = strlen(str); pos = pos && size ? (pos > 0 ? pos % size : size-1 + ((pos+1) % size)) : 0; return str + pos; } int streq( const char *string, const char *substr ) { return strcmp( string, substr ); } int streqi( const char *string, const char *substr ) { while( *string && *substr ) { int eqi = (*string++ | 32) - (*substr++ | 32); if( eqi ) return eqi; } return *string - *substr; } char *strflip(char *str) { // Based on code by Bob Stout (public domain). if(str && *str) for( char *p1 = str, *p2 = p1 + strlen(p1) - 1; p2 > p1; ++p1, --p2 ) { *p1 ^= *p2; *p2 ^= *p1; *p1 ^= *p2; } return str; } // skip all occurences of 'chars' in s const char *strskip(const char *s, const char *chars) { return s + strspn(s, chars); } // find any of 'chars' in s. null if no matches are found. const char *strfind(const char *s, const char *chars) { return strpbrk(s, chars); } // returns true if text starts with substring int strhead( const char *str, const char *substr ) { return strncmp(str, substr, strlen(substr)) == 0; } // returns true if text ends with substring int strtail( const char *s, const char *e ) { int ls = strlen(s); int le = strlen(e); if( ls < le ) return 0; return 0 == memcmp( s + ls - le, e, le ); } static char *strmap_(char *inout, const char *src, const char *dst) { char *find = strpbrk(inout, src); if(!find) return inout; char *which = strchr(src, find[0]); int distance = (int)( which - src ); int len = strlen(dst); if( distance < len ) { *find = dst[distance]; return strmap_(find+1, src, dst); // continue mapping } else { char *ptr = find; do ptr[0] = ptr[1]; while( *++ptr ); // memmove one return strmap_(find, src, dst); } } char *strmap(char *inout, const char *src, const char *dst) { // assert(strlen(src) >= strlen(dst)); if(strlen(src) == strlen(dst)) { uint8_t map[256] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; for( int i = 0; src[i]; ++i) map[(uint8_t)src[i]] = dst[i]; for( int i = 0; inout[i]; ++i ) inout[i] = map[(uint8_t)inout[i]]; return inout; } return strmap_(inout, src, dst), inout; } const char *strrstr(const char *s1, const char *s2) { int s1len = strlen(s1); char *find = strstr(s1, s2), *find1; if( !find ) return s1 + s1len; while( (find1 = strstr(find+s1len,s2)) != 0 ) find = find1; return find; } char *strlower( char *copy ) { for( char *s = copy; *s; ++s ) *s = tolower(*s); // &= ~32 return copy; } char *strupper( char *copy ) { for( char *s = copy; *s; ++s ) *s = toupper(*s); // |= 32 return copy; } char *strcamel( char *copy ) { for( char *s = copy; *s; ++s ) *s = tolower(*s); // &= ~32 char **list = strsplit(copy, " "); for( int i = 0; list[i]; ++i ) list[i][0] = toupper(list[i][0]); return strcpy(copy, strjoin(0, list, "")); } char* strtrim(char *str) { // trims leading, trailing and excess whitespaces. char *ibuf, *obuf; if( str ) { for( ibuf = obuf = str; *ibuf; ) { while( *ibuf && isspace(*ibuf) ) (ibuf++); if( *ibuf && obuf != str ) *(obuf++) = ' '; while( *ibuf && !isspace(*ibuf) ) *(obuf++) = *(ibuf++); } *obuf = 0; } return str; } char *strswap( char *copy, const char *target, const char *replacement ) { // replaced only if new text is shorter than old one int rlen = strlen(replacement), diff = strlen(target) - rlen; if( diff >= 0 ) { for( char *s = copy, *e = s + strlen(copy); /*s < e &&*/ 0 != (s = strstr(s, target)); ) { if( rlen ) s = (char*)memcpy( s, replacement, rlen ) + rlen; if( diff ) memmove( s, s + diff, (e - (s + diff)) + 1 ); } } return copy; } int strmatch(const char *s, const char *wildcard) { // returns true if wildcard matches if( *wildcard=='\0' ) return !*s; if( *wildcard=='*' ) return strmatch(s, wildcard+1) || (*s && strmatch(s+1, wildcard)); if( *wildcard=='?' ) return *s && (*s != '.') && strmatch(s+1, wildcard+1); return (*s == *wildcard) && strmatch(s+1, wildcard+1); } uint64_t strhash( const char *s ) { // for convenience. wrapper over original hash_str() function return hash_str(s); // uint64_t hash = 0; // fnv1a: 14695981039346656037ULL; // while( *s++ ) hash = ( s[-1] ^ hash ) * 131; // fnv1a: 0x100000001b3ULL; } // return hash; } array(uint32_t) strutf8( const char *utf8 ) { // Based on code by @ddiakopoulos (unlicensed). // Based on code by @nothings (public domain). array(uint32_t) out = 0; //array_reserve(out, strlen(utf8) + 1); while( *utf8 ) { const char **p = &utf8; uint32_t unicode = 0; /**/ if( (**p & 0x80) == 0x00 ) { int a = *((*p)++); unicode = a; } else if( (**p & 0xe0) == 0xc0 ) { int a = *((*p)++) & 0x1f; int b = *((*p)++) & 0x3f; unicode = (a << 6) | b; } else if( (**p & 0xf0) == 0xe0 ) { int a = *((*p)++) & 0x0f; int b = *((*p)++) & 0x3f; int c = *((*p)++) & 0x3f; unicode = (a << 12) | (b << 6) | c; } else if( (**p & 0xf8) == 0xf0 ) { int a = *((*p)++) & 0x07; int b = *((*p)++) & 0x3f; int c = *((*p)++) & 0x3f; int d = *((*p)++) & 0x3f; unicode = (a << 18) | (b << 12) | (c << 8) | d; } array_push(out, unicode); } return out; } int strchop(const char *src, const char *substr, char **left, char **right) { char *find = strstr(src, substr); if( !find ) return *left = va(""), *right = va(""), 0; *left = va("%.*s", (int)(find - src), src); *right = va("%s", find + strlen(substr)); return 1; } char *(stradd)(char **x, const char *buf) { char *z = x && *x ? *x : 0; int bl = strlen(buf); int zl = (z ? strlen(z) : 0); z = REALLOC(z, zl + bl + 1 ); memcpy(z + zl, buf, bl + 1 ); if( x && *x ) *x = z; return z; } // ----------------------------------------------------------------------------- #ifdef STRING_DEMO #pragma once int main() { // test creation & destruction char *x = strtmp("hello %d", 123); puts(x); strdel(x); assert(x == 0); // optional free, since x is a temporary string (strtmp) char *y = strnew("hello %d", 123); puts(y); strdel(y); assert(y == 0); // required free, since y was explicitly allocated (with strnew) // test concat char *z = strtmp("%d",6); z = strtmp("%s%s%s",z,z,z); assert( 0 == strcmp(z,"666") ); // test memory is never exhausted for(int i = 0; i < 10000; ++i) assert(strtmp("hello %d",123)); // test utils 1/3 char buf[128]; puts( strswap( strcpy(buf, "abracadabra"), "bra", "BRA") ); // same len puts( strswap( strcpy(buf, "abracadabra"), "bra", "BR") ); // smaller len puts( strswap( strcpy(buf, "abracadabra"), "bra", "B") ); // smaller len puts( strswap( strcpy(buf, "abracadabra"), "bra", "") ); // erase puts( strswap( strcpy(buf, "abracadabra"), "boo", "foo") ); // no matches // test utils 2/3 puts( strcamel("Camel case TEST") ); // test utils 3/3 array(char*) tokens = strsplit("JAN,;,FEB,MAR,,APR", ",;"); puts(strjoin( tokens, "/" )); // strrstr puts( strrstr("banana", "an") ); puts( strrstr("banana", "ban") ); puts( strrstr("banana", "xxx") ); // strmap char banana[] = "BANANA"; assert(!strcmp("banana", strmap(banana, "BNACXYZ ", "bnac"))); char stairway[] = "There's a lady who's sure ... "; assert(!strcmp("There'saladywho'ssure...", strmap(stairway, " ", ""))); char remap[] = "h3110 w0r1d"; assert( 0 == strcmp("hello world", strmap(remap, "310", "elo"))); // char *left, *right; strchop("abracadabra", "ca", &left, &right); assert(!strcmp("abra",left)); assert(!strcmp("dabra",right)); strchop("abracadabra", "xxx", &left, &right); assert(!strcmp("",left)); assert(!strcmp("",right)); char hello2[] = "hello cruel world"; assert( 0 == strcmp("hello world", strswap(hello2, "cruel ", "")) ); assert( 0 == strcmp("dlrow olleh", strflip(hello2)) ); // assert( strhead("banana", "ban") ); assert(!strhead("banana", "xxx") ); assert( strtail("banana", "ana") ); assert(!strtail("banana", "xxx") ); assert( strhead("hello", "hell") ); assert(!strhead("hell", "hello") ); assert( strtail("hello", "llo") ); // assert(!streq("hello", "hello") ); assert( streq("HELLO", "hello") ); assert(!streqi("hello", "hello") ); assert(!streqi("HELLO", "hello") ); assert(!strcmp(strsub("hello world",6),strsub("hello world", -5))); // world assert(!strcmp("hello world", strskip(" \t\rhello world", " \t\r"))); assert(!strcmp("(world)", strfind("hello(world)", "()"))); // test asserts are enabled assert(~puts("Ok")); } #define main main__ #endif // STRING_DEMO #endif // STRING_C ================================================ FILE: vault/net.c ================================================ // - rlyeh, public domain #ifdef NET_C //#pragma once #define ECDH_C #define TCP_C #define FRAGMENT_C #define TUNNEL_C #define WEBSERVER_C #endif #include "os.c" #include "ds.c" #include "net_tcp.c" #include "net_ecdh.h" #include "net_fragment.c" #include "net_tunnel.c" #include "net_webserver.c" ================================================ FILE: vault/net_ecdh.h ================================================ //#define ECC_CURVE NIST_K571 // 256bit, secure key is empty //#define ECC_CURVE NIST_B571 // 256bit, secure keys do not match //#define ECC_CURVE NIST_K409 // 192bit, secure key is empty #define ECC_CURVE NIST_B409 // 192bit, ok // Diffie-Hellman key exchange (without HMAC) aka ECDH_anon in RFC4492 // https://github.com/kokke/tiny-ECDH-c/ (UNLICENSE) /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ /* Crypto using elliptic curves defined over the finite binary field GF(2^m) where m is prime. The curves used are the anomalous binary curves (ABC-curves) or also called Koblitz curves. This class of curves was chosen because it yields efficient implementation of operations. Curves available - their different NIST/SECG names and eqivalent symmetric security level: NIST SEC Group strength ------------------------------------ K-163 sect163k1 80 bit B-163 sect163r2 80 bit K-233 sect233k1 112 bit B-233 sect233r1 112 bit K-283 sect283k1 128 bit B-283 sect283r1 128 bit K-409 sect409k1 192 bit B-409 sect409r1 192 bit K-571 sect571k1 256 bit B-571 sect571r1 256 bit Curve parameters from: http://www.secg.org/sec2-v2.pdf http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf Reference: https://www.ietf.org/rfc/rfc4492.txt */ #ifndef _ECDH_H__ #define _ECDH_H__ /* for size-annotated integer types: uint8_t, uint32_t etc. */ #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define NIST_B163 1 #define NIST_K163 2 #define NIST_B233 3 #define NIST_K233 4 #define NIST_B283 5 #define NIST_K283 6 #define NIST_B409 7 #define NIST_K409 8 /* currently defunct :( */ #define NIST_B571 9 #define NIST_K571 10 /* also not working... */ /* What is the default curve to use? */ #ifndef ECC_CURVE #define ECC_CURVE NIST_B163 #endif #if defined(ECC_CURVE) && (ECC_CURVE != 0) #if (ECC_CURVE == NIST_K163) || (ECC_CURVE == NIST_B163) #define CURVE_DEGREE 163 #define ECC_PRV_KEY_SIZE 24 #elif (ECC_CURVE == NIST_K233) || (ECC_CURVE == NIST_B233) #define CURVE_DEGREE 233 #define ECC_PRV_KEY_SIZE 32 #elif (ECC_CURVE == NIST_K283) || (ECC_CURVE == NIST_B283) #define CURVE_DEGREE 283 #define ECC_PRV_KEY_SIZE 36 #elif (ECC_CURVE == NIST_K409) || (ECC_CURVE == NIST_B409) #define CURVE_DEGREE 409 #define ECC_PRV_KEY_SIZE 52 #elif (ECC_CURVE == NIST_K571) || (ECC_CURVE == NIST_B571) #define CURVE_DEGREE 571 #define ECC_PRV_KEY_SIZE 72 #endif #else #error Must define a curve to use #endif #define ECC_PUB_KEY_SIZE (2 * ECC_PRV_KEY_SIZE) /******************************************************************************/ /* NOTE: assumes private is filled with random data before calling */ int ecdh_generate_keys(uint8_t* public_key, const uint8_t* private_key); /* input: own private key + other party's public key, output: shared secret */ int ecdh_shared_secret(const uint8_t* private_key, const uint8_t* others_pub, uint8_t* output); /* Broken :( .... */ int ecdsa_sign(const uint8_t* private_key, uint8_t* hash, uint8_t* random_k, uint8_t* signature); int ecdsa_verify(const uint8_t* public_key, uint8_t* hash, const uint8_t* signature); /******************************************************************************/ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* #ifndef _ECDH_H__ */ #ifdef ECDH_C #pragma once /* Crypto using elliptic curves defined over the finite binary field GF(2^m) where m is prime. The curves used are the anomalous binary curves (ABC-curves) or also called Koblitz curves. This class of curves was chosen because it yields efficient implementation of operations. Curves available - their different NIST/SECG names and eqivalent symmetric security level: NIST SEC Group strength ------------------------------------ K-163 sect163k1 80 bit B-163 sect163r2 80 bit K-233 sect233k1 112 bit B-233 sect233r1 112 bit K-283 sect283k1 128 bit B-283 sect283r1 128 bit K-409 sect409k1 192 bit B-409 sect409r1 192 bit K-571 sect571k1 256 bit B-571 sect571r1 256 bit Curve parameters from: http://www.secg.org/sec2-v2.pdf http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf Reference: https://www.ietf.org/rfc/rfc4492.txt */ #include //#include "ecdh.h" /* margin for overhead needed in intermediate calculations */ #define BITVEC_MARGIN 3 #define BITVEC_NBITS (CURVE_DEGREE + BITVEC_MARGIN) #define BITVEC_NWORDS ((BITVEC_NBITS + 31) / 32) #define BITVEC_NBYTES (sizeof(uint32_t) * BITVEC_NWORDS) /* Disable assertions? */ #ifndef DISABLE_ASSERT #define DISABLE_ASSERT 0 #endif #if defined(DISABLE_ASSERT) && (DISABLE_ASSERT == 1) #define assert(...) #else #include #endif /* Default to a (somewhat) constant-time mode? NOTE: The library is _not_ capable of operating in constant-time and leaks information via timing. Even if all operations are written const-time-style, it requires the hardware is able to multiply in constant time. Multiplication on ARM Cortex-M processors takes a variable number of cycles depending on the operands... */ #ifndef CONST_TIME #define CONST_TIME 0 #endif /* Default to using ECC_CDH (cofactor multiplication-variation) ? */ #ifndef ECDH_COFACTOR_VARIANT #define ECDH_COFACTOR_VARIANT 0 #endif /******************************************************************************/ /* the following type will represent bit vectors of length (CURVE_DEGREE+MARGIN) */ typedef uint32_t bitvec_t[BITVEC_NWORDS]; typedef bitvec_t gf2elem_t; /* this type will represent field elements */ typedef bitvec_t scalar_t; /******************************************************************************/ /* Here the curve parameters are defined. */ #if defined (ECC_CURVE) && (ECC_CURVE != 0) #if (ECC_CURVE == NIST_K163) #define coeff_a 1 #define cofactor 2 /* NIST K-163 */ const gf2elem_t polynomial = { 0x000000c9, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }; const gf2elem_t coeff_b = { 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; const gf2elem_t base_x = { 0x5c94eee8, 0xde4e6d5e, 0xaa07d793, 0x7bbc11ac, 0xfe13c053, 0x00000002 }; const gf2elem_t base_y = { 0xccdaa3d9, 0x0536d538, 0x321f2e80, 0x5d38ff58, 0x89070fb0, 0x00000002 }; const scalar_t base_order = { 0x99f8a5ef, 0xa2e0cc0d, 0x00020108, 0x00000000, 0x00000000, 0x00000004 }; #endif #if (ECC_CURVE == NIST_B163) #define coeff_a 1 #define cofactor 2 /* NIST B-163 */ const gf2elem_t polynomial = { 0x000000c9, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }; const gf2elem_t coeff_b = { 0x4a3205fd, 0x512f7874, 0x1481eb10, 0xb8c953ca, 0x0a601907, 0x00000002 }; const gf2elem_t base_x = { 0xe8343e36, 0xd4994637, 0xa0991168, 0x86a2d57e, 0xf0eba162, 0x00000003 }; const gf2elem_t base_y = { 0x797324f1, 0xb11c5c0c, 0xa2cdd545, 0x71a0094f, 0xd51fbc6c, 0x00000000 }; const scalar_t base_order = { 0xa4234c33, 0x77e70c12, 0x000292fe, 0x00000000, 0x00000000, 0x00000004 }; #endif #if (ECC_CURVE == NIST_K233) #define coeff_a 0 #define cofactor 4 /* NIST K-233 */ const gf2elem_t polynomial = { 0x00000001, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200 }; const gf2elem_t coeff_b = { 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; const gf2elem_t base_x = { 0xefad6126, 0x0a4c9d6e, 0x19c26bf5, 0x149563a4, 0x29f22ff4, 0x7e731af1, 0x32ba853a, 0x00000172 }; const gf2elem_t base_y = { 0x56fae6a3, 0x56e0c110, 0xf18aeb9b, 0x27a8cd9b, 0x555a67c4, 0x19b7f70f, 0x537dece8, 0x000001db }; const scalar_t base_order = { 0xf173abdf, 0x6efb1ad5, 0xb915bcd4, 0x00069d5b, 0x00000000, 0x00000000, 0x00000000, 0x00000080 }; #endif #if (ECC_CURVE == NIST_B233) #define coeff_a 1 #define cofactor 2 /* NIST B-233 */ const gf2elem_t polynomial = { 0x00000001, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200 }; const gf2elem_t coeff_b = { 0x7d8f90ad, 0x81fe115f, 0x20e9ce42, 0x213b333b, 0x0923bb58, 0x332c7f8c, 0x647ede6c, 0x00000066 }; const gf2elem_t base_x = { 0x71fd558b, 0xf8f8eb73, 0x391f8b36, 0x5fef65bc, 0x39f1bb75, 0x8313bb21, 0xc9dfcbac, 0x000000fa }; const gf2elem_t base_y = { 0x01f81052, 0x36716f7e, 0xf867a7ca, 0xbf8a0bef, 0xe58528be, 0x03350678, 0x6a08a419, 0x00000100 }; const scalar_t base_order = { 0x03cfe0d7, 0x22031d26, 0xe72f8a69, 0x0013e974, 0x00000000, 0x00000000, 0x00000000, 0x00000100 }; #endif #if (ECC_CURVE == NIST_K283) #define coeff_a 0 #define cofactor 4 /* NIST K-283 */ const gf2elem_t polynomial = { 0x000010a1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08000000 }; const gf2elem_t coeff_b = { 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; const gf2elem_t base_x = { 0x58492836, 0xb0c2ac24, 0x16876913, 0x23c1567a, 0x53cd265f, 0x62f188e5, 0x3f1a3b81, 0x78ca4488, 0x0503213f }; const gf2elem_t base_y = { 0x77dd2259, 0x4e341161, 0xe4596236, 0xe8184698, 0xe87e45c0, 0x07e5426f, 0x8d90f95d, 0x0f1c9e31, 0x01ccda38 }; const scalar_t base_order = { 0x1e163c61, 0x94451e06, 0x265dff7f, 0x2ed07577, 0xffffe9ae, 0xffffffff, 0xffffffff, 0xffffffff, 0x01ffffff }; #endif #if (ECC_CURVE == NIST_B283) #define coeff_a 1 #define cofactor 2 /* NIST B-283 */ const gf2elem_t polynomial = { 0x000010a1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08000000 }; const gf2elem_t coeff_b = { 0x3b79a2f5, 0xf6263e31, 0xa581485a, 0x45309fa2, 0xca97fd76, 0x19a0303f, 0xa5a4af8a, 0xc8b8596d, 0x027b680a }; const gf2elem_t base_x = { 0x86b12053, 0xf8cdbecd, 0x80e2e198, 0x557eac9c, 0x2eed25b8, 0x70b0dfec, 0xe1934f8c, 0x8db7dd90, 0x05f93925 }; const gf2elem_t base_y = { 0xbe8112f4, 0x13f0df45, 0x826779c8, 0x350eddb0, 0x516ff702, 0xb20d02b4, 0xb98fe6d4, 0xfe24141c, 0x03676854 }; const scalar_t base_order = { 0xefadb307, 0x5b042a7c, 0x938a9016, 0x399660fc, 0xffffef90, 0xffffffff, 0xffffffff, 0xffffffff, 0x03ffffff }; #endif #if (ECC_CURVE == NIST_K409) #define coeff_a 0 #define cofactor 4 /* NIST K-409 */ const gf2elem_t polynomial = { 0x00000001, 0x00000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000 }; const gf2elem_t coeff_b = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; const gf2elem_t base_x = { 0xe9023746, 0xb35540cf, 0xee222eb1, 0xb5aaaa62, 0xc460189e, 0xf9f67cc2, 0x27accfb8, 0xe307c84c, 0x0efd0987, 0x0f718421, 0xad3ab189, 0x658f49c1, 0x0060f05f }; const gf2elem_t base_y = { 0xd8e0286b, 0x5863ec48, 0xaa9ca27a, 0xe9c55215, 0xda5f6c42, 0xe9ea10e3, 0xe6325165, 0x918ea427, 0x3460782f, 0xbf04299c, 0xacba1dac, 0x0b7c4e42, 0x01e36905 }; const scalar_t base_order = { 0xe01e5fcf, 0x4b5c83b8, 0xe3e7ca5b, 0x557d5ed3, 0x20400ec4, 0x83b2d4ea, 0xfffffe5f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x007fffff }; #endif #if (ECC_CURVE == NIST_B409) #define coeff_a 1 #define cofactor 2 /* NIST B-409 */ const gf2elem_t polynomial = { 0x00000001, 0x00000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000 }; const gf2elem_t coeff_b = { 0x7b13545f, 0x4f50ae31, 0xd57a55aa, 0x72822f6c, 0xa9a197b2, 0xd6ac27c8, 0x4761fa99, 0xf1f3dd67, 0x7fd6422e, 0x3b7b476b, 0x5c4b9a75, 0xc8ee9feb, 0x0021a5c2 }; const gf2elem_t base_x = { 0xbb7996a7, 0x60794e54, 0x5603aeab, 0x8a118051, 0xdc255a86, 0x34e59703, 0xb01ffe5b, 0xf1771d4d, 0x441cde4a, 0x64756260, 0x496b0c60, 0xd088ddb3, 0x015d4860 }; const gf2elem_t base_y = { 0x0273c706, 0x81c364ba, 0xd2181b36, 0xdf4b4f40, 0x38514f1f, 0x5488d08f, 0x0158aa4f, 0xa7bd198d, 0x7636b9c5, 0x24ed106a, 0x2bbfa783, 0xab6be5f3, 0x0061b1cf }; const scalar_t base_order = { 0xd9a21173, 0x8164cd37, 0x9e052f83, 0x5fa47c3c, 0xf33307be, 0xaad6a612, 0x000001e2, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000000 }; #endif #if (ECC_CURVE == NIST_K571) #define coeff_a 0 #define cofactor 4 /* NIST K-571 */ const gf2elem_t polynomial = { 0x00000425, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08000000 }; const gf2elem_t coeff_b = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; const gf2elem_t base_x = { 0xa01c8972, 0xe2945283, 0x4dca88c7, 0x988b4717, 0x494776fb, 0xbbd1ba39, 0xb4ceb08c, 0x47da304d, 0x93b205e6, 0x43709584, 0x01841ca4, 0x60248048, 0x0012d5d4, 0xac9ca297, 0xf8103fe4, 0x82189631, 0x59923fbc, 0x026eb7a8 }; const gf2elem_t base_y = { 0x3ef1c7a3, 0x01cd4c14, 0x591984f6, 0x320430c8, 0x7ba7af1b, 0xb620b01a, 0xf772aedc, 0x4fbebbb9, 0xac44aea7, 0x9d4979c0, 0x006d8a2c, 0xffc61efc, 0x9f307a54, 0x4dd58cec, 0x3bca9531, 0x4f4aeade, 0x7f4fbf37, 0x0349dc80 }; const scalar_t base_order = { 0x637c1001, 0x5cfe778f, 0x1e91deb4, 0xe5d63938, 0xb630d84b, 0x917f4138, 0xb391a8db, 0xf19a63e4, 0x131850e1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000 }; #endif #if (ECC_CURVE == NIST_B571) #define coeff_a 1 #define cofactor 2 /* NIST B-571 */ const gf2elem_t polynomial = { 0x00000425, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08000000 }; const gf2elem_t coeff_b = { 0x2955727a, 0x7ffeff7f, 0x39baca0c, 0x520e4de7, 0x78ff12aa, 0x4afd185a, 0x56a66e29, 0x2be7ad67, 0x8efa5933, 0x84ffabbd, 0x4a9a18ad, 0xcd6ba8ce, 0xcb8ceff1, 0x5c6a97ff, 0xb7f3d62f, 0xde297117, 0x2221f295, 0x02f40e7e }; const gf2elem_t base_x = { 0x8eec2d19, 0xe1e7769c, 0xc850d927, 0x4abfa3b4, 0x8614f139, 0x99ae6003, 0x5b67fb14, 0xcdd711a3, 0xf4c0d293, 0xbde53950, 0xdb7b2abd, 0xa5f40fc8, 0x955fa80a, 0x0a93d1d2, 0x0d3cd775, 0x6c16c0d4, 0x34b85629, 0x0303001d }; const gf2elem_t base_y = { 0x1b8ac15b, 0x1a4827af, 0x6e23dd3c, 0x16e2f151, 0x0485c19b, 0xb3531d2f, 0x461bb2a8, 0x6291af8f, 0xbab08a57, 0x84423e43, 0x3921e8a6, 0x1980f853, 0x009cbbca, 0x8c6c27a6, 0xb73d69d7, 0x6dccfffe, 0x42da639b, 0x037bf273 }; const scalar_t base_order = { 0x2fe84e47, 0x8382e9bb, 0x5174d66e, 0x161de93d, 0xc7dd9ca1, 0x6823851e, 0x08059b18, 0xff559873, 0xe661ce18, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x03ffffff }; #endif #endif /*************************************************************************************************/ /* Private / static functions: */ /* some basic bit-manipulation routines that act on bit-vectors follow */ static int bitvec_get_bit(const bitvec_t x, const uint32_t idx) { return ((x[idx / 32U] >> (idx & 31U) & 1U)); } static void bitvec_clr_bit(bitvec_t x, const uint32_t idx) { x[idx / 32U] &= ~(1U << (idx & 31U)); } static void bitvec_copy(bitvec_t x, const bitvec_t y) { int i; for (i = 0; i < BITVEC_NWORDS; ++i) { x[i] = y[i]; } } static void bitvec_swap(bitvec_t x, bitvec_t y) { bitvec_t tmp; bitvec_copy(tmp, x); bitvec_copy(x, y); bitvec_copy(y, tmp); } #if defined(CONST_TIME) && (CONST_TIME == 0) /* fast version of equality test */ static int bitvec_equal(const bitvec_t x, const bitvec_t y) { int i; for (i = 0; i < BITVEC_NWORDS; ++i) { if (x[i] != y[i]) { return 0; } } return 1; } #else /* constant time version of equality test */ static int bitvec_equal(const bitvec_t x, const bitvec_t y) { int ret = 1; int i; for (i = 0; i < BITVEC_NWORDS; ++i) { ret &= (x[i] == y[i]); } return ret; } #endif static void bitvec_set_zero(bitvec_t x) { int i; for (i = 0; i < BITVEC_NWORDS; ++i) { x[i] = 0; } } #if defined(CONST_TIME) && (CONST_TIME == 0) /* fast implementation */ static int bitvec_is_zero(const bitvec_t x) { uint32_t i = 0; while (i < BITVEC_NWORDS) { if (x[i] != 0) { break; } i += 1; } return (i == BITVEC_NWORDS); } #else /* constant-time implementation */ static int bitvec_is_zero(const bitvec_t x) { int ret = 1; int i = 0; for (i = 0; i < BITVEC_NWORDS; ++i) { ret &= (x[i] == 0); } return ret; } #endif /* return the number of the highest one-bit + 1 */ static int bitvec_degree(const bitvec_t x) { int i = BITVEC_NWORDS * 32; /* Start at the back of the vector (MSB) */ x += BITVEC_NWORDS; /* Skip empty / zero words */ while ( (i > 0) && (*(--x)) == 0) { i -= 32; } /* Run through rest if count is not multiple of bitsize of DTYPE */ if (i != 0) { uint32_t u32mask = ((uint32_t)1 << 31); while (((*x) & u32mask) == 0) { u32mask >>= 1; i -= 1; } } return i; } /* left-shift by 'count' digits */ static void bitvec_lshift(bitvec_t x, const bitvec_t y, int nbits) { int nwords = (nbits / 32); /* Shift whole words first if nwords > 0 */ int i,j; for (i = 0; i < nwords; ++i) { /* Zero-initialize from least-significant word until offset reached */ x[i] = 0; } j = 0; /* Copy to x output */ while (i < BITVEC_NWORDS) { x[i] = y[j]; i += 1; j += 1; } /* Shift the rest if count was not multiple of bitsize of DTYPE */ nbits &= 31; if (nbits != 0) { /* Left shift rest */ int i; for (i = (BITVEC_NWORDS - 1); i > 0; --i) { x[i] = (x[i] << nbits) | (x[i - 1] >> (32 - nbits)); } x[0] <<= nbits; } } /*************************************************************************************************/ /* Code that does arithmetic on bit-vectors in the Galois Field GF(2^CURVE_DEGREE). */ /*************************************************************************************************/ static void gf2field_set_one(gf2elem_t x) { /* Set first word to one */ x[0] = 1; /* .. and the rest to zero */ int i; for (i = 1; i < BITVEC_NWORDS; ++i) { x[i] = 0; } } #if defined(CONST_TIME) && (CONST_TIME == 0) /* fastest check if x == 1 */ static int gf2field_is_one(const gf2elem_t x) { /* Check if first word == 1 */ if (x[0] != 1) { return 0; } /* ...and if rest of words == 0 */ int i; for (i = 1; i < BITVEC_NWORDS; ++i) { if (x[i] != 0) { break; } } return (i == BITVEC_NWORDS); } #else /* constant-time check */ static int gf2field_is_one(const gf2elem_t x) { int ret = 0; /* Check if first word == 1 */ if (x[0] == 1) { ret = 1; } /* ...and if rest of words == 0 */ int i; for (i = 1; i < BITVEC_NWORDS; ++i) { ret &= (x[i] == 0); } return ret; //(i == BITVEC_NWORDS); } #endif /* galois field(2^m) addition is modulo 2, so XOR is used instead - 'z := a + b' */ static void gf2field_add(gf2elem_t z, const gf2elem_t x, const gf2elem_t y) { int i; for (i = 0; i < BITVEC_NWORDS; ++i) { z[i] = (x[i] ^ y[i]); } } /* increment element */ static void gf2field_inc(gf2elem_t x) { x[0] ^= 1; } /* field multiplication 'z := (x * y)' */ static void gf2field_mul(gf2elem_t z, const gf2elem_t x, const gf2elem_t y) { int i; gf2elem_t tmp; #if defined(CONST_TIME) && (CONST_TIME == 1) gf2elem_t blind; bitvec_set_zero(blind); #endif assert(z != y); bitvec_copy(tmp, x); /* LSB set? Then start with x */ if (bitvec_get_bit(y, 0) != 0) { bitvec_copy(z, x); } else /* .. or else start with zero */ { bitvec_set_zero(z); } /* Then add 2^i * x for the rest */ for (i = 1; i < CURVE_DEGREE; ++i) { /* lshift 1 - doubling the value of tmp */ bitvec_lshift(tmp, tmp, 1); /* Modulo reduction polynomial if degree(tmp) > CURVE_DEGREE */ if (bitvec_get_bit(tmp, CURVE_DEGREE)) { gf2field_add(tmp, tmp, polynomial); } #if defined(CONST_TIME) && (CONST_TIME == 1) else /* blinding operation */ { gf2field_add(tmp, tmp, blind); } #endif /* Add 2^i * tmp if this factor in y is non-zero */ if (bitvec_get_bit(y, i)) { gf2field_add(z, z, tmp); } #if defined(CONST_TIME) && (CONST_TIME == 1) else /* blinding operation */ { gf2field_add(z, z, blind); } #endif } } /* field inversion 'z := 1/x' */ static void gf2field_inv(gf2elem_t z, const gf2elem_t x) { gf2elem_t u, v, g, h; int i; bitvec_copy(u, x); bitvec_copy(v, polynomial); bitvec_set_zero(g); gf2field_set_one(z); while (!gf2field_is_one(u)) { i = (bitvec_degree(u) - bitvec_degree(v)); if (i < 0) { bitvec_swap(u, v); bitvec_swap(g, z); i = -i; } #if defined(CONST_TIME) && (CONST_TIME == 1) else { bitvec_swap(u, v); bitvec_swap(v, u); } #endif bitvec_lshift(h, v, i); gf2field_add(u, u, h); bitvec_lshift(h, g, i); gf2field_add(z, z, h); } } /*************************************************************************************************/ /* The following code takes care of Galois-Field arithmetic. Elliptic curve points are represented by pairs (x,y) of bitvec_t. It is assumed that curve coefficient 'a' is {0,1} This is the case for all NIST binary curves. Coefficient 'b' is given in 'coeff_b'. '(base_x, base_y)' is a point that generates a large prime order group. */ /*************************************************************************************************/ static void gf2point_copy(gf2elem_t x1, gf2elem_t y1, const gf2elem_t x2, const gf2elem_t y2) { bitvec_copy(x1, x2); bitvec_copy(y1, y2); } static void gf2point_set_zero(gf2elem_t x, gf2elem_t y) { bitvec_set_zero(x); bitvec_set_zero(y); } static int gf2point_is_zero(const gf2elem_t x, const gf2elem_t y) { return ( bitvec_is_zero(x) && bitvec_is_zero(y)); } /* double the point (x,y) */ static void gf2point_double(gf2elem_t x, gf2elem_t y) { /* iff P = O (zero or infinity): 2 * P = P */ if (bitvec_is_zero(x)) { bitvec_set_zero(y); } else { gf2elem_t l; gf2field_inv(l, x); gf2field_mul(l, l, y); gf2field_add(l, l, x); gf2field_mul(y, x, x); gf2field_mul(x, l, l); #if (coeff_a == 1) gf2field_inc(l); #endif gf2field_add(x, x, l); gf2field_mul(l, l, x); gf2field_add(y, y, l); } } /* add two points together (x1, y1) := (x1, y1) + (x2, y2) */ static void gf2point_add(gf2elem_t x1, gf2elem_t y1, const gf2elem_t x2, const gf2elem_t y2) { if (!gf2point_is_zero(x2, y2)) { if (gf2point_is_zero(x1, y1)) { gf2point_copy(x1, y1, x2, y2); } else { if (bitvec_equal(x1, x2)) { if (bitvec_equal(y1, y2)) { gf2point_double(x1, y1); } else { gf2point_set_zero(x1, y1); } } else { /* Arithmetic with temporary variables */ gf2elem_t a, b, c, d; gf2field_add(a, y1, y2); gf2field_add(b, x1, x2); gf2field_inv(c, b); gf2field_mul(c, c, a); gf2field_mul(d, c, c); gf2field_add(d, d, c); gf2field_add(d, d, b); #if (coeff_a == 1) gf2field_inc(d); #endif gf2field_add(x1, x1, d); gf2field_mul(a, x1, c); gf2field_add(a, a, d); gf2field_add(y1, y1, a); bitvec_copy(x1, d); } } } } #if defined(CONST_TIME) && (CONST_TIME == 0) /* point multiplication via double-and-add algorithm */ static void gf2point_mul(gf2elem_t x, gf2elem_t y, const scalar_t exp) { gf2elem_t tmpx, tmpy; int i; int nbits = bitvec_degree(exp); gf2point_set_zero(tmpx, tmpy); for (i = (nbits - 1); i >= 0; --i) { gf2point_double(tmpx, tmpy); if (bitvec_get_bit(exp, i)) { gf2point_add(tmpx, tmpy, x, y); } } gf2point_copy(x, y, tmpx, tmpy); } #else /* point multiplication via double-and-add-always algorithm using scalar blinding */ static void gf2point_mul(gf2elem_t x, gf2elem_t y, const scalar_t exp) { gf2elem_t tmpx, tmpy; gf2elem_t dummyx, dummyy; int i; int nbits = bitvec_degree(exp); gf2point_set_zero(tmpx, tmpy); gf2point_set_zero(dummyx, dummyy); for (i = (nbits - 1); i >= 0; --i) { gf2point_double(tmpx, tmpy); /* Add point if bit(i) is set in exp */ if (bitvec_get_bit(exp, i)) { gf2point_add(tmpx, tmpy, x, y); } /* .. or add the neutral element to keep operation constant-time */ else { gf2point_add(tmpx, tmpy, dummyx, dummyy); } } gf2point_copy(x, y, tmpx, tmpy); } #endif /* check if y^2 + x*y = x^3 + a*x^2 + coeff_b holds */ static int gf2point_on_curve(const gf2elem_t x, const gf2elem_t y) { gf2elem_t a, b; if (gf2point_is_zero(x, y)) { return 1; } else { gf2field_mul(a, x, x); #if (coeff_a == 0) gf2field_mul(a, a, x); #else gf2field_mul(b, a, x); gf2field_add(a, a, b); #endif gf2field_add(a, a, coeff_b); gf2field_mul(b, y, y); gf2field_add(a, a, b); gf2field_mul(b, x, y); return bitvec_equal(a, b); } } /*************************************************************************************************/ /* Elliptic Curve Diffie-Hellman key exchange protocol. */ /*************************************************************************************************/ /* NOTE: private should contain random data a-priori! */ int ecdh_generate_keys(uint8_t* public_key, const uint8_t* private_key) { /* Get copy of "base" point 'G' */ gf2point_copy((uint32_t*)public_key, (uint32_t*)(public_key + BITVEC_NBYTES), base_x, base_y); /* Abort key generation if random number is too small */ if (bitvec_degree((uint32_t*)private_key) < (CURVE_DEGREE / 2)) { return 0; } else { /* Clear bits > CURVE_DEGREE in highest word to satisfy constraint 1 <= exp < n. */ int nbits = bitvec_degree(base_order); int i; for (i = (nbits - 1); i < (BITVEC_NWORDS * 32); ++i) { bitvec_clr_bit((uint32_t*)private_key, i); } /* Multiply base-point with scalar (private-key) */ gf2point_mul((uint32_t*)public_key, (uint32_t*)(public_key + BITVEC_NBYTES), (uint32_t*)private_key); return 1; } } int ecdh_shared_secret(const uint8_t* private_key, const uint8_t* others_pub, uint8_t* output) { /* Do some basic validation of other party's public key */ if ( !gf2point_is_zero ((uint32_t*)others_pub, (uint32_t*)(others_pub + BITVEC_NBYTES)) && gf2point_on_curve((uint32_t*)others_pub, (uint32_t*)(others_pub + BITVEC_NBYTES)) ) { /* Copy other side's public key to output */ unsigned int i; for (i = 0; i < (BITVEC_NBYTES * 2); ++i) { output[i] = others_pub[i]; } /* Multiply other side's public key with own private key */ gf2point_mul((uint32_t*)output,(uint32_t*)(output + BITVEC_NBYTES), (const uint32_t*)private_key); /* Multiply outcome by cofactor if using ECC CDH-variant: */ #if defined(ECDH_COFACTOR_VARIANT) && (ECDH_COFACTOR_VARIANT == 1) #if (cofactor == 2) gf2point_double((uint32_t*)output, (uint32_t*)(output + BITVEC_NBYTES)); #elif (cofactor == 4) gf2point_double((uint32_t*)output, (uint32_t*)(output + BITVEC_NBYTES)); gf2point_double((uint32_t*)output, (uint32_t*)(output + BITVEC_NBYTES)); #endif #endif return 1; } else { return 0; } } /* ECDSA is broken :( ... */ int ecdsa_sign(const uint8_t* private_key, uint8_t* hash, uint8_t* random_k, uint8_t* signature) { /* 1) calculate e = HASH(m) 2) let z be the Ln leftmost bits of e, where Ln is the bit length of the group order n 3) Select a cryptographically secure random integer k from [1, n-1] 4) Calculate the curve point (x1, y1) = k * G 5) Calculate r = x1 mod n - if (r == 0) goto 3 6) Calculate s = inv(k) * (z + r * d) mod n - if (s == 0) goto 3 7) The signature is the pair (r, s) */ assert(private_key != 0); assert(hash != 0); assert(random_k != 0); assert(signature != 0); int success = 0; if ( (bitvec_degree((uint32_t*)private_key) >= (CURVE_DEGREE / 2)) && !bitvec_is_zero((uint32_t*)random_k) ) { gf2elem_t r, s, z, k; bitvec_set_zero(r); bitvec_set_zero(s); bitvec_copy(z, (uint32_t*)hash); /* 1 + 2 */ int nbits = bitvec_degree(base_order); int i; for (i = (nbits - 1); i < BITVEC_NBITS; ++i) { bitvec_clr_bit(z, i); } /* 3 */ bitvec_copy(k, (uint32_t*)random_k); /* 4 */ gf2point_copy(r, s, base_x, base_y); gf2point_mul(r, s, k); /* 5 */ if (!bitvec_is_zero(r)) { /* 6) s = inv(k) * (z + (r * d)) mod n ==> if (s == 0) goto 3 **/ gf2field_inv(s, k); /* s = inv(k) */ gf2field_mul(r, r, (uint32_t*)private_key); /* r = (r * d) */ gf2field_add(r, r, z); /* r = z + (r * d) */ nbits = bitvec_degree(r); /* r = r mod n */ for (i = (nbits - 1); i < BITVEC_NBITS; ++i) { printf("reduction r\n"); bitvec_clr_bit(r, i); } gf2field_mul(s, s, r); /* s = inv(k) * (z * (r * d)) */ nbits = bitvec_degree(s); /* s = s mod n */ for (i = (nbits - 1); i < BITVEC_NBITS; ++i) { printf("reduction s\n"); bitvec_clr_bit(s, i); } if (!bitvec_is_zero(s)) { bitvec_copy((uint32_t*)signature, r); bitvec_copy((uint32_t*)(signature + ECC_PRV_KEY_SIZE), s); success = 1; } } } return success; } int ecdsa_verify(const uint8_t* public_key, uint8_t* hash, const uint8_t* signature) { /* 1) Verify that (r,s) are in [1, n-1] 2) e = HASH(m) 3) z = Ln leftmost bits of e 4) w = inv(s) mod n 5) u1 = (z * w) mod n u2 = (r * w) mod n 6) (x,y) = (u1 * G) + (u2 * public) 7) Signature is valid if r == x mod n && (x,y) != (0,0) */ assert(public_key != 0); assert(hash != 0); assert(signature != 0); int success = 0; gf2elem_t r, s; bitvec_copy(r, (uint32_t*)(signature)); bitvec_copy(s, (uint32_t*)(signature + ECC_PRV_KEY_SIZE)); if ( !bitvec_is_zero(s) && !bitvec_is_zero(r)) { gf2elem_t x1, y1, u1, u2, w, z; /* 3) z = Ln leftmost bits of e */ bitvec_copy(z, (uint32_t*)hash); /* r,s,z are set */ uint32_t nbits = bitvec_degree(base_order); uint32_t i; for (i = (nbits - 1); i < BITVEC_NBITS; ++i) { bitvec_clr_bit(z, i); } /* 4) w = inv(s) mod n */ gf2field_inv(w, s); /* w = inv(s) */ /* Modulo reduction polynomial if degree(tmp) > CURVE_DEGREE */ if (bitvec_get_bit(w, CURVE_DEGREE)) { printf("reduction on w\n"); gf2field_add(w, w, polynomial); } /* 5) u1 = zw mod n, u2 = rw mod n*/ gf2field_mul(u1, z, w); /* u1 = z * w */ /* Modulo reduction polynomial if degree(tmp) > CURVE_DEGREE */ if (bitvec_get_bit(u1, CURVE_DEGREE)) { printf("reduction on u1\n"); gf2field_add(u1, u1, polynomial); } gf2field_mul(u2, r, w); /* u2 = r * w */ /* Modulo reduction polynomial if degree(tmp) > CURVE_DEGREE */ if (bitvec_get_bit(u2, CURVE_DEGREE)) { printf("reduction on u2\n"); gf2field_add(u2, u2, polynomial); } /* 6) (x,y) = (u1 * G) + (u2 * public) */ bitvec_copy(x1, base_x); bitvec_copy(y1, base_y); gf2field_mul(u1, x1, y1); /* u1 * G */ bitvec_copy(w, (uint32_t*)(public_key)); bitvec_copy(z, (uint32_t*)(public_key + ECC_PRV_KEY_SIZE)); gf2field_mul(u2, w, z); /* u2 * Q */ gf2point_add(x1, y1, w, z); if (bitvec_get_bit(x1, CURVE_DEGREE)) { printf("reduction on x1\n"); gf2field_add(x1, x1, polynomial); } success = bitvec_equal(r, x1); if (!success) { printf("x = '"); for (i = 0; i < BITVEC_NWORDS; ++i) { printf("%.08x", x1[i]); } printf("' [%u]\n", i); printf("r = '"); for (i = 0; i < BITVEC_NWORDS; ++i) { printf("%.08x", r[i]); } printf("' [%u]\n", i); } } else { printf("(s or r) == zero\n"); } return success; } #endif // ECDH_C ================================================ FILE: vault/net_fragment.c ================================================ // fragment data // - rlyeh, public domain // // ## fragment format // [fragnum:16][fraglen:16][crc:32][protocol-id(*):32][fragment-data:XX][eof:8] // fragment-number: 000,001,002... // fragment-length: end of stream if length < 65535 // crc: crc32 made of protocol-id+fragment-data // protocol-id: used to calc crc of fragment-data only; never written or sent (*) // fragment-data: user data; likely crypted // end-of-fragment: least byte (lsb) from crc32. must match. #ifndef FRAGMENT_H #define FRAGMENT_H int fragment_num(int msglen); // number of total fragments required for a full split char* fragment_split(int fragment_seq, const void *msg, int msglen); // split one frag int fragment_sizeof(int fragment_seq, int msglen); // size of specific fragment int fragment_verify(array(char*) fragments); char* fragment_join(array(char*) fragments); #endif #ifdef FRAGMENT_C #pragma once #define FRAGMENT_VERSION 0x595c7b10 // crc32("0.0.1") #define FRAGMENT_MAXSIZE 65536 // 4 or 8 for testing //#define FRAGMENT_PREALLOCATE // @todo #pragma pack(push, 1) typedef struct fragment_header { uint16_t fragment_seq; uint16_t fragment_len; uint32_t crc; char fragment_bin[]; } fragment_header; #pragma pack(pop) int fragment_num(int msglen) { return 1 + (msglen / FRAGMENT_MAXSIZE); } int fragment_sizeof(int fragment_seq, int msglen) { int chunklen = fragment_seq == (fragment_num(msglen)-1) ? (msglen % FRAGMENT_MAXSIZE) : FRAGMENT_MAXSIZE; return 8 + chunklen + 1; } char* fragment_split(int fragment_seq, const void *msg, int msglen) { uint16_t fragment_len = (uint16_t)(fragment_sizeof(fragment_seq, msglen) - 8 - 1); // relocate pointer in msg msg = ((const char*)msg) + fragment_seq * FRAGMENT_MAXSIZE; uint32_t crc = crc32(FRAGMENT_VERSION, msg, fragment_len); // prepare header fragment_header header = { (uint16_t)fragment_seq, fragment_len, crc }; // store header + msg char *fragment_buf = va("%*.s", 8 + fragment_len + 1, ""); // FRAGMENT_PREALLOCATE memcpy(fragment_buf, &header, sizeof(struct fragment_header)); memcpy(fragment_buf + 8, msg, fragment_len); fragment_buf[8 + fragment_len] = header.crc & 0xFF; return fragment_buf; } // returns +N if fragment Nth has checksum error (@todo) // returns 0 if ok // returns -N if fragment Nth is missing (@todo) int fragment_verify(array(char*) fragments) { // 1. check that all fragments are present, and no dupes are found. // 2. check that fragments are not truncated // 3. check that fragments pass crc int errcode = 0, quotient = -1; int num_fragments = array_count(fragments); char *present = CALLOC(1, num_fragments); for( int i = 0; i < num_fragments; ++i ) { fragment_header *ph = (fragment_header*)fragments[i]; if( ph->fragment_seq >= num_fragments ) { FREE(present); return -1; }; // incomplete if( present[ph->fragment_seq] ) { FREE(present); return -1; }; // dupe if( ph->fragment_bin[-4] != ph->fragment_bin[ph->fragment_len] ) { FREE(present); return -1; }; // truncated. @todo: endian (-1) if( ph->crc != crc32(FRAGMENT_VERSION, ph->fragment_bin, ph->fragment_len) ) { FREE(present); return -1; }; // crc mismatch if( ph->fragment_len < FRAGMENT_MAXSIZE ) if( quotient < 0 ) quotient = ph->fragment_len; else { FREE(present); return -1; } // dupe eof present[ph->fragment_seq] = 1; // pass } FREE(present); return 0; } char* fragment_join(array(char*) fragments) { if( fragment_verify(fragments) != 0 ) return 0; static __thread char *ptr[16] = {0}; static __thread int slot = 0; slot = (slot + 1) % 16; int num_fragments = array_count(fragments); ptr[slot] = REALLOC(ptr[slot], num_fragments * FRAGMENT_MAXSIZE); // worst case for( int i = 0; i < num_fragments; ++i ) { fragment_header *ph = (fragment_header*)fragments[i]; size_t offset = ph->fragment_seq * FRAGMENT_MAXSIZE; memcpy( ptr[slot] + offset, ph->fragment_bin, ph->fragment_len ); } return ptr[slot]; } #ifdef FRAGMENT_DEMO int main() { const char *msg = "hello world"; int msglen = 12; array(char*) fragments = 0; for( int i = 0; i < fragment_num(msglen); ++i ) { char *fragment = fragment_split(i, msg, msglen); int fraglen = fragment_sizeof(i, msglen); hexdump(fragment, fraglen); array_push(fragments, fragment); } assert(fragment_verify(fragments) == 0); puts(fragment_join(fragments)); assert(~puts("Ok")); } #endif // FRAGMENT_DEMO #endif // FRAGMENT_C ================================================ FILE: vault/net_tunnel.c ================================================ // crypted transport // 1. public handshake (ecdh) -> shared secret // 2. private tunnel (arc4 crypted) // - rlyeh, public domain #ifdef CORE_C void handshake_server(const char *port) { } void handshake_client(const char *address, const char *port) { } #endif ================================================ FILE: vault/net_webserver.c ================================================ // webserver, forked from nweb23.c by Nigel Griffiths (public domain). // - rlyeh, public domain. #ifndef WEBSERVER_H #define WEBSERVER_H int webserver(int port, const char *folder); #endif // ----------------------------------------------------------------------------- #ifdef WEBSERVER_C #pragma once #include #include #include #include #include #include #include const char index_html[] = // markdeep begin "" \ "" \ "" // content "# hello\n" " - webserver.c\n" // markdeep end "" \ ""; #define WEB_VERSION 23 #define WEB_BUFSIZE 8096 #define WEB_ERROR 42 #define WEB_LOG 44 #define WEB_FORBIDDEN 403 #define WEB_NOTFOUND 404 #define WEB_GENERIC_MIME "application/octet-stream" struct { char *ext; char *filetype; } extensions [] = { {"gif", "image/gif" }, {"jpg", "image/jpg" }, {"jpeg", "image/jpeg"}, {"png", "image/png" }, {"ico", "image/ico" }, {"zip", "image/zip" }, {"gz", "image/gz" }, {"tar", "image/tar" }, {"htm", "text/html" }, {"html", "text/html" }, {0, 0} }; #define webpanic(...) do { LOG(WEB|SERVER, __VA_ARGS__); exit(-1); } while(0) #define weblogger(...) do { if(!weblogger(__VA_ARGS__)) goto clean_up; } while(0) bool (weblogger)(int type, const char *s1, const char *s2, int socket_fd) { int fd; char logbuffer[WEB_BUFSIZE * 2]; const char *notfound = "HTTP/1.1 404 Not Found\nContent-Length: 138\nConnection: close\nContent-Type: text/html\n\n\n404 Not Found\n\n

Not Found

\nThe requested URL was not found on this server.\n\n"; const char *forbidden = "HTTP/1.1 403 Forbidden\nContent-Length: 185\nConnection: close\nContent-Type: text/html\n\n\n403 Forbidden\n\n

Forbidden

\nThe requested URL, file type or operation is not allowed on this simple static file webserver.\n\n"; /**/ if( type == WEB_LOG) { sprintf(logbuffer, " INFO: %s:%s:%d", s1, s2, socket_fd); } else if( type == WEB_ERROR) { sprintf(logbuffer, "WEB_ERROR: %s:%s Errno=%d exiting pid=%d", s1, s2, errno, getpid()); } else if( type == WEB_NOTFOUND) { tcp_send(socket_fd, notfound, 224); sprintf(logbuffer, "NOT FOUND: %s:%s", s1, s2); } else if( type == WEB_FORBIDDEN) { tcp_send(socket_fd, forbidden, 271); sprintf(logbuffer, "WEB_FORBIDDEN: %s:%s", s1, s2); } /* No checks here, nothing can be done with a failure anyway */ LOG(WEB|HOST, logbuffer); return (type == WEB_ERROR || type == WEB_NOTFOUND || type == WEB_FORBIDDEN) ? false : true; } /* this is a child web server process, so we can exit on errors */ static __thread int hit = 0; static int nweb23(int fd) { int j, buflen; long i, ret, len; char *fstr; static char buffer[WEB_BUFSIZE + 1]; /* static so zero filled */ ret = tcp_recv(fd, buffer, WEB_BUFSIZE); /* read Web request in one go */ if(ret == 0 || ret == -1) /* read failure stop now */ { weblogger(WEB_FORBIDDEN, "failed to read browser request", "", fd); } if(ret > 0 && ret < WEB_BUFSIZE) /* return code is valid chars */ buffer[ret] = 0; /* terminate the buffer */ else buffer[0] = 0; for(i = 0; i < ret; i++) /* remove CF and LF characters */ if(buffer[i] == '\r' || buffer[i] == '\n') buffer[i] = '*'; weblogger(WEB_LOG, "request", buffer, hit); if( strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4) ) { weblogger(WEB_FORBIDDEN, "Only simple GET operation supported", buffer, fd); } for(i = 4; i < WEB_BUFSIZE; i++) /* null terminate after the second space to ignore extra stuff */ { if(buffer[i] == ' ') /* string is "GET URL " +lots of other stuff */ { buffer[i] = 0; break; } } for(j = 0; j < i - 1; j++) /* check for illegal parent directory use .. */ if(buffer[j] == '.' && buffer[j + 1] == '.') { weblogger(WEB_FORBIDDEN, "Parent directory (..) path names not supported", buffer, fd); } if( !strncmp(&buffer[0], "GET /\0", 6) || !strncmp(&buffer[0], "get /\0", 6) ) /* convert no filename to index file */ strcpy(buffer, "GET /index.html"); /* work out the file type and check we support it */ buflen = strlen(buffer); fstr = (char *)0; for(i = 0; extensions[i].ext != 0; i++) { len = strlen(extensions[i].ext); if( !strncmp(&buffer[buflen - len], extensions[i].ext, len)) { fstr = extensions[i].filetype; break; } } if(fstr == 0) { //weblogger(WEB_FORBIDDEN, "file extension type not supported", buffer, fd); fstr = WEB_GENERIC_MIME; } const char *fname = &buffer[5]; if( !strcmp(fname, "index.html") ) { len = strlen(index_html); weblogger(WEB_LOG, "Header", buffer, hit); sprintf(buffer, "HTTP/1.1 200 OK\nServer: net.webserver/%d.0\nContent-Length: %ld\nConnection: close\nContent-Type: %s\n\n", WEB_VERSION, len, fstr); /* Header + a blank line */ tcp_send(fd, buffer, strlen(buffer)); /* send file in 8KB block - last block may be smaller */ const char *ptr = index_html, *end = ptr + len; while( ptr < end ) { tcp_send(fd, ptr, (ptr + WEB_BUFSIZE) >= end ? end - ptr : WEB_BUFSIZE); ptr += WEB_BUFSIZE; } goto clean_up; } // open the file for reading FILE *file_fd = fopen(fname, "rb"); if( file_fd ) { fseek(file_fd, (off_t)0, SEEK_END); /* lseek to the file end to find the length */ len = (long)ftell(file_fd); fseek(file_fd, (off_t)0, SEEK_SET); /* lseek back to the file start ready for reading */ weblogger(WEB_LOG, "Header", buffer, hit); sprintf(buffer, "HTTP/1.1 200 OK\nServer: net.webserver/%d.0\nContent-Length: %ld\nConnection: close\nContent-Type: %s\n\n", WEB_VERSION, len, fstr); /* Header + a blank line */ tcp_send(fd, buffer, strlen(buffer)); /* send file in 8KB block - last block may be smaller */ weblogger(WEB_LOG, "SEND", fname, hit); while ( (ret = fread(buffer, 1, WEB_BUFSIZE, file_fd)) > 0 ) { tcp_send(fd, buffer, ret); } fclose(file_fd); } else { weblogger(WEB_NOTFOUND, "failed to open file", fname, fd); } clean_up:; /* allow socket to drain before signalling the socket is closed */ sleep_ss(1); tcp_close(fd); return 0; } #undef weblogger int webserver(int port, const char *path) { /* input checks */ if( port < 1024 || port > 60000) { webpanic("!Invalid port number (try 1024 #include size_t dlmalloc_usable_size(void*); // (android) #endif #ifdef _BSD #include #include #endif #ifdef _IOS #include #include #endif #ifdef _LIN #include #include #endif #ifdef _OSX #include #include #endif #ifdef _WIN #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 // for CONDITION_VARIABLE #endif #ifdef _TCC #include #include #else #include #include #endif #include #endif // augment vendor compatibility #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS // : UINT32_MAX... #endif // all required standard headers #include #include #include #include #include #include #include // builtins #if !defined __thread && (defined _MSC || defined _TCC) #define __thread __declspec(thread) #endif #if !defined inline && defined _MSC && !defined __cplusplus #define inline _inline #endif // autorun initializers for C // - rlyeh, public domain // // note: based on code by Joe Lowe (public domain). // note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers #ifndef AUTORUN #ifdef __cplusplus #define AUTORUN \ static void AUTORUN_U(autorun)(void); \ static const int AUTORUN_J(AUTORUN_U(autorun),__1) = (AUTORUN_U(autorun)(), 1); \ static void AUTORUN_U(autorun)(void) #elif defined _MSC #define AUTORUN \ static void AUTORUN_U(autorun)(void); \ static int AUTORUN_J(AUTORUN_U(autorun),__1) (){ AUTORUN_U(autorun)(); return 0; } \ __pragma(section(".CRT$XIU", long, read)) \ __declspec(allocate(".CRT$XIU")) \ static int(* AUTORUN_J(AUTORUN_U(autorun),__2) )() = AUTORUN_J(AUTORUN_U(autorun),__1); \ static void AUTORUN_U(autorun)(void) #else #define AUTORUN \ __attribute__((constructor)) \ static void AUTORUN_U(autorun)(void) #endif // helpers: join + unique macros #define AUTORUN_j(a, b) a##b #define AUTORUN_J(a, b) AUTORUN_j(a, b) #define AUTORUN_U(x) AUTORUN_J(x, __LINE__) #endif #ifdef AUTORUN_DEMO #include #include int main() { puts("during main()"); } void my_quit(void) { puts("after main()"); } AUTORUN { puts("before main() [1]"); } AUTORUN { puts("before main() [2]"); atexit( my_quit ); } #define main main__ #endif #endif // OS_H // ----------------------------------------------------------------------------- #ifdef OS_C #define MEMORY_C #define DLL_C #define PLUGIN_C #define TIME_C #define TRACE_C #define PANIC_C #define ASSERT_C #define MIME_C #define FILE_C #define EXEC_C #define ICON_C #define TRAY_C #define SINGLETON_C #define ANSI_C #define ENTROPY_C #define INI_C #define TITLE_C #define ARGS_C #define EXIT_C #define ENV_C #define BREAKPOINT_C #define DIALOG_C #define DIE_C #define CPU_C #define DATE_C #define TRAP_C #define TEST_C #define LOGGER_C #define LOGGER2_C #define LOGGER3_C #define LOCALE_C #endif #include "os_memory.c" #include "os_assert.c" #include "os_dll.c" #include "os_plugin.c" #include "os_time.c" #include "os_logger.c" #include "os_logger2.c" #include "os_trace.c" // after os_logger #include "os_die.c" #include "os_panic.c" #include "os_mime.c" #include "os_file.c" #include "os_exec.c" #include "os_icon.c" #include "os_tray.c" // after os_icon #include "os_singleton.c" #include "os_ansi.c" #include "os_entropy.c" #include "os_ini.c" #include "os_title.c" #include "os_args.c" #include "os_exit.c" #include "os_env.c" #include "os_breakpoint.c" #include "os_dialog.c" #include "os_cpu.c" #include "os_date.c" #include "os_trap.c" // after os_panic #include "os_test.c" #include "os_locale.c" #include "os_logger3.c" // after os_logger2+os_trace+os_ansi ================================================ FILE: vault/os_ansi.c ================================================ // - rlyeh, public domain void os_ansi(void); void os_beep(void); void os_color(uint8_t r, uint8_t g, uint8_t b); void os_reset(void); int os_columns(void); // ---------------------------------------------------------------------------- #ifdef ANSI_C #pragma once #ifndef _WIN32 # include # include # include #endif int os_columns(void) { #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { return csbi.srWindow.Right - csbi.srWindow.Left + 1; // Window width } #elif defined(TIOCGSIZE) struct ttysize ts; ioctl(STDIN_FILENO, TIOCGSIZE, &ts); return ts.ts_cols + 1; #elif defined(TIOCGWINSZ) struct winsize ts; ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); return ts.ws_col + 1; #endif return 1; } void os_ansi(void) { #ifdef _WIN32 /* Best effort enable ANSI escape processing. */ HANDLE handle; DWORD mode; handle = GetStdHandle(-11); /* STD_OUTPUT_HANDLE */ if (GetConsoleMode(handle, &mode)) { mode |= 0x0004; /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */ SetConsoleMode(handle, mode); /* ignore errors */ } #endif } void os_beep(void) { fputc('\x7', stdout); // putc(0x7, stdout) } void os_reset(void) { static int once = 0; if( !once ) { once = 1; atexit(os_reset); } #ifdef _WIN32 static CONSOLE_SCREEN_BUFFER_INFO csbi = {0}, *ever = 0; if( !ever ) { const HANDLE console = GetStdHandle( STD_OUTPUT_HANDLE ); if (console == INVALID_HANDLE_VALUE) { return; } if (!GetConsoleScreenBufferInfo(console, &csbi)) { return; } ever = &csbi; } const HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); if (console != INVALID_HANDLE_VALUE) { SetConsoleTextAttribute(console, csbi.wAttributes); } #else puts("\x1B[39;49m"); #endif } void os_color(uint8_t r, uint8_t g, uint8_t b) { static int once = 0; if( !once ) { once = 1; os_reset(); } #ifdef _WIN32 const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); auto color = ( r > 127 ? FOREGROUND_RED : 0 ) | ( g > 127 ? FOREGROUND_GREEN : 0 ) | ( b > 127 ? FOREGROUND_BLUE : 0 ); if(!(r+g+b)) color=BACKGROUND_RED|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; // bright white on red SetConsoleTextAttribute(stdout_handle, color | FOREGROUND_INTENSITY); #else if(!(r+g+b)) { printf("\033[41;97m"); return; } // bright white on red // 24-bit console ESC[ … 38;2;;; … m Select RGB foreground color // 256-color console ESC[38;5;m // 0x00-0x07: standard colors (as in ESC [ 30..37 m) // 0x08-0x0F: high intensity colors (as in ESC [ 90..97 m) // 0x10-0xE7: 6*6*6=216 colors: 16 + 36*r + 6*g + b (0≤r,g,b≤5) // 0xE8-0xFF: grayscale from black to white in 24 steps r /= 51, g /= 51, b /= 51; // [0..5] printf("\033[38;5;%dm", r*36+g*6+b+16); // "\033[0;3%sm", color_code); #endif } #endif // OS_ANSI ================================================ FILE: vault/os_assert.c ================================================ // [x] assert: which works in release builds. // [x] test: test & autotest macros. // - rlyeh, public domain // ---------------------------------------------------------------------------- #ifndef ASSERT_H #define ASSERT_H #endif #ifndef NDEBUG # include ASSERT_H #else # undef NDEBUG # include ASSERT_H # define NDEBUG #endif #ifdef SHIPPING # undef assert # define assert(expr) (void)0 #endif // ---------------------------------------------------------------------------- #ifndef TEST_H #define TEST_H static __thread int tests = 0, fails = 0; #ifdef NDEBUG #define TEST(...) (void)0 #define AUTOTEST static void AUTORUN_U(unused_test)(void) #else #define TEST(...) (printf(" #%02d (%s) %s:%d ", ++tests, #__VA_ARGS__, __FILE__, __LINE__), printf("\r%c\n", "NY"[!!(__VA_ARGS__)])) #define AUTOTEST AUTORUN #endif #endif ================================================ FILE: vault/os_breakpoint.c ================================================ void os_breakpoint(); #ifdef BREAKPOINT_C #pragma once #include void os_breakpoint() { #if _MSC_VER __debugbreak(); // msvc #elif __GNUC__ __builtin_trap(); // gcc and clang #else raise(SIGTRAP); // posix #endif } #ifdef BREAKPOINT_DEMO #include int main() { puts("trying to invoke debugger... (may crash if debugger is not attached)"); os_breakpoint(); } #endif #endif ================================================ FILE: vault/os_cpu.c ================================================ int cpu_pid(void); int cpu_cores(void); double cpu_usage(void); #ifdef CPU_DEMO #pragma once #ifdef _WIN32 #include static ULONGLONG cpu_diff_time_(const FILETIME one, const FILETIME two) { LARGE_INTEGER a, b; a.LowPart = one.dwLowDateTime; a.HighPart = one.dwHighDateTime; b.LowPart = two.dwLowDateTime; b.HighPart = two.dwHighDateTime; return a.QuadPart - b.QuadPart; } #elif defined __linux__ #include #else #include #endif // CPU currently used by current process: // src: https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process // src: https://stackoverflow.com/questions/23143693/retrieving-cpu-load-percent-total-in-windows-with-c double cpu_usage(void) { #ifndef _WIN32 return 0; #else static double last_ok = 0; static FILETIME prevSysIdle, prevSysKernel, prevSysUser; FILETIME sysIdle, sysKernel, sysUser; // sysKernel include IdleTime if (GetSystemTimes(&sysIdle, &sysKernel, &sysUser) == 0) { // GetSystemTimes func FAILED return value is zero; return last_ok; } if (prevSysIdle.dwLowDateTime != 0 && prevSysIdle.dwHighDateTime != 0) { ULONGLONG sysIdleDiff, sysKernelDiff, sysUserDiff; sysIdleDiff = cpu_diff_time_(sysIdle, prevSysIdle); sysKernelDiff = cpu_diff_time_(sysKernel, prevSysKernel); sysUserDiff = cpu_diff_time_(sysUser, prevSysUser); ULONGLONG sysTotal = sysKernelDiff + sysUserDiff; ULONGLONG kernelTotal = sysKernelDiff - sysIdleDiff; // kernelTime - IdleTime = kernelTime, because sysKernel include IdleTime if (sysTotal > 0) { // sometimes kernelTime > idleTime last_ok = (double)(((kernelTotal + sysUserDiff) * 100.0) / sysTotal); } } prevSysIdle = sysIdle; prevSysKernel = sysKernel; prevSysUser = sysUser; return last_ok; #endif } int cpu_cores(void) { #if defined __cplusplus return (int)std::thread::hardware_concurrency(); #elif defined _WIN32 DWORD_PTR pm, sm; if( GetProcessAffinityMask(GetCurrentProcess(), &pm, &sm) ) if( pm ) { int count = 0; while( pm ) { ++count; pm &= pm - 1; } return count; } { SYSTEM_INFO si; GetSystemInfo(&si); return (int)si.dwNumberOfProcessors; } #elif defined __linux__ cpu_set_t prevmask, testmask; CPU_ZERO(&prevmask); CPU_ZERO(&testmask); sched_getaffinity(0, sizeof(prevmask), &prevmask); //Get current mask sched_setaffinity(0, sizeof(testmask), &testmask); //Set zero mask sched_getaffinity(0, sizeof(testmask), &testmask); //Get mask for all CPUs sched_setaffinity(0, sizeof(prevmask), &prevmask); //Reset current mask int num = CPU_COUNT(&testmask); return (num > 1 ? num : 1); #elif defined __unix__ // unix int count = sysconf(_SC_NPROCESSORS_ONLN); return count > 0 ? count : 1; #else // omp int cores = 0; #pragma omp parallel { #pragma omp atomic ++cores; } return cores; #endif } int cpu_pid(void) { #ifdef _WIN32 return _getpid(); #else return getpid(); #endif } #ifdef CPU_DEMO #include int main() { printf("process-id: %d\n", cpu_pid()); printf("%d cpus\n", cpu_cores()); for( int i = 0; i < 100000000; ++i) { float f = sqrt(1); } printf("%5.2f%% usage\n", cpu_usage()); } #define main main__ #endif // CPU_DEMO #endif // CPU_C ================================================ FILE: vault/os_date.c ================================================ // base10 clock format // ------------------- // Every base10 calendar is a 64-bit number, where: // // 18446744073709551615 <-- largest 64-bit number // *YYYMMDDhhmmssuuuuuu // - return current date & time in base10. date() = today_gmt() + time_us() // - return format base10 as string, where "YYYY-MM-DD hh:mm:ss.uuuuuu W" YYY since 2k, u = microseconds, W = day-of-week // - return adjusted system time (gmt) // - return unadjusted system time (ust) uint64_t date(); char * date_format(uint64_t base10); uint64_t today_gmt(); uint64_t today_ust(); #ifdef DATE_C #pragma once #ifndef _WIN32 #include #endif #include static uint64_t date_base10(int64_t unixstamp) { // Reference: Fliegel, H. F. and van Flandern, T. C. (1968). // Communications of the ACM, Vol. 11, No. 10 (October, 1968). enum { RTC_EPOCH_JULIAN_DAY = 2440588 }; // January 1st, 1970. int64_t yy, mm, dd, l, n; l = unixstamp / 86400 + 68569 + RTC_EPOCH_JULIAN_DAY; n = 4 * l / 146097; l = l - (146097 * n + 3) / 4; yy = 4000 * (l + 1) / 1461001; l = l - 1461 * yy / 4 + 31; mm = 80 * l / 2447; dd = l - 2447 * mm / 80; l = mm / 11; mm = mm + 2 - 12 * l; yy = 100 * (n - 49) + yy + l; yy -= 2000; unixstamp = unixstamp % (24 * 3600); int hh = (int)unixstamp / 3600; unixstamp = unixstamp % 3600; int mn = (int)unixstamp / 60; int ss = (int)unixstamp % 60; #if 0 int id = (time_us() % 1000000); #elif 0 static uint8_t id = 0xFF; ++id; #else const uint8_t id = 0; #endif return /// Every base10 calendar is a 64-bit number: /// 18446744073709551615 /// *YYYMMDDhhmmssuuuuuu uuuuuu = microseconds yy * 10000000000000000ULL + mm * 100000000000000ULL + dd * 1000000000000ULL + hh * 10000000000ULL + mn * 100000000ULL + ss * 1000000ULL + // 000000ULL; id; } uint64_t today_ust() { return date_base10( time(0) ); } uint64_t today_gmt() { time_t t = time(0); struct tm *gtm = gmtime(&t); int hh1 = gtm->tm_hour, mm1 = gtm->tm_min; struct tm *ltm = localtime(&t); int hh2 = ltm->tm_hour, mm2 = ltm->tm_min; int hdiff = hh2-hh1; int mdiff = mm2-mm1; return date_base10( time(0) + (1) * (hdiff * 3600 + mdiff * 60) ); } uint64_t date() { return today_gmt() + time_us() % 1000000; } char *date_format( uint64_t base10 ) { int y, m, d, dow; int us = base10 % 1000000; base10 /= 1000000; int ss = base10 % 100; base10 /= 100; int mn = base10 % 100; base10 /= 100; int hh = base10 % 100; base10 /= 100; d = base10 % 100; int dd = d; base10 /= 100; m = base10 % 100; int mm = m; base10 /= 100; y = base10 % 100; int yy = y; base10 /= 100; if( 1 ) { /* Tomohiko Sakamoto's Algorithm */ /* Get day of the week [0=Sun..6=Sat]*/ int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; dow = (y -= m < 3, (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7); } // return va("%04d-%02d-%02d %02d:%02d:%02d.%06d %d", 2000 + yy, mm, dd, hh, mn, ss, us, dow); static __thread char buf[320]; static __thread int slot = 0; slot = (slot + 1) % 10; snprintf(buf + slot*32, 32, "%04d-%02d-%02d %02d:%02d:%02d.%06d %d", 2000 + yy, mm, dd, hh, mn, ss, us, dow); return buf + slot*32; } #ifdef DATE_DEMO #include int main() { uint64_t gmt = today_gmt(); printf("gmt %019llu -> %s\n", gmt, date_format(gmt) ); uint64_t ust = today_ust(); printf("ust %019llu -> %s\n", ust, date_format(ust) ); // printf("%+lld adjust time\n", (gmt - ust) / 100000000); while(1) { printf("\rnow %019llu -> %s", date(), date_format(date()) ); } } #define main main__ #endif // DATE_DEMO #endif // DATE_C ================================================ FILE: vault/os_dialog.c ================================================ // # dialog ################################################################### int os_dialog( int buttons, const char *title, const char *message ); #ifdef DIALOG_C #pragma once #include #include #include #include #ifdef _WIN32 #include #ifdef __TINYC__ #define CP_UTF8 65001 int WINAPI MultiByteToWideChar(); int WINAPI WideCharToMultiByte(); #else #include #endif wchar_t *dialog_widen(wchar_t *buf, int sz, const char *utf8) { // wide strings (windows only) int needed = 2 * MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); buf[0] = 0; if( needed < sz ) { MultiByteToWideChar(CP_UTF8, 0, utf8, -1, (void*)buf, needed); } return (wchar_t *)buf; } #endif int os_dialog( int buttons, const char *title, const char *message ) { title += title[0] == '!'; // advance text if starts with '!' (with callstack option) message += message[0] == '!'; // advance text if starts with '!' (with callstack option) char lower[256] = {0}; for( int i = 0; title[i]; ++i) lower[i] = title[i] | 32; int is_fail = !!strstr(lower, "fail") || !!strstr(lower, "error") || !!strstr(lower, "crash"); int is_help = !!strstr(lower, "help") || !!strstr(lower, "about") || !!strstr(lower, "info"); int is_warn = !!strstr(lower, "warning"); #if 0 if( is_fail || is_warn ) { if(window) glfwSetClipboardString(window, message); } #endif #ifdef _WIN32 #pragma comment(lib, "user32") wchar_t buf1[256], buf2[2048]; wchar_t *title16 = dialog_widen(buf1, 256, title), *message16 = dialog_widen(buf2, 2048, message); int rc = MessageBoxW(0, message16, title16, MB_SETFOREGROUND | (buttons >= 3 ? MB_YESNOCANCEL : buttons >= 2 ? MB_YESNO : MB_OK) | (is_fail ? MB_ICONERROR : (is_warn ? MB_ICONWARNING : (is_help ? MB_ICONQUESTION : 0))) ); /**/ if( rc == IDYES || rc == IDOK ) return 1; else if( rc == IDNO ) return 0; return 2; #else return 0; #endif } #ifdef DIALOG_DEMO #include #include int main() { int unicode = os_dialog(0, "unicode test info dialog", "私 は ガ"); int pressed1 = os_dialog(3, "3-buttons general dialog", "Bla.\nContinue?"); int pressed2 = os_dialog(2, "2-buttons warning dialog", "Bla.\nContinue?"); int pressed3 = os_dialog(1, "1-button error dialog", "Bla.\nBla."); int pressed4 = os_dialog(1, "1-button about dialog", "Bla.\nBla."); const char *buttons[] = { "no", "yes/ok", "cancel/error" }; printf("User pressed '%s'\n", buttons[ pressed1 ] ); printf("User pressed '%s'\n", buttons[ pressed2 ] ); printf("User pressed '%s'\n", buttons[ pressed3 ] ); printf("User pressed '%s'\n", buttons[ pressed4 ] ); assert( os_dialog(2, "Warning!", "This is a test.\nIs this dialog visible?" ) ); assert(~puts("Ok") ); } #define main main__ #endif // DIALOG_DEMO #endif // DIALOG_C ================================================ FILE: vault/os_die.c ================================================ #ifndef DIE_H #define DIE_H #define os_die(...) os_die(-__LINE__, __VA_ARGS__) void (os_die)(int rc, const char *fmt, ...); #endif #ifdef DIE_C #pragma once #include #include #include #include #ifdef _WIN32 #include #endif void (os_die)(int rc, const char *fmt, ...) { fflush(stdout); // msg va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); // os #ifdef _WIN32 if( GetLastError() ) { LPSTR messageBuffer = 0; size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); char buf[1024+1]; size = size < 1024 ? size : 1024; memcpy( buf, messageBuffer, size ); buf[size] = 0; LocalFree(messageBuffer); fprintf(stderr, " (%s)", buf); } #endif // crt fprintf(stderr, " (%s)\r\n", strerror(errno)); // bye exit(rc); } #ifdef DIE_DEMO int main() { errno = ENOMEM; os_die("omg cant believe you"); } #define main main__ #endif // DIE_DEMO #endif // DIE_C ================================================ FILE: vault/os_entropy.c ================================================ // - rlyeh, public domain void os_entropy( void *buf, unsigned n ); #ifdef ENTROPY_C #pragma once #ifdef _WIN32 #include #include #pragma comment(lib, "advapi32") void os_entropy_( void *buf, unsigned n ) { HCRYPTPROV provider; if( CryptAcquireContext( &provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == 0 ) { assert(!"CryptAcquireContext failed"); } int rc = CryptGenRandom( provider, n, (BYTE *)buf ); assert( rc != 0 ); CryptReleaseContext( provider, 0 ); } #elif 1 #include void os_entropy_( void *buf, unsigned n ) { FILE *fp = fopen( "/dev/urandom", "r" ); if( !fp ) assert(!"/dev/urandom open failed"); size_t read = fread( buf, 1, n, fp ); assert( read == n && "/dev/urandom read failed" ); fclose( fp ); } #elif 1 // unused for now // pseudo random number generator with 128 bit internal state... probably not suited for cryptographical usage. // [src] http://github.com/kokke (UNLICENSE) // [ref] http://burtleburtle.net/bob/rand/smallprng.html #include #ifdef _WIN32 #include #define getpid _getpid #else #include #endif static uint32_t prng_next(void) { #define prng_rotate(x,k) (x << k) | (x >> (32 - k)) #define prng_shuffle() do { \ uint32_t e = ctx[0] - prng_rotate(ctx[1], 27); \ ctx[0] = ctx[1] ^ prng_rotate(ctx[2], 17); \ ctx[1] = ctx[2] + ctx[3]; \ ctx[2] = ctx[3] + e; \ ctx[3] = e + ctx[0]; } while(0) static __thread uint32_t ctx[4], *once = 0; if( !once ) { uint32_t seed = (uint32_t)( getpid() + time(0) + ((uintptr_t)once) ); ctx[0] = 0xf1ea5eed; ctx[1] = ctx[2] = ctx[3] = seed; for (int i = 0; i < 31; ++i) { prng_shuffle(); } once = ctx; } prng_shuffle(); return ctx[3]; } void os_entropy_( void *buf, unsigned n ) { for( ; n >= 4 ; n -= 4 ) { uint32_t a = prng_next(); memcpy(buf, &a, 4); buf = ((char*)buf) + 4; } if( n > 0 ) { uint32_t a = prng_next(); memcpy(buf, &a, n); } } #endif void os_entropy(void *buf, unsigned n) { assert(n <= 2048); #if 0 os_entropy_(buf, n); #else // acquire twice, ensure 1st and 2nd parts are different // would this prevent some kind of dummy hijacking? memset(buf, 0, n); os_entropy_(buf, n/2); os_entropy_((char*)buf + n/2, n/2 + n&1); assert(0 != memcmp(buf, (char*)buf + n/2, n/2)); #endif } #ifdef ENTROPY_DEMO int main() { unsigned char buf[128]; os_entropy(buf, 128); for( int i = 0; i < 128; ++i ) { printf("%02x", buf[i]); } puts(""); } #define main __main #endif // ENTROPY_DEMO #endif // ENTROPY_C ================================================ FILE: vault/os_env.c ================================================ #ifndef ENV_H #define ENV_H // environment folders char* env_user(); // user name char* env_home(); // user home location char* env_curr(); // current directory char* env_game(); // binary location char* env_save(); // save data location char* env_temp(); // temp data location uint64_t env_free(); // available free space // environment vars const char *env_get( const char *key ); const char *env_set( const char *key, const char *value ); #endif // # env ###################################################################### // @ todo: paths must end with slash always #ifdef ENV_C #pragma once #include #include #ifndef _WIN32 #include #endif #ifndef PATH_MAX #ifdef MAX_PATH #define PATH_MAX MAX_PATH #elif defined _MAX_PATH #define PATH_MAX _MAX_PATH #else #define PATH_MAX 260 #endif #endif static char *env_get_process_full_path_() { static char *t = 0; if(t) return t; char path[PATH_MAX+1] = {0}; #ifdef __APPLE__ // _IOS || _OSX uint32_t i = sizeof(path); if (_NSGetExecutablePath(path, &i) > -1) { return t = STRDUP(path); } #elif defined __linux__ // _LIN //if (readlink(strf("/proc/%d/exe", getpid()), path, sizeof(path)) > -1) { if (readlink("/proc/self/exe", path, sizeof(path)) > -1) { return t = STRDUP(path); } #elif defined _WIN32 if (GetModuleFileNameA(0, path, sizeof(path))) { return t = STRDUP(path); } #endif //t = STRDUP( __argv[0] ); //return t = dirabs( &t ); return "./"; } // util that ensure paths end with '/' and are normalized as well. static char *env_fix_(const char *pathfile ) { // must free() after use char *buf = (char*)MALLOC( PATH_MAX + 1 ); strcpy(buf, pathfile); int len = strlen(buf); for( int i = 0; i < len; ++i ) if( buf[i] == '\\' ) buf[i] = '/'; if( buf[ len ] != '/' ) { buf[ len ] = '/'; } return buf; } char *env_user() { static char *t = 0; if(t) return t; t = t ? t : getenv("USER"); t = t ? t : getenv("USERNAME"); t = STRDUP( t ? t : "GUEST" ); return t; } char *env_home() { static char *t = 0; if(t) return t; t = t ? t : getenv("USERPROFILE"); t = t ? t : getenv("HOME"); t = env_fix_( t ? t : "./" ); return t; } char *env_save() { // envdata static char *t = 0; if(t) return t; t = t ? t : getenv("APPDATA"); t = t ? t : getenv("XDG_DATA_HOME"); t = env_fix_( t ? t : "./" ); return t; } char *env_temp() { static char *t = 0; if(t) return t; t = t ? t : getenv("TMPDIR"); t = t ? t : getenv("TMP"); t = t ? t : getenv("TEMP"); #ifdef _WIN32 // GetTempPathW(n, buf); #else t = t ? t : "/tmp"; #endif t = env_fix_( t ? t : "./" ); return t; } static __thread char cwd[PATH_MAX+1]; char *env_curr() { // envwork #ifdef _WIN32 _getcwd(cwd, sizeof(cwd)); #else getcwd(cwd, sizeof(cwd)); #endif return env_fix_( cwd ); } uint64_t env_free() { #ifdef _WIN32 DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters; if( GetDiskFreeSpaceA( ".\\", &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters ) ) { return ((uint64_t)NumberOfFreeClusters) * SectorsPerCluster * BytesPerSector; } #endif return UINT64_C(-1); // ~0LLU; } char *env_game() { // envexec, envgame static char *t = 0; if( t ) return t; t = STRDUP( env_get_process_full_path_() ); for( size_t i = strlen(t); i-- > 0; ) { if (t[i] == '\\' || t[i] == '/') { t[i] = '\0'; break; } } return env_fix_(t); } // ----------------------------------------------------------------------------- const char *env_set( const char *key, const char *value ) { //$ #ifdef _WIN32 char buf[1024]; sprintf(buf,"%s=%s", key, value ? value : ""); putenv( buf ); #else setenv( key, value ? value : "", 1 ); #endif return value; } const char *env_get( const char *key ) { //$ return getenv(key); } #ifdef ENV_DEMO #include int main() { printf("env_curr: %s\n", env_curr() ); printf("env_home: %s\n", env_home() ); printf("env_game: %s\n", env_game() ); printf("env_save: %s\n", env_save() ); printf("env_temp: %s\n", env_temp() ); printf("env_user: %s\n", env_user() ); double GiB = 1024 * 1024 * 1024; printf("env_free: %.3f GiB\n", env_free() / GiB ); env_set("HELLO", "WORLD"); assert( 0 == strcmp(env_get("HELLO"), "WORLD")); assert(~puts("Ok")); } #define main main__ #endif // ENV_DEMO #endif // ENV_C ================================================ FILE: vault/os_exec.c ================================================ // cmd system invokation // - rlyeh, public domain. char *os_exec( const char *cmd ); #ifdef EXEC_C #pragma once #ifdef _WIN32 #define popen _popen #define pclose _pclose #endif char *os_exec( const char *cmd ) { static __thread char buf[4096]; buf[0] = 0; FILE *fp = popen( cmd, "rt" ); if( fp ) { while( fgets(buf, 4096 - 1, fp) ) { char *r = strrchr(buf, '\r'); char *n = strrchr(buf, '\n'); if( r ) *r = 0; if( n ) *n = 0; //puts(buf); } pclose(fp); } return buf; } #endif ================================================ FILE: vault/os_exit.c ================================================ // - rlyeh, public domain #ifndef EXIT_H #define EXIT_H void os_exit(int rc, const char *msg); #define os_exit(rc, ...) os_exit(rc, va("Error: " __VA_ARGS__)) #endif #ifdef EXIT_C #pragma once #include #include #ifdef _WIN32 #include #endif void (os_exit)(int rc, const char *msg) { fprintf(stderr, "%s\n", msg); #ifdef _WIN32 MessageBoxA(0,msg,0,0); #endif exit(rc); } #endif ================================================ FILE: vault/os_file.c ================================================ // file functions // - rlyeh, public domain. // // @todo: file_lock, file_unlock #ifndef FILE_H #define FILE_H #include #include char* file_read( const char *pathfile ); // returns temporary data bool file_write( const char *pathfile, const void *data, int len ); bool file_append( const char *pathfile, const void *data, int len ); char* file_map( const char *pathfile, size_t offset, size_t len ); void file_unmap( char *buf, size_t len ); char* file_chunk( const char *pathfile, size_t offset, size_t len ); // returns temporary data int file_size( const char *pathfile ); // 4gib limit int file_stamp( const char *pathfile ); // Y2038 limit bool file_exact( const char *pathfile1, const char *pathfile2 ); bool file_exist( const char *pathfile ); bool file_isdir( const char *pathfile ); bool file_isfile( const char *pathfile ); bool file_islink( const char *pathfile ); bool file_copy( const char *pathsrc, const char *pathdst ); bool file_touch( const char *pathfile, size_t modtime ); // can be 0 bool file_delete( const char *path ); const char* file_base( const char *file ); char* file_norm( const char *pathfile ); // returns temporary normalized name // uint64_t file_checksum( const char *pathfile ); #endif #ifdef FILE_C #pragma once #include #include #include #include #include // stat, lstat #include // mode_t #ifdef _WIN32 // _MSC_VER #include // utimbuf, also in #else #include #endif #ifdef _WIN32 #include #else #include #endif #ifdef _WIN32 #include // O_RDONLY(00) // mmap() replacement for Windows. Placed into the public domain (Mike Frysinger) enum { PROT_READ = 0x1, PROT_WRITE = 0x2, PROT_EXEC = 0x4, MAP_SHARED = 0x01, MAP_PRIVATE = 0x02, MAP_ANON = 0x20, MAP_ANONYMOUS = MAP_ANON }; #define MAP_FAILED ((void *) -1) static void* mmap(void* start, size_t length, int prot, int flags, int fd, size_t offset) { // Offsets must be a multiple of the system's allocation granularity. We // guarantee this by making our view size equal to the allocation granularity. static int32_t page_size = -1; if (page_size < 0) { SYSTEM_INFO sysinfo = { 0 }; GetSystemInfo(&sysinfo); page_size = sysinfo.dwAllocationGranularity; } DWORD flProtect; size_t end = // length + offset; // 0; length & ~(page_size - 1) + page_size + offset & ~(page_size - 1); if( !(prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) ) { if( ( fd == -1 && (flags & MAP_ANON) && !offset ) || ( fd != -1 && !(flags & MAP_ANON) )) { if( prot & PROT_EXEC ) flProtect = prot & PROT_READ ? PAGE_EXECUTE_READ : PAGE_EXECUTE_READWRITE; else if( prot & PROT_WRITE ) flProtect = PAGE_READWRITE; else flProtect = PAGE_READONLY; HANDLE h = CreateFileMapping( fd == -1 ? INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle(fd), NULL, flProtect, (end >> 31) >> 1, (uint32_t)end, NULL); if( h != NULL ) { DWORD dwDesiredAccess = 0; dwDesiredAccess |= prot & PROT_WRITE ? FILE_MAP_WRITE : FILE_MAP_READ; dwDesiredAccess |= prot & PROT_EXEC ? FILE_MAP_EXECUTE : 0; dwDesiredAccess |= flags & MAP_PRIVATE ? FILE_MAP_COPY : 0; #if 0 void *ret = MapViewOfFile(h, dwDesiredAccess, (offset >> 31) >> 1, (uint32_t)offset, length); #else // hack: avoid ERROR_MAPPED_ALIGNMENT 1132 (0x46C) error // see https://stackoverflow.com/questions/8583449/memory-map-file-offset-low // see https://stackoverflow.com/questions/9889557/mapping-large-files-using-mapviewoffile // alignment size_t offset_page = offset & ~(page_size - 1); size_t read_size = length + (offset - offset_page); void *ret = MapViewOfFile(h, dwDesiredAccess, (offset_page >> 31) >> 1, (uint32_t) offset_page, read_size); //ASSERT(ret, "win32 error %d", GetLastError()); ret = (char*)ret + (offset - offset_page); #endif CloseHandle(h); // close the Windows Handle here (we handle the file ourselves with fd) return ret == NULL ? MAP_FAILED : ret; } } } return MAP_FAILED; } static void munmap(void* addr, size_t length) { UnmapViewOfFile(addr); } #endif #ifdef _WIN32 #ifdef __TINYC__ #define CP_UTF8 65001 int WINAPI MultiByteToWideChar(); #else #include #endif wchar_t *file_widen(const char *utf8) { // wide strings (windows only) static __thread wchar_t bufs[4][260]; static __thread int index = 0; int sz = (int)sizeof(bufs[0]); wchar_t *buf = bufs[(++index) % 4]; int needed = 2 * MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); buf[0] = 0; if( needed < sz ) { MultiByteToWideChar(CP_UTF8, 0, utf8, -1, (void*)buf, needed); } return (wchar_t *)buf; } #endif #define open8(path,mode) IFDEF(WINDOWS, _wopen(file_widen(path)) , open(path, mode) ) #define fopen8(path,mode) IFDEF(WINDOWS, _wfopen(file_widen(path),file_widen(mode)), fopen(path,mode) ) #define remove8(path) IFDEF(WINDOWS, _wremove(file_widen(path)) , remove(path) ) #define rename8(path) IFDEF(WINDOWS, _wrename(file_widen(path)) , rename(path) ) #define stat8(path,st) IFDEF(WINDOWS, _wstat(file_widen(path),st) , stat(path,st) ) // _stati64() #define stat_ IFDEF(WINDOWS, _stat, , stat_t ) // struct _stati64 char* file_map( const char *pathfile, size_t offset, size_t len ) { int file = open(pathfile, O_RDONLY); if( file < 0 ) { return 0; } void *ptr = mmap(0, len, PROT_READ, MAP_SHARED/*MAP_PRIVATE*/, file, offset); if( ptr == MAP_FAILED ) { ptr = 0; } close(file); // close file. mapping held until unmapped return (char *)ptr; } void file_unmap( char *buf, size_t len ) { munmap( buf, len ); } char* file_chunk(const char *pathfile, size_t offset, size_t len) { static __thread int map = 0; static __thread struct maplen { char *map; int len; } maps[16] = {0}; char *mem = file_map( pathfile, offset, len ); if( mem ) { struct maplen *bl = &maps[ map = ++map & (16-1) ]; if( bl->map ) file_unmap( bl->map, bl->len ); bl->map = mem; bl->len = len; } return mem; } int file_size( const char *pathfile ) { #if 0 FILE *fp = fopen(fname, "rb"); if(!fp) return 0; fseek(fp, 0L, SEEK_END); size_t sz = ftell(fp); fclose(fp); return (int)sz; #else struct stat_ st; uint64_t s = stat8(pathfile, &st) < 0 ? 0ULL : (uint64_t)st.st_size; return (int)s; #endif } int file_stamp( const char *pathfile ) { struct stat_ st; uint64_t t = stat8(pathfile, &st) < 0 ? 0ULL : (uint64_t)st.st_mtime; return (int)t; } bool file_exist( const char *pathfile ) { struct stat_ st; return stat8(pathfile, &st) < 0 ? 0 : 1; // _WIN: return _access( filename, 0 ) != -1; // else: return access( filename, F_OK ) != -1; } bool file_isdir( const char *pathfile ) { struct stat_ st; return stat8(pathfile, &st) < 0 ? 0 : S_IFDIR == ( st.st_mode & S_IFMT ); } bool file_isfile( const char *pathfile ) { struct stat_ st; return stat8(pathfile, &st) < 0 ? 0 : S_IFREG == ( st.st_mode & S_IFMT ); } bool file_islink( const char *pathfile ) { #ifdef S_IFLNK struct stat_ st; return stat8(pathfile, &st) < 0 ? 0 : S_IFLNK == ( st.st_mode & S_IFMT ); #else return 0; #endif } bool file_exact(const char *fname1, const char *fname2) { bool errors = false; for(FILE *in1 = fopen(fname1, "rb"); in1; fclose(in1), in1 = 0) for(FILE *in2 = fopen(fname2, "rb"); in2; fclose(in2), in2 = 0) { char buffer[64*1024+1] = {0}; while( !errors && fread(buffer,1,32*1024,in1)>0 && fread(buffer+32*1024,1,32*1024,in2)>0 ) { errors |= !!memcmp(buffer, buffer+32*1024, 32*1024); } errors |= !!memcmp(buffer, buffer+32*1024, 32*1024); } return !errors; } char* file_read(const char *pathfile) { #if 0 size_t sz = file_size(fname); if( !sz ) return 0; FILE *fp = fopen(fname, "rb"); if(!fp) return 0; char *data = malloc(sz+1); data[sz] = 0; if( fread(data, 1,sz, fp) != sz ) { free(data); fclose(fp); return 0; } fclose(fp); return data; #else static __thread char *bufs[16] = {0}; static __thread int buf = 0; buf = ++buf & (16-1); char **data = &bufs[ buf ]; size_t len = file_size(pathfile); FILE *fp = fopen8(pathfile, "rb"); if( fp && len ) { *data = REALLOC(*data, len + 1); len = fread(*data, 1, len, fp); len[*data] = 0; fclose(fp); } else { *data = REALLOC(*data, 8); 0[*data] = 0; } return *data; #endif } bool file_write(const char *pathfile, const void *data, int len) { bool ok = 0; for( FILE *fp = fopen8(pathfile, "wb"); fp; fclose(fp), fp = 0) { ok = len == fwrite(data, 1,len, fp); } return ok; } bool file_append(const char *pathfile, const void *data, int len) { bool ok = 0; for( FILE *fp = fopen8(pathfile, "a+b"); fp; fclose(fp), fp = 0) { ok = len == fwrite(data, 1,len, fp); } return ok; } bool file_touch( const char *pathfile, size_t modtime ) { struct stat_ st; if( stat8( pathfile, &st ) >= 0 ) { time_t date = modtime ? (time_t)modtime : time(0); struct utimbuf ut; ut.actime = st.st_atime; /* keep atime unchanged */ ut.modtime = date; /* set mtime to given time */ return utime( pathfile, &ut ) == 0; } return false; } bool file_copy( const char *pathsrc, const char *pathdst ) { # ifdef _WIN32 // _WIN return CopyFileW( file_widen(pathsrc), file_widen(pathdst), FALSE ); #elif defined __APPLE__ // _OSX return copyfile( pathsrc, pathdst, 0, COPYFILE_DATA )>=0; #else bool ok = 0; FILE *in = fopen8( pathsrc, "rb" ); if( in ) { FILE *out = fopen8( pathdst, "wb" ); if( out ) { char buf[1024]; while( int n = fread( buf, 1, 1024, in ) ){ ok = fwrite( buf, 1, n, out ) == n; if( !ok ) break; } fclose( out ); } fclose( in ); } return ok; #endif } bool file_delete( const char *path ) { remove8( path ); return !file_exist(path); } char *file_norm( const char *name ) { static __thread char bufs[4][260]; // 260: PATH_MAX static __thread int index = 0; index = ++index & 3; char *out = bufs[index], *buf = out, c; // skip lowercases+digits+underscores+slashes only. anything else is truncated. // remove dupe slashes // remove dupe underlines if( name ) do { /**/ if( *name >= 'a' && *name <= 'z' ) { *out++ = *name; } else if( *name >= 'A' && *name <= 'Z' ) { *out++ = *name - 'A' + 'a'; } else if( *name >= '0' && *name <= '9' ) { *out++ = *name; } else if( *name == '_' ) { *out++ = *name; } else if( *name == '/' || *name == '\\') { *out++ = '/'; name += strspn(name, "/\\"); --name; } else if( *name <= ' ' || *name == '.' ) { *out++ = '_'; name += strspn(name, ". "); --name; } } while( *++name ); return buf; } const char *file_base( const char *file ) { const char *end = strlen(file)+file; const char *a = strrchr(file, '/'); const char *b = strrchr(file, '\\'); if( a > b ) return a+1; if( b > a ) return b+1; return file; // end; } #if 0 uint64_t file_checksum( const char *pathfile ) { void *x = file_read(pathfile); // from scratch memory uint64_t l = file_size(pathfile); uint64_t crc = crc64( 0, x, l ); return crc; } bool file_stat( const char *uri, struct stat *out_info ) { #ifdef _WIN32 // make win32-friendly stat() call (no trailing slashes) char s[4096] = {0}; strcpy(s, uri); // strncpy(s, 4096, uri); for( int l = (int)strlen(s); --l >= 0; ) { if( s[l] == '\\' || s[l] == '/' ) s[l] = 0; } uri = s; #endif return stat( uri, out_info ) == 0; } #endif #ifdef FILE_DEMO #pragma once int main() { char *readbuf = file_chunk(__FILE__, 0, file_size(__FILE__)); printf( "read: %s\n", readbuf ); char *file1 = file_norm("There's\\\\a///Lady/who's sure all that \"glitters\"/is (gold)/file.audio"); char *file2 = file_norm(file1); // renormalize file should be safe puts(file1); puts(file2); assert( !strcmp(file1, file2) ); assert( ~puts("ok") ); } #define main main__ #endif // FILE_DEMO #endif // FILE_C ================================================ FILE: vault/os_icon.c ================================================ // change window icon. // - rlyeh, public domain. void os_icon(const char *pathfile_ico); // "" to restore (win32 only) // ---------------------------------------------------------------------------- #ifdef ICON_C #pragma once #ifdef _WIN32 static HICON os_load_ico( const char *ico_pathfile ) { HICON hi = (HICON)LoadImageA( // returns a HANDLE so we have to cast to HICON NULL, // hInstance must be NULL when loading from a file ico_pathfile, // .ico file name IMAGE_ICON, // specifies that the file is an icon 0, // width of the image (we'll specify default later on) 0, // height of the image LR_LOADFROMFILE| // we want to load a file (as opposed to a resource) LR_DEFAULTSIZE| // default metrics based on the type (IMAGE_ICON, 32x32) LR_SHARED // let the system release the handle when it's no longer used ); return hi; } void os_icon(const char *pathfile_ico) { // set app .ico after window is created HANDLE hIcon = os_load_ico(pathfile_ico); HWND hWndPseudo = (HWND)GetCurrentProcess(); HWND hWndWindow = (HWND)GetActiveWindow(); HWND hWndConsole = (HWND)GetConsoleWindow(); HWND hWnds[] = { hWndWindow, hWndConsole, hWndPseudo }; for( int i = 0; i < (sizeof(hWnds)/sizeof(0[hWnds])); ++i) { SendMessage( hWnds[i], (UINT)WM_SETICON, ICON_BIG, (LPARAM)hIcon ); SendMessage( hWnds[i], (UINT)WM_SETICON, ICON_SMALL, (LPARAM)hIcon ); } } #else void os_icon(const char *) { // @todo : Use https://wiki.libsdl.org/SDL_SetWindowIcon instead } #endif #ifdef ICON_DEMO int main() { os_icon("demo.ico"); window_title("demo.ico"); system("pause"); os_icon(""); } #define main main__ #endif // ICON_DEMO #endif // ICON_C ================================================ FILE: vault/os_ini.c ================================================ // ini+, extended ini format // - rlyeh, public domain // // # spec // // ; line comment // [user] ; map section name (optional) // name=john ; key and value (mapped here as user.name=john) // +surname=doe jr. ; sub-key and value (mapped here as user.name.surname=doe jr.) // age=30 ; numbers // color=240 ; key and value \ // color=253 ; key and value |> array: color[0], color[1] and color[2] // color=255 ; key and value / // color= ; remove key/value(s) // color=white ; recreate key; color[1] and color[2] no longer exist // [] ; unmap section // -note=keys may start with symbols (except plus and semicolon) // -note=linefeeds are either \r, \n or \r\n. // -note=utf8 everywhere. // char *ini( const char *text ); #ifdef INI_C #pragma once // @todo: // evaluate alt api: num_pairs32_t config_parse_ini(const char *ini, char **keys, char **values); char *ini( const char *s ) { char *map = 0; int mapcap = 0, maplen = 0; enum { DEL, REM, TAG, KEY, SUB, VAL } fsm = DEL; const char *cut[6] = {0}, *end[6] = {0}; while( *s ) { while( *s && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') ) ++s; /**/ if( *s == ';' ) cut[fsm = REM] = ++s; else if( *s == '[' ) cut[fsm = TAG] = ++s; else if( *s == '+' ) cut[fsm = SUB] = ++s; else if( *s == '=' ) cut[fsm = VAL] = ++s; else if( *s > ' ' && *s <= 'z' && *s != ']' ) cut[fsm = KEY] = cut[SUB] = end[SUB] = s; else { if( *s ) ++s; continue; } /**/ if( fsm == REM ) { while(*s && *s != '\r'&& *s != '\n') ++s; } else if( fsm == TAG ) { while(*s && *s != '\r'&& *s != '\n'&& *s != ']') ++s; end[TAG] = s; } else if( fsm == KEY ) { while(*s && *s > ' ' && *s <= 'z' && *s != '=') ++s; end[KEY] = s; } else if( fsm == SUB ) { while(*s && *s > ' ' && *s <= 'z' && *s != '=') ++s; end[SUB] = s; } else if( fsm == VAL ) { while(*s && *s >= ' ' && *s <= 'z' && *s != ';') ++s; end[VAL] = s; while( end[VAL][-1] <= ' ' ) { --end[VAL]; } char buf[256] = {0}, *key = buf; if( end[TAG] - cut[TAG] ) key += sprintf(key, "%.*s.", (int)(end[TAG] - cut[TAG]), cut[TAG] ); if( end[KEY] - cut[KEY] ) key += sprintf(key, "%.*s", (int)(end[KEY] - cut[KEY]), cut[KEY] ); if( end[SUB] - cut[SUB] ) key += sprintf(key, ".%.*s", (int)(end[SUB] - cut[SUB]), cut[SUB] ); int reqlen = (key - buf) + 1 + (end[VAL] - cut[VAL]) + 1 + 1; if( (reqlen + maplen) >= mapcap ) map = REALLOC( map, mapcap += reqlen + 512 ); sprintf( map + maplen, "%.*s%c%.*s%c%c", (int)(key - buf), buf, 0, (int)(end[VAL] - cut[VAL]), cut[VAL], 0, 0 ); maplen += reqlen - 1; } } return map; } #ifdef INI_DEMO int main() { char *kv = ini( "; line comment\n" "[user] ; map section name (optional)\n" "name=john ; key and value (mapped here as user.name=john)\n" "+surname=doe jr. ; sub-key and value (mapped here as user.name.surname=doe jr.)\n" "age=30 ; numbers\n" "color=240 ; key and value \\\n" "color=253 ; key and value |> array: color[0], color[1] and color[2]\n" "color=255 ; key and value /\n" "color= ; remove key/value(s)\n" "color=white ; recreate key; color[1] and color[2] no longer exist\n" "[] ; unmap section\n" "-note=keys may start with symbols (except plus and semicolon)\n" "-note=linefeeds are either \\r, \\n or \\r\\n.\n" "-note=utf8 everywhere.\n" ); if( kv ) { for( char *iter = kv; iter[0]; ) { char *key = iter; while( *iter++ ); char *val = iter; while( *iter++ ); printf("'%s' = '%s'\n", key, val ); } FREE( kv ); } } #define main main__ #endif // INI_DEMO #endif // INI_C ================================================ FILE: vault/os_locale.c ================================================ void os_locale(void); #ifdef LOCALE_C #pragma once #include void os_locale(void) { // @graphitemaster reported speed boosts at least on Linux setlocale(LC_ALL, "C"); } #endif ================================================ FILE: vault/os_logger.c ================================================ // [x] logger: info, warn, fail, quit. also todo() and fixme() // - rlyeh, public domain // ----------------------------------------------------------------------------- #ifndef LOGGER_H #define LOGGER_H #ifndef LOGLEVEL # define LOGLEVEL 4 // 0(off).. 4(max) #endif #ifdef SHIPPING # undef LOGLEVEL # define LOGLEVEL 0 #endif #if LOGLEVEL <= 0 # define LOG(tags, ...) (void)0 #else # ifdef _MSC_VER # define SHORT_FILE strrchr("\\" __FILE__, '\\') // todo: test with +1 # else # define SHORT_FILE __FILE__ # endif # define LOG(tags, ...) (fprintf(stderr,__VA_ARGS__),fprintf(stderr," (%s:%d) %s\n",SHORT_FILE,__LINE__,#tags)) #endif #if LOGLEVEL >= 1 # define LOGQUIT(tags, ...) do { LOG(tags, __VA_ARGS__); exit(-__LINE__); } while(0) #else # define LOGQUIT(tags, ...) exit(-__LINE__) #endif #if LOGLEVEL >= 2 # define LOGFAIL LOG #else # define LOGFAIL(tags, ...) (void)0 #endif #if LOGLEVEL >= 3 # define LOGWARN LOG #else # define LOGWARN(tags, ...) (void)0 #endif #if LOGLEVEL >= 4 # define LOGINFO LOG #else # define LOGINFO(tags, ...) (void)0 #endif // additional log utils #define TODO(...) do { static int todo = 0; if(!todo ) { ++todo ; LOGINFO(TODO, __VA_ARGS__); } } while(0) #define FIXME(...) do { static int fixme = 0; if(!fixme) { ++fixme; LOGINFO(FIXME, __VA_ARGS__); } } while(0) #endif // LOGGER_H ================================================ FILE: vault/os_logger2.c ================================================ #ifndef LOGGER2_H #define LOGGER2_H #include // create logger with given buffer size void log_create( FILE *fp, int bufsz ); // append line to all registered logs int log_puts( const char *msg, ... ); // utils char *log_stamp(void); // -> "2020-03-17 20.15.41 Tue" #endif #ifdef LOGGER2_C #pragma once #include #include #include static FILE *loggers[32] = {0}; char *log_stamp(void) { static __thread char date[64]; time_t rawtime; time( &rawtime ); struct tm *ti = localtime( &rawtime ); sprintf(date, "%04d-%02d-%02d %02d.%02d.%02d %.3s", 1900 + ti->tm_year, ti->tm_mon + 1, ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec, "SunMonTueWedThuFriSat"+3*ti->tm_wday ); return date; } void log_create( FILE *fp, int bufsz ) { // append logger for broadcasting for( int i = 0; i < 32; ++i) { if( !loggers[i] ) { loggers[i] = fp; break; } } // write header fprintf(fp, "--""- New session [built %s %s]\n", __DATE__, __TIME__); fflush(fp); // release builds might flush logs every 16KiB; any other build could flush every line setvbuf(fp, NULL, _IOFBF, bufsz > 2 ? bufsz : 2); fprintf(fp, "%s\n", log_stamp()); } int log_puts( const char *msg, ... ) { va_list vl; va_start(vl, msg); for( int i = 0; i < 32; ++i ) { FILE *fp = loggers[i]; if( !fp ) break; vfprintf(fp, msg, vl); fprintf(fp, "\n"); } va_end(vl); return !feof(stdout); } #ifdef LOGGER2_DEMO int main() { char logname[128]; sprintf(logname, "log %s.txt", log_stamp()); log_create( stdout, 16384 ); log_create( fopen(logname,"a+b"), 16384 ); // append mode log_create( fopen("log-latest.txt","w+b"), 16384 ); // create log_puts("hello %d", 123); log_puts("hello %s", log_stamp()); } #define main __main #endif // LOGGER_DEMO #endif // LOGGER_C ================================================ FILE: vault/os_logger3.c ================================================ // simple logger. likely slow, though. // - rlyeh, public domain. // // - [x] colors based on content. // - [x] no logging categories. throughput flood configurable by percentage (see LOG_LEVEL var). // - [x] print fatal errors always. with optional callstack (see LOG_PRINT_STACK macro). #pragma once #include #include #include # if !defined LOG_LEVEL && (defined NDEBUG || defined SHIPPING) #define LOG_LEVEL 0 // 0% - no messages logged (only fatal errors will) #elif !defined LOG_LEVEL #define LOG_LEVEL 100 // 100% - all messages logged #endif #ifndef LOG_TIMER #define LOG_TIMER() 0.0 /* user-defined apptime clock here */ #endif #ifndef LOG_PRINT_STACK #define LOG_PRINT_STACK(f) ftrace(f,+16) /* user-defined callstack printer here */ #endif # if !defined LOG_ENABLE_ANSI && defined _WIN32 // #include #define LOG_ENABLE_ANSI() os_ansi() // for(unsigned mode=0;!mode;mode=1) SetConsoleMode(GetStdHandle(-11), (GetConsoleMode(GetStdHandle(-11), &mode), mode|4)) #elif !defined LOG_ENABLE_ANSI #define LOG_ENABLE_ANSI() #endif #ifdef _MSC_VER #define LOG_DIRCHR "\\" #else #define LOG_DIRCHR "/" #endif #define LOGCOL(...) do { \ static char must_log = -1; \ static enum { undefined=-1, restore=0, r=31,g,y,b,p,t,w,inv=0x80 } color = undefined; \ char buf[2048]; snprintf(buf, 2048, "" __VA_ARGS__); \ if( color == undefined ) { \ LOG_ENABLE_ANSI(); \ char low[2048]; int i; for(i=0;buf[i];++i) low[i] = 32|buf[i]; low[i]='\0'; \ /**/ if( strstr(low,"fatal")|| strstr(low,"panic") || strstr(low,"assert") ) color=r|inv,must_log=1; \ else if( strstr(low,"fail") || strstr(low,"error") ) color=r; \ else if( strstr(low,"warn") || strstr(low,"alert") ) color=y; /*beware,caution*/ \ else if( strstr(low,"info") || strstr(low,"succe") ) color=g; /*ok, no error*/ \ else if( strstr(low,"debug") ) color=t; \ else if( strstr(low,"trace") ) color=p; else color=restore; \ if( must_log < 0 ) { /* original splitmix64 by Sebastiano Vigna (CC0)*/ \ uint64_t z = (__LINE__ + __COUNTER__ + UINT64_C(0x9E3779B97F4A7C15)); \ z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); \ must_log = (unsigned)((z ^ (z >> 31)) % 100) < LOG_LEVEL; } } \ if( must_log ) { \ double timer = LOG_TIMER(); \ const char *short_file = strrchr(LOG_DIRCHR __FILE__, 0[LOG_DIRCHR]) + 1; \ if(color&0x80) fprintf(stderr, "\033[7m"); \ if(timer>0)fprintf(stderr, "\033[%dm%07.3fs %s (%s:%d)", color&0x7f, timer, &buf[buf[0]=='!'], short_file, __LINE__); \ else fprintf(stderr, "\033[%dm%s (%s:%d)", color&0x7f, &buf[buf[0]=='!'], short_file, __LINE__); \ fprintf(stderr, "\033[%dm\n", restore); \ if(color&inv || buf[0] == '!') LOG_PRINT_STACK(stderr); \ log_puts(buf); /* <-- optional, only if logger2 is included */ } \ } while(0) #ifdef LOGGER3_C #pragma once #ifdef LOGGER3_DEMO int main() { LOGCOL("Test 1 - normal message. lines starting with '!' will print %s", "callstack"); LOGCOL("Test 2 - trace message"); LOGCOL("Test 3 - debug message"); LOGCOL("Test 4 - info message"); LOGCOL("Test 5 - warning message"); LOGCOL("Test 6 - error message"); LOGCOL("Test 7 - fatal error message (fatal errors are always printed, despite LOG_LEVEL var)"); } #define main main__ #endif // LOGGER3_DEMO #endif // LOGGER3_C ================================================ FILE: vault/os_memory.c ================================================ // - rlyeh, public domain #ifndef MEMORY_H #define MEMORY_H #include #include #ifndef REALLOC #define REALLOC realloc #endif #define MALLOC(n) REALLOC(0, n) #define FREE(p) REALLOC(p, 0) #define CALLOC(c, n) memset(MALLOC((c)*(n)), 0, (c)*(n)) #define STRDUP(s) strcpy(MALLOC(strlen(s)+1), (s)) #ifndef MSIZE # if defined _OSX || defined _BSD # define MSIZE malloc_size # elif defined _AND # define MSIZE dlmalloc_usable_size # elif defined _WIN # define MSIZE _msize # else # define MSIZE malloc_usable_size // glibc # endif #endif #endif ================================================ FILE: vault/os_mime.c ================================================ // detect file/mime types // - rlyeh, public domain. const char *os_mime(const char *filename); const char *os_mime_buf(const char *buf, int len); #ifdef MIME_C #pragma once #include const char *os_mime_buf(const char *buf, int len) { const struct type { int len; const char *ext; const char *buf; int off; } types[] = { //// Descending priority order 21, "deb", "!\x0a""debian-binary", 0, 20, "xpi", "META-INF/mozilla.rsa", 30, 14, "mxf", "\x06\x0e+4\x02\x05\x01\x01\x0d\x01\x02\x01\x01\x02", 0, 11, "hdr", "#?RADIANCE\x0a", 0, 11, "m4v", "\x00\x00\x00\x1c""ftypM4V", 0, // before mp4 10, "wmv", "0&\xb""2u""\x8e""f""\xcf\x11\xa6\xd9", 0, 8, "mkv", "matroska", 31, 8, "mov", "\x00\x00\x00\x14""ftyp", 0, 8, "msi", "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1", 0, 8, "opus", "OpusHead", 28, // before ogg 8, "wav", "RIFFWAVE", 0, 8, "woff", "wOFF\x00\x01\x00\x00", 0, 8, "woff", "wOFFOTTO", 0, 8, "woff2", "wOF2\x00\x01\x00\x00", 0, 8, "woff2", "wOF2OTTO", 0, 7, "ar", "!", 0, 7, "avi", "RIFFAVI", 0, 7, "hdr", "#?RGBE\x0a", 0, 7, "m4a", "ftypM4A", 4, 7, "rar", "Rar!\x1a\x07\x00", 0, 7, "rar", "Rar!\x1a\x07\x01", 0, 6, "7z", "7z\xbc\xaf'\x1c", 0, 6, "amr", "#!AMR\x0a", 0, 6, "xz", "\xfd""7zXZ\x00", 0, 5, "amr", "#!AMR", 0, 5, "otf", "OTTO\x00", 0, 5, "rtf", "{\rtf", 0, 5, "rtf", "{\x0dtf\x00", 0, 5, "tar", "ustar", 257, 5, "ttf", "\x00\x01\x00\x00\x00", 0, 4, "cab", "ISc(", 0, 4, "cab", "MSCF", 0, 4, "crx", "Cr24", 0, 4, "exr", "v/1\x01", 0, 4, "flac", "fLaC", 0, 4, "flif", "FLIF", 0, 4, "flv", "FLV\x01", 0, 4, "ico", "\x00\x00\x01\x00", 0, 4, "lz", "LZIP", 0, 4, "m4a", "M4A ", 0, 4, "mid", "MThd", 0, 4, "mkv", "\x1a""E""\xdf\xa3", 0, 4, "mp4", "\x00\x00\x00\x1c", 0, 4, "mp4", "3gp5", 0, 4, "mp4", "ftyp", 4, 4, "mpg", "\x00\x00\x01b", 0, 4, "nes", "NES\x1a", 0, 4, "ogg", "OggS", 0, 4, "otf", "OTTO", 0, 4, "pdf", "%PDF", 0, 4, "png", "\x89PNG", 0, 4, "psd", "8BPS", 0, 4, "rar", "Rar!", 0, 4, "rpm", "\xed\xab\xee\xdb", 0, 4, "sqlite", "SQLi", 0, 4, "svg", " int main( int argc, const char **argv ) { if( argc == 2 ) { puts( os_mime(argv[1]) ); exit(0); } // samples taken from https://github.com/mathiasbynens/small const unsigned char unknown1[] = { 0x3C,0x3F,0x78,0x6D,0x6C,0x20,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E,0x3D,0x22,0x31, 0x2E,0x31,0x22,0x3F,0x3E,0x3C,0x5F,0x2F,0x3E }; const unsigned char unknown2[] = { 0x47,0x49,0x46,0x38,0x39,0x61,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x3B }; const unsigned char unknown3[] = { 0x42,0x4D,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x0C,0x00, 0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0xFF,0x00 }; const unsigned char unknown4[] = { 0x50,0x4B,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00 }; const unsigned char unknown5[] = { 0x7B,0x5C,0x72,0x74,0x66,0x31,0x7D }; const unsigned char unknown6[] = { 0x52,0x49,0x46,0x46,0x12,0x00,0x00,0x00,0x57,0x45,0x42,0x50,0x56,0x50,0x38,0x4C, 0x06,0x00,0x00,0x00,0x2F,0x41,0x6C,0x6F,0x00,0x6B }; puts( os_mime_buf( unknown1, sizeof(unknown1) ) ); puts( os_mime_buf( unknown2, sizeof(unknown2) ) ); puts( os_mime_buf( unknown3, sizeof(unknown3) ) ); puts( os_mime_buf( unknown4, sizeof(unknown4) ) ); puts( os_mime_buf( unknown5, sizeof(unknown5) ) ); puts( os_mime_buf( unknown6, sizeof(unknown6) ) ); } #endif ================================================ FILE: vault/os_singleton.c ================================================ // - rlyeh, public domain bool os_singleton(const char *guid); #ifdef SINGLETON_C #pragma once #include #ifdef _WIN32 // from microsoft website //#include #include #include static HANDLE app_mutex = 0; void os_singleton__quit(void) { if( app_mutex ) { ReleaseMutex( app_mutex ); app_mutex = NULL; } } bool os_singleton(const char *guid) { //Make sure that you use a name that is unique for this application otherwise //two apps may think they are the same and it wont work. char buffer[128]; sprintf(buffer, "Global\\{%s}", guid); app_mutex = CreateMutexA(NULL, FALSE, buffer); if(ERROR_ALREADY_EXISTS == GetLastError()) { return false; } atexit(os_singleton__quit); // needed? return true; } #else bool os_singleton(const char *guid) { return true; } #endif #ifdef SINGLETON_DEMO #include int main() { bool ok = os_singleton("619967F0-CDD6-48b5-9C61-DE91175C3112"); if( ok ) { puts("Ok. os_singleton() initialized..."); } else { puts("Error: App already initialized..."); } system("pause"); } #define main main__ #endif // SINGLETON_DEMO #endif // SINGLETON_C ================================================ FILE: vault/os_test.c ================================================ // # tst ###################################################################### // @todo: measure time with overrides as well, so it works as benchmark. // double (*benchmark)() = 0; // unit() { now = benchmark(); } // test() { ... } int unit(const char *name); int test(int expr); #ifdef TEST_C #pragma once #include static __thread int right = 0, wrong = 0; static __thread const char *suite = ""; int unit(const char *name) { suite = name; right = wrong = 0; return 1; } int test(int expr) { right = right + !!expr; wrong = wrong + !expr; char buffer[1024]; sprintf(buffer, "%s%c Unit '%s': test %d/%d %s", // (%5.2f%%)", !expr ? "!" : "", "NY"[expr && !wrong], suite, right, right+wrong, !expr ? "FAILED" : "passed" //,100.f * right / (right+wrong) ); #if 1 fprintf(stderr, "%s\n", buffer[0] == '!' ? buffer+1 : buffer); fflush(stderr); #else LOG(TEST, "%s", buffer); #endif return !!expr; } #ifdef TEST_DEMO int main() { if( unit( "1st unit" ) ) { test( 1 < 4 ); test( 2 < 4 ); test( 3 < 4 ); } if( unit( "2nd unit") ) { test( 10 < 10 ); // <-- shall fail here } if( unit( "3rd unit" ) ) { test( 1 < 2 ); } } #define main main__ #endif // TEST_DEMO #endif // TEST_C ================================================ FILE: vault/os_trap.c ================================================ void os_trap( void(*handler)(int) ); void os_crash(void); #ifdef TRAP_C #pragma once #include // _control87 #include #include void (*os_signal)(int); void os_signal_(int handler) { // reload signal // signal(handler, os_signal_); // report if( os_signal ) { os_signal(handler); } else { const char *signame = "?"; /**/ if( handler == SIGABRT ) signame = "SIGABRT"; else if( handler == SIGFPE ) signame = "SIGFPE"; else if( handler == SIGILL ) signame = "SIGILL"; else if( handler == SIGINT ) signame = "SIGINT"; else if( handler == SIGSEGV ) signame = "SIGSEGV"; else if( handler == SIGTERM ) signame = "SIGTERM"; // panic("!exception caught, signal: %s (%d)\n", signame, handler); fprintf(stderr, "exception caught, signal: %s (%d)\n", signame, handler); exit(-1); } } void os_trap( void(*handler)(int) ) { #ifdef _MSC_VER _control87(0, _MCW_EM); // Unmask all FPE #endif signal(SIGABRT, os_signal_); // Abnormal termination signal(SIGFPE, os_signal_); // Floating-point error signal(SIGILL, os_signal_); // Illegal instruction signal(SIGINT, os_signal_); // CTRL+C signal signal(SIGSEGV, os_signal_); // Illegal storage access signal(SIGTERM, os_signal_); // Termination request os_signal = handler; } void os_crash(void) { int *p = 0; *p = 42; } #ifdef TRAP_DEMO int main() { os_trap(NULL); printf("signal? [1..6]: "); int test; if( scanf("%d", &test) ) { if( test == 1 ) { abort(); } // test SIGABRT if( test == 2 ) { float a = 0; printf("%f\n", 10/a); } // test SIGFPE if( test == 3 ) { raise(SIGILL); } // test SIGILL if( test == 4 ) { puts("ctrl-c to signal"); for(;;) {} } // test SIGINT if( test == 5 ) { char *p = 0; *p = 42; } // test SIGSEGV if( test == 6 ) { raise(SIGTERM); } // test SIGTERM } } #define main main__ #endif // TRAP_DEMO #endif // TRAP_C #if 0 void os_crash(void); void os_trap(void); #ifdef TRAP_C #pragma once #include #include #include #include #ifndef _WIN32 #include // setrlimit() #else #include #endif static void catch_signal( int sig ) { signal(sig, catch_signal); // reset for next signal /**/ if(sig == SIGTERM) panic("!catch_signal(SIGTERM): A termination request was sent to the program"); else if(sig == SIGABRT) panic("!catch_signal(SIGABRT): Usually caused by an abort() or assert()"); else if(sig == SIGSEGV) panic("!catch_signal(SIGSEGV): Segmentation Fault"); else if(sig == SIGINT ) panic("!catch_signal(SIGINT): Interactive attention signal (usually ctrl+c)"); else if(sig == SIGFPE ) panic("!catch_signal(SIGFPE): Arithmetic Exception"); else if(sig == SIGILL ) panic("!catch_signal(SIGILL): Illegal Instruction"); else panic("!catch_signal(): Unknown exception caught."); } #if defined _MSC_VER && defined __cplusplus # include // windows.h alternative # include // _set_se_translator static void _cdecl catch_se( unsigned int ex, EXCEPTION_POINTERS *p ) { /**/ if(ex == EXCEPTION_ACCESS_VIOLATION) panic("!catch_se(EXCEPTION_ACCESS_VIOLATION): Memory access violation"); else if(ex == EXCEPTION_ILLEGAL_INSTRUCTION) panic("!catch_se(EXCEPTION_ILLEGAL_INSTRUCTION): Illegal instruction"); else if(ex == EXCEPTION_INT_DIVIDE_BY_ZERO) panic("!catch_se(EXCEPTION_INT_DIVIDE_BY_ZERO): Integer divide by zero"); else if(ex == EXCEPTION_STACK_OVERFLOW) panic("!catch_se(EXCEPTION_STACK_OVERFLOW): Stack overflow"); else panic("!catch_se(): Unknown exception"); } #endif #if 0 // ndef __TINYC__ #include #ifdef _MSC_VER #pragma fenv_access(on) #else #pragma STDC FENV_ACCESS ON #endif void os_trap_fpe() { // ignore fp exceptions static fenv_t fenv = {0}; feholdexcept( &fenv ); // config fp rounding mode to chop (round towards zero); easier to emulate in software than round-to-nearest. fesetround(FE_TOWARDZERO); } #endif void os_trap(void) { // @todo: TRAP_C, TRAP_CPP, TRAP_FPE flags? // install signal handlers const int signals[] = { // The C standard defines following 6 signals: SIGABRT, // abort; abnormal termination. SIGFPE, // floating point exception. SIGILL, // illegal; invalid instruction. SIGSEGV, // segmentation violation; invalid memory access. SIGTERM, // terminate; termination request sent to the program. // SIGINT, // interrupt; interactive attention request sent to the program. SHIPPING ONLY? // OS specifics: #ifdef __APPLE__ // _OSX || _IOS SIGPIPE, #endif #ifdef _WIN32 SIGBREAK, #else SIGBUS, // Bus error SIGHUP, // sent to a process when its controlling terminal is closed //SIGQUIT, // //SIGKILL, // kernel will let go of the process without informing the process of it //SIGTRAP, // debugger //SIGSYS, #endif }; for( int i = 0; i < sizeof(signals)/sizeof(signals[0]); ++i ) { //signal(signals[i], SIG_IGN); //catch_signal); if (signal(signals[i], catch_signal) == SIG_ERR) { panic("!An error occurred while setting a signal handler."); } } #ifdef __cplusplus set_terminate(catch_signal); // C++11 #ifdef _MSC_VER // _WIN32 _set_se_translator(catch_se); #endif #endif #if 0 // ndef _WIN32 // enable coredumps rlimit core_limit = { RLIM_INFINITY, RLIM_INFINITY }; setrlimit( RLIMIT_CORE, &core_limit ); #endif #if 0 // ndef _WIN32 // daemon + unstopable + no zombies children (= no wait()) #ifndef SIGCLD #define SIGCLD SIGCHLD #endif signal(SIGCLD, SIG_IGN); // ignore child death signal(SIGHUP, SIG_IGN); // ignore terminal hangups setpgrp(); // break away from process group #endif } void os_crash(void) { int *p = 0; *p = 42; } #ifdef TRAP_DEMO int main() { os_trap(); os_crash(); } #define main main__ #endif // TRAP_DEMO #endif // TRAP_C #endif ================================================ FILE: vault/os_tray.c ================================================ // Based on code from MinimalSystray project (unlicensed) // - rlyeh, public domain. void os_tray( const char *tray_name, const char *tray_icon_pathfile ); // ---------------------------------------------------------------------------- #ifdef TRAY_C #pragma once #if defined _WIN32 //&& !defined __TINYC__ #include #include #pragma comment(lib, "user32") #pragma comment(lib, "shell32") static volatile HWND tray_window = 0; static const char *TRAY_ICON_PATHFILE = 0; static const char *TRAY_NAME = ""; static LRESULT CALLBACK SystrayThreadProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { //printf("debug msg %d\n", Msg); enum EButtons { IDM_NONE, IDM_EXIT }; static UINT s_WM_TASKBARRESTART; if (Msg == WM_COMMAND && wParam == IDM_EXIT) { NOTIFYICONDATAA i; ZeroMemory(&i, sizeof(i)); i.cbSize = sizeof(i); i.hWnd = hWnd; Shell_NotifyIconA(NIM_DELETE, &i); ExitProcess(0); } if (Msg == WM_USER && (LOWORD(lParam) == WM_LBUTTONUP || LOWORD(lParam) == WM_RBUTTONUP)) { HMENU hPopMenu = CreatePopupMenu(); InsertMenuA(hPopMenu,0xFFFFFFFF,MF_STRING|MF_GRAYED,IDM_NONE, TRAY_NAME); InsertMenuA(hPopMenu,0xFFFFFFFF,MF_SEPARATOR,IDM_NONE,NULL); InsertMenuA(hPopMenu,0xFFFFFFFF,MF_STRING,IDM_EXIT,"Exit"); SetForegroundWindow(hWnd); //cause the popup to be focused POINT lpClickPoint; GetCursorPos(&lpClickPoint); TrackPopupMenu(hPopMenu,TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y,0,hWnd,NULL); } if (Msg == WM_CREATE || Msg == s_WM_TASKBARRESTART) { if (Msg == WM_CREATE) s_WM_TASKBARRESTART = RegisterWindowMessageA(TRAY_NAME); NOTIFYICONDATAA nid; ZeroMemory(&nid, sizeof(nid)); nid.cbSize = sizeof(nid); nid.hWnd = hWnd; nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.hIcon = 0; if( TRAY_ICON_PATHFILE ) { nid.hIcon = os_load_ico(TRAY_ICON_PATHFILE); } else { //GetIconFromParentProcess(); //GetIconFromDLL( "imageres.dll", 19 ); //LoadIconA(GetModuleHandleA(NULL), "ICN"); //etc; } nid.uCallbackMessage = WM_USER; snprintf(nid.szTip, sizeof(nid.szTip), "%s", TRAY_NAME); Shell_NotifyIconA(NIM_ADD, &nid); } return DefWindowProcA(hWnd, Msg, wParam, lParam); } static DWORD CALLBACK SystrayThread(LPVOID arg) { MSG Msg; WNDCLASSA c; ZeroMemory(&c, sizeof(c)); c.lpfnWndProc = SystrayThreadProc; c.hInstance = GetModuleHandleA(NULL); c.lpszClassName = TRAY_NAME; if (!RegisterClassA(&c)) return 1; tray_window = CreateWindowA(c.lpszClassName, 0, 0, 0, 0, 0, 0, 0, 0, c.hInstance, 0); if( !tray_window ) return 1; while (GetMessageA(&Msg, 0, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessageA(&Msg); } tray_window = 0; return 0; } // install a systray icon that: // - uses __argv[0]+.ico // - has context popup menu with "title, about, exit" options void os_tray( const char *tray_name, const char *tray_icon_pathfile ) { static int ever = 0; if( !ever ) { ever = 1; TRAY_NAME = strdup(tray_name); TRAY_ICON_PATHFILE = strdup(tray_icon_pathfile); CreateThread(NULL, 0, SystrayThread, NULL, 0, NULL); while(!tray_window) Sleep(10); } } #else void os_tray( const char *title, const char *icon ) {} #endif #if defined TRAY_DEMO && defined TRAY_C int main() { os_tray( "my demo", "demo.ico" ); system("pause"); } #define main main__ #endif // TRAY_DEMO #endif // TRAY_C ================================================ FILE: vault/syn.c ================================================ // - rlyeh, public domain #ifdef SYN_C #pragma once #define ATOMIC_C #define CHANNEL_C #define CONDV_C #define MCMP_C #define MUTEX_C #define SEMAPHORE_C #define SLEEP_C #define THREAD_C #define TLS_C #define CORO_C #define FIBER_C #endif #include "c.c" #include "os.c" #include "syn_mutex.c" #include "syn_condv.c" // after mutex #include "syn_channel.c" // after condv+mutex #include "syn_tls.c" #include "syn_thread.c" // after tls #include "syn_atomic.c" #include "syn_mcmp.c" #include "syn_semaphore.c" #include "syn_sleep.c" #include "syn_coro.c" #include "syn_fiber.c" ================================================ FILE: vault/syn_atomic.c ================================================ // # atomics ################################################################## // - rlyeh, public domain // // looking for better coverage? check pfx_compiler.h by @mmozeiko (public domain) #ifndef ATOMIC_H #define ATOMIC_H #include int64_t atomic_set( int64_t *ptr, const int value ); int64_t atomic_get( int64_t *ptr ); int64_t atomic_inc( int64_t *ptr ); int64_t atomic_dec( int64_t *ptr ); #endif #ifdef ATOMIC_C #pragma once # if defined _MSC_VER # include # define __sync_add_and_fetch(p,x) (_InterlockedExchangeAdd64((__int64 volatile *)(p), (x)) + (x)) # define __sync_bool_compare_and_swap(p, c, s) (_InterlockedCompareExchange64((__int64 volatile *)(p), (__int64)(s), (__int64)(c)) == (__int64)(c)) # define __sync_lock_test_and_set(p,v) (_InterlockedExchange64( (__int64 volatile *)(p), (__int64)(v) )) #elif defined __TINYC__ # include # define __sync_add_and_fetch(p,x) (__int64)(InterlockedExchangeAdd((LONG volatile *)(p), (x)) + (x)) # define __sync_bool_compare_and_swap(p, c, s) (__int64)(InterlockedCompareExchange((LONG volatile *)(p), (__int32)(s), (__int32)(c)) == (__int32)(c)) # define __sync_lock_test_and_set(p,v) (__int64)(InterlockedExchange( (LONG volatile *)(p), (__int32)(v) )) # pragma message("TODO: Warning! 32-bit atomics untested") #else #endif int64_t atomic_set( int64_t *ptr, const int value ) { return __sync_lock_test_and_set( ptr, value ); } int64_t atomic_get( int64_t *ptr ) { return __sync_add_and_fetch( ptr, 0 ); } int64_t atomic_inc( int64_t *ptr ) { return __sync_add_and_fetch( ptr, +1 ); } int64_t atomic_dec( int64_t *ptr ) { return __sync_add_and_fetch( ptr, -1 ); } #ifdef ATOMIC_DEMO #include #include int main() { int64_t k = 0; assert( atomic_set(&k, 123) == 0 ); assert( atomic_set(&k, 123) == 123 ); assert( atomic_get(&k) == 123 ); atomic_dec(&k); assert( atomic_get(&k) == 122 ); atomic_inc(&k); assert( atomic_get(&k) == 123 ); assert( puts("ok") >= 0 ); } #define main main__ #endif // ATOMIC_DEMO #endif // ATOMIC_C ================================================ FILE: vault/syn_channel.c ================================================ // # channels ################################################################# // - rlyeh, public domain // // [src] based on cchan library by Máté Nagy (Public Domain) // // Go-style construct for inter-thread communication: each channel is a FIFO of // fixed-length messages, which grows to make space for unread messages. // - Multiple threads can write into one channel at once. Writes never block. // - Multiple threads can read from one channel at once - but each message is // received only once. If there are multiple parallel readers, a random reader // will get each message. #ifndef CHANNEL_H #define CHANNEL_H // - create a new channel // - destroy channel (not thread-safe..) // - put value in channel (never blocks) // - get value from channel (return nonzero on success, doesn't block) // - get value from channel (blocks until a value is available, or ms milliseconds have elapsed) // returns nonzero on success, zero on timeout typedef struct channel channel; channel* channel_init(int valuesize); void channel_quit(channel *chan); void channel_send(channel *chan, void *value); int channel_recv(channel *chan, void *output); int channel_wait(channel *chan, void *output, int ms); #endif #ifdef CHANNEL_C #pragma once #include #include //#include "async_mutex.c" //#include "async_condv.c" struct channel { int alloc; // allocated entries in *data int next; // index of next written entry int first; // index of first contained entry int used; // number of used entries int size; // size of one value char *data; // queue data mutex mutex; // used to lock the channel condv condv; // used to signal availability of data }; channel* channel_init(int valuesize) { channel* c = (channel*)REALLOC(0, sizeof (channel)); if(!c) abort(); c->size = valuesize; c->alloc = 1; c->next = 0; c->first = 0; c->used = 0; c->data = (char*)REALLOC(0, c->alloc * valuesize); if(!c->data) abort(); mutex_new(&c->mutex); condv_init(&c->condv); return c; } void channel_quit(channel *chan) { mutex_del(&chan->mutex); condv_quit(&chan->condv); REALLOC(chan->data, 0); REALLOC(chan, 0); } void channel_send(channel *chan, void *value) { int i; mutex_acquire(&chan->mutex); if(chan->used == chan->alloc) { // need to expand channel buffer i = chan->alloc; // save old buffer size chan->alloc *= 2; chan->data = (char*)REALLOC(chan->data, chan->alloc * chan->size); if(!chan->data) { puts("error: no mem!"); abort(); } // move part of buffer from beginning until the first contained entry // after the previous end of the buffer // [34512] -> // [ 12345 ] memcpy(chan->data + i * chan->size, chan->data, chan->first * chan->size); chan->next = i + chan->first; } memcpy(chan->data + chan->next * chan->size, value, chan->size); chan->next ++; if(chan->next == chan->alloc) chan->next = 0; chan->used ++; condv_emit(&chan->condv); mutex_release(&chan->mutex); } // get value from channel (return nonzero on success, doesn't block) int channel_recv(channel *chan, void *output) { mutex_acquire(&chan->mutex); if(!chan->used) { mutex_release(&chan->mutex); return 0; } memcpy(output, chan->data + chan->first * chan->size, chan->size); chan->first ++; if(chan->first == chan->alloc) chan->first = 0; chan->used --; mutex_release(&chan->mutex); return 1; } // get value from channel (blocks until a value is available, or ms milliseconds have elapsed) // returns nonzero on success, zero on timeout int channel_wait(channel *chan, void *output, int ms) { mutex_acquire(&chan->mutex); if(chan->used) { memcpy(output, chan->data + chan->first * chan->size, chan->size); chan->first ++; if(chan->first == chan->alloc) chan->first = 0; chan->used --; mutex_release(&chan->mutex); return 1; } condv_wait(&chan->condv, &chan->mutex, ms); if(chan->used) { memcpy(output, chan->data + chan->first * chan->size, chan->size); chan->first ++; if(chan->first == chan->alloc) chan->first = 0; chan->used --; mutex_release(&chan->mutex); return 1; } else { mutex_release(&chan->mutex); return 0; } } #endif #if CHANNEL_BENCH #include #include #include #include "async_thread.c" enum { N = 10, M = 1000 * 1000 }; void async_send( void *args ) { channel *c = (channel*)args; for( int i = 0; i < N*M; ++i ) { channel_send(c, &i); } } int main() { double t = omp_get_wtime(); channel *c = channel_init( sizeof(int) ); detach( async_send, c ); for( int i = 0; i < N*M; ++i ) { int answer; channel_wait(c, &answer, 0); assert( answer == i ); } channel_quit(c); t = omp_get_wtime() - t; int ops = (2*N*M)+2; printf("%5.2fs %5.2fM ops/s\n", t, (ops / t) / M); } #endif #if CHANNEL_DEMO #include void pstate(channel *chan) { int i, v; printf("----------------------- chan %p\n", (void *) chan); printf("alloc=%d next=%d first=%d used=%d\n", chan->alloc, chan->next, chan->first, chan->used); printf("["); for(i=0; ialloc; i++) { v = * (int *) (chan->data + i * chan->size); if(v >= 'a' && v <= 'z') printf("%c", v); else printf("."); } printf("]\n"); printf("\n"); } void precv(channel *chan) { int out; int res = channel_wait(chan, &out, 1000); if(!res) { printf("recv fail\n"); } else { printf("recv = %c\n", out); } } int main() { channel *c1 = channel_init(sizeof(int)); int v; pstate(c1); v = 'a'; channel_send(c1, &v); pstate(c1); v = 'b'; channel_send(c1, &v); v = 'c'; channel_send(c1, &v); pstate(c1); precv(c1); precv(c1); pstate(c1); v = 'd'; channel_send(c1, &v); v = 'e'; channel_send(c1, &v); v = 'f'; channel_send(c1, &v); pstate(c1); v = 'g'; channel_send(c1, &v); pstate(c1); precv(c1); precv(c1); precv(c1); precv(c1); precv(c1); precv(c1); // should this fail channel_quit(c1); } #endif ================================================ FILE: vault/syn_condv.c ================================================ // # condition variables ###################################################### // - rlyeh, public domain #ifndef CONDV_H #define CONDV_H typedef union condv { char opaque_cpp_x64_vs2013[16]; char opaque_cpp_x64_vs2015[88]; char opaque_cpp_x64_vs2017[88]; char opaque_cpp_x86_vs2013[8]; char opaque_cpp_x86_vs2015[48]; char opaque_cpp_x86_vs2017[48]; char opaque_win_x64_vs2013[8]; char opaque_win_x64_vs2015[8]; char opaque_win_x64_vs2017[8]; char opaque_win_x86_vs2013[4]; char opaque_win_x86_vs2015[4]; char opaque_win_x86_vs2017[4]; } condv; void condv_init( condv *c ); void condv_quit( condv *c ); void condv_emit( condv *c ); void condv_wait( condv *c, void *mutex, int ms ); #endif #ifdef CONDV_C #pragma once //#include "async_mutex.c" #if defined(__cplusplus) # include # include # include # define CONDV_API "cpp" # define condv_t std::condition_variable_any # define condv_initial(condv) (new (condv) condv_t) # define condv_destroy(condv) ((condv)->~condition_variable_any()) # define condv_signal(condv) ((condv)->notify_one()) # define condv_timedwait(condv,mt,ms) ((condv)->wait_for(std::unique_lock(*(mt)), std::chrono::milliseconds(ms))) #elif defined(__TINYC__) // not supported //# include # define CONDV_API "tcc" # define condv_t int # define condv_initial(condv) ((void)0) # define condv_destroy(condv) ((void)0) # define condv_signal(condv) ((void)0) # define condv_timedwait(condv,mt,ms) ((void)0) #elif defined(_WIN32) //# include # define CONDV_API "win" # define condv_t CONDITION_VARIABLE # define condv_initial(condv) (InitializeConditionVariable((condv))) # define condv_destroy(condv) ((void)0) # define condv_signal(condv) (WakeConditionVariable((condv))) # define condv_timedwait(condv,mt,ms) (SleepConditionVariableCS((condv), (PCRITICAL_SECTION)(mt), (ms) ? (ms) : INFINITE)) #else # include # include # define CONDV_API "pth" # define condv_t pthread_cond_t # define condv_initial(condv) (pthread_cond_init(condv, NULL)) # define condv_destroy(condv) (pthread_cond_destroy(condv)) # define condv_signal(condv) (pthread_cond_signal(condv)) //# define condv_timedwait(condv,mt,ms) (pthread_cond_timedwait(condv,mt,ms)) static int condv_timedwait( condv_t *c, mutex *m, int ms ) { if( !ms ) { pthread_cond_wait(c, m); return 1; } else { struct timeval now; struct timespec timeout; gettimeofday(&now, NULL); timeout.tv_sec = now.tv_sec + ms / 1000; timeout.tv_nsec = (now.tv_usec + (ms % 1000) * 1000) * 1000; if(timeout.tv_nsec > 1000000000) { timeout.tv_nsec -= 1000000000; timeout.tv_sec ++; } return pthread_cond_timedwait(c, m, &timeout); } } #endif typedef int condv__is_large_enough[ sizeof(condv) >= sizeof(condv_t) ]; const char *condv_api = CONDV_API; void condv_init( condv *c ) { condv_initial((condv_t*)c); } void condv_quit( condv *c ) { condv_destroy((condv_t*)c); } void condv_emit( condv *c ) { condv_signal((condv_t*)c); } void condv_wait( condv *c, void *m, int ms ) { condv_timedwait( (condv_t*)c, (mutex*)m, ms ); } #endif #if CONDV_DEMO #include #include #include "async_thread.c" int main() { #ifdef _MSC_VER const char *msc = "vs-deprecated"; /**/ if(_MSC_VER >= 1910) msc = "vs2017"; else if(_MSC_VER >= 1900) msc = "vs2015"; else if(_MSC_VER >= 1800) msc = "vs2013"; else if(_MSC_VER >= 1700) msc = "vs2012"; else if(_MSC_VER >= 1600) msc = "vs2010"; else if(_MSC_VER >= 1500) msc = "vs2008"; else if(_MSC_VER >= 1400) msc = "vs2005"; else if(_MSC_VER >= 1310) msc = "vs2003"; #ifdef _WIN64 printf("char opaque_%s_%s_%s[%d];\n", condv_api, "x64", msc, (int)sizeof(condv_t)); #else printf("char opaque_%s_%s_%s[%d];\n", condv_api, "x32", msc, (int)sizeof(condv_t)); #endif #endif mutex m = {0}; mutex_new( &m ); condv c = {0}; condv_init( &c ); detach( condv_emit, &c ); condv_wait( &c, &m, 1000 ); condv_quit( &c ); assert( puts("ok") >= 0 ); } #endif ================================================ FILE: vault/syn_coro.c ================================================ // from randypgaul https://github.com/RandyGaul/kk_slides (likely public domain) #pragma once typedef struct coroutine_t { float elapsed; int index; } coroutine_t; #define COROUTINE_BEGIN(co) do { switch (co->index) { default: #define COROUTINE_CASE(co, name) case __LINE__: name: co->index = __LINE__; #define COROUTINE_GOTO(co, name) do { goto name; } while (0) #define COROUTINE_EXIT(co) do { goto __co_end; } while (0) #define COROUTINE_YIELD(co) do { co->index = __LINE__; COROUTINE_EXIT(co); case __LINE__:; } while (0) #define COROUTINE_WAIT(co, time, dt) do { case __LINE__: co->index = __LINE__; co->elapsed += dt; if(co->elapsed < time) goto __co_end; co->elapsed = 0; } while (0) #define COROUTINE_END(co) } co->index = 0; __co_end:; } while (0) /* #include #include // Sleep void test1(coroutine_t* co) { static int loop_count = 0; COROUTINE_BEGIN(co); COROUTINE_CASE(co, loop_start); printf("State 0\n"); COROUTINE_YIELD(co); printf("State 1\n"); COROUTINE_YIELD(co); printf("State 2\n"); COROUTINE_YIELD(co); // [...] COROUTINE_GOTO(co, loop_start); COROUTINE_END(co); } int test2(coroutine_t* co, float dt) { int active = 1; printf("%.1f\r", co->elapsed); COROUTINE_BEGIN(co); COROUTINE_WAIT(co, 0.0f, dt); printf("State 0\n"); COROUTINE_WAIT(co, 1.0f, dt); printf("State 1\n"); COROUTINE_WAIT(co, 2.0f, dt); printf("State 2\n"); active = 0; COROUTINE_END(co); return active; } int main() { coroutine_t co1 = {0}; for(int i = 0; i < 6; ++i) test1(&co1); puts("---"); coroutine_t co2 = {0}; while(test2(&co2, 1.0/60)) Sleep(10); // should be 16 instead of 10; but Sleep() precision sucks } */ ================================================ FILE: vault/syn_fiber.c ================================================ /* libco v18 (2016-09-14) author: byuu license: public domain libco is released to the public domain by John MacFarlane (2013-2016) http://byuu.org/library/libco/ ## libco.x86 - Overhead: ~5x - Supported processor(s): 32-bit x86 - Supported compiler(s): any - Supported operating system(s): - Windows - Mac OS X - Linux - BSD ## libco.amd64 - Overhead: ~10x (Windows), ~6x (all other platforms) - Supported processor(s): 64-bit amd64 - Supported compiler(s): any - Supported operating system(s): - Windows - Mac OS X - Linux - BSD ## libco.ppc - Overhead: ~20x - Supported processor(s): 32-bit PowerPC, 64-bit PowerPC - Supported compiler(s): GNU GCC - Supported operating system(s): - Mac OS X - Linux - BSD - Playstation 3 - Note: this module contains compiler flags to enable/disable FPU and Altivec support. ## libco.fiber - Overhead: ~15x - Supported processor(s): Processor independent - Supported compiler(s): any - Supported operating system(s): - Windows ## libco.sjlj - Overhead: ~30x - Supported processor(s): Processor independent - Supported compiler(s): any - Supported operating system(s): - Mac OS X - Linux - BSD - Solaris ## libco.ucontext - Overhead: ~300x (beware!!) - Supported processor(s): Processor independent - Supported compiler(s): any - Supported operating system(s): - Linux - BSD */ #ifndef FIBER_H #define FIBER_H #if defined _MSC_VER || defined __TINYC__ // @r-lyeh #define LIBCO_MP #define LIBCO_MPROTECT #ifndef __thread #define __thread __declspec(thread) #endif #endif #define fiber cothread_t #define fiber_active co_active #define fiber_create co_create #define fiber_delete co_delete #define fiber_switch co_switch // --- typedef void* cothread_t; cothread_t co_active(); cothread_t co_create(unsigned int, void (*)(void)); void co_delete(cothread_t); void co_switch(cothread_t); #endif // FIBER_H // --- #ifdef FIBER_C #pragma once /*[amd64, arm, ppc, x86]: by default, co_swap_function is marked as a text (code) section if not supported, uncomment the below line to use mprotect instead */ /* #define LIBCO_MPROTECT */ /*[amd64]: Win64 only: provides a substantial speed-up, but will thrash XMM regs do not use this unless you are certain your application won't use SSE */ /* #define LIBCO_NO_SSE */ #if defined(__clang__) #pragma clang diagnostic ignored "-Wparentheses" #endif #if defined(__clang__) || defined(__GNUC__) #if defined(__i386__) #include "syn_fiber_x86.h" #elif defined(__amd64__) #include "syn_fiber_amd64.h" #elif defined(__arm__) #include "syn_fiber_arm.h" /* FIXME: co_create crashes on ppc64el */ /*#elif defined(_ARCH_PPC) #include "syn_fiber_ppc.h" */ #elif defined(_WIN32) #include "syn_fiber_win32.h" #else #include "syn_fiber_sjlj.h" #endif #elif defined(_MSC_VER) #if defined(_M_IX86) #include "syn_fiber_x86.h" #elif defined(_M_AMD64) #include "syn_fiber_amd64.h" #else #include "syn_fiber_win32.h" #endif #elif defined(__TINYC__) #include "syn_fiber_win32.h" #else #error "fiber: libco: unsupported processor, compiler or operating system" #endif #ifdef FIBER_DEMO #include #include fiber self; // ~main_thread void my_entry(void) { for(;;) { puts("1"); fiber_switch(self); puts("2"); fiber_switch(self); } exit(0); // unreachable } int main() { self = fiber_active(); fiber coro = fiber_create(1*1024*1024, my_entry); // 1MiB; 64K instead? fiber_switch(coro); fiber_switch(coro); fiber_switch(coro); fiber_delete(coro); } #define main main__ #endif // FIBER_DEMO #endif // FIBER_C ================================================ FILE: vault/syn_fiber_amd64.h ================================================ /* libco.amd64 (2016-09-14) author: byuu license: public domain */ #include #include static THREAD long long co_active_buffer[64]; static THREAD cothread_t co_active_handle = 0; static void (*co_swap)(cothread_t, cothread_t) = 0; #ifdef LIBCO_MPROTECT ALIGNAS(4096) #else SECTION(text) #endif #ifdef _WIN32 /* ABI: Win64 */ static const unsigned char co_swap_function[4096] = { 0x48, 0x89, 0x22, /* mov [rdx],rsp */ 0x48, 0x8b, 0x21, /* mov rsp,[rcx] */ 0x58, /* pop rax */ 0x48, 0x89, 0x6a, 0x08, /* mov [rdx+ 8],rbp */ 0x48, 0x89, 0x72, 0x10, /* mov [rdx+16],rsi */ 0x48, 0x89, 0x7a, 0x18, /* mov [rdx+24],rdi */ 0x48, 0x89, 0x5a, 0x20, /* mov [rdx+32],rbx */ 0x4c, 0x89, 0x62, 0x28, /* mov [rdx+40],r12 */ 0x4c, 0x89, 0x6a, 0x30, /* mov [rdx+48],r13 */ 0x4c, 0x89, 0x72, 0x38, /* mov [rdx+56],r14 */ 0x4c, 0x89, 0x7a, 0x40, /* mov [rdx+64],r15 */ #if !defined(LIBCO_NO_SSE) 0x0f, 0x29, 0x72, 0x50, /* movaps [rdx+ 80],xmm6 */ 0x0f, 0x29, 0x7a, 0x60, /* movaps [rdx+ 96],xmm7 */ 0x44, 0x0f, 0x29, 0x42, 0x70, /* movaps [rdx+112],xmm8 */ 0x48, 0x83, 0xc2, 0x70, /* add rdx,112 */ 0x44, 0x0f, 0x29, 0x4a, 0x10, /* movaps [rdx+ 16],xmm9 */ 0x44, 0x0f, 0x29, 0x52, 0x20, /* movaps [rdx+ 32],xmm10 */ 0x44, 0x0f, 0x29, 0x5a, 0x30, /* movaps [rdx+ 48],xmm11 */ 0x44, 0x0f, 0x29, 0x62, 0x40, /* movaps [rdx+ 64],xmm12 */ 0x44, 0x0f, 0x29, 0x6a, 0x50, /* movaps [rdx+ 80],xmm13 */ 0x44, 0x0f, 0x29, 0x72, 0x60, /* movaps [rdx+ 96],xmm14 */ 0x44, 0x0f, 0x29, 0x7a, 0x70, /* movaps [rdx+112],xmm15 */ #endif 0x48, 0x8b, 0x69, 0x08, /* mov rbp,[rcx+ 8] */ 0x48, 0x8b, 0x71, 0x10, /* mov rsi,[rcx+16] */ 0x48, 0x8b, 0x79, 0x18, /* mov rdi,[rcx+24] */ 0x48, 0x8b, 0x59, 0x20, /* mov rbx,[rcx+32] */ 0x4c, 0x8b, 0x61, 0x28, /* mov r12,[rcx+40] */ 0x4c, 0x8b, 0x69, 0x30, /* mov r13,[rcx+48] */ 0x4c, 0x8b, 0x71, 0x38, /* mov r14,[rcx+56] */ 0x4c, 0x8b, 0x79, 0x40, /* mov r15,[rcx+64] */ #if !defined(LIBCO_NO_SSE) 0x0f, 0x28, 0x71, 0x50, /* movaps xmm6, [rcx+ 80] */ 0x0f, 0x28, 0x79, 0x60, /* movaps xmm7, [rcx+ 96] */ 0x44, 0x0f, 0x28, 0x41, 0x70, /* movaps xmm8, [rcx+112] */ 0x48, 0x83, 0xc1, 0x70, /* add rcx,112 */ 0x44, 0x0f, 0x28, 0x49, 0x10, /* movaps xmm9, [rcx+ 16] */ 0x44, 0x0f, 0x28, 0x51, 0x20, /* movaps xmm10,[rcx+ 32] */ 0x44, 0x0f, 0x28, 0x59, 0x30, /* movaps xmm11,[rcx+ 48] */ 0x44, 0x0f, 0x28, 0x61, 0x40, /* movaps xmm12,[rcx+ 64] */ 0x44, 0x0f, 0x28, 0x69, 0x50, /* movaps xmm13,[rcx+ 80] */ 0x44, 0x0f, 0x28, 0x71, 0x60, /* movaps xmm14,[rcx+ 96] */ 0x44, 0x0f, 0x28, 0x79, 0x70, /* movaps xmm15,[rcx+112] */ #endif 0xff, 0xe0, /* jmp rax */ }; #include static void co_init() { #ifdef LIBCO_MPROTECT DWORD old_privileges; VirtualProtect((void*)co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READ, &old_privileges); #endif } #else /* ABI: SystemV */ static const unsigned char co_swap_function[4096] = { 0x48, 0x89, 0x26, /* mov [rsi],rsp */ 0x48, 0x8b, 0x27, /* mov rsp,[rdi] */ 0x58, /* pop rax */ 0x48, 0x89, 0x6e, 0x08, /* mov [rsi+ 8],rbp */ 0x48, 0x89, 0x5e, 0x10, /* mov [rsi+16],rbx */ 0x4c, 0x89, 0x66, 0x18, /* mov [rsi+24],r12 */ 0x4c, 0x89, 0x6e, 0x20, /* mov [rsi+32],r13 */ 0x4c, 0x89, 0x76, 0x28, /* mov [rsi+40],r14 */ 0x4c, 0x89, 0x7e, 0x30, /* mov [rsi+48],r15 */ 0x48, 0x8b, 0x6f, 0x08, /* mov rbp,[rdi+ 8] */ 0x48, 0x8b, 0x5f, 0x10, /* mov rbx,[rdi+16] */ 0x4c, 0x8b, 0x67, 0x18, /* mov r12,[rdi+24] */ 0x4c, 0x8b, 0x6f, 0x20, /* mov r13,[rdi+32] */ 0x4c, 0x8b, 0x77, 0x28, /* mov r14,[rdi+40] */ 0x4c, 0x8b, 0x7f, 0x30, /* mov r15,[rdi+48] */ 0xff, 0xe0, /* jmp rax */ }; #include #include static void co_init() { #ifdef LIBCO_MPROTECT unsigned long long addr = (unsigned long long)co_swap_function; unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE)); unsigned long long size = (addr - base) + sizeof co_swap_function; mprotect((void*)base, size, PROT_READ | PROT_EXEC); #endif } #endif static void crash() { assert(0); /* called only if cothread_t entrypoint returns */ } cothread_t co_active() { if(!co_active_handle) co_active_handle = &co_active_buffer; return co_active_handle; } cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { cothread_t handle; if(!co_swap) { co_init(); co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; } if(!co_active_handle) co_active_handle = &co_active_buffer; size += 512; /* allocate additional space for storage */ size &= ~15; /* align stack to 16-byte boundary */ if(handle = (cothread_t)malloc(size)) { long long *p = (long long*)((char*)handle + size); /* seek to top of stack */ *--p = (long long)crash; /* crash if entrypoint returns */ *--p = (long long)entrypoint; /* start of function */ *(long long*)handle = (long long)p; /* stack pointer */ } return handle; } void co_delete(cothread_t handle) { free(handle); } void co_switch(cothread_t handle) { register cothread_t co_previous_handle = co_active_handle; co_swap(co_active_handle = handle, co_previous_handle); } ================================================ FILE: vault/syn_fiber_arm.h ================================================ /* libco.arm (2016-09-14) author: byuu license: public domain */ #include #include #include #include static THREAD unsigned long co_active_buffer[64]; static THREAD cothread_t co_active_handle = 0; static void (*co_swap)(cothread_t, cothread_t) = 0; #ifdef LIBCO_MPROTECT ALIGNAS(4096) #else SECTION(text) #endif static const unsigned long co_swap_function[1024] = { 0xe8a16ff0, /* stmia r1!, {r4-r11,sp,lr} */ 0xe8b0aff0, /* ldmia r0!, {r4-r11,sp,pc} */ 0xe12fff1e, /* bx lr */ }; static void co_init() { #ifdef LIBCO_MPROTECT unsigned long addr = (unsigned long)co_swap_function; unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE)); unsigned long size = (addr - base) + sizeof co_swap_function; mprotect((void*)base, size, PROT_READ | PROT_EXEC); #endif } cothread_t co_active() { if(!co_active_handle) co_active_handle = &co_active_buffer; return co_active_handle; } cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { unsigned long* handle = 0; if(!co_swap) { co_init(); co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; } if(!co_active_handle) co_active_handle = &co_active_buffer; size += 256; size &= ~15; if(handle = (unsigned long*)malloc(size)) { unsigned long* p = (unsigned long*)((unsigned char*)handle + size); handle[8] = (unsigned long)p; handle[9] = (unsigned long)entrypoint; } return handle; } void co_delete(cothread_t handle) { free(handle); } __attribute__((target("arm"))) // @smibarber void co_switch(cothread_t handle) { cothread_t co_previous_handle = co_active_handle; co_swap(co_active_handle = handle, co_previous_handle); } ================================================ FILE: vault/syn_fiber_ppc.h ================================================ /* libco.ppc (2016-09-14) author: blargg license: public domain */ #include #include #include #if LIBCO_MPROTECT #include #include #endif /* state format (offsets in 32-bit words) +0 pointer to swap code rest of function descriptor for entry function +8 PC +10 SP special registers GPRs FPRs VRs stack */ enum { state_size = 1024 }; enum { above_stack = 2048 }; enum { stack_align = 256 }; static THREAD cothread_t co_active_handle = 0; /* determine environment */ #define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) /* whether function calls are indirect through a descriptor, or are directly to function */ #ifndef LIBCO_PPCDESC #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) #define LIBCO_PPCDESC 1 #endif #endif #ifdef LIBCO_MPROTECT ALIGNAS(4096) #else SECTION(text) #endif static const uint32_t libco_ppc_code[1024] = { #if LIBCO_PPC64 0x7d000026, /* mfcr r8 */ 0xf8240028, /* std r1,40(r4) */ 0x7d2802a6, /* mflr r9 */ 0xf9c40048, /* std r14,72(r4) */ 0xf9e40050, /* std r15,80(r4) */ 0xfa040058, /* std r16,88(r4) */ 0xfa240060, /* std r17,96(r4) */ 0xfa440068, /* std r18,104(r4) */ 0xfa640070, /* std r19,112(r4) */ 0xfa840078, /* std r20,120(r4) */ 0xfaa40080, /* std r21,128(r4) */ 0xfac40088, /* std r22,136(r4) */ 0xfae40090, /* std r23,144(r4) */ 0xfb040098, /* std r24,152(r4) */ 0xfb2400a0, /* std r25,160(r4) */ 0xfb4400a8, /* std r26,168(r4) */ 0xfb6400b0, /* std r27,176(r4) */ 0xfb8400b8, /* std r28,184(r4) */ 0xfba400c0, /* std r29,192(r4) */ 0xfbc400c8, /* std r30,200(r4) */ 0xfbe400d0, /* std r31,208(r4) */ 0xf9240020, /* std r9,32(r4) */ 0xe8e30020, /* ld r7,32(r3) */ 0xe8230028, /* ld r1,40(r3) */ 0x48000009, /* bl 1 */ 0x7fe00008, /* trap */ 0x91040030, /*1:stw r8,48(r4) */ 0x80c30030, /* lwz r6,48(r3) */ 0x7ce903a6, /* mtctr r7 */ 0xe9c30048, /* ld r14,72(r3) */ 0xe9e30050, /* ld r15,80(r3) */ 0xea030058, /* ld r16,88(r3) */ 0xea230060, /* ld r17,96(r3) */ 0xea430068, /* ld r18,104(r3) */ 0xea630070, /* ld r19,112(r3) */ 0xea830078, /* ld r20,120(r3) */ 0xeaa30080, /* ld r21,128(r3) */ 0xeac30088, /* ld r22,136(r3) */ 0xeae30090, /* ld r23,144(r3) */ 0xeb030098, /* ld r24,152(r3) */ 0xeb2300a0, /* ld r25,160(r3) */ 0xeb4300a8, /* ld r26,168(r3) */ 0xeb6300b0, /* ld r27,176(r3) */ 0xeb8300b8, /* ld r28,184(r3) */ 0xeba300c0, /* ld r29,192(r3) */ 0xebc300c8, /* ld r30,200(r3) */ 0xebe300d0, /* ld r31,208(r3) */ 0x7ccff120, /* mtcr r6 */ #else 0x7d000026, /* mfcr r8 */ 0x90240028, /* stw r1,40(r4) */ 0x7d2802a6, /* mflr r9 */ 0x91a4003c, /* stw r13,60(r4) */ 0x91c40040, /* stw r14,64(r4) */ 0x91e40044, /* stw r15,68(r4) */ 0x92040048, /* stw r16,72(r4) */ 0x9224004c, /* stw r17,76(r4) */ 0x92440050, /* stw r18,80(r4) */ 0x92640054, /* stw r19,84(r4) */ 0x92840058, /* stw r20,88(r4) */ 0x92a4005c, /* stw r21,92(r4) */ 0x92c40060, /* stw r22,96(r4) */ 0x92e40064, /* stw r23,100(r4) */ 0x93040068, /* stw r24,104(r4) */ 0x9324006c, /* stw r25,108(r4) */ 0x93440070, /* stw r26,112(r4) */ 0x93640074, /* stw r27,116(r4) */ 0x93840078, /* stw r28,120(r4) */ 0x93a4007c, /* stw r29,124(r4) */ 0x93c40080, /* stw r30,128(r4) */ 0x93e40084, /* stw r31,132(r4) */ 0x91240020, /* stw r9,32(r4) */ 0x80e30020, /* lwz r7,32(r3) */ 0x80230028, /* lwz r1,40(r3) */ 0x48000009, /* bl 1 */ 0x7fe00008, /* trap */ 0x91040030, /*1:stw r8,48(r4) */ 0x80c30030, /* lwz r6,48(r3) */ 0x7ce903a6, /* mtctr r7 */ 0x81a3003c, /* lwz r13,60(r3) */ 0x81c30040, /* lwz r14,64(r3) */ 0x81e30044, /* lwz r15,68(r3) */ 0x82030048, /* lwz r16,72(r3) */ 0x8223004c, /* lwz r17,76(r3) */ 0x82430050, /* lwz r18,80(r3) */ 0x82630054, /* lwz r19,84(r3) */ 0x82830058, /* lwz r20,88(r3) */ 0x82a3005c, /* lwz r21,92(r3) */ 0x82c30060, /* lwz r22,96(r3) */ 0x82e30064, /* lwz r23,100(r3) */ 0x83030068, /* lwz r24,104(r3) */ 0x8323006c, /* lwz r25,108(r3) */ 0x83430070, /* lwz r26,112(r3) */ 0x83630074, /* lwz r27,116(r3) */ 0x83830078, /* lwz r28,120(r3) */ 0x83a3007c, /* lwz r29,124(r3) */ 0x83c30080, /* lwz r30,128(r3) */ 0x83e30084, /* lwz r31,132(r3) */ 0x7ccff120, /* mtcr r6 */ #endif #ifndef LIBCO_PPC_NOFP 0xd9c400e0, /* stfd f14,224(r4) */ 0xd9e400e8, /* stfd f15,232(r4) */ 0xda0400f0, /* stfd f16,240(r4) */ 0xda2400f8, /* stfd f17,248(r4) */ 0xda440100, /* stfd f18,256(r4) */ 0xda640108, /* stfd f19,264(r4) */ 0xda840110, /* stfd f20,272(r4) */ 0xdaa40118, /* stfd f21,280(r4) */ 0xdac40120, /* stfd f22,288(r4) */ 0xdae40128, /* stfd f23,296(r4) */ 0xdb040130, /* stfd f24,304(r4) */ 0xdb240138, /* stfd f25,312(r4) */ 0xdb440140, /* stfd f26,320(r4) */ 0xdb640148, /* stfd f27,328(r4) */ 0xdb840150, /* stfd f28,336(r4) */ 0xdba40158, /* stfd f29,344(r4) */ 0xdbc40160, /* stfd f30,352(r4) */ 0xdbe40168, /* stfd f31,360(r4) */ 0xc9c300e0, /* lfd f14,224(r3) */ 0xc9e300e8, /* lfd f15,232(r3) */ 0xca0300f0, /* lfd f16,240(r3) */ 0xca2300f8, /* lfd f17,248(r3) */ 0xca430100, /* lfd f18,256(r3) */ 0xca630108, /* lfd f19,264(r3) */ 0xca830110, /* lfd f20,272(r3) */ 0xcaa30118, /* lfd f21,280(r3) */ 0xcac30120, /* lfd f22,288(r3) */ 0xcae30128, /* lfd f23,296(r3) */ 0xcb030130, /* lfd f24,304(r3) */ 0xcb230138, /* lfd f25,312(r3) */ 0xcb430140, /* lfd f26,320(r3) */ 0xcb630148, /* lfd f27,328(r3) */ 0xcb830150, /* lfd f28,336(r3) */ 0xcba30158, /* lfd f29,344(r3) */ 0xcbc30160, /* lfd f30,352(r3) */ 0xcbe30168, /* lfd f31,360(r3) */ #endif #ifdef __ALTIVEC__ 0x7ca042a6, /* mfvrsave r5 */ 0x39040180, /* addi r8,r4,384 */ 0x39240190, /* addi r9,r4,400 */ 0x70a00fff, /* andi. r0,r5,4095 */ 0x90a40034, /* stw r5,52(r4) */ 0x4182005c, /* beq- 2 */ 0x7e8041ce, /* stvx v20,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7ea049ce, /* stvx v21,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7ec041ce, /* stvx v22,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7ee049ce, /* stvx v23,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7f0041ce, /* stvx v24,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7f2049ce, /* stvx v25,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7f4041ce, /* stvx v26,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7f6049ce, /* stvx v27,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7f8041ce, /* stvx v28,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7fa049ce, /* stvx v29,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7fc041ce, /* stvx v30,r0,r8 */ 0x7fe049ce, /* stvx v31,r0,r9 */ 0x80a30034, /*2:lwz r5,52(r3) */ 0x39030180, /* addi r8,r3,384 */ 0x39230190, /* addi r9,r3,400 */ 0x70a00fff, /* andi. r0,r5,4095 */ 0x7ca043a6, /* mtvrsave r5 */ 0x4d820420, /* beqctr */ 0x7e8040ce, /* lvx v20,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7ea048ce, /* lvx v21,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7ec040ce, /* lvx v22,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7ee048ce, /* lvx v23,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7f0040ce, /* lvx v24,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7f2048ce, /* lvx v25,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7f4040ce, /* lvx v26,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7f6048ce, /* lvx v27,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7f8040ce, /* lvx v28,r0,r8 */ 0x39080020, /* addi r8,r8,32 */ 0x7fa048ce, /* lvx v29,r0,r9 */ 0x39290020, /* addi r9,r9,32 */ 0x7fc040ce, /* lvx v30,r0,r8 */ 0x7fe048ce, /* lvx v31,r0,r9 */ #endif 0x4e800420, /* bctr */ }; #if LIBCO_PPCDESC /* function call goes through indirect descriptor */ #define CO_SWAP_ASM(x, y) ((void (*)(cothread_t, cothread_t))(uintptr_t)x)(x, y) #else /* function call goes directly to code */ #define CO_SWAP_ASM(x, y) ((void (*)(cothread_t, cothread_t))(uintptr_t)libco_ppc_code)(x, y) #endif static uint32_t* co_create_(unsigned size, uintptr_t entry) { (void)entry; uint32_t* t = (uint32_t*)malloc(size); #if LIBCO_PPCDESC if(t) { memcpy(t, (void*)entry, sizeof(void*) * 3); /* copy entry's descriptor */ *(const void**)t = libco_ppc_code; /* set function pointer to swap routine */ } #endif return t; } cothread_t co_create(unsigned int size, void (*entry_)(void)) { uintptr_t entry = (uintptr_t)entry_; uint32_t* t = 0; /* be sure main thread was successfully allocated */ if(co_active()) { size += state_size + above_stack + stack_align; t = co_create_(size, entry); } if(t) { uintptr_t sp; int shift; /* save current registers into new thread, so that any special ones will have proper values when thread is begun */ CO_SWAP_ASM(t, t); #if LIBCO_PPCDESC entry = (uintptr_t)*(void**)entry; /* get real address */ #endif /* put stack near end of block, and align */ sp = (uintptr_t)t + size - above_stack; sp -= sp % stack_align; /* on PPC32, we save and restore GPRs as 32 bits. for PPC64, we save and restore them as 64 bits, regardless of the size the ABI uses. so, we manually write pointers at the proper size. we always save and restore at the same address, and since PPC is big-endian, we must put the low byte first on PPC32. */ /* if uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts and don't have to care how many bits uintptr_t is. */ #if LIBCO_PPC64 shift = 16; #else shift = 0; #endif /* set up so entry will be called on next swap */ t[ 8] = (uint32_t)(entry >> shift >> shift); t[ 9] = (uint32_t)entry; t[10] = (uint32_t)(sp >> shift >> shift); t[11] = (uint32_t)sp; } return t; } void co_delete(cothread_t t) { free(t); } static void co_init_(void) { #if LIBCO_MPROTECT long page_size = sysconf(_SC_PAGESIZE); if(page_size > 0) { uintptr_t align = page_size; uintptr_t begin = (uintptr_t)libco_ppc_code; uintptr_t end = begin + sizeof libco_ppc_code; /* align beginning and end */ end += align - 1; end -= end % align; begin -= begin % align; mprotect((void*)begin, end - begin, PROT_READ | PROT_EXEC); } #endif co_active_handle = co_create_(state_size, (uintptr_t)&co_switch); } cothread_t co_active() { if(!co_active_handle) co_init_(); return co_active_handle; } void co_switch(cothread_t t) { cothread_t old = co_active_handle; co_active_handle = t; CO_SWAP_ASM(t, old); } ================================================ FILE: vault/syn_fiber_sjlj.h ================================================ /* libco.sjlj (2008-01-28) author: Nach license: public domain */ /* note this was designed for UNIX systems. Based on ideas expressed in a paper by Ralf Engelschall. for SJLJ on other systems, one would want to rewrite springboard() and co_create() and hack the jmb_buf stack pointer. */ #include #include #include typedef struct { sigjmp_buf context; void (*coentry)(void); void* stack; } cothread_struct; static THREAD cothread_struct co_primary; static THREAD cothread_struct* creating; static THREAD cothread_struct* co_running = 0; static void springboard(int ignored) { if(sigsetjmp(creating->context, 0)) { co_running->coentry(); } } cothread_t co_active() { if(!co_running) co_running = &co_primary; return (cothread_t)co_running; } cothread_t co_create(unsigned int size, void (*coentry)(void)) { if(!co_running) co_running = &co_primary; cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); if(thread) { struct sigaction handler; struct sigaction old_handler; stack_t stack; stack_t old_stack; thread->coentry = thread->stack = 0; stack.ss_flags = 0; stack.ss_size = size; thread->stack = stack.ss_sp = malloc(size); if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) { handler.sa_handler = springboard; handler.sa_flags = SA_ONSTACK; sigemptyset(&handler.sa_mask); creating = thread; if(!sigaction(SIGUSR1, &handler, &old_handler)) { if(!raise(SIGUSR1)) { thread->coentry = coentry; } sigaltstack(&old_stack, 0); sigaction(SIGUSR1, &old_handler, 0); } } if(thread->coentry != coentry) { co_delete(thread); thread = 0; } } return (cothread_t)thread; } void co_delete(cothread_t cothread) { if(cothread) { if(((cothread_struct*)cothread)->stack) { free(((cothread_struct*)cothread)->stack); } free(cothread); } } void co_switch(cothread_t cothread) { if(!sigsetjmp(co_running->context, 0)) { co_running = (cothread_struct*)cothread; siglongjmp(co_running->context, 1); } } ================================================ FILE: vault/syn_fiber_ucontext.h ================================================ /* libco.ucontext (2008-01-28) author: Nach license: public domain */ /* WARNING: the overhead of POSIX ucontext is very high, assembly versions of libco or libco_sjlj should be much faster this library only exists for two reasons: 1: as an initial test for the viability of a ucontext implementation 2: to demonstrate the power and speed of libco over existing implementations, such as pth (which defaults to wrapping ucontext on unix targets) use this library only as a *last resort* */ #define _BSD_SOURCE #include #include static THREAD ucontext_t co_primary; static THREAD ucontext_t* co_running = 0; cothread_t co_active() { if(!co_running) co_running = &co_primary; return (cothread_t)co_running; } cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { if(!co_running) co_running = &co_primary; ucontext_t* thread = (ucontext_t*)malloc(sizeof(ucontext_t)); if(thread) { if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) { thread->uc_link = co_running; thread->uc_stack.ss_size = heapsize; makecontext(thread, coentry, 0); } else { co_delete((cothread_t)thread); thread = 0; } } return (cothread_t)thread; } void co_delete(cothread_t cothread) { if(cothread) { if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); } free(cothread); } } void co_switch(cothread_t cothread) { ucontext_t* old_thread = co_running; co_running = (ucontext_t*)cothread; swapcontext(old_thread, co_running); } ================================================ FILE: vault/syn_fiber_win32.h ================================================ /* libco.win (2008-01-28) authors: Nach, byuu license: public domain */ #ifndef WINVER #define WINVER 0x0400 #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #include static THREAD cothread_t co_active_ = 0; static void __stdcall co_thunk(void* coentry) { ((void (*)(void))coentry)(); } cothread_t co_active() { if(!co_active_) { ConvertThreadToFiber(0); co_active_ = GetCurrentFiber(); } return co_active_; } cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { if(!co_active_) { ConvertThreadToFiber(0); co_active_ = GetCurrentFiber(); } return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry); } void co_delete(cothread_t cothread) { DeleteFiber(cothread); } void co_switch(cothread_t cothread) { co_active_ = cothread; SwitchToFiber(cothread); } ================================================ FILE: vault/syn_fiber_x86.h ================================================ /* libco.x86 (2016-09-14) author: byuu license: public domain */ #include #include #if defined(__clang__) || defined(__GNUC__) #define fastcall __attribute__((fastcall)) #elif defined(_MSC_VER) #define fastcall __fastcall #else #error "libco: please define fastcall macro" #endif static THREAD long co_active_buffer[64]; static THREAD cothread_t co_active_handle = 0; static void (fastcall *co_swap)(cothread_t, cothread_t) = 0; #ifdef LIBCO_MPROTECT ALIGNAS(4096) #else SECTION(text) #endif /* ABI: fastcall */ static const unsigned char co_swap_function[4096] = { 0x89, 0x22, /* mov [edx],esp */ 0x8b, 0x21, /* mov esp,[ecx] */ 0x58, /* pop eax */ 0x89, 0x6a, 0x04, /* mov [edx+ 4],ebp */ 0x89, 0x72, 0x08, /* mov [edx+ 8],esi */ 0x89, 0x7a, 0x0c, /* mov [edx+12],edi */ 0x89, 0x5a, 0x10, /* mov [edx+16],ebx */ 0x8b, 0x69, 0x04, /* mov ebp,[ecx+ 4] */ 0x8b, 0x71, 0x08, /* mov esi,[ecx+ 8] */ 0x8b, 0x79, 0x0c, /* mov edi,[ecx+12] */ 0x8b, 0x59, 0x10, /* mov ebx,[ecx+16] */ 0xff, 0xe0, /* jmp eax */ }; #ifdef _WIN32 #include static void co_init() { #ifdef LIBCO_MPROTECT DWORD old_privileges; VirtualProtect((void*)co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READ, &old_privileges); #endif } #else #include #include static void co_init() { #ifdef LIBCO_MPROTECT unsigned long addr = (unsigned long)co_swap_function; unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE)); unsigned long size = (addr - base) + sizeof co_swap_function; mprotect((void*)base, size, PROT_READ | PROT_EXEC); #endif } #endif static void crash() { assert(0); /* called only if cothread_t entrypoint returns */ } cothread_t co_active() { if(!co_active_handle) co_active_handle = &co_active_buffer; return co_active_handle; } cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { cothread_t handle; if(!co_swap) { co_init(); co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function; } if(!co_active_handle) co_active_handle = &co_active_buffer; size += 256; /* allocate additional space for storage */ size &= ~15; /* align stack to 16-byte boundary */ if(handle = (cothread_t)malloc(size)) { long *p = (long*)((char*)handle + size); /* seek to top of stack */ *--p = (long)crash; /* crash if entrypoint returns */ *--p = (long)entrypoint; /* start of function */ *(long*)handle = (long)p; /* stack pointer */ } return handle; } void co_delete(cothread_t handle) { free(handle); } void co_switch(cothread_t handle) { register cothread_t co_previous_handle = co_active_handle; co_swap(co_active_handle = handle, co_previous_handle); } ================================================ FILE: vault/syn_mcmp.c ================================================ // # lockfree queues (multiple consumer-multiple producer) ##################### // License: WTFPL. https://github.com/darkautism/lfqueue // Use -O0 flag to compile (needed?). #ifndef MCMP_H #define MCMP_H struct mcmp; int mcmp_new(struct mcmp *ctx); int mcmp_del(struct mcmp *ctx); int mcmp_add(struct mcmp *ctx, void *data); void *mcmp_pop(struct mcmp *ctx ); #endif #ifdef MCMP_C #pragma once //#include "../memory/memory_realloc.c" //#include "async_atomic.c" #include #include #include struct mcmp_node { void * data; struct mcmp_node *next; }; struct mcmp { struct mcmp_node *head; struct mcmp_node *tail; size_t count; // int }; int mcmp_new(struct mcmp *ctx) { struct mcmp_node * tmpnode = memset( (char*)REALLOC(0,sizeof(struct mcmp_node)), 0, sizeof(struct mcmp_node)); if (!tmpnode) return -errno; memset(ctx,0,sizeof(struct mcmp)); ctx->head=ctx->tail=tmpnode; return 0; } int mcmp_del(struct mcmp *ctx){ if ( ctx->tail && ctx->head ) { // if have data in queue struct mcmp_node * walker = ctx->head, *tmp; while ( walker != ctx->tail ) { // while still have node tmp = walker->next; REALLOC(walker, 0); walker=tmp; } REALLOC(ctx->head, 0); // free the empty node memset(ctx,0,sizeof(struct mcmp)); } return 0; } int mcmp_add(struct mcmp *ctx, void * data) { struct mcmp_node * p; struct mcmp_node * tmpnode = memset( (char*)REALLOC(0,sizeof(struct mcmp_node)), 0, sizeof(struct mcmp_node)); if (!tmpnode) return -errno; tmpnode->data=data; do { p = ctx->tail; if ( __sync_bool_compare_and_swap(&ctx->tail,p,tmpnode)) { p->next=tmpnode; break; } } while(1); __sync_add_and_fetch( &ctx->count, 1); return 0; } void * mcmp_pop(struct mcmp *ctx ) { void * ret=0; struct mcmp_node * p; do { p = ctx->head; } while(p==0 || !__sync_bool_compare_and_swap(&ctx->head,p,0)); if( p->next==0) { ctx->head=p; return 0; } ret=p->next->data; ctx->head=p->next; __sync_add_and_fetch( &ctx->count, -1); REALLOC(p, 0); return ret; } #ifdef MCMP_DEMO #include #include int main() { long ret; struct mcmp ctx; mcmp_new(&ctx); mcmp_add(&ctx,(void *)1); mcmp_add(&ctx,(void *)3); mcmp_add(&ctx,(void *)5); mcmp_add(&ctx,(void *)8); mcmp_add(&ctx,(void *)4); mcmp_add(&ctx,(void *)6); while ( (ret = *(int*)mcmp_pop(&ctx)) != 0 ) printf("lfq_dequeue %ld\n", ret); mcmp_del(&ctx); } #define main main__ #endif // MCMP_DEMO #endif // MCMP_C ================================================ FILE: vault/syn_mutex.c ================================================ // # mutexes ################################################################### // - rlyeh, public domain #ifndef MUTEX_H #define MUTEX_H #include typedef union mutex mutex; void mutex_new(mutex *m); void mutex_del(mutex *m); bool mutex_trylock(mutex *m); void mutex_acquire(mutex *m); void mutex_release(mutex *m); #endif #ifdef MUTEX_C #pragma once union mutex { char opaque_win_x64[40]; char opaque_cpp_x64_vs2015[8]; char opaque_cpp_x64_vs2017[80]; char opaque_cpp_x32_vs2017[48]; }; #if defined(__cplusplus) # include # define MUTEX_API "cpp" # define mutex_t std::recursive_mutex # define mutex_initial_(mutex) (new (mutex) mutex_t) # define mutex_trylock_(mutex) ((mutex)->try_lock()) # define mutex_acquire_(mutex) ((mutex)->lock()) # define mutex_release_(mutex) ((mutex)->unlock()) # define mutex_destroy_(mutex) ((mutex)->~recursive_mutex()) #elif defined(_WIN32) //# include # define MUTEX_API "win" # define mutex_t CRITICAL_SECTION # define mutex_initial_(mutex) (InitializeCriticalSection(mutex)) # define mutex_trylock_(mutex) (TryEnterCriticalSection(mutex)) # define mutex_acquire_(mutex) (EnterCriticalSection(mutex)) # define mutex_release_(mutex) (LeaveCriticalSection(mutex)) # define mutex_destroy_(mutex) (DeleteCriticalSection(mutex)) #else # define _GNU_SOURCE # include # define MUTEX_API "pth" # define mutex_t pthread_mutex_t //# define mutex_initial_(mutex) (pthread_mutex_init(mutex, NULL) # define mutex_trylock_(mutex) (pthread_mutex_trylock(mutex)) # define mutex_acquire_(mutex) (pthread_mutex_lock(mutex)) # define mutex_release_(mutex) (pthread_mutex_unlock(mutex)) # define mutex_destroy_(mutex) (pthread_mutex_destroy(mutex)) static void mutex_initial_(pthread_mutex_t *mutex) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(mutex, &attr); pthread_mutexattr_destroy(&attr); } #endif typedef int mtx_is_large_enough[ sizeof(mutex) >= sizeof(mutex_t) ]; const char *mutex_api = MUTEX_API; void mutex_new(mutex *m) { mutex v = {0}; *m = v; mutex_initial_((mutex_t*)m); } void mutex_del(mutex *m) { mutex_destroy_((mutex_t*)m); } bool mutex_trylock(mutex *m) { return !!mutex_trylock_((mutex_t*)m); } void mutex_acquire(mutex *m) { mutex_acquire_((mutex_t*)m); } void mutex_release(mutex *m) { mutex_release_((mutex_t*)m); } #endif #if MUTEX_BENCH #include #include int main() { enum { N = 100, M = 1000 * 1000 }; double t = omp_get_wtime(); mutex m = {0}; mutex_new(&m); for( int i = 0; i < N*M; ++i ) { mutex_acquire(&m); mutex_release(&m); } mutex_del(&m); t = omp_get_wtime() - t; printf("%5.2fs %6.2fM ops/s (api: %s)\n", t, ((2*N*M+2)/t) / M, mutex_api ); } // 2.87s 69.63M ops/s (api: cpp) // 2.03s 98.59M ops/s (api: win) #endif #if MUTEX_DEMO #include #include int main() { mutex m = {0}; mutex_new(&m); mutex_acquire(&m); mutex_release(&m); assert( mutex_trylock(&m) ); assert( mutex_trylock(&m) ); mutex_release(&m); assert( puts("ok") >= 0 ); } #endif ================================================ FILE: vault/syn_semaphore.c ================================================ // # semaphores ################################################################ // combination of mutex+condvar. used to perform operations on shared data. // - rlyeh, public domain. #ifndef SEMAPHORE_H #define SEMAPHORE_H typedef struct semaphore semaphore; void semaphore_init( semaphore *s ); void semaphore_wait( semaphore *s ); void semaphore_emit( semaphore *s ); void semaphore_quit( semaphore *s ); #endif #ifdef SEMAPHORE_C #pragma once //#include "async_condv.c" //#include "async_mutex.c" struct semaphore { mutex mtx; condv cnd; int alarm; }; void semaphore_init( semaphore *s ) { semaphore v = {0}; *s = v; mutex_new( &s->mtx ); condv_init( &s->cnd ); } void semaphore_wait( semaphore *s ) { mutex_acquire( &s->mtx ); while( !s->alarm ) { condv_wait(&s->cnd, &s->mtx, 0); } s->alarm = 0; mutex_release(&s->mtx); } void semaphore_emit( semaphore *s ) { mutex_acquire(&s->mtx); s->alarm = 1; mutex_release(&s->mtx); condv_emit(&s->cnd); } void semaphore_quit( semaphore *s ) { condv_quit( &s->cnd ); mutex_del( &s->mtx ); semaphore v = {0}; *s = v; } #endif #if SEMAPHORE_DEMO #include #include int main() { semaphore s = {0}; semaphore_init(&s); semaphore_emit(&s); semaphore_quit(&s); assert( puts("ok") >= 0 ); } #endif ================================================ FILE: vault/syn_sleep.c ================================================ // # sleep functions ########################################################### // - rlyeh, public domain #ifndef SLEEP_H #define SLEEP_H void sleep_ns( double ns ); void sleep_us( double us ); void sleep_ms( double ms ); void sleep_ss( double ss ); #endif #ifdef SLEEP_C #pragma once #ifdef _WIN32 //#include #else #include #endif void sleep_ns( double ns ) { if( ns > 0 ) { #ifdef _WIN32 LARGE_INTEGER li; // Windows sleep in 100ns units HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL); li.QuadPart = (LONGLONG)(__int64)(-ns/100); // Negative for relative time SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); #else struct timespec wait = {0}; wait.tv_sec = ns / 1e9; wait.tv_nsec = ns - wait.tv_sec * 1e9; nanosleep(&wait, NULL); #endif } } void sleep_us( double us ) { sleep_ns(us * 1e3); } void sleep_ms( double ms ) { sleep_ns(ms * 1e6); } void sleep_ss( double ss ) { sleep_ns(ss * 1e9); } #endif ================================================ FILE: vault/syn_thread.c ================================================ // # threads ################################################################### // - rlyeh, public domain // // [ref] http://bisqwit.iki.fi/story/howto/openmp/ // [ref] https://github.com/tinycthread/tinycthread/ // [ref] https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 // todo: affinities #ifndef THREAD_H #define THREAD_H #include #include uint64_t thread_self(); void thread_yield(); bool detach( void (*func)(void *), void *arg ); bool thread(int thread_id, void (*func)(void *), void *arg); bool join(int thread_id); #endif #ifdef THREAD_C #pragma once //#include "c_ifdef.c" //#include "async_tls.c" #include #include #ifdef _WIN32 //# include # include // SwitchToThread() # include // _getpid() # define THREAD_API "win" #else # include # include // sched_yield() # include // timespec # include // nanosleep() # include // getpid() # if __linux__ # include # endif # define THREAD_API "pth" #endif #if defined(__cplusplus) # include # undef THREAD_API # define THREAD_API "cpp" #endif #ifndef THREAD_MAX #define THREAD_MAX 1024 #endif const char *thdapi = THREAD_API; uint64_t thread_self() { IFDEF(IOS, return (mach_port_t)pthread_mach_thread_np( pthread_self() ) ); IFDEF(OSX, return (mach_port_t)pthread_mach_thread_np( pthread_self() ) ); IFDEF(LINUX, return (uint64_t)syscall(SYS_gettid) ); IFDEF(WINDOWS, return GetCurrentThreadId() ); IFDEF(ANDROID, return pthread_self() ); // fallback static __thread int id; return (intptr_t)&id; } void thread_yield() { IFDEF(CPP, std::this_thread::yield(), IFDEF(WINDOWS, SwitchToThread(), // also, YieldProcessor(); Sleep(0); SleepEx(0, FALSE); _mm_pause(); pthread_yield() // also, sched_yield(); __asm__ __volatile__("pause" ::: "memory"); nanosleep(); ); ); } #if defined(__cplusplus) static std::thread threads[THREAD_MAX]; bool detach( void (*func)(void *), void *arg ) { return std::thread( [=]{ tls_init(); func(arg); tls_quit(); }).detach(), true; } bool thread(int thread_id, void (*func)(void *), void *arg) { return threads[thread_id] = std::thread( [&]{ tls_init(); func(arg); tls_quit(); } ), true; } bool join(int thread_id) { return threads[thread_id].join(), true; } #else static void *threads[THREAD_MAX]; typedef struct thread_args { void *func; void *args; } thread_args; #ifdef _WIN32 static DWORD WINAPI thread_wrapper(LPVOID opaque) { #else static void *thread_wrapper( void *opaque ) { #endif void (*func)(void *) = ((thread_args *)opaque)->func; void *arg0 = ((thread_args *)opaque)->args; REALLOC( opaque, 0 ); tls_init(); func( arg0 ); tls_quit(); IFNDEF(WINDOWS, pthread_exit((void**)0) ); return 0; } static void* launch( void (*func)(void *), void *args ) { thread_args *ta = (struct thread_args*)REALLOC( 0, sizeof(struct thread_args)); ta->func = func; ta->args = args; IFDEF(WINDOWS, { return CreateThread(NULL, 0, thread_wrapper, (LPVOID)ta, 0, NULL); }, { void *ret; if( pthread_create(&ret, NULL, thread_wrapper, (void *)ta) != 0 ) { *ret = 0; } return ret; }); } bool detach( void (*func)(void *), void *arg ) { void *thr = launch(func, arg); IFDEF(WINDOWS, return CloseHandle(thr) != 0 ? true : false, return pthread_detach(thr) == 0 ? true : false ); } bool thread(int thread_id, void (*func)(void *), void *arg) { return !!(threads[thread_id] = launch(func, arg)); } bool join(int thread_id) { IFDEF(WINDOWS, { if( WaitForSingleObject(threads[thread_id], INFINITE) != WAIT_FAILED ) { return (CloseHandle(threads[thread_id]), true); } return false; }, { void *nil; int err; do { err = pthread_join(threads[thread_id], &nil ); } while( err == EINTR ); return err == 0; } ); } #endif #endif #if THREAD_DEMO #include #include int writes = 0; int N = 1024, M = 1024 * 1024; void write( void *arg ) { for( int j = 0; j < M; ++j ) ++writes; } int main() { double t = omp_get_wtime(); for( int id = 0; id < N; ++id ) { thread( id, write, &writes ); } for( int id = 0; id < N; ++id ) { join( id ); } t = omp_get_wtime() - t; printf("%5.2fs %6.2fM ops/s\n", t, ((N*M)/t) / M ); printf("thread-id %lld, %5.2f%% concurrent-writes found\n", thread_self(), (writes*100.) / (N*M)); } #endif ================================================ FILE: vault/syn_tls.c ================================================ // # thread-local storage ###################################################### // - rlyeh, public domain. #ifndef TLS_H #define TLS_H void tls_add_init( void (*func)(void *arg), void *arg ); void tls_add_quit( void (*func)(void *arg), void *arg ); void tls_init(); void tls_quit(); #endif #ifdef TLS_C #pragma once static struct tls { void (*funs[256])( void *arg ); void *args[256]; int count; } inits = {0}, quits = {0}; void tls_add_init( void (*func)(void *arg), void *arg ) { int with = inits.count++; inits.funs[ with ] = func; inits.args[ with ] = arg; } void tls_add_quit( void (*func)(void *arg), void *arg ) { int with = quits.count++; quits.funs[ with ] = func; quits.args[ with ] = arg; } void tls_init() { for( int i = 0; i < inits.count; ++i ) { inits.funs[i]( inits.args[i] ); } } void tls_quit() { for( int i = inits.count; i-- > 0; ) { quits.funs[i]( quits.args[i] ); } } /* void tlspush(void*); void*tlspop(); __thread any argstack[32] = {0}; __thread void **argptr = argstack; void tlspush(void *arg) { *argptr++ = arg; } void*tlspop() { return *(--argptr); } */ #endif #if TLS_DEMO #include #include "async_thread.c" #include "async_sleep.c" void myinit1(void *arg) { puts("hi1!"); } void myinit2(void *arg) { puts("hi2!"); } void myquit2(void *arg) { puts("bye2!"); } void myquit1(void *arg) { puts("bye1!"); } void hello(void *arg) { puts("hello"); } int main() { tls_add_init( myinit1, 0 ); tls_add_quit( myquit1, 0 ); tls_add_init( myinit2, 0 ); tls_add_quit( myquit2, 0 ); detach( hello, 0 ); sleep_ss(1); thread( 10, hello, 0 ); join(10); } #endif