Search This Blog

Monday, April 18, 2016

Map compression

Work on the map saving & restore went further and in order to decrease the amount of data stored on the server I decided to compress the map while keeping the data as string (and therefore don't have issues with JS). If would accept to keep the data as binary I could use a GZip or something similar.

The first step would be to store numbers in another format to be able to reduce the size. 0-9 cannot be reduced if you want to keep one them split and not merge them into a single byte. However bigger numbers like 2112 (just a number picked randomly) takes 4 bytes to write as string yet could be stored in a more compact form. Either being in HEX or... by storing it to a more compact form. For example using a-zA-Z as base (0 being a, 1 b and so on).

Let's write a small JS function for that:
var numberCompressionPossibleChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Numbers must be positive!
function NumberToString(source, nbChar)
{
var result = "";
var rest = source;
for (var i = 0; i < nbChar; i++)
{
result += numberCompressionPossibleChars.charAt(rest % numberCompressionPossibleChars.length);
rest = Math.floor(rest / numberCompressionPossibleChars.length);
}
return result;
}
 A JS Fiddle has been created for it: https://jsfiddle.net/68konLxw/

To transform this back into a number:
function StringToNumber(source, position, nbChar)
{
var result = 0;
for (var i = 0; i < nbChar; i++)
{
var c = source.charAt(i + position);
result += numberCompressionPossibleChars.indexOf(c) * Math.pow(numberCompressionPossibleChars.length, i);
}
return result;
}
And the JS Fiddle here allows to go from one and back: https://jsfiddle.net/gLwtwo73/

Now that's just the work of a single number, and we don't gain much unless the numbers are big. But what can we do on an array of numbers? Well, the next step is to implement the Run Length Encoding which counts the number of times the same piece comes:
// Numbers must be positive!
function NumberToString(source, nbChar)
{
var result = "";
var rest = source;
for (var i = 0; i < nbChar; i++)
{
result += numberCompressionPossibleChars.charAt(rest % numberCompressionPossibleChars.length);
rest = Math.floor(rest / numberCompressionPossibleChars.length);
}
return result;
}
function StringToNumber(source, position, nbChar)
{
var result = 0;
for (var i = 0; i < nbChar; i++)
{
var c = source.charAt(i + position);
result += numberCompressionPossibleChars.indexOf(c) * Math.pow(numberCompressionPossibleChars.length, i);
}
return result;
}
JS Fiddle of the array compression: https://jsfiddle.net/gfpyq7qo/

The first few characters of the compressed array contains the number of character used to compress the number.

And the last piece of our work, decompress this array:
function StringToArray(source)
{
var result = [];
var strNb = "";
var i = 0;
for (; i < source.length; i++)
{
var c = source.charAt(i);
if (c == "-")
break;
strNb += c;
}
i++;
var nbChar = parseInt(strNb);
strNb = "";
for (; i < source.length; i++)
{
var k = source.charCodeAt(i);
if (k >= 48 && k <= 57)
strNb += source.charAt(i);
else
{
var nb = StringToNumber(source, i, nbChar);
i += nbChar - 1;
if (strNb == "")
result.push(nb);
else
{
var n = parseInt(strNb);
for (var j = 0; j < n; j++)
result.push(nb);
strNb = "";
}
}
}
return result;
}
And the last Fiddle of this post: https://jsfiddle.net/p3o0rems/

No comments:

Post a Comment