mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
169 lines
4.4 KiB
JavaScript
169 lines
4.4 KiB
JavaScript
const charmap =
|
||
"!#%&'()*+,-./:;<=>?@[]^_`{|}~¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||
|
||
let compressionCache = {};
|
||
let decompressionCache = {};
|
||
|
||
/**
|
||
* Compresses an integer into a tight string representation
|
||
* @param {number} i
|
||
* @returns {string}
|
||
*/
|
||
function compressInt(i) {
|
||
// Zero value breaks
|
||
i += 1;
|
||
|
||
// save `i` as the cache key
|
||
// to avoid it being modified by the
|
||
// rest of the function.
|
||
const cache_key = i;
|
||
|
||
if (compressionCache[cache_key]) {
|
||
return compressionCache[cache_key];
|
||
}
|
||
let result = "";
|
||
do {
|
||
result += charmap[i % charmap.length];
|
||
i = Math.floor(i / charmap.length);
|
||
} while (i > 0);
|
||
return (compressionCache[cache_key] = result);
|
||
}
|
||
|
||
/**
|
||
* Decompresses an integer from its tight string representation
|
||
* @param {string} s
|
||
* @returns {number}
|
||
*/
|
||
function decompressInt(s) {
|
||
if (decompressionCache[s]) {
|
||
return decompressionCache[s];
|
||
}
|
||
s = "" + s;
|
||
let result = 0;
|
||
for (let i = s.length - 1; i >= 0; --i) {
|
||
result = result * charmap.length + charmap.indexOf(s.charAt(i));
|
||
}
|
||
// Fixes zero value break fix from above
|
||
result -= 1;
|
||
return (decompressionCache[s] = result);
|
||
}
|
||
|
||
// Sanity
|
||
if (G_IS_DEV) {
|
||
for (let i = 0; i < 10000; ++i) {
|
||
if (decompressInt(compressInt(i)) !== i) {
|
||
throw new Error(
|
||
"Bad compression for: " +
|
||
i +
|
||
" compressed: " +
|
||
compressInt(i) +
|
||
" decompressed: " +
|
||
decompressInt(compressInt(i))
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param {any} obj
|
||
* @param {Map} keys
|
||
* @param {Map} values
|
||
* @returns {any[]|object|number|string}
|
||
*/
|
||
function compressObjectInternal(obj, keys, values) {
|
||
if (Array.isArray(obj)) {
|
||
let result = [];
|
||
for (let i = 0; i < obj.length; ++i) {
|
||
result.push(compressObjectInternal(obj[i], keys, values));
|
||
}
|
||
return result;
|
||
} else if (typeof obj === "object" && obj !== null) {
|
||
let result = {};
|
||
for (const key in obj) {
|
||
let index = keys.get(key);
|
||
if (index === undefined) {
|
||
index = keys.size;
|
||
keys.set(key, index);
|
||
}
|
||
const value = obj[key];
|
||
result[compressInt(index)] = compressObjectInternal(value, keys, values);
|
||
}
|
||
return result;
|
||
} else if (typeof obj === "string") {
|
||
let index = values.get(obj);
|
||
if (index === undefined) {
|
||
index = values.size;
|
||
values.set(obj, index);
|
||
}
|
||
return compressInt(index);
|
||
}
|
||
return obj;
|
||
}
|
||
|
||
/**
|
||
* @param {Map} hashMap
|
||
* @returns {Array}
|
||
*/
|
||
function indexMapToArray(hashMap) {
|
||
const result = new Array(hashMap.size);
|
||
hashMap.forEach((index, key) => {
|
||
result[index] = key;
|
||
});
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* @param {object} obj
|
||
*/
|
||
export function compressObject(obj) {
|
||
const keys = new Map();
|
||
const values = new Map();
|
||
const data = compressObjectInternal(obj, keys, values);
|
||
return {
|
||
keys: indexMapToArray(keys),
|
||
values: indexMapToArray(values),
|
||
data,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* @param {object} obj
|
||
* @param {string[]} keys
|
||
* @param {any[]} values
|
||
* @returns {object}
|
||
*/
|
||
function decompressObjectInternal(obj, keys = [], values = []) {
|
||
if (Array.isArray(obj)) {
|
||
let result = [];
|
||
for (let i = 0; i < obj.length; ++i) {
|
||
result.push(decompressObjectInternal(obj[i], keys, values));
|
||
}
|
||
return result;
|
||
} else if (typeof obj === "object" && obj !== null) {
|
||
let result = {};
|
||
for (const key in obj) {
|
||
const realIndex = decompressInt(key);
|
||
const value = obj[key];
|
||
result[keys[realIndex]] = decompressObjectInternal(value, keys, values);
|
||
}
|
||
return result;
|
||
} else if (typeof obj === "string") {
|
||
const realIndex = decompressInt(obj);
|
||
return values[realIndex];
|
||
}
|
||
return obj;
|
||
}
|
||
|
||
/**
|
||
* @param {object} obj
|
||
*/
|
||
export function decompressObject(obj) {
|
||
if (obj.keys && obj.values && obj.data) {
|
||
const keys = obj.keys;
|
||
const values = obj.values;
|
||
const result = decompressObjectInternal(obj.data, keys, values);
|
||
return result;
|
||
}
|
||
return obj;
|
||
}
|