Skip to content

Commit 1ad9d03

Browse files
committed
feat(client,utils) refactor random char generation
- Generate actually unique GUIDs - Use a better random character generator - Use a better alphabet for join codes which eliminates ambiguity
1 parent ecde249 commit 1ad9d03

File tree

2 files changed

+79
-42
lines changed

2 files changed

+79
-42
lines changed
Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,9 @@
11
/**
2-
* Generates a random 8 character long string.
3-
*
4-
* @returns {string}
5-
*/
6-
export function generate8Characters() {
7-
const buf = new Uint16Array(2);
8-
9-
window.crypto.getRandomValues(buf);
10-
11-
return `${s4(buf[0])}${s4(buf[1])}`;
12-
}
13-
14-
/**
15-
* Generate a likely-to-be-unique guid.
2+
* Generate a GUID.
163
*
174
* @private
185
* @returns {string} The generated string.
196
*/
207
export function generateGuid() {
21-
const buf = new Uint16Array(8);
22-
23-
window.crypto.getRandomValues(buf);
24-
25-
return `${s4(buf[0])}${s4(buf[1])}-${s4(buf[2])}-${s4(buf[3])}-${
26-
s4(buf[4])}-${s4(buf[5])}${s4(buf[6])}${s4(buf[7])}`;
27-
}
28-
29-
/**
30-
* Converts the passed in number to a string and ensure it is at least 4
31-
* characters in length, prepending 0's as needed.
32-
*
33-
* @param {number} num - The number to pad and convert to a string.
34-
* @private
35-
* @returns {string} - The number converted to a string.
36-
*/
37-
function s4(num) {
38-
let ret = num.toString(16);
39-
40-
while (ret.length < 4) {
41-
ret = `0${ret}`;
42-
}
43-
44-
return ret;
8+
return window.crypto.randomUUID();
459
}
Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,85 @@
1+
/**
2+
* Helper class to generate random values one by one, using an auxiliary
3+
* pool to avoid calling getRandomValues every time.
4+
*/
5+
class RandomPool {
6+
/**
7+
* Default size for the pool.
8+
*/
9+
SIZE = 256;
10+
11+
/**
12+
* Build a new pool.
13+
*/
14+
constructor() {
15+
this._rArray = new Uint8Array(this.SIZE);
16+
this._idx = this.SIZE; // Fill the pool on first use.
17+
}
18+
19+
/**
20+
* Gets a random number in the Uint8 range.
21+
*
22+
* @returns {number}
23+
*/
24+
getValue() {
25+
if (this._idx > this.SIZE - 1) {
26+
// Fill the pool.
27+
window.crypto.getRandomValues(this._rArray);
28+
this._idx = 0;
29+
}
30+
31+
return this._rArray[this._idx++];
32+
}
33+
}
34+
35+
const pool = new RandomPool();
36+
37+
/**
38+
* Choose `num` elements from `seq`, randomly.
39+
*
40+
* @param {string} seq - Sequence of characters, the alphabet.
41+
* @param {number} num - The amount of characters from the alphabet we want.
42+
* @returns {string}
43+
*/
44+
function randomChoice(seq, num) {
45+
const x = seq.length - 1;
46+
const r = new Array(num);
47+
48+
while (num--) {
49+
// Make sure the random value is in our alphabet's range.
50+
const idx = pool.getValue() & x;
51+
52+
r.push(seq[idx]);
53+
}
54+
55+
return r.join('');
56+
}
57+
58+
/**
59+
* This alphabet removes all potential ambiguous symbols, so it's well suited for a code.
60+
*/
61+
const BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
62+
163
/**
264
* A method to generate a random string, intended to be used to create a random join code.
365
*
466
* @param {number} length - The desired length of the random string.
567
* @returns {string}
668
*/
769
export function generateRandomString(length) {
8-
// XXX the method may not always give desired length above 9
9-
return Math.random()
10-
.toString(36)
11-
.slice(2, 2 + length);
70+
return randomChoice(BASE32, length);
71+
}
72+
73+
/**
74+
* This alphabet is similar to BASE32 above, but includes lowercase characters too.
75+
*/
76+
const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
77+
78+
/**
79+
* Generates a random 8 character long string.
80+
*
81+
* @returns {string}
82+
*/
83+
export function generate8Characters() {
84+
return randomChoice(BASE58, 8);
1285
}

0 commit comments

Comments
 (0)