ErrorsManager/Public/ecma/ErrorsManager.ecma.js

743 lines
23 KiB
JavaScript

"use strict";
/**
* @class ErrorsManager
* @constructor
* @param {?(Object.<string, any|null>|Array.<any|null>|string)} [inputs = null]
* @returns {void}
* @access public
* @static
*/
export const ErrorsManager = (function(){
/**
* @constructs ErrorsManager
* @param {?(Object.<string, any|null>|Array.<any|null>|string)} [inputs = null]
* @returns {void}
* @access private
* @static
*/
const ErrorsManager = function(inputs = null){
/** @type {ErrorsManager} */
const self = this,
/** @type {Array.<string>} */
alphabet = [],
/** @type {Object.<string, number>} */
dictionary = {};
/** @type {number} */
let error = 0,
/** @type {number} */
base,
/** @type {number} */
power;
/**
* @returns {void}
* @access private
*/
const constructor = () => {
self.set_alphabet(
ErrorsManager.get("alphabet", inputs, ErrorsManager.ALPHABET),
ErrorsManager.get("base", inputs, 64)
);
};
/**
* @param {?(string|Array.<string>)} [new_alphabet = null]
* @param {!number} [new_base = 64]
* @returns {number}
* @access public
*/
this.set_alphabet = (new_alphabet = null, new_base = 64) => {
/** @type {number} */
let original_length;
error = 0;
// alphabet.splice(0, alphabet.length);
alphabet.length = 0;
if(ErrorsManager.is_null_or_undefined(new_alphabet))
alphabet.push(...ErrorsManager.ALPHABET);
else if(ErrorsManager.is_string(new_alphabet))
alphabet.push(...new_alphabet.split(""));
else if(ErrorsManager.is_array(new_alphabet))
alphabet.push(...new_alphabet);
else{
error |= 1 << 2;
alphabet.push(...ErrorsManager.ALPHABET);
};
original_length = alphabet.length;
for(let i = alphabet.length - 1; i >= 0; i--)
alphabet.indexOf(alphabet[i]) != i && alphabet.splice(i, 1);
if(alphabet.length < 2){
error |= 1 << 3;
alphabet.splice(0, alphabet.length);
alphabet.push(...ErrorsManager.ALPHABET);
};
if(alphabet.length != original_length)
error |= 1 << 4;
if(alphabet.length > 128)
error |= 1 << 5;
error |= (
new_base < 2 ? 1 << 0 :
new_base > 128 ? 1 << 1 :
new_base >= alphabet.length ? 1 << 2 :
0) << 6;
if(!(error >> 6))
alphabet.splice(new_base, alphabet.length - new_base);
alphabet.splice(base = 2 ** (power = Math.log2(alphabet.slice(0, 128).length) >> 0));
for(const key in dictionary)
delete dictionary[key];
for(let i = alphabet.length - 1; i >= 0; i--)
dictionary[alphabet[i]] = i;
return error;
};
/**
* @returns {string}
* @access public
*/
this.get_alphabet = () => alphabet.join("");
/**
* @param {!(string|Array.<number>|number)} code
* @return {Array.<number>}
* @access public
*/
this.to_array = code => {
if(ErrorsManager.is_string(code))
return code.split("").map(character => dictionary[character]);
if(ErrorsManager.is_array(code))
return code;
if(ErrorsManager.is_integer(code)){
/** @type {Array.<number>} */
const hexas = [];
while(code){
hexas.push(code % base);
code = code / base >> 0;
};
return hexas;
};
return [];
};
/**
* @param {!(string|Array.<number>|number)} code
* @return {string}
* @access public
*/
this.to_string = code => {
if(ErrorsManager.is_string(code))
return code;
if(ErrorsManager.is_array(code))
return code.map(number => alphabet[number]).join("");
if(ErrorsManager.is_integer(code)){
/** @type {string} */
let string = "";
while(code){
string += alphabet[code % base];
code = code / base >> 0;
};
return string;
};
return "";
};
/**
* @param {!(string|Array.<number>|number)} code
* @return {number}
* @access public
*/
this.to_integer = code => {
if(ErrorsManager.is_string(code))
return code.split("").reduce((integer, character) => integer * base + dictionary[character], 0);
if(ErrorsManager.is_array(code))
return code.reduce((integer, number) => integer * base + number, 0);
if(ErrorsManager.is_integer(code))
return code;
return 0;
};
/**
* @param {!(string|Array.<number>|number)} code
* @return {string}
* @access public
*/
this.to_string_binary = code => {
if(ErrorsManager.is_string(code))
return code.split("").map(character => dictionary[character].toString(2).padStart(power, "0")).join("");
if(ErrorsManager.is_array(code))
return code.map(number => number.toString(2).padStart(power, "0")).join("");
if(ErrorsManager.is_integer(code)){
/** @type {string} */
let string = "";
while(code){
string = (code % base).toString(2).padStart(power, "0") + string;
code = code / base >> 0;
};
return string;
};
return "";
};
/**
* @param {!(string|Array.<number>|number)} code
* @return {number}
* @access public
*/
this.get_bits = code => {
if(ErrorsManager.is_string(code))
return (code.length - 1) * power + Math.ceil(Math.log2(dictionary[code[code.length - 1]] + 1));
if(ErrorsManager.is_array(code))
return (code.length - 1) * power + Math.ceil(Math.log2(code[code.length - 1] + 1));
if(ErrorsManager.is_integer(code))
return Math.ceil(Math.log2(code + 1));
return 0;
};
/**
* @param {!Array.<number>} code
* @param {!number} from
* @param {!number} bits
* @returns {![number, number]}
* @access private
*/
const get_from_bits_array = (code, from, bits) => {
if(from < 0){
from = self.get_bits(code) + from;
if(from < 0)
from = 0;
};
if(bits < 0){
from += bits;
bits *= -1;
if(from < 0){
bits += from;
from = 0;
};
};
return [from, bits];
};
/**
* @param {!(string|Array.<number>|number)} code
* @param {!number} from
* @param {!number} bits
* @returns {![number, number]}
* @access public
*/
this.get_from_bits = (code, from, bits) => (
ErrorsManager.is_string(code) ? get_from_bits_array(code.split("").map(character => dictionary[character]), from, bits) :
ErrorsManager.is_array(code) ? get_from_bits_array(code, from, bits) :
ErrorsManager.is_integer(code) ? get_from_bits_array(self.to_array(code), from, bits) :
[from, bits]);
/**
* @param {!(string|Array.<number>|number)} code
* @returns {string|Array.<number>|number|null}
* @access public
*/
this.clean = code => {
if(ErrorsManager.is_string(code)){
/** @type {number} */
let l = code.length;
for(; l > 0 && code[l - 1] == alphabet[0]; l --);
return (
l == code.length ? code :
!l ? alphabet[0] :
code.slice(0, l));
};
if(ErrorsManager.is_array(code)){
/** @type {number} */
let l = code.length;
for(; l > 0 && code[l - 1] == 0; l --);
return (
l == code.length ? code :
!l ? [0] :
code.slice(0, l));
};
if(ErrorsManager.is_integer(code))
return code;
return null;
};
/**
* @param {!(string|Array.<number>|number)} code
* @param {!number} from
* @param {!number} bits
* @returns {!(string|Array.<number>|number|null)}
* @access public
*/
this.get_range = (code, from, bits) => {
if(ErrorsManager.is_array(code)){
/** @type {Array.<number>} */
const hexas = [];
[from, bits] = self.get_from_bits(code, from, bits);
if(bits == 0)
bits = self.get_bits(code) - from;
if(bits <= 0)
return [0];
hexas.push(...code);
if(from > 0){
/** @type {number} */
const shift = from % power,
/** @type {number} */
mask = ~-base;
hexas = hexas.slice(from / power >> 0);
if(shift != 0 && hexas.length){
/** @type {number} */
const l = hexas.length - 1;
for(let i = 0; i < l; i ++)
hexas[i] = ((hexas[i] >> shift) | (hexas[i + 1] << power - shift)) & mask;
hexas[l] >>= shift;
};
}
if(bits > 0){
/** @type {number} */
const shift = bits % power,
/** @type {number} */
l = Math.ceil(bits / power);
hexas = hexas.slice(l, hexas.length - l);
if(shift && hexas.length)
hexas[hexas.length - 1] &= ~(-1 << shift);
};
return self.clean(hexas);
};
if(ErrorsManager.is_string(code))
return self.to_string(self.get_range(self.to_array(code), from, bits));
if(ErrorsManager.is_integer(code)){
[from, bits] = self.get_from_bits(code, from, bits);
from > 0 && (code = (code >> from) & ~-(1 << 31 - from));
if(bits <= 0 || bits >= 31)
return code;
from + bits > 31 && (bits = 31 - from);
return code & ~-(1 << bits);
};
return null;
};
/**
* @param {!(string|Array.<string>|number)} code
* @param {!Array.<string>} messages
* @param {?Object.<number, number>} [blocks = null]
* @returns {Array.<[number, string]>}
* @access public
*/
this.process = (code, messages, blocks = null) => {
/** @type {Array.<[number, string]>} */
const response = [],
/** @type {number} */
l = (code = self.to_array(code)).length;
/** @type {number} */
let k = 0;
blocks || (blocks = {});
for(let i = 0; i < l;)
if(blocks[i] !== undefined){
/** @type {number} */
const logarithm = Math.log2(blocks[i]) >> 0;
/** @type {number} */
let _k = self.to_integer(self.get_range(code, i, logarithm ? logarithm : logarithm = 1));
if(_k > 0){
_k = k + _k;
response.push([k, messages[k] || "error_message_" + k]);
};
k += blocks[i];
i += logarithm;
}else{
code[i / power >> 0] & (1 << i % power) && response.push([k, messages[k] || "error_message_" + k]);
k ++;
i ++;
};
return response;
};
/**
* @param {!(string|Array.<number>|number)} code
* @param {!number} bits
* @return {string|Array.<number>|number|null}
* @access public
*/
this.bitwise = (code, bits) => {
if(ErrorsManager.is_array(code)){
if(!code.length || !bits)
return code;
/** @type {number} */
const shift = Math.abs(bits) % power,
/** @type {number} */
mask = ~-base,
/** @type {Array.<number>} */
hexas = [...code];
if(bits < 0){
hexas.splice(0, -bits / power >> 0);
if(shift != 0 && hexas.length){
/** @type {number} */
const l = hexas.length - 1;
for(let i = 0; i < l; i ++)
hexas[i] = (hexas[i] >> shift | (hexas[i + 1] << power - shift)) & mask;
hexas[l] >>= shift;
};
}else{
if(shift != 0){
/** @type {number} */
const last_hexa = hexas[hexas.length - 1] << shift;
for(let i = hexas.length - 1; i > 0; i --)
hexas[i] = ((hexas[i] << shift) | (hexas[i - 1] >> power - shift)) & mask;
hexas[0] = (hexas[0] << shift) & mask;
if(last_hexa)
hexas.push(last_hexa);
};
for(let i = bits / power >> 0; i > 0; i --)
hexas.unshift(0);
};
return self.clean(hexas);
};
if(ErrorsManager.is_string(code))
return self.to_string(self.bitwise(self.to_array(code), bits));
if(ErrorsManager.is_integer(code)){};
return null;
};
/**
* @param {!(string|Array.<number>|number)} code
* @param {!number} from
* @param {!number} bits
* @param {!boolean} reversed
* @returns {!(string|Array.<number>|number|null)}
* @access public
*/
this.reset = (code, from, bits = 0, reversed = false) => {
if(ErrorsManager.is_array(code)){
/** @type {Array.<number>} */
const hexas = [...code];
/** @type {[number, number, number]} */
let l, from_mask, to_mask;
[from, bits] = self.get_from_bits(hexas, from, bits);
/** @type {number} */
const hexa_from = from / power >> 0,
/** @type {number} */
hexas_to = Math.ceil((from + bits) / power);
if(reversed){
l = from % power;
from_mask = ~-(1 << power) << l;
to_mask = ~-(1 << (from + bits) % power);
for(let i = 0, l = hexas.length; i < l; i ++)
if(i < hexa_from || i > hexas_to)
hexas[i] = 0;
if(hexa_from == hexa_to)
hexas[hexa_to] &= from_mask | to_mask;
else{
hexas[hexa_from] &= from_mask;
if(hexa_to < hexas.length)
hexas[hexa_to] &= to_mask;
};
}else{
l = (from + bits) % power;
from_mask = ~-(1 << from % power);
to_mask = ~-(1 << l) << l;
if(hexa_from == hexa_to)
hexas[hexa_to] &= from_mask | to_mask;
else{
hexas[hexa_from] &= from_mask;
for(let i = hexa_from + 1; i < hexas_to && i < hexas.length; i ++)
hexas[i] = 0;
if(hexa_to < hexas.length)
hexas[hexa_to] &= to_mask;
};
};
return self.clean(hexas);
};
if(ErrorsManager.is_string(code))
return self.to_string(self.reset(self.to_array(code), from, bits, reversed));
if(ErrorsManager.is_integer(code)){
[from, bits] = self.get_from_bits(code, from, bits);
from + bits > 31 && (bits = 31 - from);
return code & (
reversed ? ~-(1 << bits) << from :
(~-(1 << self.get_bits(code)) << from + bits) | ~-(1 << from));
};
return null;
};
/**
* @param {!(string|Array.<number>|number)} code
* @param {!number} from
* @param {!number} bits
* @return {boolean|null}
* @access public
*/
this.has = (code, from = 0, bits = 0) => (
ErrorsManager.is_string(code) ? self.get_range(code, from, bits).split("").some(character => character != alphabet[0]) :
ErrorsManager.is_array(code) ? self.get_range(code, from, bits).some(number => number != 0) :
ErrorsManager.is_integer(code) ? !!self.get_range(code, from, bits) :
null);
/**
* @param {!(string|Array.<number>|number)} error
* @param {!(string|Array.<number>|number)} code
* @param {!number} from
* @param {!number} bits
* @return {string|Array.<number>|number|null}
* @access public
*/
this.set = (error, code, from = 0, bits = 0) => {
if(ErrorsManager.is_array(code)){
let l,
m = error.length,
n = code.length;
const results = [];
bits && (m = (error = self.reset(error, from, bits)).length);
from && (n = (code = self.bitwise(code, from)).length);
l = m > n ? m : n;
for(let i = 0; i < l; i ++)
results.push(
(i < m ? error[i] : 0) |
(i < n ? code[i] : 0)
);
return self.clean(results);
};
if(ErrorsManager.is_string(code))
return self.to_string(self.set(self.to_array(error), self.to_array(code), from, bits));
if(ErrorsManager.is_integer(code)){
bits && (error = self.reset(error, from, bits));
from && (code = self.bitwise(code, from));
return error | code;
};
return null;
};
constructor();
};
/** @type {Array.<string>} */
ErrorsManager.ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split("");
/** @type {Array.<string>} */
ErrorsManager.ERRORS_MESSAGES = [
"invalid_alphabet",
"invalid_base",
"invalid_alphabet_type",
"too_short_alphabet",
"repeated_characters_in_alphabet",
"too_long_alphabet",
"base_lower_than_2",
"base_greater_than_128",
"base_greater_than_alphabet"
];
/** @type {RegExp} */
ErrorsManager.RE_KEY = /^[a-z_][a-z0-9_]*$/i;
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
ErrorsManager.is_array = item => item instanceof Array;
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
ErrorsManager.is_dictionary = item => item instanceof Object && item.constructor === Object;
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
ErrorsManager.is_string = item => typeof item == "string";
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
ErrorsManager.is_key = item => ErrorsManager.is_string(item) && ErrorsManager.RE_KEY.test(item);
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
ErrorsManager.is_integer = item => typeof item == "number" && item >> 0 == item;
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
ErrorsManager.is_null_or_undefined = item => item === undefined || item === null;
/**
* @param {?any} inputs
* @returns {Array.<string>}
* @access public
* @static
*/
ErrorsManager.get_keys = inputs => {
/** @type {Array.<string>} */
const keys = [];
if(ErrorsManager.is_key(inputs))
keys.push(inputs);
else if(ErrorsManager.is_array(inputs))
for(const item of inputs)
ErrorsManager.get_keys(item).forEach(key => {
keys.includes(key) || keys.push(key);
})
return keys;
};
/**
* @param {?any} inputs
* @returns {Array.<Object.<string, any|null>>}
* @access public
* @static
*/
ErrorsManager.get_dictionaries = inputs => {
/** @type {Array.<Object.<string, any|null>>} */
const dictionaries = [];
if(ErrorsManager.is_dictionary(inputs))
dictionaries.push(inputs);
else if(ErrorsManager.is_array(inputs))
for(const item of inputs)
dictionaries.push(...ErrorsManager.get_dictionaries(item));
return dictionaries;
};
/**
* @param {!(string|Array.<string>)} keys
* @param {?any} inputs
* @param {?any} [_default = null]
* @returns {any|null}
* @access public
* @static
*/
ErrorsManager.get = (keys, inputs, _default = null) => {
if((keys = ErrorsManager.get_keys(keys)).length)
for(const dictionary of ErrorsManager.get_dictionaries(inputs))
for(const key of keys)
if(key in dictionary)
return dictionary[key];
return _default;
};
/**
* @param {!(string|Array.<any|null>)} items
* @returns {string|Array.<any|null>}
* @access public
* @static
*/
ErrorsManager.unique = items => (
ErrorsManager.is_array(items) ? items.filter((item, i) => items.indexOf(item) == i) :
items.split("").filter((item, i) => items.indexOf(item) == i).join(""));
return ErrorsManager;
})();