diff --git a/Makefile b/Makefile index 4fa24ad..3403ed9 100644 --- a/Makefile +++ b/Makefile @@ -28,14 +28,15 @@ OPT_FLAGS?= $(addprefix -march=,$(TARGET_CPU)) -fgraphite -fopenmp -floop-parall CXX_OPT_FLAGS?= $(OPT_FLAGS) # Static analyzer currently generates false positives for C++, enable it only for C -CFLAGS += $(COMMON_FLAGS) --std=gnu17 -fanalyzer +CFLAGS += $(COMMON_FLAGS) --std=gnu17 +#-fanalyzer CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 -felide-constructors LDFLAGS += STRIP=strip RELEASE_COMMON_FLAGS+= -Werror -DEBUG_COMMON_FLAGS+= -fanalyzer +DEBUG_COMMON_FLAGS+= ifneq ($(TARGET_SPEC_FLAGS),no) RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS) diff --git a/include/rngxx.h b/include/rngxx.h index f3ee4dd..c843fbb 100644 --- a/include/rngxx.h +++ b/include/rngxx.h @@ -31,9 +31,13 @@ enum rng_next_flag { RNG_TYQ_CHAOS = 1 << 1, }; // 2 bits +typedef int rng_bool_t; + enum rng_next_type { RNG_TY_BLOB = 0, + RNG_TY_BOOL, // Expects type `rng_bool_t`. + RNG_TY_INT8, RNG_TY_INT16, RNG_TY_INT32, @@ -65,8 +69,15 @@ typedef __typeof( ((struct rng_next_opt*)NULL)->ty ) rng_next_tyq; // Qualified // -- // -- #ifndef _RNGXX_IMPL_ONLY_TYPES + // -- C API functions -- (C++ NO compat) -rng_t* rng_new(enum rng_kind kind, u64 seed[static restrict 1]); +int rng_raw(rng_t* engine, void* restrict output, struct rng_next_opt opt); +void rng_next_bin(rng_t* engine, unsigned char* restrict output, size_t n); +//TODO: Specialised `rng_next` functions +void* rng_next_ty(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags); +void* rng_next_tyb(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, const void* pmin, const void* pmax); + +rng_t* rng_new(enum rng_kind kind, const u64 seed[static restrict 1]); void rng_free(rng_t* rng); // -- // -- diff --git a/include/rngxx.hpp b/include/rngxx.hpp index e396e16..e4a852a 100644 --- a/include/rngxx.hpp +++ b/include/rngxx.hpp @@ -159,8 +159,16 @@ struct Random return std::bit_cast(arr); } + template + inline T next(T min, T max); + template + inline T next(T max); + template inline iterator iter() { return iterator(*this); } //TODO: An iterator that yields `next()` forever. + + template + constexpr inline T max_for() const; protected: //TODO: Should we have _min_* functions too? or just continue to use 0 as the lower bound for next_*(..max)? I think use 0... #define MAX(T, M) constexpr inline virtual T _max_ ## T() const { return M; } @@ -170,7 +178,8 @@ protected: MAXX(32) MAXX(64) #undef MAXX -#undef MAX +#undef MAX +protected: //constexpr inline virtual i8 _max() const { return 100; } // use limits.h stuff instead. // Vectorised versions of `next_bytes()`. These will fall back to that if they are not overriden, but if the implementation has a more efficient way of generating 4/8 bytes of random data it should override these. @@ -205,14 +214,24 @@ protected: } } }; -#define DEFT(T) template<> inline T Random::next< T >() { return next_ ## T(); } #define DEFTT(n) DEFT(i ## n) DEFT(u ## n) - DEFT(bool) - DEFTT(8) - DEFTT(16) - DEFTT(32) - DEFTT(64) +#define DEFT(T) template<> inline T Random::max_for< T >() const { return _max_ ## T(); } +#define DEF \ + DEFTT(8) \ + DEFTT(16) \ + DEFTT(32) \ + DEFTT(64) \ //DEFTT(128) +DEF +#undef DEFT +#define DEFT(T) template<> inline T Random::next< T >() { return next_ ## T(); } \ + template<> inline T Random::next< T >(T min, T max) { return next_ ## T(min, max); } \ + template<> inline T Random::next< T >(T max) { return next_ ## T(max); } +template<> inline bool Random::next() { return next_bool(); } +template<> inline f64 Random::next() { return next_f64(); } +template<> inline f32 Random::next() { return next_f32(); } +DEF +#undef DEF #undef DEFTT #undef DEFT diff --git a/src/capi-bridge.cpp b/src/capi-bridge.cpp index 523a97d..f0a30d7 100644 --- a/src/capi-bridge.cpp +++ b/src/capi-bridge.cpp @@ -5,29 +5,88 @@ #include "capi-bridge.h" +template +static inline consteval bool is_same_any() { + return (std::is_same_v || ...); +} namespace bridge _export(internal) { - void nblob(Random& restrict engine, unsigned char* restrict output, size_t es, size_t l) + template + inline T next_typed(Random& engine, const void* minp = nullptr, const void* maxp = nullptr) + { + if constexpr(!is_same_any()) { + if(minp && maxp) return engine.next(*reinterpret_cast(minp), *reinterpret_cast(maxp)); + else if(maxp) return engine.next(*reinterpret_cast(maxp)); + else if(minp) return engine.next(*reinterpret_cast(minp), engine.max_for()); + } + return engine.next(); + } + + template + inline void next_typed_into(Random& engine, void* restrict output, const void* minp = nullptr, const void* maxp = nullptr) + { + *reinterpret_cast(output) = next_typed(engine, minp, maxp); + } + + inline void nblob(Random& engine, unsigned char* restrict output, size_t es, size_t l) { size_t len = es * l; // full length (bytes) + engine.next_bytes(output, len); } - inline void nblob(Random& restrict engine, unsigned char* restrict output, size_t len) { nblob(engine, output, 1, len); } + inline void nblob(Random& engine, unsigned char* restrict output, size_t len) { nblob(engine, output, 1, len); } + + constexpr inline size_t ty_alignof(rng_next_type ty) + { + switch(ty) + { + case RNG_TY_BOOL: return alignof(bool); + case RNG_TY_INT8: return alignof(i8); + case RNG_TY_INT16: return alignof(i16); + case RNG_TY_INT32: return alignof(i32); + case RNG_TY_INT64: return alignof(i64); + case RNG_TY_F32: return alignof(f32); + case RNG_TY_F64: return alignof(f64); + + case RNG_TY_BLOB: return 0; + default: return 0; + } + } constexpr inline size_t ty_sizeof(rng_next_type ty) { - //TODO: + switch(ty) + { + case RNG_TY_BOOL: return sizeof(bool); + + case RNG_TY_INT8: return sizeof(i8); + case RNG_TY_INT16: return sizeof(i16); + case RNG_TY_INT32: return sizeof(i32); + case RNG_TY_INT64: return sizeof(i64); + case RNG_TY_F32: return sizeof(f32); + case RNG_TY_F64: return sizeof(f64); + + case RNG_TY_BLOB: return 0; + default: return 0; + } } constexpr inline bool ty_signed(rng_next_flag f) { return ! (f & RNG_TYQ_UNSIGNED); } constexpr inline bool ty_signed(const rng_next_tyq& qt) { return ty_signed(qt.mod); } + + template + inline auto next_fun_of(const rng_next_tyq& ty) + { + if(ty_signed(ty)) return &next_typed_into; + else return &next_typed_into; + } } extern "C" { // Internal bridge members _export(internal) - rng_t* RNG_IMPL(mkdriver)(rng_kind kind, u64* restrict seed) + rng_t* RNG_IMPL(mkdriver)(rng_kind kind, const u64* restrict seed) { switch(kind) { @@ -37,22 +96,47 @@ extern "C" { } } _export(internal) - int RNG_IMPL(mnext)(rng_t* restrict engine, void* restrict output, const rng_next_opt* restrict opt) + int RNG_IMPL(mnext)(rng_t* engine, void* restrict output, const rng_next_opt* restrict opt) { if(opt->ty.mod & RNG_TYQ_CHAOS) { - bridge::nblob(*engine, reinterpret_cast(output), bridge::ty_sizeof(opt->ty.type), opt->bound.len); + bridge::nblob(*engine, reinterpret_cast(output), bridge::ty_sizeof(opt->ty.type)); return 1; } switch(opt->ty.type) { case RNG_TY_BLOB: bridge::nblob(*engine, reinterpret_cast(output), opt->bound.len); break; - //TODO: Rest of types + +#define INTTY(bits) case RNG_TY_INT ## bits: \ + bridge::next_fun_of(opt->ty)(*engine, output, opt->bound.range.pmin, opt->bound.range.pmax); break + INTTY(8); + INTTY(16); + INTTY(32); + INTTY(64); +#undef INTTY + case RNG_TY_F32: + bridge::next_typed_into(*engine, output); + break; + case RNG_TY_F64: + bridge::next_typed_into(*engine, output); + break; + case RNG_TY_BOOL: + *reinterpret_cast(output) = bridge::next_typed(*engine, output) ? 1 : 0; + break; default: return 0; } return 1; } + _export(internal) + int RNG_IMPL(m_allocsz_for)(enum rng_next_type ty, size_t* restrict size, size_t* restrict align) + { + int ok = 0; + if(size) ok |= ((*size = bridge::ty_sizeof(ty)) > 0) ? 1 : 0; + if(align) ok |= ((*align = bridge::ty_alignof(ty)) > 0) ? 2 : 0; + return ok; + } + // Direct C interface members void rng_free(rng_t* rng) { diff --git a/src/capi-bridge.h b/src/capi-bridge.h index c4de1cc..82955a0 100644 --- a/src/capi-bridge.h +++ b/src/capi-bridge.h @@ -16,8 +16,10 @@ extern "C" { #endif -rng_t* RNG_IMPL(mkdriver)(enum rng_kind kind, u64* restrict seed) _export(internal); -int RNG_IMPL(mnext)(rng_t* restrict engine, void* restrict output, const struct rng_next_opt* restrict opt) _export(internal); +rng_t* RNG_IMPL(mkdriver)(enum rng_kind kind, const u64* restrict seed) _export(internal); +int RNG_IMPL(mnext)(rng_t* engine, void* restrict output, const struct rng_next_opt* restrict opt) _export(internal); + +int RNG_IMPL(m_allocsz_for)(enum rng_next_type ty, size_t* restrict size, size_t* restrict align) _export(internal); #ifdef __cplusplus } diff --git a/src/capi.c b/src/capi.c index 2903697..0034f57 100644 --- a/src/capi.c +++ b/src/capi.c @@ -13,13 +13,57 @@ if(UNLIKELY(_nn__expr == NULL)) { fprintf(stderr, "fatal (unexpected null pointer): " __VA_ARGS__); abort(); } \ _nn__expr; }) -rng_t* rng_new(enum rng_kind kind, u64 seed[static restrict 1]) +rng_t* rng_new(enum rng_kind kind, const u64 seed[static restrict 1]) { return assert_not_null(RNG_IMPL(mkdriver) (kind, &seed[0]), "invalid kind %d", (int)kind); } // void rng_free() - direct bridge -int rng_next(rng_t* restrict engine, void* restrict output, struct rng_next_opt opt) +int rng_raw(rng_t* engine, void* restrict output, struct rng_next_opt opt) { return RNG_IMPL(mnext)(engine, output, &opt); } + +void rng_next_bin(rng_t* engine, unsigned char* restrict output, size_t n) +{ + RNG_IMPL(mnext)(engine, (void* restrict)output, &(struct rng_next_opt){ + .bound.len = n, + .ty.type = RNG_TY_BLOB, + }); +} + +static void* m_alloc_for(enum rng_next_type ty) +{ + size_t s, a; + switch( RNG_IMPL(m_allocsz_for)(ty, &s, &a) ) + { + case 2: // size 0, align nonzero + case 0: break; + case 1: return malloc(s); + case 3: return aligned_alloc(a, s); + } + return NULL; +} + +void* rng_next_tyb(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, const void* pmin, const void* pmax) +{ + if(! ( output = output ?: m_alloc_for(ty)) ) return NULL; + + RNG_IMPL(mnext)(engine, output, &(struct rng_next_opt){ + .bound.range = { + .pmin = pmin, + .pmax = pmax, + }, + .ty = { + .type = ty, + .mod = flags, + }, + }); + + return output; +} + +void* rng_next_ty(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags) +{ + return rng_next_tyb(engine, output, ty, flags, NULL, NULL); +}