"use strict"; /** * @class * @constructor * @param {string|Array.} [alphabet] * @returns {void} * @access public */ export const ErrorsManager = (function(){ /** * @constructs ErrorsManager * @param {string|Array.} [alphabet] * @returns {void} * @access private */ const ErrorsManager = function(alphabet){ /** @type {ErrorsManager} */ const self = this; /** @type {number} */ let error = 0; // /** @type {RegExp|null} */ // let re_hexa_error = null; /** * @returns {void} * @access private */ const constructor = () => { self.set_alphabet(alphabet); }; /** * @param {!(string|Array.)} new_alphabet * @returns {number} * @access public */ this.set_alphabet = new_alphabet => { error = ( new_alphabet === undefined ? 0 : new_alphabet === null ? 0 : !ErrorsManager.is_string(new_alphabet) && !ErrorsManager.is_array(new_alphabet) ? 1 << 2 : 0) << 1; if(!error){ if(new_alphabet){ /** @type {number} */ const original_length = new_alphabet.length, /** @type {number} */ final_length = (new_alphabet = (new_alphabet instanceof Array ? new_alphabet : new_alphabet.split("")).filter((character, i) => ( ErrorsManager.is_string(character) && character.length == 1 && new_alphabet.indexOf(character) == i ))).length; error |= ( original_length != final_length ? 1 << 0 : final_length < 64 ? 1 << 1 : 0) << 5; }; }; alphabet = error || !new_alphabet ? ( alphabet && alphabet.length ? alphabet : ErrorsManager.BASE64) : new_alphabet; // re_hexa_error = new RegExp("[^" + alphabet[0] + "]"); return error; }; /** * @param {!(string|number|Array.)} code * @param {!number} [length = 0] * @returns {Array.} * @access public */ this.to_array = (code, length = 0) => { /** @type {Array.} */ let array = []; if(ErrorsManager.is_string(code)) array = code.split("").map(hexa => alphabet.indexOf(hexa)); else if(ErrorsManager.is_integer(code)){ while(code){ array.push(code & 0x3F); code >>>= 6; }; }else if(ErrorsManager.is_array(code)) array = [].concat(code); while(array.length < length) array.push(0); return array && array.length ? array : [0]; }; /** * @param {!(number|string|Array.)} error * @param {!Array.} messages * @returns {Array.} * @access public */ this.process = (error, messages) => { /** @type {Array.} */ const response = [], /** @type {number} */ m = messages.length, /** @type {Array.} */ array_error = self.to_array(error), /** @type {number} */ l = array_error.length; for(let i = 0; i < l; i ++) for(let j = 0; j < 6; j ++) if(array_error[i] & (1 << j)){ /** @type {number} */ const k = j + i * 6; response.push([k, k < m && messages[k] ? messages[k] : "error_message_" + k]); }; return response; }; /** * @returns {Array.} * @access public */ this.get_alphabet = () => alphabet.join(""); /** * @param {!(string|number|Array.)} code * @returns {number} * @access public */ this.bits = code => { if(ErrorsManager.is_integer(code)) return code ? Math.log2(code) : 1; ErrorsManager.is_string(code) && (code = self.to_array(code)); if(ErrorsManager.is_array(code)) return !(code = self.compact(code)).length || !code[code.length - 1] ? 1 : (code.length - 1) * 6 + (Math.log2(code[code.length - 1]) + 1 >> 0); return null; }; /** * @param {!(string|number|Array.)} code * @returns {Array.} * @access public */ this.to_array_binary = code => (code = self.to_array(code)).map(hexa => ("000000" + hexa.toString(2)).slice(-6)); /** * @param {!(string|number|Array.)} code * @returns {number} * @access public */ this.to_integer = code => { if(ErrorsManager.is_integer(code)) return code; if(ErrorsManager.is_array(code)) return code.length ? code.length > 1 ? code.reduce((total, hexa, i) => total | (hexa << i * 6)) : code[0] : 0; if(!ErrorsManager.is_string(code)) return 0; return code ? code.length > 1 ? code.split("").reduce((total, hexa, i) => (i > 1 ? total : alphabet.indexOf(total)) | (alphabet.indexOf(hexa) << i * 6)) : alphabet.indexOf(code) : 0; }; /** * @param {!(string|number|Array.)} code * @param {!number} [length = 0] * @returns {string} * @access public */ this.to_string = (code, length = 0) => { /** @type {string} */ let string = ""; if(ErrorsManager.is_string(code)) string = code; else{ if(ErrorsManager.is_integer(code)){ while(code){ string += alphabet[code & 0x3F]; code >>>= 6; }; }else if(ErrorsManager.is_array(code)) string = code.length ? code.length > 1 ? code.reduce((total, hexa, i) => (i > 1 ? total : alphabet[total]) + alphabet[hexa]) : alphabet[code[0]] : alphabet[0]; }; while(string.length < length) string += alphabet[0]; return string || alphabet[0]; }; /** * @param {!(string|number|Array.)} code * @param {!(number|Array.)} [bits] * @returns {boolean|null} * @access public */ this.has = (code, bits) => { if(!ErrorsManager.is_integer(bits) && !ErrorsManager.is_array(bits)) return ( ErrorsManager.is_string(code) ? code.split("").some(hexa => hexa != alphabet[0]) : ErrorsManager.is_integer(code) ? !!code : ErrorsManager.is_array(code) ? code.some(hexa => !!hexa) : null); /** @type {Array.} */ const error = self.to_array(code), /** @type {number} */ l = error.length * 6; return l ? ( AnP.is_array(bits) ? bits : [bits] ).some(bit => bit <= l && error[bit / 6 >> 0] & (1 << bit % 6)) : false; }; /** * @param {?any} code * @returns {any|null} * @access public */ this.to_unknown = code => code; /** * @param {!(string|number|Array.)} code * @returns {string|number|Array.} * @access public */ this.compact = code => { if(ErrorsManager.is_string(code)){ while(code && code[code.length - 1] == alphabet[0]) code = code.substr(0, code.length - 1); return code || alphabet[0]; }; if(ErrorsManager.is_array(code)){ code = [].concat(code); while(code.length && !code[code.length - 1]) code.pop(); return code.length ? code : [0]; }; if(ErrorsManager.is_integer(code)) return code; return 0; }; /** * @param {!(string|number|Array.)} code * @param {!number} bits * @returns {string|number|Array.} * @access public */ this.bitwise = (code, bits) => { if(!bits || !self.has(code)) return code; /** @type {boolean} */ const reverse = bits < 0, /** @type {number} */ start = (reverse ? bits *= -1 : bits) / 6 >> 0, /** @type {number} */ rest = bits % 6, /** @type {string} */ type_method = "to_" + ErrorsManager.type(code); if(reverse){ code = self.to_string(code).substring(start); if(rest){ if(code){ /** @type {number} */ const l = (code = self.to_array(code)).length - 1, /** @type {number} */ r = 6 - rest, /** @type {number} */ block = ~-(1 << rest); for(let i = 0; i < l; i ++) code[i] = (code[i] >> rest) | ((code[i + 1] & block) << r); code[l] >>= rest; }else code = [0]; }; }else{ code = self.to_array(code); if(rest){ /** @type {number} */ const r = 6 - rest, /** @type {Array.} */ block = [~-(1 << r), ~-(1 << rest) << r], /** @type {number} */ l = code.length - 1, /** @type {number} */ last = (code[l] & block[1]) >> r; last && (code[l + 1] = last); for(let i = l; i > 0; i --) code[i] = ((code[i] & block[0]) << rest) | (code[i - 1] >> r); code[0] = (code[0] & block[0]) << rest; }; for(let i = 0; i < start; i ++) code.unshift(0); }; return self[type_method](code); }; /** * @param {!(string|number|Array.)} code * @param {!(string|number|Array.)} error * @param {!number} [bit = 0] * @param {!number} [length = 0] * @returns {string} * @access public */ this.set = (code, error, bit = 0, length = 0) => { code = self.to_array(code); error = self.to_array(error); bit && (error = self.bitwise(error, bit)); /** @type {number} */ let i = bit / 6 >> 0; if(length){ /** @type {Array.} */ const ampersand = [], /** @type {number} */ start = bit % 6, /** @type {number} */ m = (length + start) / 6 >> 0, /** @type {number} */ end = (start + length) % 6; for(let j = 0; j < m; j ++) ampersand.push(0); ampersand[0] |= ~-(1 << start); end && ampersand.push(~-(1 << (6 - end)) << end); ampersand.forEach((hexa, j) => code[j + i] &= hexa); }; if(has(error)){ /** @type {number} */ const l = error.length; while(code.length <= i) code.push(0); for(; i < l; i ++) code[i] = (code[i] || 0) | error[i]; }; return self.compact(self.to_string(code)); }; /** * @param {!(string|number|Array.)} code * @param {!(string|number|Array.)} error * @param {!number} [bit = 0] * @param {!number} [length = 0] * @returns {string} * @access public */ this.join = (code, error, bit = 0, length = 0) => self.set(code, error, bit, length); /** * @param {!(string|number|Array.)} code * @param {!Array.>} blocks * @param {!number} [bit = 0] * @param {!number} [length = 0] * @returns {string} * @access public */ this.set_blocks = (code, blocks, bit = 0, length = 0) => { /** @type {number} */ const l = blocks.length; length && (code = self.set(code, 0, bit, length)); for(let i = 0; i < l; i ++) blocks[i] && (code = self.set(code, blocks[i], bit)); return code || alphabet[0]; }; /** * @param {!(string|number|Array.)} code * @param {!number} _from * @param {!number} [_to = 0] * @returns {string|number|Array.|null} * @access public */ this.slice = (code, _from, _to = 0) => { if(!self.has(code)) return code; /** @type {number} */ const bits = self.bits(code); /** @type {number} */ let rest; _from < 0 && (_from = bits + _from); _to = ( _to > bits ? bits : _to < 0 ? bits - _to : _to) - _from; rest = _to % 6; code = self.bitwise(code, -_from); return ( ErrorsManager.is_string(code) ? code.slice(0, _to / 6 >> 0) + (rest ? alphabet[alphabet.indexOf(code.slice(-1)) & ~-(1 << rest)] : "") : ErrorsManager.is_array(code) ? code.slice(0, _to / 6 >> 0).concat(rest ? [code[code.length - 1] & ~-(1 << rest)] : []) : ErrorsManager.is_integer(code) ? code & ~-(1 << _to) : null); }; /** * @param {!(string|number|Array.)} code * @param {!number} _from * @param {!number} [_to = 0] * @returns {boolean} * @access public */ this.has_range = (code, _from, _to = 0) => self.has(self.slice(code, _from, _to)); constructor(); }; // /** @type {Array.} */ // ErrorsManager.BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""); /** * @param {?any} value * @returns {boolean} * @access public * @static */ ErrorsManager.is_string = value => typeof value == "string"; /** * @param {?any} value * @returns {boolean} * @access public * @static */ ErrorsManager.is_integer = value => typeof value == "number" && value == value >> 0; /** * @param {?any} value * @returns {boolean} * @access public * @static */ ErrorsManager.is_array = value => typeof value == "object" && value instanceof Array; /** * @param {!(string|number|Array.)} code * @returns {string} * @access public * @static */ ErrorsManager.type = code => { /** @type {Array.} */ const types = ["string", "integer", "array"]; for(let i = 0; i < 3; i ++) if(ErrorsManager["is_" + types[i]](code)) return types[i]; return "unknown"; }; return ErrorsManager; })();