Skip to content

Commit 83c2830

Browse files
committed
Module Random: revised initialization of PRNG from array of integers
Sometimes we are given a single integer (as in Random.init) and sometimes we are given an array of 12 bytes (as in Random.self_init with the /dev/urandom implementation). In the first case, from a single integer we need to come up with 4 values for the 4 components of the PRNG state, avoiding bad values like 0, 0 for the x component. In the second case, we need to collect the 96 bits of entropy spread among these 12 bytes, then distribute them on the 4 components of the PRNG state. This commit treats the array as a string of 64-bit characters and applies a hash function to this string, producing a 256-bit hash, which is then used as the initial PRNG state. The hash function used in FNV1a, because it supports 256-bit outputs and it is relatively easy to implement.
1 parent dc2d66c commit 83c2830

5 files changed

Lines changed: 106 additions & 62 deletions

File tree

runtime/prng.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
/* */
1414
/**************************************************************************/
1515

16+
#include <string.h>
1617
#include "caml/alloc.h"
1718
#include "caml/bigarray.h"
1819
#include "caml/mlvalues.h"
@@ -34,7 +35,7 @@ struct LXM_state {
3435

3536
#define LXM_val(v) ((struct LXM_state *) Caml_ba_data_val(v))
3637

37-
static inline uint64_t rotl(const uint64_t x, int k) {
38+
Caml_inline uint64_t rotl(const uint64_t x, int k) {
3839
return (x << k) | (x >> (64 - k));
3940
}
4041

@@ -77,17 +78,58 @@ static void caml_lxm_set(value v, uint64_t i1, uint64_t i2,
7778
st->s = i4;
7879
}
7980

81+
static void add256(uint64_t x[4], uint64_t y[4])
82+
{
83+
int i;
84+
unsigned int carry = 0;
85+
for (i = 0; i < 4; i++) {
86+
uint64_t t1 = x[i];
87+
uint64_t t2 = t1 + y[i];
88+
uint64_t t3 = t2 + carry;
89+
x[i] = t3;
90+
carry = (t2 < t1) + (t3 < t2);
91+
}
92+
}
93+
94+
static void shl256(uint64_t x[4], int amount)
95+
{
96+
while (amount >= 64) {
97+
x[3] = x[2]; x[2] = x[1]; x[1] = x[0]; x[0] = 0;
98+
amount -= 64;
99+
}
100+
if (amount == 0) return;
101+
x[3] = x[3] << amount | x[2] >> (64 - amount);
102+
x[2] = x[2] << amount | x[1] >> (64 - amount);
103+
x[1] = x[1] << amount | x[0] >> (64 - amount);
104+
x[0] = x[0] << amount;
105+
}
106+
80107
CAMLprim value caml_lxm_init(value v, value a)
81108
{
82-
const uint64_t mix = 6364136223846793005;
83-
/* Multiplier taken from the MMIX LCG, Knoth TAOCP vol 2, 1998 edition */
84-
uint64_t d[4] = {0, 0, 0, 0};
109+
/* The FNV1-256 offset basis,
110+
1000292579580525809070709686206257048370927960
111+
14241193945225284501741471925557,
112+
as four 64-bit digits, little endian */
113+
uint64_t h[4] = { 0x1023b4c8caee0535,
114+
0xc8b1536847b6bbb3,
115+
0x2d98c384c4e576cc,
116+
0xdd268dbcaac55036 };
117+
uint64_t t[4];
85118
mlsize_t i, len;
86119

87120
for (i = 0, len = Wosize_val(a); i < len; i++) {
88-
d[i % 4] = d[i % 4] * mix + Long_val(Field(a, i));
121+
/* On 32-bit hosts, force sign-extension to 64 bits, so that
122+
the results are the same on 32-bit and 64-bit platforms */
123+
h[0] ^= (int64_t) Long_val(Field(a, i));
124+
/* Multiply by the FNV1-256 prime, 2^168 + 2^8 + 2^6 + 2^5 + 2^1 + 2^0 */
125+
memcpy(t, h, sizeof(t));
126+
shl256(t, 1-0); add256(h, t);
127+
shl256(t, 5-1); add256(h, t);
128+
shl256(t, 6-5); add256(h, t);
129+
shl256(t, 8-6); add256(h, t);
130+
shl256(t, 168-8); add256(h, t);
89131
}
90-
caml_lxm_set(v, d[0], d[1], d[2], d[3]);
132+
caml_lxm_set(v, h[0], h[1], h[2], h[3]);
91133
return Val_unit;
92134
}
93135

testsuite/tests/lib-dynlink-native/main.reference

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Registering module Plugin2
66
1
77
2
88
6
9-
6
9+
1
1010
XXX
1111
Loading plugin_thread.so
1212
Registering module Plugin_thread
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
175 168 780 564 484 867 123 178 768 467 576 542 797 603 992 635 874 323 778 676 422
1+
192 796 468 294 544 121 972 623 517 515 878 696 2 285 379 415 680 7 351 147 607
22

3-
669.156953125 242.875937515 704.511016963 148.823985021 483.099782622 777.74287 771.413854095 894.790411 761.141039066 921.111045515 984.657815283 468.838002847 891.446431758 87.8734596194 708.49973828 110.822253298 484.719704342 157.513223036 714.773419366 59.3660539904 701.591141363
3+
511.211801957 844.958249829 499.607291677 491.690183172 363.251949921 511.447587009 742.101717446 159.235602873 217.702834638 93.2460661129 791.464061354 58.9841280761 839.865622773 681.503997651 738.538364284 898.529623312 190.903176011 431.537064919 270.998675184 430.84362776 424.00611019
44
All tests succeeded.

testsuite/tests/lib-random/testvectors.ml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
(* Check the numbers drawn from a known state against the numbers
55
obtained from the reference Java implementation. *)
66

7-
open Random
7+
open Bigarray
88

99
let _ =
10-
full_init [| 1; 2; 3; 4 |];
10+
let a = Array1.of_array Int64 C_layout [| 1L; 2L; 3L; 4L |] in
11+
(* Violate abstraction of type Random.State.t to manipulate state directly *)
12+
let r = (Obj.magic a : Random.State.t) in
1113
for i = 0 to 49 do
12-
Printf.printf "%Ld\n" (bits64 ())
14+
Printf.printf "%Ld\n" (Random.State.bits64 r)
1315
done
1416

1517
let _ = exit 0
Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,50 @@
1-
1125592859273023139
2-
-8555116859351185690
3-
1740974640465038003
4-
-114103598616990594
5-
-4537459191886299567
6-
6291660581237232798
7-
4399537237262424059
8-
1444832433108046408
9-
-885086767605962159
10-
5035407203018967627
11-
2454027010810206102
12-
-3730687192620176031
13-
-9121837924364225174
14-
5158779247817520871
15-
346180386384953991
16-
-5994913815963856552
17-
6869762371190108983
18-
-351309903469929100
19-
-4064370530550403097
20-
-1705171716212891727
21-
-2597460413620866941
22-
3726722190751020339
23-
-2161674832373023869
24-
1104736721682310151
25-
-8637978141014867568
26-
563925799384532606
27-
-8148729055587940695
28-
-2600730985292293862
29-
8948433074316439393
30-
227565377938431895
31-
-8692872471483886118
32-
4158043793302624760
33-
-1494134816437625629
34-
3067733291085251934
35-
-2202687045387381141
36-
5220625884742653315
37-
-3801895474121595209
38-
8988626187385988242
39-
-25423021318571012
40-
-1519542409018449719
41-
8318221973461969899
42-
8866230809488490189
43-
8260650303702392390
44-
-1241814289009151420
45-
4356707657081988900
46-
-855677221024118431
47-
-5700738296854965036
48-
-2490506808380844303
49-
-7083650095061909347
50-
-6592815571846300453
1+
3860816457867857678
2+
21223322560256856
3+
3500884966496404595
4+
1500794501795755166
5+
-4410813772481188553
6+
-4572900151748500348
7+
4265851130944421541
8+
7572018898959715966
9+
-2937174041541593693
10+
-1802830867415637455
11+
8366519798672692173
12+
-6590954168183217171
13+
-4921483866549552021
14+
-3689136988381952376
15+
1051817605112975897
16+
-7328239262545604981
17+
5123201494011052613
18+
-2341724972498217376
19+
9115420906531662800
20+
-2254527559866817705
21+
6561621740404805009
22+
-3304584699295016645
23+
-6306474873117248843
24+
-8254191423720223116
25+
7918980772536573383
26+
3812821519051744912
27+
3169704758133872700
28+
1303179780519235243
29+
2791474158728480712
30+
5710006355063646940
31+
-4119479146064124870
32+
4742660959001555540
33+
-1339660087226824925
34+
-3233387961267533196
35+
-5600632561554148951
36+
6742170938733010945
37+
6703547594041469408
38+
6019528866072981890
39+
1653175532392381808
40+
1251959135604991018
41+
-3454440012186344425
42+
-6919427552847598775
43+
-9047964058899192553
44+
-4707136377285073135
45+
-5588355652467780140
46+
1107195072897197378
47+
-3891759467946419528
48+
97248838865565210
49+
-5756171654209134086
50+
1043655509106157291

0 commit comments

Comments
 (0)