1032 lines
36 KiB
JavaScript
1032 lines
36 KiB
JavaScript
/**
|
|
* @author KyMAN
|
|
* @version 20241105
|
|
*/
|
|
|
|
/**
|
|
* @callback anyanka_keys_default_callback
|
|
* @returns {void}
|
|
*/
|
|
|
|
/**
|
|
* @callback anyanka_keys_convert_callback
|
|
* @param {!number} value
|
|
* @returns {void}
|
|
*/
|
|
|
|
/**
|
|
* @callback anyanka_keys_data_callback
|
|
* @param {!string} data
|
|
* @param {!number} length
|
|
* @returns {void}
|
|
*/
|
|
|
|
/**
|
|
* @callback anyanka_keys_value_callback
|
|
* @param {!number} value
|
|
* @param {!number} i
|
|
* @returns {void}
|
|
*/
|
|
|
|
/**
|
|
* @class
|
|
* @constructor
|
|
* @param {Object.<string, any|null>} [inputs = {}]
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
export const AnyankaKeys = (function(){
|
|
|
|
/**
|
|
* @constructs AnyankaKeys
|
|
* @param {Object.<string, any|null>} [inputs = {}]
|
|
* @returns {void}
|
|
* @access private
|
|
*/
|
|
const AnyankaKeys = function(inputs = {}){
|
|
|
|
/** @type {AnyankaKeys} */
|
|
const self = this,
|
|
/** @type {Array.<number>} */
|
|
public_key = [],
|
|
/** @type {Array.<number>} */
|
|
private_key = [],
|
|
/** @type {AnyankaKeys.Options} */
|
|
options = new AnyankaKeys.Options(inputs);
|
|
/** @type {string} */
|
|
let alphabet = AnyankaKeys.SETTINGS.alpahbet,
|
|
/** @type {number} */
|
|
base_from = AnyankaKeys.SETTINGS.base_from,
|
|
/** @type {number} */
|
|
base_to = AnyankaKeys.SETTINGS.base_to,
|
|
/** @type {number} */
|
|
divisor = AnyankaKeys.SETTINGS.divisor,
|
|
/** @type {number} */
|
|
public_key_length = AnyankaKeys.SETTINGS.public_key_length,
|
|
/** @type {number} */
|
|
private_key_length = AnyankaKeys.SETTINGS.private_key_length,
|
|
/** @type {number} */
|
|
segments_key_length = AnyankaKeys.SETTINGS.segments_key_length,
|
|
/** @type {number|boolean|null} */
|
|
summatory_value = AnyankaKeys.SETTINGS.summatory,
|
|
/** @type {boolean} */
|
|
has_incremental = AnyankaKeys.SETTINGS.incremental,
|
|
/** @type {boolean|number|null} */
|
|
random_private_start = AnyankaKeys.SETTINGS.random_private_start,
|
|
/** @type {number} */
|
|
hash_length = AnyankaKeys.SETTINGS.hash_length,
|
|
/** @type {number} */
|
|
hash_power = AnyankaKeys.SETTINGS.hash_power;
|
|
|
|
/**
|
|
* @returns {void}
|
|
* @access private
|
|
*/
|
|
const constructor = () => {
|
|
|
|
if(inputs){
|
|
if(typeof inputs == "string")
|
|
inputs = {alphabet : inputs};
|
|
else if(!AnyankaKeys.is_dictionary(inputs))
|
|
inputs = {};
|
|
}else
|
|
inputs = {};
|
|
|
|
inputs.alphabet !== undefined && (alphabet = inputs.alphabet);
|
|
inputs.base_from !== undefined && (base_from = inputs.base_from);
|
|
inputs.base_to !== undefined && (base_to = inputs.base_to);
|
|
inputs.public_key_length !== undefined && (public_key_length = inputs.public_key_length);
|
|
inputs.private_key_length !== undefined && (private_key_length = inputs.private_key_length);
|
|
inputs.segments_key_length !== undefined && (segments_key_length = inputs.segments_key_length);
|
|
inputs.summatory !== undefined && (summatory_value = inputs.summatory);
|
|
inputs.incremental !== undefined && (has_incremental = inputs.incremental);
|
|
inputs.random_private_start !== undefined && (random_private_start = inputs.random_private_start);
|
|
inputs.hash_length !== undefined && (hash_length = inputs.hash_length);
|
|
inputs.hash_power !== undefined && (hash_power = inputs.hash_power);
|
|
|
|
(
|
|
typeof (alphabet = inputs.alphabet) != "string" ||
|
|
(alphabet = [...alphabet].filter((character, index) => alphabet.indexOf(character) == index)).length < 1
|
|
) && (alphabet = AnyankaKeys.SETTINGS.alphabet);
|
|
(
|
|
typeof (base_from = inputs.base_from) != "number" ||
|
|
base_from != base_from >> 0 ||
|
|
base_from < 2
|
|
) && (base_from = AnyankaKeys.SETTINGS.base_from);
|
|
(
|
|
typeof (base_to = inputs.base_to) != "number" ||
|
|
base_to != base_to >> 0 ||
|
|
base_to < 2 ||
|
|
base_to >= alphabet.length
|
|
) && (base_to = AnyankaKeys.SETTINGS.base_to);
|
|
|
|
self.set_public_key(true);
|
|
|
|
typeof summatory_value != "number" && summatory_value !== true && summatory_value !== null && (summatory_value = AnyankaKeys.SETTINGS.summatory);
|
|
typeof has_incremental != "boolean" && (has_incremental = AnyankaKeys.SETTINGS.incremental);
|
|
typeof random_private_start != "number" && random_private_start !== true && random_private_start !== null && (random_private_start = AnyankaKeys.SETTINGS.random_private_start);
|
|
typeof hash_length != "number" && (hash_length = AnyankaKeys.SETTINGS.hash_length);
|
|
typeof hash_power != "number" && (hash_power = AnyankaKeys.SETTINGS.hash_power);
|
|
|
|
};
|
|
|
|
/**
|
|
* @returns {string|null}
|
|
* @access public
|
|
*/
|
|
this.get_public_key = () => public_key.length ? public_key.map(key => alphabet[key]).join("") : null;
|
|
|
|
/**
|
|
* @param {!number} x
|
|
* @returns {number}
|
|
* @access private
|
|
*/
|
|
const next = x => {
|
|
|
|
x += 1 + divisor;
|
|
while(x >= 2)
|
|
x --;
|
|
|
|
return x;
|
|
};
|
|
|
|
/**
|
|
* @param {string|boolean|null} [new_key = null]
|
|
* @returns {number}
|
|
* @access public
|
|
*/
|
|
this.set_public_key = (new_key = null) => {
|
|
|
|
/** @type {number} */
|
|
let error = 0;
|
|
|
|
if(new_key === undefined)
|
|
error |= 1 << 1;
|
|
else{
|
|
if(new_key === null){
|
|
AnyankaKeys.reset_array_with(public_key);
|
|
AnyankaKeys.reset_array_with(private_key, [0]);
|
|
}else{
|
|
|
|
/** @type {Array.<number>|null} */
|
|
let new_public_key = null;
|
|
|
|
switch(typeof new_key){
|
|
case "boolean":
|
|
if(new_key)
|
|
new_public_key = Array.from({
|
|
length : public_key_length
|
|
}, () => Math.random() * base_to >> 0);
|
|
else
|
|
error |= 1 << 4;
|
|
break;
|
|
case "string":
|
|
new_public_key = [...new_key].map(character => alphabet.indexOf(character));
|
|
if(Math.min(new_public_key) == -1)
|
|
error |= 1 << 5;
|
|
break;
|
|
default:
|
|
error |= 1 << 3;
|
|
break;
|
|
};
|
|
|
|
if(!error){
|
|
|
|
AnyankaKeys.reset_array_with(public_key, new_public_key);
|
|
|
|
/** @type {number} */
|
|
const segment = public_key.length / segments_key_length >> 0,
|
|
/** @type {number} */
|
|
start = Number(("" + divisor).split(".")[1]) % base_to;
|
|
/** @type {number} */
|
|
let x = 1;
|
|
|
|
public_key.length && AnyankaKeys.reset_array_with(private_key, Array.from({
|
|
length : private_key_length
|
|
}, (_, i) => (AnyankaKeys.summatory(
|
|
segments_key_length,
|
|
j => public_key[(j * segment + i) % public_key_length],
|
|
start,
|
|
base_to
|
|
) * (x = next(x)) >> 0) % base_to));
|
|
|
|
};
|
|
|
|
};
|
|
};
|
|
|
|
return error;
|
|
};
|
|
|
|
/**
|
|
* @param {!number} i
|
|
* @param {!number} value
|
|
* @returns {string}
|
|
* @access private
|
|
*/
|
|
const set_key = (i, value) => alphabet[(value + private_key[i % private_key_length]) % base_to];
|
|
|
|
/**
|
|
* @param {!(any|null)} data
|
|
* @returns {string}
|
|
* @access public
|
|
*/
|
|
this.encrypt = data => {
|
|
|
|
/** @type {string} */
|
|
let header = "",
|
|
/** @type {string} */
|
|
body = "",
|
|
/** @type {number} */
|
|
pre_empties = 0,
|
|
/** @type {number} */
|
|
i = 0;
|
|
/** @type {AnyankaKeys.BaseChange.Parameters} */
|
|
const parameters = new AnyankaKeys.BaseChange.Parameters({
|
|
/** @type {number} */
|
|
from : base_from,
|
|
/** @type {number} */
|
|
to : base_to
|
|
}),
|
|
/** @type {Array.<number>} */
|
|
header_data = (
|
|
random_private_start === true ? Array.from({length : 2}, () => Math.random() * base_to >> 0) :
|
|
typeof random_private_start == "number" ? Array.from([base_to, 1], i => (random_private_start / i >> 0) % base_to) :
|
|
[0, 0]),
|
|
/** @type {number} */
|
|
summatory = (summatory_value === true ? Math.random() * base_to >> 0 : summatory_value % 58) || 1;
|
|
/** @type {number} */
|
|
private_start = (header_data[0] * base_to + header_data[1]) % private_key_length,
|
|
/** @type {anyanka_keys_convert_callback} */
|
|
set = value => body = set_key(private_start + (has_incremental ? i ++ : 1) * summatory, value) + body,
|
|
/** @type {AnyankaKeys.Type} */
|
|
type = AnyankaKeys.get_each_bytes(data, byte => AnyankaKeys.change_base_iteration(byte, set, parameters), options);
|
|
|
|
if([5, 6, 7, 8].includes(type.code))
|
|
data.some(value => {
|
|
if(value)
|
|
return true;
|
|
pre_empties ++;
|
|
});
|
|
else if(type.code == 10)
|
|
pre_empties = type.empties;
|
|
|
|
parameters.end(set);
|
|
header += alphabet[header_data[0]] + alphabet[header_data[1]] + set_key(private_start, summatory);
|
|
[type.code, pre_empties].forEach((value, i) => header += set_key(private_start + (has_incremental ? i : 1) * summatory, value));
|
|
|
|
return header + body;
|
|
};
|
|
|
|
/**
|
|
* @param {!number} i
|
|
* @param {!string} character
|
|
* @returns {number}
|
|
* @access private
|
|
*/
|
|
const get_key = (i, character) => {
|
|
|
|
/** @type {number} */
|
|
let value = alphabet.indexOf(character) - private_key[i % private_key_length];
|
|
|
|
return value < 0 ? value + base_to : value;
|
|
};
|
|
|
|
/**
|
|
* @param {!string} data
|
|
* @returns {any|null}
|
|
* @access public
|
|
*/
|
|
this.decrypt = data => {
|
|
|
|
/** @type {string} */
|
|
const header = data.substring(0, 5),
|
|
/** @type {AnyankaKeys.BaseChange.Parameters} */
|
|
parameters = new AnyankaKeys.BaseChange.Parameters({
|
|
/** @type {number} */
|
|
from : base_to,
|
|
/** @type {number} */
|
|
to : base_from
|
|
}),
|
|
/** @type {string} */
|
|
body = data.substring(5),
|
|
/** @type {number} */
|
|
body_l = body.length - 1,
|
|
/** @type {number} */
|
|
private_start = (alphabet.indexOf(header[0]) * base_to + alphabet.indexOf(header[1])) % private_key_length,
|
|
/** @type {number} */
|
|
summatory = get_key(private_start, header[2]),
|
|
/** @type {AnyankaKeys.Type} */
|
|
type = new AnyankaKeys.Type(get_key(
|
|
private_start + (has_incremental ? 0 : summatory), header[3]
|
|
), body, parameters.get_size(body), options);
|
|
/** @type {number} */
|
|
let pre_empties = get_key(private_start + summatory, header[4]);
|
|
|
|
[...body].forEach((character, i) => AnyankaKeys.change_base_iteration(get_key(
|
|
private_start + (has_incremental ? body_l - i : 1) * summatory, character
|
|
), type.set_value, parameters));
|
|
parameters.end(type.set_value);
|
|
(pre_empties == type.length() ? -- pre_empties : pre_empties) > 0 && type.set_pre_empties(pre_empties);
|
|
type.build();
|
|
|
|
return type.data;
|
|
};
|
|
|
|
/**
|
|
* @param {any|null} data
|
|
* @returns {string}
|
|
* @access public
|
|
*/
|
|
this.hash = data => {
|
|
|
|
/** @type {number} */
|
|
let summatory = 1,
|
|
/** @type {number} */
|
|
i = 0,
|
|
/** @type {number} */
|
|
x = 1,
|
|
/** @type {number} */
|
|
wise = 0;
|
|
/** @type {Array.<number>} */
|
|
const hash = Array.from({length : hash_length}, () => 0),
|
|
/** @type {number} */
|
|
value_limit = base_to ** hash_power,
|
|
/** @type {AnyankaKeys.Type} */
|
|
type = AnyankaKeys.get_each_bytes(data, byte => {
|
|
|
|
/** @type {number} */
|
|
const k = i ++ % hash_length;
|
|
|
|
summatory = (summatory + byte) % value_limit;
|
|
hash[k] = (hash[k] + byte) % value_limit;
|
|
|
|
}, new AnyankaKeys.Options()),
|
|
/** @type {number} */
|
|
base = summatory ** divisor / ++ i ** divisor;
|
|
|
|
hash[0] += type.code;
|
|
|
|
return hash.map(value => {
|
|
|
|
value = wise ** (x = next(x)) + (base * value) ** x + base ** x;
|
|
while((value *= base_to) < value_limit);
|
|
wise += (value = (value >> 0) % base_to);
|
|
|
|
return alphabet[value];
|
|
}).join("");
|
|
};
|
|
|
|
constructor();
|
|
|
|
};
|
|
|
|
/** @type {Object.<string, string|number|boolean|null>} */
|
|
AnyankaKeys.SETTINGS = {
|
|
/** @type {string} */
|
|
alphabet : "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
/** @type {number} */
|
|
base_to : 58, // Cast for unique alphabet characters.
|
|
/** @type {number} */
|
|
base_from : 256,
|
|
/** @type {boolean|string|null} */
|
|
public_key : true, // true = Random || null = No keys || String = Default key
|
|
/** @type {number} */
|
|
public_key_length : 1 << 8,
|
|
/** @type {number} */
|
|
private_key_length : 1 << 12,
|
|
/** @type {number} */
|
|
segments_key_length : 13,
|
|
/** @type {boolean|number|null} */
|
|
summatory : true, // true = Random || null = No summatory || number = Default summatory
|
|
/** @type {boolean} */
|
|
incremental : true,
|
|
/** @type {boolean|number|null} */
|
|
random_private_start : true,
|
|
/** @type {number} */
|
|
divisor : .21417,
|
|
/** @type {number} */
|
|
hash_length : 47,
|
|
/** @type {number} */
|
|
hash_power : 1.89673,
|
|
/** @type {number} */
|
|
scape_character : 92, // Character ["\\", 92]
|
|
/** @type {number} */
|
|
scape_blocks : 2 // Bits: [UTF-8, UTF-16, URI, Force UTF-16]
|
|
};
|
|
|
|
/**
|
|
* @param {!number} length
|
|
* @param {!anyanka_keys_convert_callback} item_callback
|
|
* @returns {void}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.bucle = (length, item_callback) => {
|
|
|
|
/** @type {number} */
|
|
let i = 0;
|
|
|
|
while(i < length)
|
|
item_callback(i ++);
|
|
|
|
};
|
|
|
|
/**
|
|
* @param {!number} length
|
|
* @param {!anyanka_keys_convert_callback} callback
|
|
* @param {!number} [start = 0]
|
|
* @param {?number} [limit = null]
|
|
* @returns {number}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.summatory = (length, callback, start = 0, limit = null) => {
|
|
|
|
limit || (limit = ~-(1 << 31));
|
|
|
|
AnyankaKeys.bucle(length, i => start = (start + callback(i)) % limit);
|
|
|
|
return start;
|
|
};
|
|
|
|
/**
|
|
* @param {!Array.<any|null>} array
|
|
* @param {Array.<any|null>} [new_data = []]
|
|
* @returns {void}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.reset_array_with = (array, new_data = []) => {
|
|
array.splice(0, array.length, ...new_data);
|
|
};
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {!Object.<string, number>} [inputs = {}]
|
|
* @returns {void}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.Options = function(inputs = {}){
|
|
|
|
/** @type {number} */
|
|
this.scape_blocks = (
|
|
inputs.scape_blocks !== undefined &&
|
|
typeof inputs.scape_blocks == "number" &&
|
|
inputs.scape_blocks >= 0 &&
|
|
inputs.scape_blocks <= 4
|
|
) ? inputs.scape_blocks : AnyankaKeys.SETTINGS.scape_blocks;
|
|
/** @type {number} */
|
|
this.scape_character = (
|
|
inputs.scape_character !== undefined &&
|
|
typeof inputs.scape_character == "number" &&
|
|
inputs.scape_character & 0xFF == inputs.scape_character
|
|
) ? inputs.scape_character : AnyankaKeys.SETTINGS.scape_character;
|
|
|
|
/** @type {boolean} */
|
|
this.scape_utf8 = !!(this.scape_blocks & (1 << 0));
|
|
/** @type {boolean} */
|
|
this.scape_utf16 = !!(this.scape_blocks & (1 << 1));
|
|
/** @type {boolean} */
|
|
this.scape_uri = !!(this.scape_blocks & (1 << 2));
|
|
/** @type {boolean} */
|
|
this.force_utf16 = !!(this.scape_blocks & (1 << 3));
|
|
/** @type {boolean} */
|
|
this.scape = this.scape_utf8 || this.scape_utf16;
|
|
|
|
};
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {string|number} [name = "unknown"]
|
|
* @param {string|null} [data = null]
|
|
* @param {number} [length = 0]
|
|
* @param {?AnyankaKeys.Options} [options = null]
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
AnyankaKeys.Type = function(name = "unknown", data = null, length = 0, options = null){
|
|
|
|
/** @type {AnyankaKeys.Type} */
|
|
const self = this;
|
|
/** @type {any|null} */
|
|
let cache = null,
|
|
/** @type {Array.<number>|null} */
|
|
string_cache = null;
|
|
|
|
/** @type {number} */
|
|
this.code = 0;
|
|
/** @type {string} */
|
|
this.name = name;
|
|
/** @type {any|null} */
|
|
this.data = null;
|
|
/** @type {number} */
|
|
this.empties = 0;
|
|
|
|
/**
|
|
* @returns {void}
|
|
* @access private
|
|
*/
|
|
const constructor = () => {
|
|
|
|
if(typeof name == "number")
|
|
self.name = AnyankaKeys.Type.TYPES[self.code = name];
|
|
else if(name){
|
|
if(self.code = AnyankaKeys.Type.TYPES.indexOf(name = name.toLowerCase()) != -1)
|
|
self.name = name;
|
|
else
|
|
self.code = 0;
|
|
};
|
|
|
|
data !== null && self.start(data, length);
|
|
options || (options = new AnyankaKeys.Options());
|
|
|
|
};
|
|
|
|
/**
|
|
* @param {!number} code
|
|
* @param {?name} [name = null]
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.set = (code, name = null) => {
|
|
self.code = code;
|
|
name && (self.name = name);
|
|
};
|
|
|
|
/**
|
|
* @param {!number} byte
|
|
* @returns {string}
|
|
* @access private
|
|
*/
|
|
const get_character_by_byte = (byte) => {
|
|
|
|
/** @type {string} */
|
|
let character = "",
|
|
/** @type {boolean} */
|
|
force_scape = false;
|
|
|
|
if(options.scape){
|
|
if(string_cache === null){
|
|
if(byte == options.scape_character)
|
|
string_cache = [];
|
|
else
|
|
character = String.fromCharCode(byte);
|
|
}else if(byte == options.scape_character){
|
|
if(!string_cache.length)
|
|
character = String.fromCharCode(byte);
|
|
else{
|
|
|
|
/** @type {number} */
|
|
let code = 0;
|
|
|
|
string_cache.forEach((byte, i) => code += byte << i * 8);
|
|
character = String.fromCharCode(code);
|
|
force_scape = true;
|
|
string_cache = [];
|
|
|
|
};
|
|
}else if(!string_cache.length){
|
|
if(options.scape_utf16)
|
|
string_cache.push(byte);
|
|
else
|
|
character = String.fromCharCode(string_cache[0]);
|
|
}else
|
|
character = String.fromCharCode(string_cache[0] | (byte << 8));
|
|
}else
|
|
character = String.fromCharCode(byte);
|
|
|
|
string_cache !== null && !force_scape && character !== "" && (string_cache = null);
|
|
|
|
return character;
|
|
};
|
|
|
|
/** @type {Object.<string, Object.<string, anyanka_keys_data_callback>|Object.<string, anyanka_keys_value_callback>|Object.<string, anyanka_keys_convert_callback>|Object.<string, anyanka_keys_default_callback>>} */
|
|
this.action = {
|
|
/** @type {Object.<string, anyanka_keys_data_callback>} */
|
|
start : {
|
|
unknown : (data, length) => {},
|
|
string : (data, length) => self.data = "",
|
|
undefined : (data, length) => self.data = undefined,
|
|
null : (data, length) => {},
|
|
json : (data, length) => cache = "",
|
|
utf8 : (data, length) => cache = [],
|
|
utf16 : (data, length) => cache = [],
|
|
utf32 : (data, length) => cache = [],
|
|
bytes : (data, length) => self.data = [],
|
|
bool : (data, length) => {},
|
|
integer : (data, length) => self.data = 0,
|
|
float : (data, length) => {},
|
|
custom : (data, length) => cache = []
|
|
},
|
|
/** @type {Object.<string, anyanka_keys_value_callback>} */
|
|
set_value : {
|
|
unknown : (value, i) => {},
|
|
string : (value, i) => self.data = get_character_by_byte(value) + self.data,
|
|
undefined : (value, i) => {},
|
|
null : (value, i) => {},
|
|
json : (value, i) => cache = get_character_by_byte(value) + cache,
|
|
utf8 : (value, i) => cache.unshift(value),
|
|
utf16 : (value, i) => {
|
|
if(i % 2)
|
|
cache[0] = (cache[0] << 8) | value;
|
|
else
|
|
cache.unshift(value);
|
|
},
|
|
utf32 : (value, i) => {
|
|
if(i % 4)
|
|
cache[0] = (cache[0] << 8) | value;
|
|
else
|
|
cache.unshift(value);
|
|
},
|
|
bytes : (value, i) => self.data.unshift(value),
|
|
bool : (value, i) => self.data = (
|
|
i ? null :
|
|
value === 1 ? true :
|
|
value === 0 ? false :
|
|
null),
|
|
integer : (value, i) => self.data = (self.data << 8) | value,
|
|
float : (value, i) => {},
|
|
custom : (value, i) => cache.push(value)
|
|
},
|
|
/** @type {Object.<string, anyanka_keys_convert_callback>} */
|
|
set_pre_empties : {
|
|
unknown : empties => {},
|
|
string : empties => {},
|
|
undefined : empties => {},
|
|
null : empties => {},
|
|
json : empties => {},
|
|
utf8 : empties => AnyankaKeys.bucle(empties, () => cache.unshift(0)),
|
|
utf16 : empties => AnyankaKeys.bucle(empties, () => cache.unshift(0)),
|
|
utf32 : empties => AnyankaKeys.bucle(empties, () => cache.unshift(0)),
|
|
bytes : empties => AnyankaKeys.bucle(empties, () => self.data.unshift(0)),
|
|
bool : empties => {},
|
|
integer : empties => self.data <<= empties * 8,
|
|
float : empties => {},
|
|
custom : empties => AnyankaKeys.bucle(empties, () => self.data.unshift(0))
|
|
},
|
|
/** @type {Object.<string, anyanka_keys_default_callback>} */
|
|
build : {
|
|
unknown : () => {},
|
|
string : () => {
|
|
string_cache !== null && (self.data = String.fromCharCode(0) + self.data);
|
|
options.scape_uri && (self.data = decodeURIComponent(self.data));
|
|
},
|
|
undefined : () => {},
|
|
null : () => {},
|
|
json : () => {
|
|
string_cache !== null && (cache = String.fromCharCode(0) + cache);
|
|
self.data = JSON.parse(cache ? options.scape_uri ? decodeURIComponent(cache) : cache : "[]");
|
|
},
|
|
utf8 : () => self.data = new Uint8Array(cache),
|
|
utf16 : () => self.data = new Uint16Array(cache),
|
|
utf32 : () => self.data = new Uint32Array(cache),
|
|
bytes : () => {},
|
|
bool : () => {},
|
|
integer : () => {},
|
|
float : () => {},
|
|
custom : () => {}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {!string} data
|
|
* @param {!number} length
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.start = (data, length) => {
|
|
self.action.start[self.name](data, length);
|
|
};
|
|
|
|
/**
|
|
* @param {!number} value
|
|
* @param {!number} i
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.set_value = (value, i) => {
|
|
self.action.set_value[self.name](value, i);
|
|
}
|
|
|
|
/**
|
|
* @param {!number} empties
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.set_pre_empties = empties => {
|
|
self.action.set_pre_empties[self.name](empties);
|
|
}
|
|
|
|
/**
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.build = () => {
|
|
self.action.build[self.name]();
|
|
}
|
|
|
|
/**
|
|
* @returns {number}
|
|
* @access public
|
|
*/
|
|
this.length = () => (cache || self.data || []).length;
|
|
|
|
constructor();
|
|
|
|
};
|
|
|
|
/** @type {Array.<string>} */
|
|
AnyankaKeys.Type.TYPES = [
|
|
"unknown", "string", "undefined", "null", "json", "utf8", "utf16",
|
|
"utf32", "bytes", "bool", "integer", "float", "custom"
|
|
];
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {?Object.<string, number>} [inputs = null]
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
AnyankaKeys.BaseChange = function(inputs = null){
|
|
/** @type {number} */
|
|
this.FROM = (inputs || (inputs = {})).from || inputs.base_from || AnyankaKeys.SETTINGS.base_from;
|
|
/** @type {number} */
|
|
this.TO = inputs.to || inputs.base_to || AnyankaKeys.SETTINGS.base_to;
|
|
};
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {?Object.<string, number>} [inputs = null]
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
AnyankaKeys.BaseChange.Parameters = function(inputs){
|
|
|
|
/** @type {AnyankaKeys.BaseChange.Parameters} */
|
|
const self = this;
|
|
/** @type {number} */
|
|
let i = 0;
|
|
|
|
/** @type {AnyankaKeys.BaseChange} */
|
|
this.BASE = new AnyankaKeys.BaseChange(inputs);
|
|
/** @type {number|null} */
|
|
this.summatory = null;
|
|
|
|
/**
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.go_to_next = () => {
|
|
self.summatory = (self.summatory || 0) / self.BASE.TO >> 0;
|
|
i ++;
|
|
};
|
|
|
|
/**
|
|
* @param {!number} value
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.add = value => {
|
|
self.summatory = (self.summatory || 0) * self.BASE.FROM + value;
|
|
};
|
|
|
|
/**
|
|
* @param {!anyanka_keys_convert_callback} callback
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.process = callback => {
|
|
if(self.summatory !== null)
|
|
while(self.summatory >= self.BASE.TO){
|
|
callback(self.summatory % self.BASE.TO, i);
|
|
self.go_to_next();
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param {!anyanka_keys_convert_callback} callback
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
this.end = callback => {
|
|
self.process(callback);
|
|
if(self.summatory !== null && (!i || self.summatory)){
|
|
callback(self.summatory, i);
|
|
self.go_to_next();
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param {!string} data
|
|
* @returns {number}
|
|
* @access public
|
|
*/
|
|
this.get_size = data => Math.round(data.length * Math.log2(self.BASE.FROM) / Math.log2(self.BASE.TO));
|
|
|
|
};
|
|
|
|
/**
|
|
* @param {!(any|null)} value
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.is_dictionary = value => (
|
|
value !== undefined &&
|
|
value !== null &&
|
|
value.constructor == Object
|
|
);
|
|
|
|
/**
|
|
* @param {!string} string
|
|
* @param {!anyanka_keys_convert_callback} set
|
|
* @param {!AnyankaKeys.Options} options
|
|
* @returns {void}
|
|
* @access public
|
|
*/
|
|
AnyankaKeys.get_each_bytes_in_string = (string, set, options) => [...(
|
|
options.scape_uri ? encodeURIComponent(string) : string
|
|
)].forEach(character => {
|
|
|
|
/** @type {number} */
|
|
const code = character.charCodeAt(0),
|
|
/** @type {boolean} */
|
|
is_utf8 = (code & 0xFF) == code;
|
|
|
|
if(options.force_utf16)
|
|
[8, 0].forEach(wise => set((code >> wise) & 0xFF));
|
|
else if(options.scape){
|
|
if(code == options.scape_character)
|
|
[null, null].forEach(() => set(code));
|
|
else if(is_utf8){
|
|
set(code);
|
|
options.scape_utf8 && set(options.scape_character);
|
|
}else{
|
|
[8, 0].forEach(wise => set((code >> wise) & 0xFF));
|
|
options.scape_utf16 && set(options.scape_character);
|
|
};
|
|
}else
|
|
set(code);
|
|
|
|
});
|
|
|
|
/**
|
|
* @param {!(any|null)} data
|
|
* @param {!anyanka_keys_convert_callback} set
|
|
* @param {!AnyankaKeys.Options} options
|
|
* @returns {AnyankaKeys.Type}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.get_each_bytes = (data, set, options) => {
|
|
|
|
/** @type {AnyankaKeys.Type} */
|
|
let type = new AnyankaKeys.Type();
|
|
|
|
if(data === undefined)
|
|
type.set(2);
|
|
else if(data === null)
|
|
type.set(3);
|
|
else
|
|
switch(typeof data){
|
|
case "string":
|
|
AnyankaKeys.get_each_bytes_in_string(data, set, options);
|
|
type.set(1);
|
|
break;
|
|
case "boolean":
|
|
set(data ? 1 : 0);
|
|
type.set(9);
|
|
break;
|
|
case "number":
|
|
if(data == data >> 0){
|
|
|
|
let empties_done = false;
|
|
|
|
do{
|
|
set(data & 0xFF);
|
|
if(!empties_done){
|
|
if(data & 0xFF)
|
|
empties_done = true;
|
|
else
|
|
type.empties ++;
|
|
};
|
|
}while(data >>= 8);
|
|
|
|
type.set(10);
|
|
|
|
}else{
|
|
[...("" + data)].forEach(character => set(character.charCodeAt(0)));
|
|
type.set(11);
|
|
};
|
|
break;
|
|
case "object":
|
|
if(data instanceof Uint8Array){
|
|
type.set(5);
|
|
[...data].forEach(set);
|
|
}else if(data instanceof Uint16Array){
|
|
[...data].forEach(smallint => [0, 1].forEach(i => set((smallint >> i * 8) & 0xFF)));
|
|
type.set(6);
|
|
}else if(data instanceof Uint32Array){
|
|
[...data].forEach(integer => [0, 1, 2, 3].forEach(i => set((integer >> i * 8) & 0xFF)));
|
|
type.set(7);
|
|
}else if(data instanceof Array && !data.some(value => typeof value != "number" || value < 0 || value > 0xFF)){
|
|
data.forEach(set);
|
|
type.set(8);
|
|
}else if(["Array", "Object"].includes(data.constructor.name)){
|
|
AnyankaKeys.get_each_bytes_in_string(JSON.stringify(data), set, options);
|
|
// [...JSON.stringify(data)].forEach(character => set(character.charCodeAt(0)));
|
|
type.set(4);
|
|
}else{
|
|
type.set(12, data.constructor.name);
|
|
try{
|
|
AnyankaKeys.get_each_bytes_in_string(JSON.stringify(data), set, options);
|
|
// [...JSON.stringify(data)].forEach(character => set(character.charCodeAt(0)));
|
|
}catch(exception){
|
|
type.set(0);
|
|
};
|
|
};
|
|
break;
|
|
};
|
|
|
|
return type;
|
|
};
|
|
|
|
/**
|
|
* @param {!(number|Array.<number>)} data
|
|
* @param {!anyanka_keys_convert_callback} set
|
|
* @param {?(AnyankaKeys.BaseChange.Parameters|Object.<string, any|null>)} [parameters = null]
|
|
* @returns {void}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.change_base_iteration = (data, set, parameters = null) => {
|
|
|
|
(
|
|
!parameters ||
|
|
!(parameters instanceof AnyankaKeys.BaseChange.Parameters)
|
|
) && (parameters = new AnyankaKeys.BaseChange.Parameters(inputs));
|
|
|
|
(data instanceof Array ? data : [data]).forEach(value => {
|
|
parameters.add(value);
|
|
parameters.process(set);
|
|
});
|
|
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>)} value
|
|
* @param {!number} to
|
|
* @param {?number} [from = null]
|
|
* @param {string|null} [type = null]
|
|
* @returns {string|Array.<number>|null}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
AnyankaKeys.change_base = (value, to, from = null, type = null) => {
|
|
|
|
from || (from = AnyankaKeys.SETTINGS.base_from);
|
|
type || (type = (
|
|
typeof value == "number" && value == value >> 0 ? "integer" :
|
|
typeof value == "string" ? "string" :
|
|
value instanceof Array ? "array" :
|
|
"unknown"));
|
|
|
|
/** @type {string|Array.<number>|null} */
|
|
let response = null,
|
|
/** @type {anyanka_keys_convert_callback|null} */
|
|
set = null,
|
|
/** @type {anyanka_keys_default_callback|null} */
|
|
iterator = null;
|
|
/** @type {AnyankaKeys.BaseChange.Parameters} */
|
|
const parameters = new AnyankaKeys.BaseChange.Parameters({
|
|
/** @type {number} */
|
|
from : from,
|
|
/** @type {number} */
|
|
to : to
|
|
});
|
|
|
|
switch(type){
|
|
case "string":
|
|
response = "";
|
|
set = value => response = String.fromCharCode(value) + response;
|
|
iterator = () => [...value].map(character => AnyankaKeys.change_base_iteration(character.charCodeAt(0), set, parameters));
|
|
break;
|
|
case "array":
|
|
response = [];
|
|
set = value => response.unshift(value);
|
|
iterator = () => AnyankaKeys.change_base_iteration(value, set, parameters);
|
|
break;
|
|
};
|
|
|
|
if(response !== null){
|
|
iterator();
|
|
parameters.end(set);
|
|
};
|
|
|
|
return response;
|
|
};
|
|
|
|
return AnyankaKeys;
|
|
})(); |