#include #include #include #include #include #include "crand.h" #define internal _export(internal) typedef unsigned short uint48_t[3]; struct jr_state { union { long result; double fresult; }; struct drand48_data data; union { //unsigned short xsubi[3]; uint48_t xsubi; struct { uint64_t xsubh : 48; uint16_t _xsub : 16; }; uint64_t xsubl; } st; }; typedef __typeof(((struct jr_state*)NULL)->st) jr_xsub_t; _Static_assert( sizeof(uint48_t) == (sizeof(uint16_t) * 3), "bad uint48 (ushort[3])"); _Static_assert( sizeof(((struct jr_state*)NULL)->st) == sizeof(uint64_t), "bad uint64 (union st)"); inline static unsigned short* IFUNC_DEF(_jr_st_resolv, (jr_xsub_t* restrict state) __attribute__((const, nonnull, returns_nonnull))); inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, low) (jr_xsub_t* restrict state) { return state->xsubi; } inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, high) (jr_xsub_t* restrict state) { return state->xsubi+1; } __attribute__((const)) inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict state) { // This ifunc is free. // The optimiser recognises the return value of this function at compile time, and discards the unused function, removing the need for any runtime ifunc resolution. // The ifunc `_jr_st_resolv()` is essentially (almost) the same as a symbol-aliased constexpr function. // Ain't that neat? struct jr_state chk = {0}; chk.st.xsubh = JR_MAX; return chk.st._xsub ? & IFUNC_NAME(_jr_st_resolv, high) : & IFUNC_NAME(_jr_st_resolv, low); } internal void _jr_seed(struct jr_state* restrict state, unsigned long with) { state->st.xsubh = with; seed48_r(_jr_st_resolv(&state->st), &state->data); } internal long _jr_proc(struct jr_state* restrict state) { jrand48_r(_jr_st_resolv(&state->st), &state->data, &state->result); return state->result; } internal double _jr_procf(struct jr_state* restrict state) { erand48_r(_jr_st_resolv(&state->st), &state->data, &state->fresult); return state->fresult; } __attribute__((malloc(_jr_free))) internal struct jr_state* _jr_alloc() { struct jr_state* bx = aligned_alloc(_Alignof(struct jr_state), sizeof(struct jr_state)); if(UNLIKELY(!bx)) return NULL; memset(bx, 0, sizeof(struct jr_state)); return bx; } __attribute__((malloc(_jr_free))) internal struct jr_state* _jr_new(unsigned long with) { struct jr_state* state = _jr_alloc(); if(UNLIKELY(!state)) return NULL; _jr_seed(state, with); return state; } internal void _jr_free(struct jr_state* restrict state) { if(LIKELY(state)) free(state); } void __TEST__jr_test() { struct jr_state* st = _jr_alloc(); assert(!st->st._xsub); _jr_seed(st, ~0UL); const volatile unsigned short* res_state = _jr_st_resolv(&st->st); printf("seeded: %lu (full %lu, spill %u). xsubi = [%04x, %04x, %04x) %04x], resolv = [%04x, %04x, %04x) %04x]\n", (uint64_t)st->st.xsubh, st->st.xsubl, st->st._xsub, st->st.xsubi[0], st->st.xsubi[1], st->st.xsubi[2], st->st.xsubi[3], res_state[0], res_state[1], res_state[2], res_state[3]); assert(!st->st._xsub); for(int i=0;i<10;i++) { printf("res: %ld\n", _jr_proc(st)); printf("state: %lu (full %lu, spill %u). xsubi = %p, resolv = %p\n", (uint64_t)st->st.xsubh, st->st.xsubl, st->st._xsub, (const void*)st->st.xsubi, (const void*)_jr_st_resolv(&st->st)); } printf("ended: %lu (full %lu, spill %u). xsubi = [%04x, %04x, %04x) %04x], resolv = [%04x, %04x, %04x) %04x]\n", (uint64_t)st->st.xsubh, st->st.xsubl, st->st._xsub, st->st.xsubi[0], st->st.xsubi[1], st->st.xsubi[2], st->st.xsubi[3], res_state[0], res_state[1], res_state[2], res_state[3]); assert(!st->st._xsub); _jr_free(st); }