-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.js
78 lines (68 loc) · 2.25 KB
/
main.js
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
/* eslint-disable no-mixed-operators,unicorn/prevent-abbreviations */
/**
* 32-bit FNV-1a hash algorithm taken from https://github.com/sindresorhus/fnv1a
* @param {string} string
* @returns {bigint} between 0 and 2^32
*/
export function fnv1a32(string) {
// FNV-1a hashing
let hash = 2_166_136_261n;
const fnvPrime = 16_777_619n;
// Handle Unicode code points > 0x7f
let isUnicoded = false;
for (let index = 0; index < string.length; index++) {
let characterCode = string.charCodeAt(index);
// Non-ASCII characters trigger the Unicode escape logic
if (characterCode > 0x7F && !isUnicoded) {
string = unescape(encodeURIComponent(string));
characterCode = string.charCodeAt(index);
isUnicoded = true;
}
hash ^= BigInt(characterCode);
hash = BigInt.asUintN(32, hash * fnvPrime);
}
return hash;
}
/**
* Mulberry32 seeded PRNG algorithm taken from https://github.com/sadeqush/Shuffle-Deshuffle-Array
* @param {number|bigint} seed
* @returns {number} between 0 and 2^32
*/
export function mulberry32(seed) {
// Mulberry32 PRNG
let t = Number(seed) + 0x6D_2B_79_F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
// Unlike original Mulberry32 function, we dont need to divide by 2^32
return (t ^ t >>> 14) >>> 0;
}
/**
* Hash function algorithm: mulberry32(fnv1a(string))
* 32-bit FNV-1a hash algorithm taken from https://github.com/sindresorhus/fnv1a
* Mulberry32 seeded PRNG algorithm taken from https://github.com/sadeqush/Shuffle-Deshuffle-Array
* @param {string} string
* @returns {number} between 0 and 2^32
*/
export function hashFunc(string) {
const hash = fnv1a32(string);
// A seeded pseudorandom number generator gives more uniform distribution
// on consecutive serial strings (e.g. 'img-1', 'img-2', 'img-3'...) than
// using fnv1a hash alone.
// Mulberry32 PRNG
return mulberry32(hash);
}
/**
* @param {string|number} key
* @param {string[]} destinations
* @returns {string[]} destinations sorted in highest to lowest preference
* @throws {Error} When key is invalid
*/
export function hrwHash(key, destinations) {
return destinations
.map(destination => ({
d: destination,
w: hashFunc(String(key) + destination), // Weight
}))
.sort((a, b) => b.w - a.w)
.map(item => item.d);
}