mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
2c4dccad33
Must not be a bad idea to improve documents. [ci skip]
304 lines
9.6 KiB
C++
304 lines
9.6 KiB
C++
#ifndef RUBY_RANDOM_H /*-*-C++-*-vi:se ft=cpp:*/
|
|
#define RUBY_RANDOM_H 1
|
|
/**
|
|
* @file
|
|
* @date Sat May 7 11:51:14 JST 2016
|
|
* @copyright 2007-2020 Yukihiro Matsumoto
|
|
* @copyright This file is a part of the programming language Ruby.
|
|
* Permission is hereby granted, to either redistribute and/or
|
|
* modify this file, provided that the conditions mentioned in the
|
|
* file COPYING are met. Consult the file for details.
|
|
*
|
|
* This is a set of APIs to roll your own subclass of ::rb_cRandom. An
|
|
* illustrative example of such PRNG can be found at
|
|
* `ext/-test-/ramdom/loop.c`.
|
|
*/
|
|
|
|
#include "ruby/ruby.h"
|
|
|
|
RBIMPL_SYMBOL_EXPORT_BEGIN()
|
|
|
|
/**
|
|
* Base components of the random interface.
|
|
*
|
|
* @internal
|
|
*
|
|
* Ideally this could be an empty class if we could assume C++, but in C a
|
|
* struct must have at least one field.
|
|
*/
|
|
struct rb_random_struct {
|
|
/** Seed, passed through e.g. `Random.new` */
|
|
VALUE seed;
|
|
};
|
|
typedef struct rb_random_struct rb_random_t; /**< @see ::rb_random_struct */
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
/**
|
|
* This is the type of functions called when your random object is initialised.
|
|
* Passed buffer is the seed object basically. But in Ruby a number can be
|
|
* really big. This type of functions accept such big integers as a series of
|
|
* machine words.
|
|
*
|
|
* @param[out] rng Your random struct to fill in.
|
|
* @param[in] buf Seed, maybe converted from a bignum.
|
|
* @param[in] len Number of words of `buf`.
|
|
* @post `rng` is initialised using the passed seeds.
|
|
*/
|
|
typedef void rb_random_init_func(rb_random_t *rng, const uint32_t *buf, size_t len);
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
/**
|
|
* This is the type of functions called from your object's `#rand` method.
|
|
*
|
|
* @param[out] rng Your random struct to extract an integer from.
|
|
* @return A random number.
|
|
* @post `rng` is consumed somehow.
|
|
*/
|
|
typedef unsigned int rb_random_get_int32_func(rb_random_t *rng);
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
/**
|
|
* This is the type of functions called from your object's `#bytes` method.
|
|
*
|
|
* @param[out] rng Your random struct to extract an integer from.
|
|
* @param[out] buf Return buffer of at least `len` bytes length.
|
|
* @param[in] len Number of bytes of `buf`.
|
|
* @post `rng` is consumed somehow.
|
|
* @post `buf` is filled with random bytes.
|
|
*/
|
|
typedef void rb_random_get_bytes_func(rb_random_t *rng, void *buf, size_t len);
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
/**
|
|
* This is the type of functions called from your object's `#rand` method.
|
|
*
|
|
* @param[out] rng Your random struct to extract an integer from.
|
|
* @param[in] excl Pass nonzero value here to indicate you don't want 1.0.
|
|
* @return A random number of range 0.0 to 1.0.
|
|
* @post `rng` is consumed somehow.
|
|
*/
|
|
typedef double rb_random_get_real_func(rb_random_t *rng, int excl);
|
|
|
|
/** PRNG algorithmic interface, analogous to Ruby level classes. */
|
|
typedef struct {
|
|
/** Number of bits of seed numbers. */
|
|
size_t default_seed_bits;
|
|
|
|
/** Initialiser function. */
|
|
rb_random_init_func *init;
|
|
|
|
/** Function to obtain a random integer. */
|
|
rb_random_get_int32_func *get_int32;
|
|
|
|
/**
|
|
* Function to obtain a series of random bytes. If your PRNG have a native
|
|
* method to yield arbitrary number of bytes use that to implement this.
|
|
* But in case you lack such things, you can do so by using
|
|
* rb_rand_bytes_int32()
|
|
*
|
|
* ```CXX
|
|
* extern rb_random_get_int32_func your_get_int32_func;
|
|
*
|
|
* void
|
|
* your_get_byes_func(rb_random_t *rng, void *buf, size_t len)
|
|
* {
|
|
* rb_rand_bytes_int32(your_get_int32_func, rng, buf, len);
|
|
* }
|
|
* ```
|
|
*/
|
|
rb_random_get_bytes_func *get_bytes;
|
|
|
|
/**
|
|
* Function to obtain a random double. If your PRNG have a native method
|
|
* to yield a floating point random number use that to implement this. But
|
|
* in case you lack such things, you can do so by using
|
|
* rb_int_pair_to_real().
|
|
*
|
|
* ```CXX
|
|
* extern rb_random_get_int32_func your_get_int32_func;
|
|
*
|
|
* void
|
|
* your_get_real_func(rb_random_t *rng, int excl)
|
|
* {
|
|
* auto a = your_get_int32_func(rng);
|
|
* auto b = your_get_int32_func(rng);
|
|
* return rb_int_pair_to_real(a, b, excl);
|
|
* }
|
|
* ```
|
|
*/
|
|
rb_random_get_real_func *get_real;
|
|
} rb_random_interface_t;
|
|
|
|
/**
|
|
* This utility macro defines 3 functions named prefix_init, prefix_get_int32,
|
|
* prefix_get_bytes.
|
|
*/
|
|
#define RB_RANDOM_INTERFACE_DECLARE(prefix) \
|
|
static void prefix##_init(rb_random_t *, const uint32_t *, size_t); \
|
|
static unsigned int prefix##_get_int32(rb_random_t *); \
|
|
static void prefix##_get_bytes(rb_random_t *, void *, size_t)
|
|
|
|
/**
|
|
* Identical to #RB_RANDOM_INTERFACE_DECLARE except it also declares
|
|
* prefix_get_real.
|
|
*/
|
|
#define RB_RANDOM_INTERFACE_DECLARE_WITH_REAL(prefix) \
|
|
RB_RANDOM_INTERFACE_DECLARE(prefix); \
|
|
static double prefix##_get_real(rb_random_t *, int)
|
|
|
|
/**
|
|
* This utility macro expands to the names declared using
|
|
* #RB_RANDOM_INTERFACE_DECLARE. Expected to be used inside of a
|
|
* ::rb_random_interface_t initialiser:
|
|
*
|
|
* ```CXX
|
|
* RB_RANDOM_INTERFACE_DECLARE(foo);
|
|
*
|
|
* static inline constexpr rb_random_interface_t foo_interface = {
|
|
* 32768, // bits
|
|
* RB_RANDOM_INTERFACE_DEFINE(foo),
|
|
* };
|
|
* ```
|
|
*/
|
|
#define RB_RANDOM_INTERFACE_DEFINE(prefix) \
|
|
prefix##_init, \
|
|
prefix##_get_int32, \
|
|
prefix##_get_bytes
|
|
|
|
/**
|
|
* Identical to #RB_RANDOM_INTERFACE_DEFINE except it also defines
|
|
* prefix_get_real.
|
|
*/
|
|
#define RB_RANDOM_INTERFACE_DEFINE_WITH_REAL(prefix) \
|
|
RB_RANDOM_INTERFACE_DEFINE(prefix), \
|
|
prefix##_get_real
|
|
|
|
#if defined _WIN32 && !defined __CYGWIN__
|
|
typedef rb_data_type_t rb_random_data_type_t;
|
|
# define RB_RANDOM_PARENT 0
|
|
#else
|
|
|
|
/** This is the type of ::rb_random_data_type. */
|
|
typedef const rb_data_type_t rb_random_data_type_t;
|
|
|
|
/**
|
|
* This utility macro can be used when you define your own PRNG type:
|
|
*
|
|
* ```CXX
|
|
* static inline constexpr rb_random_interface_t your_if = {
|
|
* 0, RB_RANDOM_INTERFACE_DEFINE(your),
|
|
* };
|
|
*
|
|
* static inline constexpr your_prng = {
|
|
* "your PRNG",
|
|
* { rb_random_mark, },
|
|
* RB_RANDOM_PARENT, // <<-- HERE
|
|
* &your_if,
|
|
* 0,
|
|
* }
|
|
* ```
|
|
*/
|
|
# define RB_RANDOM_PARENT &rb_random_data_type
|
|
#endif
|
|
|
|
/**
|
|
* This macro is expected to be called exactly once at the beginning of a
|
|
* program, possibly from inside of your `Init_Foo()` function. Depending on
|
|
* platforms #RB_RANDOM_PARENT can require a fixup. This routine does that
|
|
* when necessary.
|
|
*/
|
|
#define RB_RANDOM_DATA_INIT_PARENT(random_data) \
|
|
rbimpl_random_data_init_parent(&random_data)
|
|
|
|
/**
|
|
* This is the implementation of ::rb_data_type_struct::dmark for
|
|
* ::rb_random_data_type. In case your PRNG does not involve Ruby objects at
|
|
* all (which is quite likely), you can simply reuse it.
|
|
*
|
|
* @param[out] ptr Target to mark, which is a ::rb_random_t this case.
|
|
*/
|
|
void rb_random_mark(void *ptr);
|
|
|
|
/**
|
|
* Initialises an allocated ::rb_random_t instance. Call it from your own
|
|
* initialiser appropriately.
|
|
*
|
|
* @param[out] rnd Your PRNG's base part.
|
|
* @post `rnd` is filled with an initial state.
|
|
*/
|
|
void rb_random_base_init(rb_random_t *rnd);
|
|
|
|
/**
|
|
* Generates a 64 bit floating point number by concatenating two 32bit unsigned
|
|
* integers.
|
|
*
|
|
* @param[in] a Most significant 32 bits of the result.
|
|
* @param[in] b Least significant 32 bits of the result.
|
|
* @param[in] excl Whether the result should exclude 1.0 or not.
|
|
* @return A double, whose range is either `[0, 1)` or `[0, 1]`.
|
|
* @see ::rb_random_interface_t::get_real()
|
|
*
|
|
* @internal
|
|
*
|
|
* This in fact has nothing to do with PRNGs.
|
|
*/
|
|
double rb_int_pair_to_real(uint32_t a, uint32_t b, int excl);
|
|
|
|
/**
|
|
* Repeatedly calls the passed function over and over again until the passed
|
|
* buffer is filled with random bytes.
|
|
*
|
|
* @param[in] func Generator function.
|
|
* @param[out] prng Passed as-is to `func`.
|
|
* @param[out] buff Return buffer.
|
|
* @param[in] size Number of words of `buff`.
|
|
* @post `buff` is filled with random bytes.
|
|
* @post `prng` is updated by `func`.
|
|
* @see ::rb_random_interface_t::get_bytes()
|
|
*/
|
|
void rb_rand_bytes_int32(rb_random_get_int32_func *func, rb_random_t *prng, void *buff, size_t size);
|
|
|
|
/**
|
|
* The data that holds the backend type of ::rb_cRandom. Used as your PRNG's
|
|
* ::rb_data_type_struct::parent.
|
|
*/
|
|
RUBY_EXTERN const rb_data_type_t rb_random_data_type;
|
|
|
|
RBIMPL_SYMBOL_EXPORT_END()
|
|
|
|
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
|
|
/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
|
|
/**
|
|
* Queries the interface of the passed random object.
|
|
*
|
|
* @param[in] obj An instance (of a subclass) of ::rb_cRandom.
|
|
* @return Its corresponding ::rb_random_interface_t interface.
|
|
*/
|
|
static inline const rb_random_interface_t *
|
|
rb_rand_if(VALUE obj)
|
|
{
|
|
RBIMPL_ASSERT_OR_ASSUME(RTYPEDDATA_P(obj));
|
|
const struct rb_data_type_struct *t = RTYPEDDATA_TYPE(obj);
|
|
const void *ret = t->data;
|
|
return RBIMPL_CAST((const rb_random_interface_t *)ret);
|
|
}
|
|
|
|
RBIMPL_ATTR_NOALIAS()
|
|
/**
|
|
* @private
|
|
*
|
|
* This is an implementation detail of #RB_RANDOM_DATA_INIT_PARENT. People
|
|
* don't use it directly.
|
|
*
|
|
* @param[out] random_data Region to fill.
|
|
* @post ::rb_random_data_type is filled appropriately.
|
|
*/
|
|
static inline void
|
|
rbimpl_random_data_init_parent(rb_random_data_type_t *random_data)
|
|
{
|
|
#if defined _WIN32 && !defined __CYGWIN__
|
|
random_data->parent = &rb_random_data_type;
|
|
#endif
|
|
}
|
|
|
|
#endif /* RUBY_RANDOM_H */
|