joyson2
Version:
JOYFUL JSON | A smarter approach to serialize more than JSON, faster than ever!
131 lines (112 loc) • 3.84 kB
JavaScript
class SnappyLiteTypedCompressor {
constructor(arrayType) {
this.arrayType = arrayType;
this.bytesPerElement = arrayType.BYTES_PER_ELEMENT;
this.bitMask = (1 << (this.bytesPerElement * 8)) - 1;
}
deltaEncode(data) {
let out = new this.arrayType(data.length);
out[0] = data[0];
for (let i = 1; i < data.length; i++) {
out[i] = (data[i] - data[i - 1]) & this.bitMask;
}
return out;
}
deltaDecode(delta) {
let out = new this.arrayType(delta.length);
out[0] = delta[0];
for (let i = 1; i < delta.length; i++) {
out[i] = (out[i - 1] + delta[i]) & this.bitMask;
}
return out;
}
compress(data) {
const delta = this.deltaEncode(data);
const view = new DataView(delta.buffer);
const output = [];
let i = 0;
while (i < delta.length) {
const start = i;
const val = delta[i];
let run = 1;
// Check for repeat run
while (i + run < delta.length && delta[i + run] === val && run < 255) run++;
if (run >= 4) {
output.push(0b01000000); // repeat tag
this._writeValue(output, val);
output.push(run);
i += run;
continue;
}
// Check for zero run
if (val === 0) {
run = 1;
while (i + run < delta.length && delta[i + run] === 0 && run < 255) run++;
if (run >= 4) {
output.push(0b10000000); // zero tag
output.push(run);
i += run;
continue;
}
}
// Otherwise, literal
let litStart = i;
let litRun = 1;
while (
litStart + litRun < delta.length &&
(delta[litStart + litRun] !== delta[litStart + litRun - 1] || litRun < 4) &&
litRun < 60
) litRun++;
output.push(0b00000000 | litRun);
for (let j = 0; j < litRun; j++) {
this._writeValue(output, delta[i + j]);
}
i += litRun;
}
return new Uint8Array(output);
}
decompress(compressed) {
const data = [];
const bytes = compressed;
let i = 0;
while (i < bytes.length) {
const tag = bytes[i++];
const type = tag >> 6;
if (type === 0) {
// Literal
const length = tag & 0x3F;
for (let j = 0; j < length; j++) {
data.push(this._readValue(bytes, i));
i += this.bytesPerElement;
}
} else if (type === 1) {
// Repeat
const value = this._readValue(bytes, i);
i += this.bytesPerElement;
const count = bytes[i++];
for (let j = 0; j < count; j++) data.push(value);
} else if (type === 2) {
// Zero run
const count = bytes[i++];
for (let j = 0; j < count; j++) data.push(0);
} else {
throw new Error("Unknown control type");
}
}
return this.deltaDecode(new this.arrayType(data));
}
_writeValue(out, val) {
for (let b = 0; b < this.bytesPerElement; b++) {
out.push((val >> (8 * b)) & 0xFF);
}
}
_readValue(bytes, index) {
let val = 0;
for (let b = 0; b < this.bytesPerElement; b++) {
val |= bytes[index + b] << (8 * b);
}
return val;
}
}
var engine = new SnappyLiteTypedCompressor();
export default engine;