Skip to content

Commit f06f94a

Browse files
committed
fix random seed logic
1 parent a4f0c40 commit f06f94a

File tree

1 file changed

+36
-18
lines changed

1 file changed

+36
-18
lines changed
Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
1-
function randomInRange(min: number, max: number, seed: Uint8Array = crypto.getRandomValues(new Uint8Array(4))): number {
2-
const range = max - min + 1;
3-
const randomValue = (seed[0] << 24) | (seed[1] << 16) | (seed[2] << 8) | seed[3];
4-
return min + (randomValue % range);
1+
function makePRNG(seed: number) {
2+
let state = seed >>> 0;
3+
return () => {
4+
// xorshift32
5+
state ^= state << 13;
6+
state ^= state >>> 17;
7+
state ^= state << 5;
8+
return state >>> 0;
9+
};
510
}
611

712
export function makeRandomSeed(): Uint8Array {
813
return crypto.getRandomValues(new Uint8Array(4));
914
}
1015

11-
export function secureShuffle<T>(array: T[], seed: Uint8Array = crypto.getRandomValues(new Uint8Array(4))): T[] {
12-
const numbers = [];
16+
function randomInRange(min: number, max: number, nextRand: () => number): number {
17+
const range = max - min + 1;
18+
const maxRand = 0xFFFFFFFF;
19+
const limit = Math.floor(maxRand / range) * range;
1320

14-
// asynchronously generate an array of random numbers using a CSPRNG
15-
for (let i = array.length - 1; i > 0; i--) {
16-
numbers.push(randomInRange(0, i, seed));
17-
}
21+
let randomValue: number;
22+
do {
23+
randomValue = nextRand();
24+
} while (randomValue >= limit);
25+
26+
return min + (randomValue % range);
27+
}
28+
29+
export function secureShuffle<T>(array: T[], seedArray?: Uint8Array): T[] {
30+
// Convert Uint8Array seed (4 bytes) into 32-bit integer
31+
const seed =
32+
seedArray
33+
? ((seedArray[0] << 24) | (seedArray[1] << 16) | (seedArray[2] << 8) | seedArray[3]) >>> 0
34+
: crypto.getRandomValues(new Uint8Array(4)).reduce((acc, v, i) => acc | (v << ((3 - i) * 8)), 0);
1835

19-
// apply durstenfeld shuffle with previously generated random numbers
20-
for (let i = array.length - 1; i > 0; i--) {
21-
const j = numbers[array.length - i - 1];
22-
const temp = array[i];
23-
array[i] = array[j];
24-
array[j] = temp;
36+
const nextRand = makePRNG(seed);
37+
const result = array.slice();
38+
39+
// Fisher–Yates shuffle using seeded randoms
40+
for (let i = result.length - 1; i > 0; i--) {
41+
const j = randomInRange(0, i, nextRand);
42+
[result[i], result[j]] = [result[j], result[i]];
2543
}
2644

27-
return array;
28-
}
45+
return result;
46+
}

0 commit comments

Comments
 (0)