1 | // This file replaces `async/format.js` in bundlers like webpack or Rollup,
|
2 | // according to `browser` config in `package.json`.
|
3 |
|
4 | module.exports = function (random, alphabet, size) {
|
5 | // We can’t use bytes bigger than the alphabet. To make bytes values closer
|
6 | // to the alphabet, we apply bitmask on them. We look for the closest
|
7 | // `2 ** x - 1` number, which will be bigger than alphabet size. If we have
|
8 | // 30 symbols in the alphabet, we will take 31 (00011111).
|
9 | // We do not use faster Math.clz32, because it is not available in browsers.
|
10 | var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1
|
11 | // Bitmask is not a perfect solution (in our example it will pass 31 bytes,
|
12 | // which is bigger than the alphabet). As a result, we will need more bytes,
|
13 | // than ID size, because we will refuse bytes bigger than the alphabet.
|
14 |
|
15 | // Every hardware random generator call is costly,
|
16 | // because we need to wait for entropy collection. This is why often it will
|
17 | // be faster to ask for few extra bytes in advance, to avoid additional calls.
|
18 |
|
19 | // Here we calculate how many random bytes should we call in advance.
|
20 | // It depends on ID length, mask / alphabet size and magic number 1.6
|
21 | // (which was selected according benchmarks).
|
22 |
|
23 | // -~f => Math.ceil(f) if n is float number
|
24 | // -~i => i + 1 if n is integer number
|
25 | var step = -~(1.6 * mask * size / alphabet.length)
|
26 |
|
27 | function tick (id) {
|
28 | return random(step).then(function (bytes) {
|
29 | // Compact alternative for `for (var i = 0; i < step; i++)`
|
30 | var i = step
|
31 | while (i--) {
|
32 | // If random byte is bigger than alphabet even after bitmask,
|
33 | // we refuse it by `|| ''`.
|
34 | id += alphabet[bytes[i] & mask] || ''
|
35 | // More compact than `id.length + 1 === size`
|
36 | if (id.length === +size) return id
|
37 | }
|
38 | return tick(id)
|
39 | })
|
40 | }
|
41 |
|
42 | return tick('')
|
43 | }
|