You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rngxx/src/capi-bridge.cpp

232 lines
6.7 KiB

// Contains the work-doing code for the C API interface, which should be exported to the C API shim in the header "capi-bridge.h"
#include <cstdio>
#include <rngxx.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include <init.hpp>
#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)
{
template<typename T>
inline T next_typed(Random& engine, const void* minp = nullptr, const void* maxp = nullptr)
{
if constexpr(!is_same_any<T, 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 constexpr(!is_same_any<T, f32, f64>())
if(minp)return engine.next(*reinterpret_cast<const T*>(minp), engine.max_for<T>());
}
}
return engine.next<T>();
}
template<typename T>
inline void rep_ptr(void* vptr, size_t n, auto lambda)
{
T* ptr = reinterpret_cast<T*>(vptr);
if(!n) lambda(ptr);
else while( n --> 0 ) lambda(ptr++);
}
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);
}
template<typename T>
inline void next_typed_into_n(Random& engine, void* restrict output, size_t rep = 0, const void* minp = nullptr, const void* maxp = nullptr)
{
if(!rep) return next_typed_into<T>(engine, output, minp, maxp);
T* restrict ro = reinterpret_cast<T*>(output);
while( rep --> 0 )
next_typed_into<T>(engine, reinterpret_cast<void*>(ro++), 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& 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(rng_bool_t);
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)
{
switch(ty)
{
case RNG_TY_BOOL: return sizeof(rng_bool_t);
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); }
constexpr inline bool ty_hasflag(rng_next_flag hay, rng_next_flag nee) { return !!(hay & nee); }
constexpr inline bool ty_hasflag(const rng_next_tyq& q, rng_next_flag nee) { return ty_hasflag(q.mod, nee); }
template<typename S, typename U>
inline auto next_fun_of(const rng_next_tyq& ty)
{
if(ty_signed(ty)) return &next_typed_into_n<S>; //ty.hasflag(ty, RNG_TYQ_ARRAY) ? &next_typed_into_n<S> : &next_typed_into<S>;
else return &next_typed_into_n<U>; //ty.hasflag(ty, RNG_TYQ_ARRAY) ? &next_typed_into_n<U> : &next_typed_into<U>;
}
auto dc_lookup(const char* name)
{
namespace init = rng::init;
try {
auto& ctor = init::get(name);
return &ctor;
} catch(init::DCLookupFailed& f) {
#ifdef DEBUG
fprintf(stderr, "Lookup failed for `%s' (%s)\n", name, f.name.c_str());
#else
(void)f;
#endif
return reinterpret_cast<std::remove_reference_t<decltype(init::get(""))>*>( 0 ); //auto deduction fails here?
}
}
}
extern "C" {
// Internal bridge members
_export(internal)
rng_t* RNG_IMPL(mkdriver)(rng_kind kind, const u64* restrict seed)
{
switch(kind)
{
case RNG_KIND_CRAND:
return new rng::crand(seed[0]);
case RNG_KIND_SM64:
return new rng::sm64(seed[0]);
default: return NULL;
}
}
_export(internal)
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));
return 1;
}
const size_t array = bridge::ty_hasflag(opt->ty, RNG_TYQ_ARRAY)
? static_cast<size_t>(opt->ex.array_size)
: 0;
switch(opt->ty.type)
{
case RNG_TY_BLOB: bridge::nblob(*engine, reinterpret_cast<unsigned char*>(output), opt->bound.len); break;
#define INTTY(bits) case RNG_TY_INT ## bits: \
bridge::next_fun_of<i ## bits, u ## bits>(opt->ty)(*engine, output, array, 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_n<f32>(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax);
break;
case RNG_TY_F64:
bridge::next_typed_into_n<f64>(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax);
break;
case RNG_TY_BOOL:
bridge::rep_ptr<rng_bool_t>(output, array, [&](rng_bool_t* restrict output) {
*output = bridge::next_typed<bool>(*engine) ? 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)
{
delete rng;
}
_export(internal)
rng_t* RNG_IMPL(dynctor_trycall)(const char* name, const void* seed)
{
auto cptr = bridge::dc_lookup(name);
if(LIKELY(cptr)) return (*cptr)(seed);
else return nullptr;
}
_export(internal)
rng_t* RNG_IMPL(dynctor_callref)(struct rng_dyn_ctor* ptr, const void* seed)
{
auto& ctor = *reinterpret_cast<decltype(bridge::dc_lookup(""))>(ptr);
return ctor(seed);
}
_export(internal)
rng_dyn_ctor* RNG_IMPL(dynctor_refer)(const char* name)
{
auto ctor = bridge::dc_lookup(name);
return reinterpret_cast<rng_dyn_ctor*>(ctor);
}
rng_ctor_fn rng_ctor(const char* name)
{
auto cptr = bridge::dc_lookup(name);
if(LIKELY(cptr)) {
auto& ctor = *cptr;
if(ctor) {
rng_ctor_fn* fun = ctor.target<rng_ctor_fn>();
if(fun) return *fun;
}
}
return nullptr;
}
}