gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
506 lines • 17.2 kB
JavaScript
// Ported from https://github.com/101arrowz/fflate
// DEFLATE is a complex format; to read this code, you should probably check the RFC first:
// https://tools.ietf.org/html/rfc1951
// You may also wish to take a look at the guide I made about this program:
// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad
// Some of the following code is similar to that of UZIP.js:
// https://github.com/photopea/UZIP.js
// However, the vast majority of the codebase has diverged from UZIP.js to increase performance
// and reduce bundle size.
// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint
// is better for memory in most engines (I *think*).
// fixed length extra bits
const fleb = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
/* unused */ 0, 0, /* impossible */ 0,
]);
// fixed distance extra bits
const fdeb = new Uint8Array([
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
/* unused */ 0, 0,
]);
// code length index map
const clim = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
const { b: fl } = freb(fleb, 2);
// we can ignore the fact that the other numbers are wrong; they never happen anyway
fl[28] = 258;
const { b: fd } = freb(fdeb, 0);
// map of value to reverse (assuming 16 bits)
const rev = new Uint16Array(32768);
for (let i = 0; i < 32768; ++i) {
// reverse table algorithm from SO
let x = ((i & 0xaaaa) >> 1) | ((i & 0x5555) << 1);
x = ((x & 0xcccc) >> 2) | ((x & 0x3333) << 2);
x = ((x & 0xf0f0) >> 4) | ((x & 0x0f0f) << 4);
rev[i] = (((x & 0xff00) >> 8) | ((x & 0x00ff) << 8)) >> 1;
}
// fixed length tree
const flt = new Uint8Array(288);
for (let i = 0; i < 144; ++i)
flt[i] = 8;
for (let i = 144; i < 256; ++i)
flt[i] = 9;
for (let i = 256; i < 280; ++i)
flt[i] = 7;
for (let i = 280; i < 288; ++i)
flt[i] = 8;
// fixed distance tree
const fdt = new Uint8Array(32);
for (let i = 0; i < 32; ++i)
fdt[i] = 5;
// fixed length map
const flrm = hMap(flt, 9, 1);
// fixed distance map
const fdrm = hMap(fdt, 5, 1);
/**
* Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format
* @param data The data to decompress
* @param dict - The dictionary used to compress the original data. If no dictionary was used during
* compression, this option has no effect. Supplying the wrong dictionary during decompression
* usually yields corrupt output or causes an invalid distance error.
* @returns The decompressed version of the data
*/
export function decompressSync(data, dict) {
return data[0] === 31 && data[1] === 139 && data[2] === 8
? gunzipSync(data, dict)
: (data[0] & 15) !== 8 || data[0] >> 4 > 7 || ((data[0] << 8) | data[1]) % 31 !== 0
? inflateSync(data, dict)
: unzlibSync(data, dict);
}
/**
* Expands GZIP data
* @param data The data to decompress
* @param dict - The dictionary used to compress the original data. If no dictionary was used during
* compression, this option has no effect. Supplying the wrong dictionary during decompression
* usually yields corrupt output or causes an invalid distance error.
* @returns The decompressed version of the data
*/
export function gunzipSync(data, dict) {
const st = gzs(data);
if (st + 8 > data.length)
err(6, 'invalid gzip data');
return inflt(data.subarray(st, -8), new Uint8Array(gzl(data)), dict);
}
/**
* Expands DEFLATE data with no wrapper
* @param data The data to decompress
* @param dict - The dictionary used to compress the original data. If no dictionary was used during
* compression, this option has no effect. Supplying the wrong dictionary during decompression
* usually yields corrupt output or causes an invalid distance error.
* @returns The decompressed version of the data
*/
export function inflateSync(data, dict) {
return inflt(data, undefined, dict);
}
/**
* Expands Zlib data
* @param data The data to decompress
* @param dict - The dictionary used to compress the original data. If no dictionary was used during
* compression, this option has no effect. Supplying the wrong dictionary during decompression
* usually yields corrupt output or causes an invalid distance error.
* @returns The decompressed version of the data
*/
export function unzlibSync(data, dict) {
return inflt(data.subarray(zls(data, dict), -4), undefined, dict);
}
/**
* expands raw DEFLATE data
* @param dat - The data to decompress
* @param bf - The output buffer
* @param dict - The dictionary used to compress the original data. If no dictionary was used during
* compression, this option has no effect.
* @returns - The decompressed version of the data
*/
function inflt(dat, bf, dict) {
// source lengt - dict length
const sl = dat.length, dl = dict?.length ?? 0;
if (sl === 0)
return bf ?? new Uint8Array(0);
const noBuf = bf === undefined;
// have to estimate size
const resize = noBuf;
// Assumes roughly 33% compression ratio average
let buf = bf ?? new Uint8Array(sl * 3);
/**
* ensure buffer can fit at least l elements
* @param l - The number of elements
*/
const cbuf = (l) => {
const bl = buf.length;
// need to increase size to fit
if (l > bl) {
// Double or set to necessary, whichever is greater
const nbuf = new Uint8Array(Math.max(bl * 2, l));
nbuf.set(buf);
buf = nbuf;
}
};
// last chunk - bitpos - bytes
let final = 0, pos = 0, bt = 0, lm = undefined, dm = undefined, lbt = 0, dbt = 0;
// total bits
const tbts = sl * 8;
do {
if (lm === undefined) {
// BFINAL - this is only 1 when last chunk is next
final = bits(dat, pos, 1);
// type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
const type = bits(dat, pos + 1, 3);
pos += 3;
if (type === 0) {
// go to end of byte boundary
const s = shft(pos) + 4;
const l = dat[s - 4] | (dat[s - 3] << 8);
const t = s + l;
if (t > sl)
err(0);
// ensure size
if (resize)
cbuf(bt + l);
// Copy over uncompressed data
buf.set(dat.subarray(s, t), bt);
// Get new bitpos, update byte count
bt += l;
pos = t * 8;
continue;
}
else if (type === 1) {
lm = flrm;
dm = fdrm;
lbt = 9;
dbt = 5;
}
else if (type === 2) {
// literal & lengths
const hLit = bits(dat, pos, 31) + 257;
const hcLen = bits(dat, pos + 10, 15) + 4;
const tl = hLit + bits(dat, pos + 5, 31) + 1;
pos += 14;
// length+distance tree
const ldt = new Uint8Array(tl);
// code length tree
const clt = new Uint8Array(19);
for (let i = 0; i < hcLen; ++i) {
// use index map to get real code
clt[clim[i]] = bits(dat, pos + i * 3, 7);
}
pos += hcLen * 3;
// code lengths bits
const clb = max(clt), clbmsk = (1 << clb) - 1;
// code lengths map
const clm = hMap(clt, clb, 1);
for (let i = 0; i < tl;) {
const r = clm[bits(dat, pos, clbmsk)];
// bits read
pos += r & 15;
// symbol
const s = r >> 4;
// code length to copy
if (s < 16) {
ldt[i++] = s;
}
else {
// copy & count
let c = 0, n = 0;
if (s === 16) {
n = 3 + bits(dat, pos, 3);
pos += 2;
c = ldt[i - 1];
}
else if (s === 17) {
n = 3 + bits(dat, pos, 7);
pos += 3;
}
else if (s === 18) {
n = 11 + bits(dat, pos, 127);
pos += 7;
}
while (n-- !== 0)
ldt[i++] = c;
}
}
// length tree & distance tree
const lt = ldt.subarray(0, hLit);
const dt = ldt.subarray(hLit);
// max length bits
lbt = max(lt);
// max dist bits
dbt = max(dt);
lm = hMap(lt, lbt, 1);
dm = hMap(dt, dbt, 1);
}
else
err(1);
if (pos > tbts)
err(0);
}
// Make sure the buffer can hold this + the largest possible addition
// Maximum chunk size (practically, theoretically infinite) is 2^17
if (resize)
cbuf(bt + 131072);
const lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
for (;;) {
// bits read, code
const c = lm[bits16(dat, pos) & lms], sym = c >> 4;
pos += c & 15;
if (pos > tbts) {
err(0);
}
if (c === 0)
err(2);
if (sym < 256)
buf[bt++] = sym;
else if (sym === 256) {
lm = undefined;
break;
}
else {
let add = sym - 254;
// no extra bits needed if less
if (sym > 264) {
// index
const i = sym - 257, b = fleb[i];
add = bits(dat, pos, (1 << b) - 1) + fl[i];
pos += b;
}
// dist
const d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
if (d === 0)
err(3);
pos += d & 15;
let dt = fd[dsym];
if (dsym > 3) {
const b = fdeb[dsym];
dt += bits16(dat, pos) & ((1 << b) - 1);
pos += b;
}
if (pos > tbts)
err(0);
if (resize)
cbuf(bt + 131072);
const end = bt + add;
if (bt < dt) {
const shift = dl - dt, dend = Math.min(dt, end);
if (shift + bt < 0)
err(3);
for (; bt < dend; ++bt) {
buf[bt] = dict[shift + bt];
}
}
for (; bt < end; ++bt) {
if (bt >= dt)
buf[bt] = buf[bt - dt];
}
}
}
if (lm !== undefined)
final = 1;
} while (final === 0);
// don't reallocate for streams or user buffers
return bt !== buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
}
/**
* get base, reverse index map from extra bits
* @param eb - extra bits
* @param start - start
* @returns `{b, r}`
*/
function freb(eb, start) {
const b = new Uint16Array(31);
for (let i = 0; i < 31; ++i) {
b[i] = start += 1 << eb[i - 1];
}
// numbers here are at max 18 bits
const r = new Int32Array(b[30]);
for (let i = 1; i < 30; ++i) {
for (let j = b[i]; j < b[i + 1]; ++j) {
r[j] = ((j - b[i]) << 5) | i;
}
}
return { b, r };
}
/**
* create huffman tree from u8 "map": index -> code length for code index
* mb (max bits) must be at most 15
* @param cd - input u8 "map": index -> code
* @param mb - max bits
* @param r - 0 for encoder, 1 for decoder
* @returns - u16 "map": index -> code
*/
function hMap(cd, mb, r) {
const s = cd.length;
// index
let i = 0;
// u16 "map": index -> # of codes with bit length = index
const l = new Uint16Array(mb);
// length of cd must be 288 (total # of codes)
for (; i < s; ++i) {
if (cd[i] !== 0)
++l[cd[i] - 1];
}
// u16 "map": index -> minimum code for bit length = index
const le = new Uint16Array(mb);
for (i = 1; i < mb; ++i) {
le[i] = (le[i - 1] + l[i - 1]) << 1;
}
let co;
if (r !== 0) {
// u16 "map": index -> number of actual bits, symbol for code
co = new Uint16Array(1 << mb);
// bits to remove for reverser
const rvb = 15 - mb;
for (i = 0; i < s; ++i) {
// ignore 0 lengths
if (cd[i] !== 0) {
// num encoding both symbol and bits read
const sv = (i << 4) | cd[i];
// free bits
const r = mb - cd[i];
// start value
let v = le[cd[i] - 1]++ << r;
// m is end value
const m = v | ((1 << r) - 1);
for (; v <= m; ++v) {
// every 16 bit value starting with the code yields the same result
co[rev[v] >> rvb] = sv;
}
}
}
}
else {
// ENCODER:
co = new Uint16Array(s);
// for (i = 0; i < s; ++i) {
// if (cd[i] !== 0) {
// co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);
// }
// }
}
return co;
}
/**
* find max of array
* @param a - input array
* @returns - max
*/
function max(a) {
let m = a[0];
for (let i = 1; i < a.length; ++i) {
if (a[i] > m)
m = a[i];
}
return m;
}
/**
* read d, starting at bit p and mask with m
* @param d - input
* @param p - bit position
* @param m - mask
* @returns - bit value
*/
function bits(d, p, m) {
const o = (p / 8) | 0;
return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;
}
/**
* read d, starting at bit p continuing for at least 16 bits
* @param d - input
* @param p - bit position
* @returns - bit value
*/
function bits16(d, p) {
const o = (p / 8) | 0;
return (d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7);
}
/**
* get end of byte
* @param p - bit position
* @returns - end of byte
*/
function shft(p) {
return ((p + 7) / 8) | 0;
}
/**
* typed array slice - allows garbage collector to free original reference,
* while being more compatible than .slice
* @param v - typed array
* @param s - start
* @param e - end (full length if undefined)
* @returns - new typed array
*/
function slc(v, s, e) {
if (s === undefined || s < 0)
s = 0;
if (e === undefined || e > v.length)
e = v.length;
// can't use .constructor in case user-supplied
return new Uint8Array(v.subarray(s, e));
}
// error codes
const ec = [
'unexpected EOF',
'invalid block type',
'invalid length/literal',
'invalid distance',
'stream finished',
'no stream handler', // determined by compression function
'no callback',
// OTHER ERRORS LISTED GO BEYOND WHAT WE USE IN THIS LIBRARY
// 'invalid UTF-8 data',
// 'extra field too long',
// 'date not in range 1980-2099',
// 'filename too long',
// 'stream finishing',
// 'invalid zip data',
// determined by unknown compression method
];
/**
* An error generated within this library
* @param ind - error code
* @param msg - error message
*/
function err(ind, msg) {
const e = new Error(msg ?? ec[ind]);
e.code = ind;
if (Error.captureStackTrace !== undefined)
Error.captureStackTrace(e, err);
throw e;
}
/**
* gzip start
* @param d - gzip data
* @returns - start
*/
function gzs(d) {
if (d[0] !== 31 || d[1] !== 139 || d[2] !== 8)
err(6, 'invalid gzip data');
const flg = d[3];
let st = 10;
if ((flg & 4) !== 0)
st += (d[10] | (d[11] << 8)) + 2;
for (let zs = ((flg >> 3) & 1) + ((flg >> 4) & 1); zs > 0; zs -= Number(d[st++] === 0))
;
return st + (flg & 2);
}
/**
* gzip length
* @param d - gzip data
* @returns - length
*/
function gzl(d) {
const l = d.length;
return (d[l - 4] | (d[l - 3] << 8) | (d[l - 2] << 16) | (d[l - 1] << 24)) >>> 0;
}
/**
* zlib start
* @param d - zlib data
* @param dict - dictionary
* @returns - number
*/
function zls(d, dict) {
if ((d[0] & 15) !== 8 || d[0] >> 4 > 7 || ((d[0] << 8) | d[1]) % 31 !== 0)
err(6, 'invalid zlib data');
if (Boolean((d[1] >> 5) & 1) === (dict === undefined))
err(6, 'invalid zlib data: ' + ((d[1] & 32) !== 0 ? 'need' : 'unexpected') + ' dictionary');
return ((d[1] >> 3) & 4) + 2;
}
//# sourceMappingURL=fflate.js.map