diff --git a/core/include/blue/core/init.h b/core/include/blue/core/init.h new file mode 100644 index 0000000..764a68b --- /dev/null +++ b/core/include/blue/core/init.h @@ -0,0 +1,32 @@ +#ifndef BLUELIB_INIT_H_ +#define BLUELIB_INIT_H_ + +#ifdef __cplusplus +#define B_INIT(f) \ + static void f(void); \ + struct f##_t_ { \ + f##_t_(void) \ + { \ + f(); \ + } \ + }; \ + static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSB_VER) +#pragma section(".CRT$XCU", read) +#define B_INIT2_(f, p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker, "/include:" p #f "_")) static void f(void) +#ifdef _WIN64 +#define B_INIT(f) B_INIT2_(f, "") +#else +#define B_INIT(f) B_INIT2_(f, "_") +#endif +#else +#define B_INIT(f) \ + static void f(void) __attribute__((constructor)); \ + static void f(void) +#endif + +#endif diff --git a/core/include/blue/core/misc.h b/core/include/blue/core/misc.h index 31938a2..a99ed46 100644 --- a/core/include/blue/core/misc.h +++ b/core/include/blue/core/misc.h @@ -1,5 +1,5 @@ -#ifndef BLUELIB_MISC_H_ -#define BLUELIB_MISC_H_ +#ifndef BLUELIB_MISB_H_ +#define BLUELIB_MISB_H_ #define b_unbox(type, box, member) \ ((type *_Nonnull)((box) ? (uintptr_t)(box) - (offsetof(type, member)) : 0)) @@ -10,4 +10,4 @@ #define z__b_numargs(arg_type, ...) \ (sizeof((arg_type[]) {__VA_ARGS__}) / sizeof(arg_type)) -#endif // C_MISC_H_ +#endif // B_MISB_H_ diff --git a/core/include/blue/core/random.h b/core/include/blue/core/random.h new file mode 100644 index 0000000..cbe9dc2 --- /dev/null +++ b/core/include/blue/core/random.h @@ -0,0 +1,37 @@ +#ifndef BLUELIB_RANDOM_H_ +#define BLUELIB_RANDOM_H_ + +#include +#include + +struct b_random_algorithm; + +typedef enum b_random_flags { + /* algorithm selection */ + B_RANDOM_MT19937 = 0x01u, + + /* generation flags */ + B_RANDOM_SECURE = 0x100u, +} b_random_flags; + +typedef struct b_random_ctx { + b_random_flags __f; + struct b_random_algorithm *__a; + + union { + struct { + unsigned long long mt[312]; + size_t mti; + } __mt19937; + }; +} b_random_ctx; + +extern b_random_ctx *b_random_global_ctx(void); + +extern b_status b_random_init(b_random_ctx *ctx, b_random_flags flags); +extern unsigned long long b_random_next_int64(b_random_ctx *ctx); +extern double b_random_next_double(b_random_ctx *ctx); +extern void b_random_next_bytes( + b_random_ctx *ctx, unsigned char *out, size_t nbytes); + +#endif diff --git a/core/mt19937.c b/core/mt19937.c new file mode 100644 index 0000000..9c67122 --- /dev/null +++ b/core/mt19937.c @@ -0,0 +1,243 @@ +/* + A C-program for MT19937-64 (2004/9/29 version). + Coded by Takuji Nishimura and Makoto Matsumoto. + + This is a 64-bit version of Mersenne Twister pseudorandom number + generator. + + Before using, initialize the state by using init_genrand64(seed) + or init_by_array64(init_key, key_length). + + Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + References: + T. Nishimura, ``Tables of 64-bit Mersenne Twisters'' + ACM Transactions on Modeling and + Computer Simulation 10. (2000) 348--357. + M. Matsumoto and T. Nishimura, + ``Mersenne Twister: a 623-dimensionally equidistributed + uniform pseudorandom number generator'' + ACM Transactions on Modeling and + Computer Simulation 8. (Jan. 1998) 3--30. + + Any feedback is very welcome. + http://www.math.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove spaces) +*/ + +#include "random.h" + +#include + +#define NN 312 +#define MM 156 +#define MATRIX_A 0xB5026F5AA96619E9ULL +#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ +#define LM 0x7FFFFFFFULL /* Least significant 31 bits */ + +/* initializes mt[NN] with a seed */ +static void init_genrand64(struct b_random_ctx *context, unsigned long long seed) +{ + context->__mt19937.mt[0] = seed; + for (context->__mt19937.mti = 1; context->__mt19937.mti < NN; + context->__mt19937.mti++) + context->__mt19937.mt[context->__mt19937.mti] + = (6364136223846793005ULL + * (context->__mt19937.mt[context->__mt19937.mti - 1] + ^ (context->__mt19937 + .mt[context->__mt19937.mti - 1] + >> 62)) + + context->__mt19937.mti); +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +static void init_by_array64( + struct b_random_ctx *context, unsigned long long init_key[], + unsigned long long key_length) +{ + unsigned long long i, j, k; + init_genrand64(context, 19650218ULL); + i = 1; + j = 0; + k = (NN > key_length ? NN : key_length); + for (; k; k--) { + context->__mt19937.mt[i] + = (context->__mt19937.mt[i] + ^ ((context->__mt19937.mt[i - 1] + ^ (context->__mt19937.mt[i - 1] >> 62)) + * 3935559000370003845ULL)) + + init_key[j] + j; /* non linear */ + i++; + j++; + if (i >= NN) { + context->__mt19937.mt[0] = context->__mt19937.mt[NN - 1]; + i = 1; + } + if (j >= key_length) + j = 0; + } + for (k = NN - 1; k; k--) { + context->__mt19937.mt[i] + = (context->__mt19937.mt[i] + ^ ((context->__mt19937.mt[i - 1] + ^ (context->__mt19937.mt[i - 1] >> 62)) + * 2862933555777941757ULL)) + - i; /* non linear */ + i++; + if (i >= NN) { + context->__mt19937.mt[0] = context->__mt19937.mt[NN - 1]; + i = 1; + } + } + + context->__mt19937.mt[0] + = 1ULL << 63; /* MSB is 1; assuring non-zero initial array */ +} + +/* generates a random number on [0, 2^64-1]-interval */ +static unsigned long long genrand64_int64(struct b_random_ctx *context) +{ +#if 0 + /* This is the original implementation. It is replaced by the alternate implementation, below. */ + int i; + unsigned long long x; + static unsigned long long mag01[2]={0ULL, MATRIX_A}; + + if (mti >= NN) { /* generate NN words at one time */ + + /* if init_genrand64() has not been called, */ + /* a default initial seed is used */ + if (mti == NN+1) + init_genrand64(5489ULL); + + for (i=0;i>1) ^ mag01[(int)(x&1ULL)]; + } + for (;i>1) ^ mag01[(int)(x&1ULL)]; + } + x = (mt[NN-1]&UM)|(mt[0]&LM); + mt[NN-1] = mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; + + mti = 0; + } + + x = mt[mti++]; + + x ^= (x >> 29) & 0x5555555555555555ULL; + x ^= (x << 17) & 0x71D67FFFEDA60000ULL; + x ^= (x << 37) & 0xFFF7EEE000000000ULL; + x ^= (x >> 43); + + return x; +#else + /* This is the altered Cocoa with Love implementation. */ + size_t i; + size_t j; + unsigned long long result; + + if (context->__mt19937.mti >= NN) { /* generate NN words at one time */ + size_t mid = NN / 2; + unsigned long long stateMid = context->__mt19937.mt[mid]; + unsigned long long x; + unsigned long long y; + + /* NOTE: this "untwist" code is modified from the original to + * improve performance, as described here: + * http://www.cocoawithlove.com/blog/2016/05/19/random-numbers.html + * These modifications are offered for use under the original + * icense at the top of this file. + */ + for (i = 0, j = mid; i != mid - 1; i++, j++) { + x = (context->__mt19937.mt[i] & UM) + | (context->__mt19937.mt[i + 1] & LM); + context->__mt19937.mt[i] + = context->__mt19937.mt[i + mid] ^ (x >> 1) + ^ ((context->__mt19937.mt[i + 1] & 1) * MATRIX_A); + y = (context->__mt19937.mt[j] & UM) + | (context->__mt19937.mt[j + 1] & LM); + context->__mt19937.mt[j] + = context->__mt19937.mt[j - mid] ^ (y >> 1) + ^ ((context->__mt19937.mt[j + 1] & 1) * MATRIX_A); + } + x = (context->__mt19937.mt[mid - 1] & UM) | (stateMid & LM); + context->__mt19937.mt[mid - 1] = context->__mt19937.mt[NN - 1] + ^ (x >> 1) + ^ ((stateMid & 1) * MATRIX_A); + y = (context->__mt19937.mt[NN - 1] & UM) + | (context->__mt19937.mt[0] & LM); + context->__mt19937.mt[NN - 1] + = context->__mt19937.mt[mid - 1] ^ (y >> 1) + ^ ((context->__mt19937.mt[0] & 1) * MATRIX_A); + + context->__mt19937.mti = 0; + } + + result = context->__mt19937.mt[context->__mt19937.mti]; + context->__mt19937.mti = context->__mt19937.mti + 1; + + result ^= (result >> 29) & 0x5555555555555555ULL; + result ^= (result << 17) & 0x71D67FFFEDA60000ULL; + result ^= (result << 37) & 0xFFF7EEE000000000ULL; + result ^= (result >> 43); + + return result; +#endif +} + +/* generates a random number on [0,1]-real-interval */ +double genrand64_real1(struct b_random_ctx *context) +{ + return (genrand64_int64(context) >> 11) * (1.0 / 9007199254740991.0); +} + +/* generates a random number on [0,1)-real-interval */ +double genrand64_real2(struct b_random_ctx *context) +{ + return (genrand64_int64(context) >> 11) * (1.0 / 9007199254740992.0); +} + +/* generates a random number on (0,1)-real-interval */ +double genrand64_real3(struct b_random_ctx *context) +{ + return ((genrand64_int64(context) >> 12) + 0.5) + * (1.0 / 4503599627370496.0); +} + +struct b_random_algorithm z__b_gen_mt19937 = { + .gen_name = "mt19937", + .gen_init = init_genrand64, + .gen_getrand = genrand64_int64, +}; diff --git a/core/random.c b/core/random.c new file mode 100644 index 0000000..20408bc --- /dev/null +++ b/core/random.c @@ -0,0 +1,103 @@ +#include "random.h" + +#include + +#define GET_ALGORITHM_ID(flags) ((flags) & 0xFF) + +extern struct b_random_algorithm z__b_gen_mt19937; + +static struct b_random_algorithm *generators[] = { + [B_RANDOM_MT19937] = &z__b_gen_mt19937, +}; +static size_t nr_generators = sizeof generators / sizeof generators[0]; + +static struct b_random_ctx g_global_ctx = {0}; + +static void init_global_ctx(void) +{ + b_random_init(&g_global_ctx, B_RANDOM_MT19937); +} + +struct b_random_ctx *b_random_global_ctx(void) +{ + if (!g_global_ctx.__f) { + init_global_ctx(); + } + + return &g_global_ctx; +} + +b_status b_random_init(struct b_random_ctx *ctx, b_random_flags flags) +{ + unsigned int algorithm_id = GET_ALGORITHM_ID(flags); + if (algorithm_id >= nr_generators || !generators[algorithm_id]) { + return B_ERR_INVALID_ARGUMENT; + } + + struct b_random_algorithm *algorithm = generators[algorithm_id]; + + ctx->__f = flags; + ctx->__a = algorithm; + + unsigned long long seed; + if (flags & B_RANDOM_SECURE) { + seed = z__b_platform_random_seed_secure(); + } else { + seed = z__b_platform_random_seed(); + } + + algorithm->gen_init(ctx, seed); + + return B_SUCCESS; +} + +unsigned long long b_random_next_int64(struct b_random_ctx *ctx) +{ + if (!ctx->__a || !ctx->__a->gen_getrand) { + return 0; + } + + return ctx->__a->gen_getrand(ctx); +} + +double b_random_next_double(struct b_random_ctx *ctx) +{ + unsigned long long v = b_random_next_int64(ctx); + if (!v) { + return 0.0; + } + + return (double)(v >> 11) * (1.0 / 9007199254740991.0); +} + +void b_random_next_bytes(struct b_random_ctx *ctx, unsigned char *out, size_t nbytes) +{ + size_t n_qwords = 0; + n_qwords = nbytes >> 3; + + if ((n_qwords << 3) < nbytes) { + n_qwords++; + } + + size_t bytes_written = 0; + for (size_t i = 0; i < n_qwords; i++) { + uint64_t *dest = (uint64_t *)&out[bytes_written]; + *dest = b_random_next_int64(ctx); + bytes_written += 8; + } + + if (bytes_written == nbytes) { + return; + } + + uint64_t padding = b_random_next_int64(ctx); + unsigned char *padding_bytes = (unsigned char *)&padding; + + for (size_t i = 0; i < 8; i++) { + out[bytes_written++] = padding_bytes[i]; + + if (bytes_written == nbytes) { + break; + } + } +} diff --git a/core/random.h b/core/random.h new file mode 100644 index 0000000..c5d5422 --- /dev/null +++ b/core/random.h @@ -0,0 +1,17 @@ +#ifndef _BLUELIB_RANDOM_H_ +#define _BLUELIB_RANDOM_H_ + +#include + +struct b_random_ctx; + +struct b_random_algorithm { + const char *gen_name; + void(*gen_init)(struct b_random_ctx *, unsigned long long seed); + uint64_t(*gen_getrand)(struct b_random_ctx *); +}; + +extern uint64_t z__b_platform_random_seed(); +extern uint64_t z__b_platform_random_seed_secure(); + +#endif diff --git a/core/sys/darwin/random.c b/core/sys/darwin/random.c new file mode 100644 index 0000000..7effc91 --- /dev/null +++ b/core/sys/darwin/random.c @@ -0,0 +1,35 @@ +#include +#include +#include + +uint64_t z__b_platform_random_seed() +{ + int fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) { + return (uint64_t)-1; + } + + uint64_t v = 0; + if (read(fd, &v, sizeof v) != sizeof v) { + close(fd); + return (uint64_t)-1; + } + + return v; +} + +uint64_t z__b_platform_random_seed_secure() +{ + int fd = open("/dev/random", O_RDONLY); + if (fd == -1) { + return (uint64_t)-1; + } + + uint64_t v = 0; + if (read(fd, &v, sizeof v) != sizeof v) { + close(fd); + return (uint64_t)-1; + } + + return v; +} diff --git a/core/sys/linux/random.c b/core/sys/linux/random.c new file mode 100644 index 0000000..63172ec --- /dev/null +++ b/core/sys/linux/random.c @@ -0,0 +1,35 @@ +#include +#include +#include + +uint64_t z__c_platform_random_seed() +{ + int fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) { + return (uint64_t)-1; + } + + uint64_t v = 0; + if (read(fd, &v, sizeof v) != sizeof v) { + close(fd); + return (uint64_t)-1; + } + + return v; +} + +uint64_t z__c_platform_random_seed_secure() +{ + int fd = open("/dev/random", O_RDONLY); + if (fd == -1) { + return (uint64_t)-1; + } + + uint64_t v = 0; + if (read(fd, &v, sizeof v) != sizeof v) { + close(fd); + return (uint64_t)-1; + } + + return v; +}