// 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 #include #include #include #include #include "capi-bridge.h" template static inline consteval bool is_same_any() { return (std::is_same_v || ...); } namespace bridge _export(internal) { 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 constexpr(!is_same_any()) if(minp)return engine.next(*reinterpret_cast(minp), engine.max_for()); } } return engine.next(); } template inline void rep_ptr(void* vptr, size_t n, auto lambda) { T* ptr = reinterpret_cast(vptr); if(!n) lambda(ptr); else while( n --> 0 ) lambda(ptr++); } 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); } template 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(engine, output, minp, maxp); T* restrict ro = reinterpret_cast(output); while( rep --> 0 ) next_typed_into(engine, reinterpret_cast(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 inline auto next_fun_of(const rng_next_tyq& ty) { if(ty_signed(ty)) return &next_typed_into_n; //ty.hasflag(ty, RNG_TYQ_ARRAY) ? &next_typed_into_n : &next_typed_into; else return &next_typed_into_n; //ty.hasflag(ty, RNG_TYQ_ARRAY) ? &next_typed_into_n : &next_typed_into; } 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*>( 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(output), bridge::ty_sizeof(opt->ty.type)); return 1; } const size_t array = bridge::ty_hasflag(opt->ty, RNG_TYQ_ARRAY) ? static_cast(opt->ex.array_size) : 0; switch(opt->ty.type) { case RNG_TY_BLOB: bridge::nblob(*engine, reinterpret_cast(output), opt->bound.len); break; #define INTTY(bits) case RNG_TY_INT ## bits: \ bridge::next_fun_of(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(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax); break; case RNG_TY_F64: bridge::next_typed_into_n(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax); break; case RNG_TY_BOOL: bridge::rep_ptr(output, array, [&](rng_bool_t* restrict output) { *output = bridge::next_typed(*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(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(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(); if(fun) return *fun; } } return nullptr; } }