File: duk_util_tinyrandom.c

package info (click to toggle)
duktape 2.7.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 21,160 kB
  • sloc: ansic: 215,359; python: 5,961; javascript: 4,555; makefile: 477; cpp: 205
file content (126 lines) | stat: -rw-r--r-- 3,486 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 *  A tiny random number generator used for Math.random() and other internals.
 *
 *  Default algorithm is xoroshiro128+: http://xoroshiro.di.unimi.it/xoroshiro128plus.c
 *  with SplitMix64 seed preparation: http://xorshift.di.unimi.it/splitmix64.c.
 *
 *  Low memory targets and targets without 64-bit types use a slightly smaller
 *  (but slower) algorithm by Adi Shamir:
 *  http://www.woodmann.com/forum/archive/index.php/t-3100.html.
 *
 */

#include "duk_internal.h"

#if !defined(DUK_USE_GET_RANDOM_DOUBLE)

#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS)
#define DUK__RANDOM_SHAMIR3OP
#else
#define DUK__RANDOM_XOROSHIRO128PLUS
#endif

#if defined(DUK__RANDOM_SHAMIR3OP)
#define DUK__UPDATE_RND(rnd) \
	do { \
		(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 void duk_util_tinyrandom_prepare_seed(duk_hthread *thr) {
	DUK_UNREF(thr); /* Nothing now. */
}

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;

	n = 53; /* enough to cover the whole mantissa */
	t = 0.0;

	do {
		DUK__UPDATE_RND(rnd);
		t += DUK__RND_BIT(rnd);
		t /= 2.0;
	} while (--n);

	thr->heap->rnd_state = rnd;

	DUK_ASSERT(t >= (duk_double_t) 0.0);
	DUK_ASSERT(t < (duk_double_t) 1.0);

	return t;
}
#endif /* DUK__RANDOM_SHAMIR3OP */

#if defined(DUK__RANDOM_XOROSHIRO128PLUS)
DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_splitmix64(duk_uint64_t *x) {
	duk_uint64_t z;
	z = (*x += DUK_U64_CONSTANT(0x9E3779B97F4A7C15));
	z = (z ^ (z >> 30U)) * DUK_U64_CONSTANT(0xBF58476D1CE4E5B9);
	z = (z ^ (z >> 27U)) * DUK_U64_CONSTANT(0x94D049BB133111EB);
	return z ^ (z >> 31U);
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_rotl(const duk_uint64_t x, duk_small_uint_t k) {
	return (x << k) | (x >> (64U - k));
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__xoroshiro128plus(duk_uint64_t *s) {
	duk_uint64_t s0;
	duk_uint64_t s1;
	duk_uint64_t res;

	s0 = s[0];
	s1 = s[1];
	res = s0 + s1;
	s1 ^= s0;
	s[0] = duk__rnd_rotl(s0, 55) ^ s1 ^ (s1 << 14U);
	s[1] = duk__rnd_rotl(s1, 36);

	return res;
}

DUK_INTERNAL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr) {
	duk_small_uint_t i;
	duk_uint64_t x;

	/* Mix both halves of the initial seed with SplitMix64.  The intent
	 * is to ensure that very similar raw seeds (which is usually the case
	 * because current seed is Date.now()) result in different xoroshiro128+
	 * seeds.
	 */
	x = thr->heap->rnd_state[0]; /* Only [0] is used as input here. */
	for (i = 0; i < 64; i++) {
		thr->heap->rnd_state[i & 0x01] = duk__rnd_splitmix64(&x); /* Keep last 2 values. */
	}
}

DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) {
	duk_uint64_t v;
	duk_double_union du;

	/* For big and little endian the integer and IEEE double byte order
	 * is the same so a direct assignment works.  For mixed endian the
	 * 32-bit parts must be swapped.
	 */
	v = (DUK_U64_CONSTANT(0x3ff) << 52U) | (duk__xoroshiro128plus((duk_uint64_t *) thr->heap->rnd_state) >> 12U);
	du.ull[0] = v;
#if defined(DUK_USE_DOUBLE_ME)
	do {
		duk_uint32_t tmp;
		tmp = du.ui[0];
		du.ui[0] = du.ui[1];
		du.ui[1] = tmp;
	} while (0);
#endif
	return du.d - 1.0;
}
#endif /* DUK__RANDOM_XOROSHIRO128PLUS */

#endif /* !DUK_USE_GET_RANDOM_DOUBLE */