"use strict"; /** * @class ErrorsManager * @constructor * @param {?(Object.|Array.|string)} [inputs = null] * @returns {void} * @access public * @static */ export const ErrorsManager = (function(){ /** * @constructs ErrorsManager * @param {?(Object.|Array.|string)} [inputs = null] * @returns {void} * @access private * @static */ const ErrorsManager = function(inputs = null){ /** @type {ErrorsManager} */ const self = this, /** @type {Array.} */ alphabet = [], /** @type {Object.} */ 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.)} [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)} code * @return {Array.} * @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.} */ const hexas = []; while(code){ hexas.push(code % base); code = code / base >> 0; }; return hexas; }; return []; }; /** * @param {!(string|Array.|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)} 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)} 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)} 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.} 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)} 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)} code * @returns {string|Array.|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)} code * @param {!number} from * @param {!number} bits * @returns {!(string|Array.|number|null)} * @access public */ this.get_range = (code, from, bits) => { if(ErrorsManager.is_array(code)){ /** @type {Array.} */ 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.|number)} code * @param {!Array.} messages * @param {?Object.} [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)} code * @param {!number} bits * @return {string|Array.|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.} */ 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)} code * @param {!number} from * @param {!number} bits * @param {!boolean} reversed * @returns {!(string|Array.|number|null)} * @access public */ this.reset = (code, from, bits = 0, reversed = false) => { if(ErrorsManager.is_array(code)){ /** @type {Array.} */ 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)} 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)} error * @param {!(string|Array.|number)} code * @param {!number} from * @param {!number} bits * @return {string|Array.|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.} */ ErrorsManager.ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""); /** @type {Array.} */ 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.} * @access public * @static */ ErrorsManager.get_keys = inputs => { /** @type {Array.} */ 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.>} * @access public * @static */ ErrorsManager.get_dictionaries = inputs => { /** @type {Array.>} */ 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.)} 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.)} items * @returns {string|Array.} * @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; })();