@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
117 lines (97 loc) • 2.84 kB
JavaScript
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import { getGlobal } from "../types/global.mjs";
export { random };
/**
* this function uses crypt and returns a random number.
*
* @param {number} min starting value of the definition set (default is 0)
* @param {number} max end value of the definition set (default is 1000000000)
* @return {number}
* @throws {Error} missing crypt
* @throws {Error} we cannot generate numbers larger than 53 bits.
* @throws {Error} the distance is too small to create a random number.
* @license AGPLv3
* @since 1.0.0
* @copyright schukai GmbH
*/
function random(min, max) {
if (min === undefined) {
min = 0;
}
if (max === undefined) {
max = MAX;
}
if (max < min) {
throw new Error("max must be greater than min");
}
return Math.round(create(min, max));
}
/**
* @private
* @type {number}
*/
const MAX = 1000000000;
Math.log2 =
Math.log2 ||
function (n) {
return Math.log(n) / Math.log(2);
};
/**
*
* @param {number} min
* @param {number} max
* @return {number}
* @private
* @throws {Error} missing crypt
* @throws {Error} we cannot generate numbers larger than 53 bits.
* @throws {Error} the distance is too small to create a random number.
*/
function create(min, max) {
const globalReference = getGlobal();
const crypt =
globalReference?.["crypto"] ||
globalReference?.["msCrypto"] ||
globalReference?.["crypto"] ||
undefined;
if (typeof crypt === "undefined") {
throw new Error("missing crypt");
}
let rval = 0;
const range = max - min;
if (range < 2) {
throw new Error("the distance is too small to create a random number.");
}
const bitsNeeded = Math.ceil(Math.log2(range));
if (bitsNeeded > 53) {
throw new Error("we cannot generate numbers larger than 53 bits.");
}
const bytesNeeded = Math.ceil(bitsNeeded / 8);
const mask = Math.pow(2, bitsNeeded) - 1;
const byteArray = new Uint8Array(bytesNeeded);
crypt.getRandomValues(byteArray);
let p = (bytesNeeded - 1) * 8;
for (let i = 0; i < bytesNeeded; i++) {
rval += byteArray[i] * Math.pow(2, p);
p -= 8;
}
rval = rval & mask;
if (rval >= range) {
return create(min, max);
}
if (rval < min) {
rval += min;
}
return rval;
}