From dfc26f4ac48264de3de2a066234d299f7b00ac75 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sun, 29 May 2016 04:41:15 +0300 Subject: [PATCH] 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[. --- src-input/duk_bi_array.c | 6 ++-- src-input/duk_bi_math.c | 2 +- src-input/duk_heap.h | 2 ++ src-input/duk_heap_alloc.c | 11 ++++-- src-input/duk_util.h | 9 ++++- src-input/duk_util_tinyrandom.c | 64 +++++++++++++++++++++------------ 6 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src-input/duk_bi_array.c b/src-input/duk_bi_array.c index bf7834a6..86e6a88e 100644 --- a/src-input/duk_bi_array.c +++ b/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); diff --git a/src-input/duk_bi_math.c b/src-input/duk_bi_math.c index 61f10fa4..cf332578 100644 --- a/src-input/duk_bi_math.c +++ b/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; } diff --git a/src-input/duk_heap.h b/src-input/duk_heap.h index 75156e62..576dd678 100644 --- a/src-input/duk_heap.h +++ b/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 diff --git a/src-input/duk_heap_alloc.c b/src-input/duk_heap_alloc.c index a1b2212d..ff35cf08 100644 --- a/src-input/duk_heap_alloc.c +++ b/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 */ diff --git a/src-input/duk_util.h b/src-input/duk_util.h index c1bb4c7b..b83ed552 100644 --- a/src-input/duk_util.h +++ b/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); diff --git a/src-input/duk_util_tinyrandom.c b/src-input/duk_util_tinyrandom.c index 99e4223c..7bdb9d87 100644 --- a/src-input/duk_util_tinyrandom.c +++ b/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 */