gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
322 lines • 12.6 kB
JavaScript
import { U32I32F32 } from '../util.js';
/* this header byte needs to change in case incompatible change happen */
export const AC_HEADER_BYTE = 2;
export const AC_BUFFER_SIZE = 4096;
export const AC__MinLength = 0x01000000; // threshold for renormalization
export const AC__MaxLength = 0xffffffff; // maximum AC interval length
// Maximum values for binary models
export const BM__LengthShift = 13; // length bits discarded before mult.
export const BM__MaxCount = 8_192; // 1 << BM__LengthShift; // for adaptive models
// Maximum values for general models
export const DM__LengthShift = 15; // length bits discarded before mult.
export const DM__MaxCount = 32_768; // 1 << DM__LengthShift; // for adaptive models
/**
* https://github.com/LASzip/LASzip/blob/master/src/arithmeticdecoder.cpp
*/
export class ArithmeticDecoder {
reader;
// U32 value, length;
value = 0; // U32
length = AC__MaxLength; // U32
/** @param reader - The input reader */
constructor(reader) {
this.reader = reader;
}
/** @param reallyInit - if set to true, initializes the value */
init(reallyInit = true) {
this.length = AC__MaxLength;
if (reallyInit)
this.value = this.reader.getUint32();
}
/**
* @param m - The arithmetic bit model
* @returns - The decoded bit
*/
decodeBit(m) {
const x = m.bit0Prob * Math.trunc(this.length >>> BM__LengthShift); // product l x p0
const sym = this.value >= x ? 1 : 0; // decision
// update & shift interval
if (sym === 0) {
this.length = x;
++m.bit0Count;
}
else {
this.value -= x; // shifted interval base = 0
this.length -= x;
}
if (this.length < AC__MinLength)
this.renormDecInterval(); // renormalization
if (--m.bitsUntilUpdate === 0)
m.update(); // periodic model update
return sym; // return data bit value
}
/**
* @param m - The arithmetic model
* @returns - The decoded symbol
*/
decodeSymbol(m) {
let n, sym, x, y = this.length;
if (m.decoderTableIndex !== -1) {
this.length = Math.trunc(this.length >>> DM__LengthShift);
const dv = Math.trunc(this.value / this.length);
const t = Math.trunc(dv >>> m.tableShift);
sym = m.distribution[m.decoderTableIndex + t]; // initial decision based on table look-up
n = m.distribution[m.decoderTableIndex + t + 1] + 1;
while (n > sym + 1) {
// finish with bisection search
const k = Math.trunc((sym + n) >>> 1);
if (m.distribution[k] > dv)
n = k;
else
sym = k;
}
// compute products
x = m.distribution[sym] * this.length;
if (sym !== m.lastSymbol)
y = m.distribution[sym + 1] * this.length;
}
else {
// decode using only multiplications
x = sym = 0;
this.length = Math.trunc(this.length >>> DM__LengthShift);
let k = Math.trunc((n = m.symbols) >>> 1);
// decode via bisection search
do {
const z = (this.length * m.distribution[k]) >>> 0;
if (z > this.value) {
n = k;
y = z; // value is smaller
}
else {
sym = k;
x = z; // value is larger or equal
}
} while ((k = Math.trunc(sym + n) >>> 1) !== sym);
}
this.value -= x; // update interval
this.length = y - x;
if (this.length < AC__MinLength)
this.renormDecInterval(); // renormalization
++m.distribution[m.symbolCountIndex + sym];
if (--m.symbolsUntilUpdate === 0)
m.update(); // periodic model update
// assert(sym < m.symbols);
return sym;
}
// /** @returns - The decoded bit */
// readBit(): number {
// this.length = Math.trunc(this.length >>> 1);
// const sym = Math.trunc(this.value / this.length); // decode symbol, change length
// this.value -= this.length * sym; // update interval
// if (this.length < AC__MinLength) this.renormDecInterval(); // renormalization
// if (sym >= 2) throw new Error('4711');
// return sym;
// }
/**
* @param bits - The number of bits
* @returns - The decoded bits
*/
readBits(bits) {
// assert(bits && (bits <= 32));
if (bits > 19) {
const tmp = this.readShort();
bits = bits - 16;
const tmp1 = this.readBits(bits) * 65_536;
return tmp1 | tmp;
}
this.length = Math.trunc(this.length >>> bits);
const sym = Math.trunc(this.value / this.length); // decode symbol, change length
this.value -= this.length * sym; // update interval
if (this.length < AC__MinLength)
this.renormDecInterval(); // renormalization
if (sym >= 1 << bits)
throw new Error('4711');
return sym;
}
// /** @returns - The decoded byte */
// readByte(): number {
// this.length = Math.trunc(this.length >>> 8);
// const sym = Math.trunc(this.value / this.length); // decode symbol, change length
// this.value -= this.length * sym; // update interval
// if (this.length < AC__MinLength) this.renormDecInterval(); // renormalization
// if (sym >= 1 << 8) throw new Error('4711');
// return sym;
// }
/** @returns - The decoded short */
readShort() {
this.length = Math.trunc(this.length >>> 16);
const sym = Math.trunc(this.value / this.length); // decode symbol, change length
this.value -= this.length * sym; // update interval
if (this.length < AC__MinLength)
this.renormDecInterval(); // renormalization
if (sym >= 65_536)
throw new Error('4711');
return sym;
}
/** @returns - The decoded int */
readInt() {
const lowerInt = this.readShort();
const upperInt = this.readShort();
return ((upperInt * 65_536) | lowerInt) >>> 0;
}
// /** @returns - The decoded float */
// readFloat(): number {
// /* danger in float reinterpretation */
// const u32i32f32 = new U32I32F32(this.readInt(), 'u32');
// return u32i32f32.f32;
// }
/** @returns - The decoded int64 */
readInt64() {
const lowerInt = this.readInt();
const upperInt = this.readInt();
return (BigInt(upperInt) << 32n) | BigInt(lowerInt);
}
// /** @returns - The decoded double */
// readDouble(): number {
// /* danger in float reinterpretation */
// const lowerInt = this.readInt();
// const upperInt = this.readInt();
// const u64 = (BigInt(upperInt) << 32n) | BigInt(lowerInt);
// const u64i64f64 = new U64I64F64(u64, 'u64');
// return u64i64f64.f64;
// }
/** Renormalize the decoder interval */
renormDecInterval() {
const value = new U32I32F32(this.value, 'u32');
const length = new U32I32F32(this.length, 'u32');
do {
const nextByte = this.reader.getUint8();
// read least-significant byte
value.u32 = (value.u32 * 256) | nextByte;
} while ((length.u32 *= 256) < AC__MinLength); // length multiplied by 256
this.value = value.u32;
this.length = length.u32;
}
}
/** Arithmetic Model */
export class ArithmeticModel {
symbols;
compress;
distribution = [];
symbolCountIndex = 0;
decoderTableIndex = -1;
totalCount = 0;
updateCycle = 0;
symbolsUntilUpdate = 0;
lastSymbol = 0;
tableSize = 0;
tableShift = 0;
/**
* @param symbols - The number of symbols
* @param compress - If the model has been compressed
*/
constructor(symbols, compress = false) {
this.symbols = symbols;
this.compress = compress;
}
/** @param table - The table */
init(table) {
if (this.distribution.length === 0) {
// 2048 === (1 << 11)
if (this.symbols < 2 || this.symbols > 2_048)
throw Error('invalid number of symbols');
this.lastSymbol = this.symbols - 1;
if (!this.compress && this.symbols > 16) {
let tableBits = 3;
while (this.symbols > 1 << (tableBits + 2))
++tableBits;
this.tableSize = 1 << tableBits;
this.tableShift = DM__LengthShift - tableBits;
this.distribution = new Array(2 * this.symbols + this.tableSize + 2);
this.decoderTableIndex = 2 * this.symbols;
}
else {
// small alphabet: no table needed
this.decoderTableIndex = -1;
this.tableSize = this.tableShift = 0;
this.distribution = new Array(2 * this.symbols).fill(0);
}
this.symbolCountIndex = this.symbols;
}
this.totalCount = 0;
this.updateCycle = this.symbols;
if (table !== undefined)
for (let k = 0; k < this.symbols; k++)
this.distribution[this.symbolCountIndex + k] = table[k];
else
for (let k = 0; k < this.symbols; k++)
this.distribution[this.symbolCountIndex + k] = 1;
this.update();
this.symbolsUntilUpdate = this.updateCycle = (this.symbols + 6) >>> 1;
}
/** Update the model */
update() {
// halve counts when a threshold is reached
if ((this.totalCount += this.updateCycle) > DM__MaxCount) {
this.totalCount = 0;
for (let n = 0; n < this.symbols; n++) {
this.totalCount += this.distribution[this.symbolCountIndex + n] = Math.trunc((this.distribution[this.symbolCountIndex + n] + 1) >>> 1);
}
}
// compute cumulative distribution, decoder table
let k, sum = 0, s = 0;
const scale = Math.trunc(0x80000000 / this.totalCount);
if (this.compress || this.tableSize === 0) {
for (k = 0; k < this.symbols; k++) {
this.distribution[k] = (scale * sum) >>> (31 - DM__LengthShift);
sum += this.distribution[this.symbolCountIndex + k];
}
}
else {
for (k = 0; k < this.symbols; k++) {
this.distribution[k] = (scale * sum) >>> (31 - DM__LengthShift);
sum += this.distribution[this.symbolCountIndex + k];
const w = this.distribution[k] >>> this.tableShift;
while (s < w)
this.distribution[this.decoderTableIndex + ++s] = k - 1;
}
this.distribution[this.decoderTableIndex] = 0;
while (s <= this.tableSize)
this.distribution[this.decoderTableIndex + ++s] = this.symbols - 1;
}
// set frequency of model updates
this.updateCycle = (5 * this.updateCycle) >>> 2;
const maxCycle = (this.symbols + 6) << 3;
if (this.updateCycle > maxCycle)
this.updateCycle = maxCycle;
this.symbolsUntilUpdate = this.updateCycle;
}
}
/** Arithmetic Bit Model */
export class ArithmeticBitModel {
// start with frequent updates
updateCycle = 4;
bitsUntilUpdate = 4;
// initialization to equiprobable model
bit0Prob = 1 << (BM__LengthShift - 1);
bit0Count = 1;
bitCount = 2;
/** @returns - 0 */
init() {
return 0;
}
/** Update the model */
update() {
// halve counts when a threshold is reached
if ((this.bitCount += this.updateCycle) > BM__MaxCount) {
this.bitCount = Math.trunc((this.bitCount + 1) >>> 1);
this.bit0Count = Math.trunc((this.bit0Count + 1) >>> 1);
if (this.bit0Count === this.bitCount)
++this.bitCount;
}
// compute scaled bit 0 probability
const scale = Math.trunc(0x80000000 / this.bitCount);
this.bit0Prob = Math.trunc((this.bit0Count * scale) >>> (31 - BM__LengthShift));
// set frequency of model updates
this.updateCycle = Math.trunc((5 * this.updateCycle) >>> 2);
if (this.updateCycle > 64)
this.updateCycle = 64;
this.bitsUntilUpdate = this.updateCycle;
}
}
//# sourceMappingURL=arithmeticDecoder.js.map