Browse Source

Add support for user provided random provider

Remove tinyrandom "get bits" variant altogether to simplify the needs:
there's just one primitive, to return an IEEE double in the range [0,1[.
pull/824/head
Sami Vaarala 9 years ago
parent
commit
dfc26f4ac4
  1. 6
      src-input/duk_bi_array.c
  2. 2
      src-input/duk_bi_math.c
  3. 2
      src-input/duk_heap.h
  4. 11
      src-input/duk_heap_alloc.c
  5. 9
      src-input/duk_util.h
  6. 64
      src-input/duk_util_tinyrandom.c

6
src-input/duk_bi_array.c

@ -770,7 +770,6 @@ DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int
#endif
DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_int_t p, l, r;
/* The lo/hi indices may be crossed and hi < 0 is possible at entry. */
@ -794,10 +793,9 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) {
DUK_ASSERT(hi - lo + 1 >= 2);
/* randomized pivot selection */
p = lo + (duk_util_tinyrandom_get_bits(thr, 30) % (hi - lo + 1)); /* rnd in [lo,hi] */
p = lo + (duk_int_t) (DUK_UTIL_GET_RANDOM_DOUBLE((duk_hthread *) ctx) * (duk_double_t) (hi - lo + 1));
DUK_ASSERT(p >= lo && p <= hi);
DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld",
(long) lo, (long) hi, (long) p));
DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld", (long) lo, (long) hi, (long) p));
/* move pivot out of the way */
duk__array_sort_swap(ctx, p, lo);

2
src-input/duk_bi_math.c

@ -318,7 +318,7 @@ DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_context *ctx) {
}
DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_context *ctx) {
duk_push_number(ctx, (duk_double_t) duk_util_tinyrandom_get_double((duk_hthread *) ctx));
duk_push_number(ctx, (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE((duk_hthread *) ctx));
return 1;
}

2
src-input/duk_heap.h

@ -417,7 +417,9 @@ struct duk_heap {
duk_uint32_t hash_seed;
/* rnd_state for duk_util_tinyrandom.c */
#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
duk_uint32_t rnd_state;
#endif
/* For manual debugging: instruction count based on executor and
* interrupt counter book-keeping. Inspect debug logs to see how

11
src-input/duk_heap_alloc.c

@ -894,7 +894,7 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
/* XXX: use the pointer as a seed for now: mix in time at least */
/* The casts through duk_intr_pt is to avoid the following GCC warning:
/* The casts through duk_intptr_t is to avoid the following GCC warning:
*
* warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
*
@ -906,7 +906,6 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
res->hash_seed = (duk_uint32_t) DUK__FIXED_HASH_SEED;
#else /* DUK_USE_ROM_STRINGS */
res->hash_seed = (duk_uint32_t) (duk_intptr_t) res;
res->rnd_state = (duk_uint32_t) (duk_intptr_t) res;
#if !defined(DUK_USE_STRHASH_DENSE)
res->hash_seed ^= 5381; /* Bernstein hash init value is normally 5381; XOR it in in case pointer low bits are 0 */
#endif
@ -1032,6 +1031,14 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
}
DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object);
/*
* Odds and ends depending on the heap thread
*/
#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
res->rnd_state = (duk_uint32_t) DUK_USE_DATE_GET_NOW((duk_context *) res->heap_thread);
#endif
/*
* All done
*/

9
src-input/duk_util.h

@ -9,6 +9,12 @@
#define DUK_UTIL_GET_HASH_PROBE_STEP(hash) (duk_util_probe_steps[(hash) & 0x1f])
#if defined(DUK_USE_GET_RANDOM_DOUBLE)
#define DUK_UTIL_GET_RANDOM_DOUBLE(thr) DUK_USE_GET_RANDOM_DOUBLE((thr)->heap_udata)
#else
#define DUK_UTIL_GET_RANDOM_DOUBLE(thr) duk_util_tinyrandom_get_double(thr)
#endif
/*
* Endian conversion
*/
@ -498,8 +504,9 @@ DUK_INTERNAL_DECL duk_int32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk
DUK_INTERNAL_DECL void duk_be_encode(duk_bitencoder_ctx *ctx, duk_uint32_t data, duk_small_int_t bits);
DUK_INTERNAL_DECL void duk_be_finish(duk_bitencoder_ctx *ctx);
DUK_INTERNAL_DECL duk_uint32_t duk_util_tinyrandom_get_bits(duk_hthread *thr, duk_small_int_t n);
#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
DUK_INTERNAL_DECL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr);
#endif
DUK_INTERNAL_DECL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf);
DUK_INTERNAL_DECL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size);

