Skip to content

Commit 58bd408

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 58bd408

File tree

2 files changed

+64
-42
lines changed

2 files changed

+64
-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: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,70 @@
1+
class RandomPool {
2+
SIZE = 256;
3+
4+
constructor() {
5+
this._rArray = new Uint8Array(this.SIZE);
6+
this._idx = this.SIZE; // Fill the pool on first use.
7+
}
8+
9+
getValue() {
10+
if (this._idx > this.SIZE - 1) {
11+
// Fill the pool.
12+
globalThis.crypto.getRandomValues(this._rArray);
13+
this._idx = 0;
14+
}
15+
16+
return this._rArray[this._idx++];
17+
}
18+
}
19+
20+
const pool = new RandomPool();
21+
22+
/**
23+
* Choose `num` elements from `seq`, randomly.
24+
*
25+
* @param {string} seq - Sequence of characters, the alphabet.
26+
* @param {number} num - The amount of characters from the alphabet we want.
27+
* @returns {string}
28+
*/
29+
function randomChoice(seq, num) {
30+
const x = seq.length - 1;
31+
const r = new Array(num);
32+
33+
while (num--) {
34+
// Make sure the random value is in our alphabet's range.
35+
const idx = pool.getValue() & x;
36+
37+
r.push(seq[idx]);
38+
}
39+
40+
return r.join('');
41+
}
42+
43+
/**
44+
* This alphabet removes all potential ambiguous symbols, so it's well suited for a code.
45+
*/
46+
const BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
47+
148
/**
249
* A method to generate a random string, intended to be used to create a random join code.
350
*
451
* @param {number} length - The desired length of the random string.
552
* @returns {string}
653
*/
754
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);
55+
return randomChoice(BASE32, length);
56+
}
57+
58+
/**
59+
* This alphabet is similar to BASE32 above, but includes lowercase characters too.
60+
*/
61+
const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
62+
63+
/**
64+
* Generates a random 8 character long string.
65+
*
66+
* @returns {string}
67+
*/
68+
export function generate8Characters() {
69+
return randomChoice(BASE58, 8);
1270
}

0 commit comments

Comments
 (0)