Compare commits

...

19 Commits
capi ... master

Author SHA1 Message Date
Avril 3461794a68
Changed ifunc resolver for _jr_resolv to use the `static const` initialiser in the new macro-based constant expression xsubi endianess resolver. The ifunc resolver always had resolved the correct function at compile-time and consists of a single `jmp` into it, this doesn"t change anything but make it consistent with the constant expression resolver"s methods (which do matter for that one.)
3 years ago
Avril 045cb7189b
Added compile-time (macro) determiation of endianess shift needed for jr_state"s bitfield over xsubi. Reduces code size and removes 2 levels of indirection.
3 years ago
Avril bd22305f77
started: Adding read_only access qualifier to params of internal X-TU function protos.
3 years ago
Avril 8eb88a65b4
Added attr retain to dynctors. Dunno if this does anything, but it doesn"t complain and still works.
3 years ago
Avril 4da128e510
Moved dynctor registration into `rng.cpp` (seems to work in *this* TU too. Why not `rng/sm64.cpp` specifically???? very annoying.)
3 years ago
Avril 3165ef13fd
Hack to work arround dynctor not being registered when not defined in `crand.cpp" (no idea why, the symbol exists. looking into it.)
3 years ago
Avril dfb3d74816
Added SM64 engine to C hard-coded new API
3 years ago
Avril 8a0ecc0e32
XXX: Why is sm64"s dynctor not being called?
3 years ago
Avril d38f0ebd62
Cannot register sm64 dyn constructor for some reason...
3 years ago
Avril 86b855bda3
Added seeding interface.
3 years ago
Avril d9e34e7e70
Start: Seeding API
3 years ago
Avril 3ff3db0e3e
C++ iterators are absolute retarded shite. Their ```design""" makes no sense.
3 years ago
Avril b071cbb1ad
Added `nexta[m]` array handling functions.
3 years ago
Avril 5a4b68ab3f
Started implementing next for arrays.
3 years ago
Avril 1cf5ca05b2 Added noexcept to global dtor & ctor
3 years ago
Avril 4b988ba83b Added name-based runtime engine lookup.
3 years ago
Avril ebc16f5638 C API: Added individual primitive types `rng_next_*()`
3 years ago
Avril 12f5e7dd97
Better debugging of test program
3 years ago
Avril 25c86023a9
Fix common.h type alias issue
3 years ago

1
.gitignore vendored

@ -5,3 +5,4 @@ vgcore.*
*-test
*.so*
*.a
rngrx

@ -3,7 +3,7 @@
PROJECT=rngxx
AUTHOR=Avril (Flanchan) <flanchan@cumallover.me>
VERSION_MAJOR=0
VERSION_MAJOR=1
VERSION_MINOR=1
VERSION=$(VERSION_MAJOR).$(VERSION_MINOR)
@ -164,6 +164,8 @@ uninstall:
-rmdir $(DESTDIR)$(PREFIX)/include/$(PROJECT)/internal
-rmdir $(DESTDIR)$(PREFIX)/include/$(PROJECT)
$(PROJECT)-test: lib$(PROJECT).so
$(CC) $(CFLAGS) src/test/*.c -o $@ -l:$< -lfmt -Wl,-flto -Wl,-O3 $(LDFLAGS)
$(PROJECT)-test: LDFLAGS+= -lfmt -lstdc++
$(PROJECT)-test: CFLAGS+= -Og -g
$(PROJECT)-test: lib$(PROJECT)-debug.a
$(CC) $(CFLAGS) src/test/*.c -o $@ -l:$< $(LDFLAGS)
-valgrind ./$@

@ -22,6 +22,7 @@ typedef struct Random rng_t;
enum rng_kind {
RNG_KIND_CRAND,
RNG_KIND_SM64,
};
enum rng_next_flag {
@ -29,7 +30,8 @@ enum rng_next_flag {
RNG_TYQ_UNSIGNED = 1 << 0,
RNG_TYQ_CHAOS = 1 << 1,
}; // 2 bits
RNG_TYQ_ARRAY = 1 << 2,
}; // 3 bits
typedef int rng_bool_t;
@ -60,26 +62,103 @@ struct rng_next_opt {
struct {
enum rng_next_type type : 4;
enum rng_next_flag mod : 2;
uint8_t _unused : 2;
} ty; // 8 bits
enum rng_next_flag mod : 3;
uint8_t _unused0 : 1;
//first byte
uint32_t _padding : 24;
} ty; // 32 bits :/
union {
uint32_t array_size;
} ex; // 32 bits. fills hole
};
typedef __typeof( ((struct rng_next_opt*)NULL)->ty ) rng_next_tyq; // Qualified type
typedef rng_t* (*rng_ctor_fn)(const void*);
typedef struct rng_dyn_ctor* rng_dyn_ctor_ref;
// -- // --
#ifndef _RNGXX_IMPL_ONLY_TYPES
_Static_assert(sizeof(rng_next_tyq) == sizeof(uint32_t));
_Static_assert(sizeof( ((struct rng_next_opt*)NULL)->ex ) == sizeof(uint32_t));
// -- C API functions -- (C++ NO compat)
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);
void* rng_next_ty(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags);
void* rng_next_tya(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz);
void* rng_next_tyab(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz, const void* pmin, const void* pmax);
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_dyn_ctor_ref rng_ctor_ref(const char* name);
rng_ctor_fn rng_ctor(const char* name) __attribute__((deprecated("many dynamic ctors will have state that cannot be expressed through a raw function pointer, and so this method will not work for them. use `rng_ctor_ref` and `rng_ctor_call` instead where possible, unless you know that the dynamic ctor is a bare function pointer."))); // Get a dynamic ctor that is a raw funciton pointer. If the ctor for this name is not a raw function pointer (if it has state), then NULL is returned.
rng_t* rng_ctor_call(rng_dyn_ctor_ref ctor, const void* seed);
rng_t* rng_new_named(const char* name, const void* seed);
rng_t* rng_new(enum rng_kind kind, const u64 seed[static restrict 1]);
void rng_free(rng_t* rng);
#ifdef _RNGXX_INLINE_ONLY
#define _rng__always_inline __attribute__((gnu_inline)) inline extern
#else
#define _rng__always_inline __attribute__((gnu_inline)) inline static
#endif
#define _NEXT_TYPED_(name, T, N, S) \
_rng__always_inline T rng_next_ ## name (rng_t* engine, const T* min, const T* max) \
{ \
T output; \
rng_next_tyb(engine, &output, RNG_TY_ ## N, RNG_TYQ_ ## S, min, max); \
return output; \
} \
_rng__always_inline T* rng_nexta_ ## name(uint32_t len; rng_t* engine, T output[restrict len], uint32_t len, const T* min, const T* max) \
{ \
return rng_next_tyab(engine, output, RNG_TY_ ## N, RNG_TYQ_ ## S, len, min, max); \
} \
_rng__always_inline T* rng_nextam_ ## name(rng_t* engine, uint32_t len, const T* min, const T* max) \
{ \
return rng_next_tyab(engine, NULL, RNG_TY_ ## N, RNG_TYQ_ ## S, len, min, max); \
}
#define _NEXT_TYPED(T, N, S) _NEXT_TYPED_(T, T, N, S)
#define NEXT_TYPED_INT(n) _NEXT_TYPED(i ## n, INT ## n, SIGNED) \
_NEXT_TYPED(u ## n, INT ## n, UNSIGNED)
NEXT_TYPED_INT(8)
NEXT_TYPED_INT(16)
NEXT_TYPED_INT(32)
NEXT_TYPED_INT(64)
_NEXT_TYPED(f32, F32, SIGNED)
_NEXT_TYPED(f64, F64, SIGNED)
_rng__always_inline rng_bool_t rng_next_bool(rng_t* engine)
{
rng_bool_t output;
rng_next_tyb(engine, &output, RNG_TY_BOOL, 0, NULL, NULL);
return output & 1;
}
_rng__always_inline rng_bool_t* rng_nexta_bool(uint32_t len; rng_t* engine, rng_bool_t output[restrict len], uint32_t len)
{
return rng_next_tyab(engine, output, RNG_TY_BOOL, 0, len, NULL, NULL);
}
_rng__always_inline rng_bool_t* rng_nextam_bool(rng_t* engine, uint32_t len)
{
return rng_next_tyab(engine, NULL, RNG_TY_BOOL, 0, len, NULL, NULL);
}
#undef NEXT_TYPED_INT
#undef _NEXT_TYPED
#undef _NEXT_TYPED_
#undef _rng__always_inline
// -- // --
#endif
#ifdef __cplusplus

@ -14,6 +14,8 @@
#undef _RNGXX_COMMON_MINIMAL
#endif
#define RNGXX_DCTOR_NAME(T) _rng__init_for_ ## T
#define CTOR_COPY(name) name(const name& copy)
#define CTOR_MOVE(name) name(name&& move)
@ -59,6 +61,7 @@ struct Random
{
template<typename T>
struct iterator { //TODO: Implement this in another file (has to be header because of template :/)
//XXX: C++ iterators are absolute jank. This is far harder than it should be.
friend class Random;
//TODO: Make this work with foreach(), and STL iterator APIs somehow
@ -70,6 +73,15 @@ struct Random
inline virtual ~iterator(){}
inline T next() { return _sample(); }
inline iterator& operator++() { return *this; }
inline iterator& operator++(int) { return *this; }
inline T operator*() { return next(); }
friend auto operator<=>(const iterator<T>&, const iterator<T>&) = default;
protected:
virtual T _sample() { if (LIKELY(rng)) return rng->next<T>(); else throw ObjectMoved(); }
virtual inline void _init() {}
@ -165,7 +177,7 @@ struct Random
inline T next(T max);
template<typename T>
inline iterator<T> iter() { return iterator(*this); } //TODO: An iterator that yields `next<T>()` forever.
inline iterator<T> iter() { return iterator<T>(*this); } //TODO: An iterator that yields `next<T>()` forever.
template<typename T>
constexpr inline T max_for() const;
@ -228,8 +240,10 @@ DEF
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(); }
//template<> inline f64 Random::next<f64>() { return next_f64(); }
//template<> inline f32 Random::next<f32>() { return next_f32(); }
DEFT(f32)
DEFT(f64)
DEF
#undef DEF
#undef DEFTT

@ -1,10 +1,11 @@
#pragma once
#include "internal/common.h"
#include <rngxx.hpp>
#include "internal/mem.h"
#include "internal/common.h"
namespace rng
{
struct crand final : public Random
@ -40,4 +41,5 @@ namespace rng
explicit crand(_opaque* raw);
i64 _sample_int();
};
}

@ -8,6 +8,8 @@ extern "C" {
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#ifndef _RNGXX_COMMON_MINIMAL
#include <stdlib.h>

@ -0,0 +1,14 @@
#ifndef _RNGXX_DCTOR_HACK_H
#define _RNGXX_DCTOR_HACK_H
#include <rngxx.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include <init.hpp>
// For some reason, dctors defined in some files do not work??!
RNGXX_APPLY_CTOR(rng, sm64, u64);
RNGXX_APPLY_CTOR(rng, crand, u64);
#endif /* _RNGXX_DCTOR_HACK_H */

@ -0,0 +1,33 @@
#pragma once
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
#include "common.h"
#include <rngxx.hpp>
namespace rng::init
{
using ctor_func = std::function<Random* (const void*)>;
struct DCLookupFailed final : std::exception{ inline DCLookupFailed(const std::string_view& n) : std::exception(), name(std::move(n)){} std::string name; };
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept;
template<typename T, typename _Ptr = void>
inline auto apply(std::string_view name, auto&& lam) noexcept
{
return apply_raw(name, [lam](const void* seed) -> Random* {
return static_cast<Random*>(lam(reinterpret_cast<const _Ptr*>(seed)));
});
}
ctor_func& get(std::string_view name);
}
// To add to dynamic CTOR map
#define RNGXX_APPLY_CTOR(ns, T, TSeed) __attribute__((used, retain))\
const static auto& RNGXX_DCTOR_NAME(T) = rng::init::apply<ns::T, TSeed>(#T, [](const TSeed* ptr) { return new ns::T(*ptr); })
//const extern init::ctor_func& RNGXX_DCTOR_NAME(sm64);

@ -0,0 +1,37 @@
#pragma once
#include "common.h"
namespace rng
{
template<typename T>
struct seed_gen
{
virtual T generate_seed() = 0;
inline virtual ~seed_gen(){}
};
namespace seed
{
struct splitmix64 : public virtual seed_gen<u64>
{
inline splitmix64(const splitmix64&) = default;
explicit inline splitmix64(seed_gen<u64>& from) : m_state(from.generate_seed()){}
splitmix64(u64 i);
u64 generate_seed() override;
f64 generate_f64();
inline virtual ~splitmix64() override{}
static u64 oneshot(u64& state);
inline static u64 oneshot(const u64& state) { u64 s = state; return oneshot(s); }
static f64 oneshotf(u64& state);
inline static f64 oneshotf(const u64& state) { u64 s = state; return oneshotf(s); }
static splitmix64 random();
private:
u64 m_state;
};
}
}

@ -0,0 +1,22 @@
#pragma once
#include <rngxx.hpp>
#include "internal/seed.h"
namespace rng
{
struct sm64 final : public Random
{
inline sm64() : m_generator(seed::splitmix64::random()){}
inline sm64(const seed::splitmix64& g) : m_generator(g){}
explicit inline sm64(seed_gen<u64>& generator) : m_generator(generator){}
inline sm64(u64 seed) : Random(), m_generator(seed){}
RNG_OVERRIDE(inline u64, next_u64, ()) { return m_generator.generate_seed(); }
protected:
inline f64 _sample() override { return m_generator.generate_f64(); }
private:
seed::splitmix64 m_generator;
};
}

@ -1,7 +1,11 @@
// 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"
@ -15,19 +19,38 @@ 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, f32, f64, bool>()) {
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(minp) return engine.next(*reinterpret_cast<const T*>(minp), engine.max_for<T>());
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)
{
@ -41,7 +64,7 @@ namespace bridge _export(internal)
{
switch(ty)
{
case RNG_TY_BOOL: return alignof(bool);
case RNG_TY_BOOL: return alignof(rng_bool_t);
case RNG_TY_INT8: return alignof(i8);
case RNG_TY_INT16: return alignof(i16);
@ -58,7 +81,7 @@ namespace bridge _export(internal)
{
switch(ty)
{
case RNG_TY_BOOL: return sizeof(bool);
case RNG_TY_BOOL: return sizeof(rng_bool_t);
case RNG_TY_INT8: return sizeof(i8);
case RNG_TY_INT16: return sizeof(i16);
@ -75,15 +98,35 @@ namespace bridge _export(internal)
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<S>;
else return &next_typed_into<U>;
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)
@ -92,6 +135,8 @@ extern "C" {
{
case RNG_KIND_CRAND:
return new rng::crand(seed[0]);
case RNG_KIND_SM64:
return new rng::sm64(seed[0]);
default: return NULL;
}
}
@ -103,25 +148,30 @@ extern "C" {
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, opt->bound.range.pmin, opt->bound.range.pmax); break
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<f32>(*engine, output);
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<f64>(*engine, output);
bridge::next_typed_into_n<f64>(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax);
break;
case RNG_TY_BOOL:
*reinterpret_cast<rng_bool_t*>(output) = bridge::next_typed<bool>(*engine, output) ? 1 : 0;
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;
}
@ -142,4 +192,40 @@ extern "C" {
{
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;
}
}

@ -16,11 +16,19 @@
extern "C" {
#endif
#define _ro_p(n) __attribute__((access (read_only, n)))
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(mnext)(rng_t* engine, void* restrict output, const struct rng_next_opt* restrict opt) _ro_p(3) _export(internal);
int RNG_IMPL(m_allocsz_for)(enum rng_next_type ty, size_t* restrict size, size_t* restrict align) _export(internal);
rng_t* RNG_IMPL(dynctor_trycall)(const char* name, const void* seed) _export(internal);
struct rng_dyn_ctor* RNG_IMPL(dynctor_refer)(const char* name) _export(internal);
rng_t* RNG_IMPL(dynctor_callref)(struct rng_dyn_ctor* ptr, const void* seed) _export(internal);
#undef _ro_p
#ifdef __cplusplus
}
#endif

@ -45,6 +45,37 @@ static void* m_alloc_for(enum rng_next_type ty)
return NULL;
}
static void* m_alloc_array(enum rng_next_type ty, size_t ars)
{
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 calloc(ars, s);
case 3: return aligned_alloc(a, s * ars);
}
return NULL;
}
void* rng_next_tyab(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz, const void* pmin, const void* pmax)
{
if(!sz) return output;
if(! (output = output ?: m_alloc_array(ty, (size_t)sz)) ) return NULL;
RNG_IMPL(mnext)(engine, output, &(struct rng_next_opt){
.bound.range = { pmin, pmax },
.ty = {
.type = ty,
.mod = flags | (sz > 1 ? RNG_TYQ_ARRAY : 0 ),
},
.ex = {
.array_size = sz,
},
});
return output;
}
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;
@ -58,6 +89,7 @@ void* rng_next_tyb(rng_t* engine, void* restrict output, enum rng_next_type ty,
.type = ty,
.mod = flags,
},
.ex = {0},
});
return output;
@ -67,3 +99,24 @@ void* rng_next_ty(rng_t* engine, void* restrict output, enum rng_next_type ty, e
{
return rng_next_tyb(engine, output, ty, flags, NULL, NULL);
}
void* rng_next_tya(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz)
{
if(!sz) return output;
return rng_next_tyab(engine, output, ty, flags, sz, NULL, NULL);
}
rng_t* rng_new_named(const char* name, const void* seed)
{
return RNG_IMPL(dynctor_trycall)(name, seed);
}
rng_dyn_ctor_ref rng_ctor_ref(const char* name)
{
return RNG_IMPL(dynctor_refer)(name);
}
rng_t* rng_ctor_call(rng_dyn_ctor_ref ctor, const void* seed)
{
return RNG_IMPL(dynctor_callref)(ctor, seed);
}

@ -0,0 +1,41 @@
#include <unordered_map>
#include <string>
#include <init.hpp>
using ctor_map = std::unordered_map<std::string_view, rng::init::ctor_func>;
static ctor_map* INIT_MAP=nullptr;
namespace rng::init
{
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept
{
if(!INIT_MAP) INIT_MAP = new ctor_map();
auto& map = *INIT_MAP;
map[name] = std::move(fn);
return map[name];
}
ctor_func& get(std::string_view name)
{
if(UNLIKELY(!INIT_MAP)) throw DCLookupFailed ( name );
auto& map = *INIT_MAP;
if(map.contains(name)) return map[name];
else throw DCLookupFailed ( name );
}
namespace {
__attribute__((destructor))
static void _rng_ctor__fini() noexcept{
if(LIKELY(INIT_MAP)) {
delete INIT_MAP;
INIT_MAP = nullptr;
}
}
}
}

@ -75,3 +75,6 @@ N_INTS
#undef NEXTT
#undef NEXT
// ---
//TODO: Why does this have to be in a specific TU to work?
#include "../internal/dctor.h"

@ -33,7 +33,7 @@ _Static_assert( sizeof(uint48_t) == (sizeof(uint16_t) * 3), "bad uint48 (ushort[
_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)));
__attribute__((const, nonnull, returns_nonnull, access(none, 1))));
inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, low) (jr_xsub_t* restrict state)
{
@ -43,6 +43,18 @@ inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, high) (jr_xsub_t* restri
{
return state->xsubi+1;
}
inline static __attribute__((artificial, always_inline, gnu_inline, const)) int _resv_is_high()
{
// This being `static const` initialised makes this function be seen as a proper constant expression. Nothing is leaked on to the stack of the caller and the function is replaced with a single `lea`.
static const struct jr_state chk = {
.st.xsubl = JR_MAX,
};
return chk.st._xsub
? 1
: 0;
}
__attribute__((const))
inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict state)
{
@ -51,13 +63,31 @@ inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict
// The ifunc `_jr_st_resolv()` is essentially (almost) the same as a symbol-aliased constexpr function.
// Ain't that neat?
#ifdef _RNGXX_JR_RESOLV_IFUNC_OLD_STACKDYN_CHECK
// The old, dynamic stack alloc of the struct, instead of putting it in global r/o data
struct jr_state chk = {0};
chk.st.xsubh = JR_MAX;
chk.st.xsubl = JR_MAX;
#else
// The new, static const alloc of the struct, puts the value in at compile-time. Better optimisation opportunities.
// NOTE: Both stratergies make the ifunc resolve its target at compile-time, there is no difference in this ifunc resolver between the two. However, in `_resv_is_high()`, the old (this) stratergy prevents it being recognised as a constant-expression and stack-allocates the useless memory in the function's (macro-expanded) caller. Causing a spill of useless instructions that the new method changes to one `lea'.
static const struct jr_state chk = {
.st.xsubl = JR_MAX,
};
#endif
return chk.st._xsub
? & IFUNC_NAME(_jr_st_resolv, high)
: & IFUNC_NAME(_jr_st_resolv, low);
}
#ifndef _RNGXX_JR_RESOLV_RUNTIME
#define _jr_st_resolv_f (_resv_is_high() \
? & IFUNC_NAME(_jr_st_resolv, high) \
: & IFUNC_NAME(_jr_st_resolv, low))
//unsigned short* (*const _jr_st_resolv_fp)(jr_xsub_t* restrict state) = _jr_st_resolv_f; // "not a constant expr ://
#define _jr_st_resolv(st) _jr_st_resolv_f (st)
#else
#define _jr_st_resolv_f _jr_st_resolv
#endif
_fspec(readonly) internal long _jr_lastl(const struct jr_state* restrict state)
{

@ -0,0 +1,14 @@
#include <bit>
#include <climits>
#include <cmath>
#include <rngxx.hpp>
#include <mem.h>
#include <init.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include "crand.h"
//TODO: Why does dynctors defined in this TU not function properly? Is it because there's nothing else in here?

@ -0,0 +1,31 @@
#include <cmath>
#include <ctime>
#include <seed.h>
#include <common.h>
static u64 _sm64_next(u64& x)
{
u64 z = (x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
namespace rng::seed
{
splitmix64::splitmix64(u64 u) : m_state(u){}
u64 splitmix64::generate_seed() { return _sm64_next(m_state); }
f64 splitmix64::generate_f64() { return generate_seed() / pow(2.0, 64); }
u64 splitmix64::oneshot(u64& state) { return _sm64_next(state); }
f64 splitmix64::oneshotf(u64& state) { return _sm64_next(state) / pow(2.0, 64); }
splitmix64 splitmix64::random()
{
splitmix64 r (oneshot(time(NULL)));
return r;
}
}

@ -1,8 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define _RNGXX_INLINE_ONLY
#include <rngxx.h>
static int next(rng_t* rng, const int* min, const int* max)
{
int output;
@ -13,10 +17,65 @@ static int next(rng_t* rng, const int* min, const int* max)
}
#define TREF(x) ( (const __typeof(x)[]){ (x) } )
int main()
const struct { const char* const name; const enum rng_kind static_kind; } DEFAULT_ALG = { "crand", RNG_KIND_CRAND };
int main(int argc, char** argv)
{
rng_t* engine = rng_new(RNG_KIND_CRAND, (const u64[]){ 1000 });
(void)argc;
const char* alg_name = argv[1] ?: DEFAULT_ALG.name;
const enum rng_kind* alg_dev = ((int)DEFAULT_ALG.static_kind) == -1 ? NULL : &DEFAULT_ALG.static_kind;
printf("constructing for %s...\n", alg_name);
rng_dyn_ctor_ref rcref = rng_ctor_ref(alg_name);
rng_ctor_fn rctor = rng_ctor(alg_name);
rng_t* engine = NULL;
if(rctor) {
printf("constructing direct\n");
engine = rctor(TREF((u64)time(NULL)));
} else {
printf("constructing through ref\n");
engine = rng_ctor_call(rcref, TREF((u64)time(NULL)));
}
if(!engine) {
printf("fallback to `rng_new_named()`\n");
engine = rng_new_named(alg_name, TREF((u64)time(NULL))); //rng_new(rng_kind_crand, (const u64[]){ time(null) });
}
if(!engine && alg_dev) {
printf("fallback to static init of default arg (%d)\n", *alg_dev);
engine = rng_new(*alg_dev, (const u64[]){ time(NULL) });
}
if(!engine) {
fprintf(stderr, "failed to create engine\n");
return 1;
}
printf("%d %d %d\n", next(engine, NULL, TREF(100)), next(engine, TREF(10), TREF(20)), next(engine, NULL, NULL));
printf("%ld\n", rng_next_i64(engine, NULL, NULL));
printf("%lf\n", rng_next_f64(engine, TREF(1.0), TREF(100.0)));
printf("%d\n", rng_next_bool(engine));
rng_bool_t* array = rng_nextam_bool(engine, 100);
if(!array) {
fprintf(stderr, "failed to allocate for array\n");
return 1;
}
for(int i=0;i<100;i++)
printf("%d ", array[i]);
printf("\n"); free(array);
int ints[100];
if(!rng_nexta_i32(engine, ints, 100, NULL, TREF(100))) {
fprintf(stderr, "failed to compute int array\n");
return 1;
}
for(size_t i=0;i<sizeof(ints)/sizeof(ints[0]);i++)
printf("%d ", ints[i]);
printf("\n");
rng_free(engine);
return 0;
}

@ -0,0 +1,17 @@
#include <fmt/format.h>
#include <rngxx.hpp>
#include <rngxx/crand.h>
void iter(Random& rng)
{
for(auto&& i : rng.iter<int>())
fmt::print("{} ", i);
}
extern "C" {
void cpp_test()
{
auto random = new rng::crand(100);
iter(random);
}
}
Loading…
Cancel
Save