64
src-input/duk_util_tinyrandom.c

@ -8,56 +8,74 @@
#include "duk_internal.h"
#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
#define DUK__UPDATE_RND(rnd) do { \
(rnd) += ((rnd) * (rnd)) | 0x05; \
(rnd) = ((rnd) & 0xffffffffU); /* if duk_uint32_t is exactly 32 bits, this is a NOP */ \
(rnd) += ((rnd) * (rnd)) | 0x05UL; \
(rnd) = ((rnd) & 0xffffffffUL); /* if duk_uint32_t is exactly 32 bits, this is a NOP */ \
} while (0)
#define DUK__RND_BIT(rnd) ((rnd) >> 31) /* only use the highest bit */
DUK_INTERNAL duk_uint32_t duk_util_tinyrandom_get_bits(duk_hthread *thr, duk_small_int_t n) {
duk_small_int_t i;
duk_uint32_t res = 0;
#if 1
DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) {
duk_double_t t;
duk_small_int_t n;
duk_uint32_t rnd;
rnd = thr->heap->rnd_state;
for (i = 0; i < n; i++) {
n = 53; /* enough to cover the whole mantissa */
t = 0.0;
do {
DUK__UPDATE_RND(rnd);
res <<= 1;
res += DUK__RND_BIT(rnd);
}
t += DUK__RND_BIT(rnd);
t /= 2.0;
} while (--n);
thr->heap->rnd_state = rnd;
return res;
}
DUK_ASSERT(t >= (duk_double_t) 0.0);
DUK_ASSERT(t < (duk_double_t) 1.0);
return t;
}
#else
/* Not much faster, larger footprint. */
DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) {
duk_double_t t;
duk_small_int_t n;
duk_small_int_t i;
duk_uint32_t rnd;
/*
* XXX: could make this a lot faster if we create the double memory
* representation directly. Feasible easily (must be uniform random).
*/
duk_uint32_t rndbit;
duk_double_union du;
rnd = thr->heap->rnd_state;
n = 53; /* enough to cover the whole mantissa */
t = 0.0;
du.ui[DUK_DBL_IDX_UI0] = 0x3ff00000UL;
du.ui[DUK_DBL_IDX_UI1] = 0x00000000UL;
do {
DUK_ASSERT(DUK_DBL_IDX_UI0 ^ 1 == DUK_DBL_IDX_UI1); /* Indices are 0,1 or 1,0. */
DUK_ASSERT(DUK_DBL_IDX_UI1 ^ 1 == DUK_DBL_IDX_UI0);
/* Fill double representation mantissa bits with random number.
* With the implicit leading 1-bit we get a value in [1,2[.
*/
for (i = 0; i < 52; i++) {
DUK__UPDATE_RND(rnd);
t += DUK__RND_BIT(rnd);
t /= 2.0;
} while (--n);
rndbit = DUK__RND_BIT(rnd);
DUK_ASSERT((i >> 5) == 0 || (i >> 5) == 1);
du.ui[DUK_DBL_IDX_UI1 ^ (i >> 5)] |= rndbit << (i & 0x1fUL);
}
thr->heap->rnd_state = rnd;
t = du.d - 1.0; /* Subtract implicit 1-bit to get [0,1[. */
DUK_ASSERT(t >= (duk_double_t) 0.0);
DUK_ASSERT(t < (duk_double_t) 1.0);
return t;
}
#endif
#endif /* !DUK_USE_GET_RANDOM_DOUBLE */

Loading…
Cancel
Save