Compare commits

..

1 Commits
master ... iter

Author SHA1 Message Date
Avril e0518f2115
Attempted a better iterator design but C++ doesn"t make it easy
3 years ago

1
.gitignore vendored

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

@ -4,7 +4,7 @@ PROJECT=rngxx
AUTHOR=Avril (Flanchan) <flanchan@cumallover.me>
VERSION_MAJOR=1
VERSION_MINOR=1
VERSION_MINOR=0
VERSION=$(VERSION_MAJOR).$(VERSION_MINOR)
ifeq ($(PREFIX),)

@ -22,7 +22,6 @@ typedef struct Random rng_t;
enum rng_kind {
RNG_KIND_CRAND,
RNG_KIND_SM64,
};
enum rng_next_flag {

@ -14,8 +14,6 @@
#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)

@ -1,11 +1,10 @@
#pragma once
#include "internal/common.h"
#include <rngxx.hpp>
#include "internal/mem.h"
#include "internal/common.h"
namespace rng
{
struct crand final : public Random

@ -1,14 +0,0 @@
#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 */

@ -10,10 +10,9 @@
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;
const std::function<Random* (const void*)>& apply_raw(std::string_view name, std::function<Random* (const void*)> fn) noexcept;
template<typename T, typename _Ptr = void>
inline auto apply(std::string_view name, auto&& lam) noexcept
@ -23,11 +22,11 @@ namespace rng::init
});
}
ctor_func& get(std::string_view name);
std::function<Random* (const void*)>& get(std::string_view name);
}
#define RNGXX_DCTOR_NAME(T) _rng__init_for_ ## T
// 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); })
#define RNGXX_APPLY_CTOR(ns, T, TSeed) \
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,62 @@
#pragma once
#include <new>
#include <utility>
namespace rng::util
{
template<typename T>
struct uninit final
{
inline static T* align_buffer(unsigned char* buffer) { return reinterpret_cast<T*>(buffer + alignof(T) - reinterpret_cast<intptr_t>(buffer) % alignof(T)); }
inline static const T* align_buffer(const unsigned char* buffer) { return reinterpret_cast<const T*>(buffer + alignof(T) - reinterpret_cast<intptr_t>(buffer) % alignof(T)); }
inline T* ptr() { return align_buffer(&a_data[0]); }
inline const T* ptr() const { return align_buffer(&a_data[0]); }
inline unsigned char* bytes() { return &a_data[0]; }
inline const unsigned char* bytes() const { return &a_data[0]; }
inline uninit(T&& value) { give(std::move(value)); }
inline uninit(){}
inline void drop() { ptr()->~T(); }
inline T&& take() { return std::move(*ptr()); }
inline void give(T&& value) { new (ptr()) T(std::move(value)); }
inline T& operator=(T&& value) { give(std::move(value)); return *ptr(); }
inline T replace(T&& value) {
auto x = take();
drop();
give(std::move(value));
return x;
}
inline static uninit<T> move(uninit<T>&& m) { return uninit<T>(std::move(m)); }
inline static uninit<T> copy(const uninit<T>& c) { return uninit<T>(c); }
inline uninit<T> bitwise_copy() const {
uninit u;
u.a_data = a_data;
return u;
}
private:
inline uninit(const uninit<T>& copy) : uninit(T(*copy.ptr())){}
inline uninit(uninit<T>&& move) : uninit(std::move(*move.ptr())){}
std::array<unsigned char, sizeof(T)+alignof(T)> a_data;
};
template<typename T>
struct generic_iterator
{
inline T try_next()
{
uninit<T> v;
if(next(v)) { return v.take(); }
else throw "c++ sucks"; //XXX: eh... this is too draining
}
protected:
virtual bool next(uninit<T>& output) = 0;
};
}

@ -1,37 +0,0 @@
#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;
};
}
}

@ -1,22 +0,0 @@
#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;
};
}

@ -3,7 +3,6 @@
#include <rngxx.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include <init.hpp>
@ -135,8 +134,6 @@ 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;
}
}

@ -16,10 +16,8 @@
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) _ro_p(3) _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);
@ -27,8 +25,6 @@ rng_t* RNG_IMPL(dynctor_trycall)(const char* name, const void* seed) _export(int
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

@ -3,22 +3,22 @@
#include <init.hpp>
using ctor_map = std::unordered_map<std::string_view, rng::init::ctor_func>;
using ctor_map = std::unordered_map<std::string_view, std::function<Random* (const void*)>>;
static ctor_map* INIT_MAP=nullptr;
namespace rng::init
{
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept
const std::function<Random* (const void*)>& apply_raw(std::string_view name, std::function<Random* (const void*)> 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)
std::function<Random* (const void*)>& get(std::string_view name)
{
if(UNLIKELY(!INIT_MAP)) throw DCLookupFailed ( name );
auto& map = *INIT_MAP;

@ -0,0 +1,17 @@
#include <rngxx.hpp>
#include <iter.h>
void d(rng::util::generic_iterator<int>& i)
{
i.try_next();
}
//XXX: Doing this is not going to work ...
#if 0
void t(Random& r)
{
for(auto&& i : r.iter<int>()) (void)i;
}
#endif

@ -75,6 +75,3 @@ 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, access(none, 1))));
__attribute__((const, nonnull, returns_nonnull)));
inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, low) (jr_xsub_t* restrict state)
{
@ -43,18 +43,6 @@ 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)
{
@ -63,31 +51,13 @@ 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.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
chk.st.xsubh = JR_MAX;
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)
{

@ -7,6 +7,7 @@
#include <rngxx.hpp>
#include <mem.h>
#include <init.hpp>
#include <rngxx/crand.h>
#include "crand.h"
@ -70,3 +71,4 @@ namespace rng
}
RNGXX_APPLY_CTOR(rng, crand, u64);

@ -1,14 +0,0 @@
#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?

@ -1,31 +0,0 @@
#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;
}
}

@ -18,17 +18,10 @@ static int next(rng_t* rng, const int* min, const int* max)
#define TREF(x) ( (const __typeof(x)[]){ (x) } )
const struct { const char* const name; const enum rng_kind static_kind; } DEFAULT_ALG = { "crand", RNG_KIND_CRAND };
int main(int argc, char** argv)
int main()
{
(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_dyn_ctor_ref rcref = rng_ctor_ref("crand");
rng_ctor_fn rctor = rng_ctor("crand");
rng_t* engine = NULL;
if(rctor) {
@ -40,11 +33,11 @@ int main(int argc, char** argv)
}
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) });
engine = rng_new_named("crand", 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) {
printf("fallback to static init\n");
engine = rng_new(RNG_KIND_CRAND, (const u64[]){ time(NULL) });
}
if(!engine) {
fprintf(stderr, "failed to create engine\n");

Loading…
Cancel
Save