Avoid integer overflow when computing shortcodes

Although javascript's numbers are double precision floating point
number which support 52 bits of precision the bitwise operations are
only guaranteed to work at 32 bits of precision so we need to avoid
relying on them doing more than that.
This commit is contained in:
Matt Amos 2010-12-05 11:00:57 +00:00 committed by Tom Hughes
parent 616e9ce7b9
commit 42ef10ba80

View file

@ -254,23 +254,35 @@ function i18n(string, keys) {
return string; return string;
} }
/*
* Called to interlace the bits in x and y, making a Morton code.
*/
function interlace(x, y) {
x = (x | (x << 8)) & 0x00ff00ff;
x = (x | (x << 4)) & 0x0f0f0f0f;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00ff00ff;
y = (y | (y << 4)) & 0x0f0f0f0f;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return (x << 1) | y;
}
/*
* Called to create a short code for the short link.
*/
function makeShortCode(lat, lon, zoom) { function makeShortCode(lat, lon, zoom) {
char_array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; char_array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
var x = Math.round((lon + 180.0) * ((1 << 30) / 90.0)); var x = Math.round((lon + 180.0) * ((1 << 30) / 90.0));
var y = Math.round((lat + 90.0) * ((1 << 30) / 45.0)); var y = Math.round((lat + 90.0) * ((1 << 30) / 45.0));
// hack around the fact that JS apparently only allows 53-bit integers?!? // JavaScript only has to keep 32 bits of bitwise operators, so this has to be
// note that, although this reduces the accuracy of the process, it's fine for // done in two parts. each of the parts c1/c2 has 30 bits of the total in it
// z18 so we don't need to care for now. // and drops the last 4 bits of the full 64 bit Morton code.
var c1 = 0, c2 = 0;
for (var i = 31; i > 16; --i) {
c1 = (c1 << 1) | ((x >> i) & 1);
c1 = (c1 << 1) | ((y >> i) & 1);
}
for (var i = 16; i > 1; --i) {
c2 = (c2 << 1) | ((x >> i) & 1);
c2 = (c2 << 1) | ((y >> i) & 1);
}
var str = ""; var str = "";
var c1 = interlace(x >>> 17, y >>> 17), c2 = interlace((x >>> 2) & 0x7fff, (y >>> 2) & 0x7fff);
for (var i = 0; i < Math.ceil((zoom + 8) / 3.0) && i < 5; ++i) { for (var i = 0; i < Math.ceil((zoom + 8) / 3.0) && i < 5; ++i) {
digit = (c1 >> (24 - 6 * i)) & 0x3f; digit = (c1 >> (24 - 6 * i)) & 0x3f;
str += char_array.charAt(digit); str += char_array.charAt(digit);