C API added

(testing needed)

Fortune for rngxx's current commit: Half blessing − 半吉
capi
Avril 3 years ago
parent ed1f1d5f03
commit 29187574be
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -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)

@ -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);
// -- // --

@ -159,8 +159,16 @@ struct Random
return std::bit_cast<T>(arr);
}
template<typename T>
inline T next(T min, T max);
template<typename T>
inline T next(T max);
template<typename T>
inline iterator<T> iter() { return iterator(*this); } //TODO: An iterator that yields `next<T>()` forever.
template<typename T>
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<bool>() { return next_bool(); }
template<> inline f64 Random::next<f64>() { return next_f64(); }
template<> inline f32 Random::next<f32>() { return next_f32(); }
DEF
#undef DEF
#undef DEFTT
#undef DEFT

@ -5,29 +5,88 @@
#include "capi-bridge.h"
template<typename T, typename... Args>
static inline consteval bool is_same_any() {
return (std::is_same_v<T, Args> || ...);
}
namespace bridge _export(internal)
{
void nblob(Random& restrict engine, unsigned char* restrict output, size_t es, size_t l)
template<typename T>
inline T next_typed(Random& engine, const void* minp = nullptr, const void* maxp = nullptr)
{
if constexpr(!is_same_any<T, f32, f64, bool>()) {
if(minp && maxp) return engine.next(*reinterpret_cast<const T*>(minp), *reinterpret_cast<const T*>(maxp));
else if(maxp) return engine.next(*reinterpret_cast<const T*>(maxp));
else if(minp) return engine.next(*reinterpret_cast<const T*>(minp), engine.max_for<T>());
}
return engine.next<T>();
}
template<typename T>
inline void next_typed_into(Random& engine, void* restrict output, const void* minp = nullptr, const void* maxp = nullptr)
{
*reinterpret_cast<T* restrict>(output) = next_typed<T>(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<typename S, typename U>
inline auto next_fun_of(const rng_next_tyq& ty)
{
if(ty_signed(ty)) return &next_typed_into<S>;
else return &next_typed_into<U>;
}
}
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<unsigned char*>(output), bridge::ty_sizeof(opt->ty.type), opt->bound.len);
bridge::nblob(*engine, reinterpret_cast<unsigned char*>(output), bridge::ty_sizeof(opt->ty.type));
return 1;
}
switch(opt->ty.type)
{
case RNG_TY_BLOB: bridge::nblob(*engine, reinterpret_cast<unsigned char*>(output), opt->bound.len); break;
//TODO: Rest of types
#define INTTY(bits) case RNG_TY_INT ## bits: \
bridge::next_fun_of<i ## bits, u ## bits>(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<f32>(*engine, output);
break;
case RNG_TY_F64:
bridge::next_typed_into<f64>(*engine, output);
break;
case RNG_TY_BOOL:
*reinterpret_cast<rng_bool_t*>(output) = bridge::next_typed<bool>(*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)
{

@ -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
}

@ -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);
}

Loading…
Cancel
Save