Files
Projekt-Visualisierung/node_modules/@maplibre/mlt/dist/encoding/fastPforEncoder.js
2026-04-15 17:08:39 +02:00

310 lines
13 KiB
JavaScript

import { MASKS, DEFAULT_PAGE_SIZE, BLOCK_SIZE, greatestMultiple, roundUpToMultipleOf32, normalizePageSize, } from "../decoding/fastPforShared";
const EXCEPTION_OVERHEAD_BITS = 8;
const MAX_BIT_WIDTH = 32;
const BIT_WIDTH_SLOTS = MAX_BIT_WIDTH + 1;
const PAGE_SIZE = normalizePageSize(DEFAULT_PAGE_SIZE);
const INITIAL_PACKED_BUFFER_SIZE_WORDS = (PAGE_SIZE / 32) * 4;
const BYTE_CONTAINER_SIZE = ((3 * PAGE_SIZE) / BLOCK_SIZE + PAGE_SIZE) | 0;
function requiredBits(value) {
return 32 - Math.clz32(value >>> 0);
}
function ensureInt32Capacity(buffer, requiredLength) {
if (requiredLength <= buffer.length)
return buffer;
let newLength = buffer.length === 0 ? 1 : buffer.length;
while (newLength < requiredLength) {
newLength *= 2;
}
const next = new Uint32Array(newLength);
next.set(buffer);
return next;
}
function ensureUint8Capacity(buffer, requiredLength) {
if (requiredLength <= buffer.length)
return buffer;
let newLength = buffer.length === 0 ? 1 : buffer.length;
while (newLength < requiredLength) {
newLength *= 2;
}
const next = new Uint8Array(newLength);
next.set(buffer);
return next;
}
export function fastPack32(inValues, inPos, out, outPos, bitWidth) {
if (bitWidth === 0)
return;
if (bitWidth === 32) {
out.set(inValues.subarray(inPos, inPos + 32), outPos);
return;
}
const mask = MASKS[bitWidth] >>> 0;
let outputWordIndex = outPos;
let bitOffset = 0;
let currentWord = 0;
for (let i = 0; i < 32; i++) {
const value = (inValues[inPos + i] >>> 0) & mask;
if (bitOffset + bitWidth <= 32) {
currentWord |= value << bitOffset;
bitOffset += bitWidth;
if (bitOffset === 32) {
out[outputWordIndex++] = currentWord | 0;
bitOffset = 0;
currentWord = 0;
}
}
else {
const lowBits = 32 - bitOffset;
const lowMask = MASKS[lowBits] >>> 0;
currentWord |= (value & lowMask) << bitOffset;
out[outputWordIndex++] = currentWord | 0;
currentWord = value >>> lowBits;
bitOffset = bitWidth - lowBits;
}
}
}
export function createFastPforEncoderWorkspace() {
const dataToBePacked = new Array(BIT_WIDTH_SLOTS);
for (let k = 1; k < BIT_WIDTH_SLOTS; k++) {
dataToBePacked[k] = new Uint32Array(INITIAL_PACKED_BUFFER_SIZE_WORDS);
}
return {
dataToBePacked,
dataPointers: new Int32Array(BIT_WIDTH_SLOTS),
byteContainer: new Uint8Array(BYTE_CONTAINER_SIZE),
bitWidthFrequencies: new Int32Array(BIT_WIDTH_SLOTS),
bestBitWidthPlan: new Int32Array(3),
};
}
function computeBestBitWidthPlan(inValues, pos, workspace) {
const bitWidthFrequencies = workspace.bitWidthFrequencies;
const bestBitWidthPlan = workspace.bestBitWidthPlan;
bitWidthFrequencies.fill(0);
for (let k = pos, kEnd = pos + BLOCK_SIZE; k < kEnd; k++) {
bitWidthFrequencies[requiredBits(inValues[k])]++;
}
let maxBitWidth = MAX_BIT_WIDTH;
while (bitWidthFrequencies[maxBitWidth] === 0)
maxBitWidth--;
let bestBitWidth = maxBitWidth;
let bestCost = maxBitWidth * BLOCK_SIZE;
let exceptionCount = 0;
let bestExceptionCount = exceptionCount;
for (let candidateBitWidth = maxBitWidth - 1; candidateBitWidth >= 0; candidateBitWidth--) {
exceptionCount += bitWidthFrequencies[candidateBitWidth + 1];
if (exceptionCount === BLOCK_SIZE)
break;
let candidateCost = exceptionCount * EXCEPTION_OVERHEAD_BITS +
exceptionCount * (maxBitWidth - candidateBitWidth) +
candidateBitWidth * BLOCK_SIZE +
8;
if (maxBitWidth - candidateBitWidth === 1)
candidateCost -= exceptionCount;
if (candidateCost < bestCost) {
bestCost = candidateCost;
bestBitWidth = candidateBitWidth;
bestExceptionCount = exceptionCount;
}
}
bestBitWidthPlan[0] = bestBitWidth;
bestBitWidthPlan[1] = bestExceptionCount;
bestBitWidthPlan[2] = maxBitWidth;
}
function writeByte(workspace, byteContainerPos, byteValue) {
if (byteContainerPos >= workspace.byteContainer.length) {
workspace.byteContainer = ensureUint8Capacity(workspace.byteContainer, byteContainerPos + 1);
}
workspace.byteContainer[byteContainerPos] = byteValue & 0xff;
return byteContainerPos + 1;
}
function ensureExceptionValuesCapacity(dataToBePacked, dataPointers, exceptionBitWidth, exceptionCount) {
if (exceptionBitWidth === 1)
return;
const needed = dataPointers[exceptionBitWidth] + exceptionCount;
const currentExceptionValues = dataToBePacked[exceptionBitWidth];
if (!currentExceptionValues || needed >= currentExceptionValues.length) {
let newSize = 2 * needed;
newSize = roundUpToMultipleOf32(newSize);
const next = new Uint32Array(newSize);
if (currentExceptionValues)
next.set(currentExceptionValues);
dataToBePacked[exceptionBitWidth] = next;
}
}
function writeBlockHeader(workspace, byteContainerPos, bitWidth, exceptionCount, maxBitWidth) {
byteContainerPos = writeByte(workspace, byteContainerPos, bitWidth);
byteContainerPos = writeByte(workspace, byteContainerPos, exceptionCount);
if (exceptionCount > 0) {
byteContainerPos = writeByte(workspace, byteContainerPos, maxBitWidth);
}
return byteContainerPos;
}
function recordBlockExceptions(workspace, inValues, blockPos, bitWidth, exceptionCount, exceptionBitWidth, byteContainerPos) {
if (exceptionCount === 0)
return byteContainerPos;
const dataToBePacked = workspace.dataToBePacked;
const dataPointers = workspace.dataPointers;
ensureExceptionValuesCapacity(dataToBePacked, dataPointers, exceptionBitWidth, exceptionCount);
for (let k = 0; k < BLOCK_SIZE; k++) {
const value = inValues[blockPos + k] >>> 0;
if (value >>> bitWidth !== 0) {
byteContainerPos = writeByte(workspace, byteContainerPos, k);
if (exceptionBitWidth !== 1) {
const exceptionValues = dataToBePacked[exceptionBitWidth];
exceptionValues[dataPointers[exceptionBitWidth]++] = (value >>> bitWidth) | 0;
}
}
}
return byteContainerPos;
}
function packBlock(inValues, blockPos, bitWidth, state) {
for (let k = 0; k < BLOCK_SIZE; k += 32) {
state.out = ensureInt32Capacity(state.out, state.outPos + bitWidth);
fastPack32(inValues, blockPos + k, state.out, state.outPos, bitWidth);
state.outPos += bitWidth;
}
}
function padByteContainerToInt32(workspace, byteContainerPos) {
while ((byteContainerPos & 3) !== 0) {
byteContainerPos = writeByte(workspace, byteContainerPos, 0);
}
return byteContainerPos;
}
function writeByteContainerInts(workspace, state, byteContainerPos) {
const howManyInts = byteContainerPos / 4;
state.out = ensureInt32Capacity(state.out, state.outPos + howManyInts);
const byteContainer = workspace.byteContainer;
for (let i = 0; i < howManyInts; i++) {
const base = i * 4;
const packedWord = byteContainer[base] |
(byteContainer[base + 1] << 8) |
(byteContainer[base + 2] << 16) |
(byteContainer[base + 3] << 24) |
0;
state.out[state.outPos + i] = packedWord;
}
state.outPos += howManyInts;
}
function computeExceptionBitmap(dataPointers) {
let bitmap = 0;
for (let k = 2; k <= MAX_BIT_WIDTH; k++) {
if (dataPointers[k] !== 0) {
bitmap |= k === MAX_BIT_WIDTH ? 0x80000000 : 1 << (k - 1);
}
}
return bitmap;
}
function writeExceptionStreams(workspace, state) {
const dataPointers = workspace.dataPointers;
const dataToBePacked = workspace.dataToBePacked;
const bitmap = computeExceptionBitmap(dataPointers);
state.out = ensureInt32Capacity(state.out, state.outPos + 1);
state.out[state.outPos++] = bitmap;
for (let k = 2; k <= MAX_BIT_WIDTH; k++) {
const size = dataPointers[k];
if (size !== 0) {
state.out = ensureInt32Capacity(state.out, state.outPos + 1);
state.out[state.outPos++] = size | 0;
let j = 0;
for (; j < size; j += 32) {
const exceptionValues = dataToBePacked[k];
state.out = ensureInt32Capacity(state.out, state.outPos + k);
fastPack32(exceptionValues, j, state.out, state.outPos, k);
state.outPos += k;
}
const overflow = j - size;
state.outPos -= (overflow * k) >>> 5;
}
}
}
function encodePage(inValues, thisSize, state, workspace) {
const headerPos = state.outPos;
state.out = ensureInt32Capacity(state.out, headerPos + 1);
state.outPos = (state.outPos + 1) | 0;
const dataPointers = workspace.dataPointers;
dataPointers.fill(0);
let byteContainerPos = 0;
let tmpInPos = state.inPos;
const finalInPos = tmpInPos + thisSize - BLOCK_SIZE;
for (; tmpInPos <= finalInPos; tmpInPos += BLOCK_SIZE) {
computeBestBitWidthPlan(inValues, tmpInPos, workspace);
const bestBitWidthPlan = workspace.bestBitWidthPlan;
const bitWidth = bestBitWidthPlan[0];
const exceptionCount = bestBitWidthPlan[1];
const maxBitWidth = bestBitWidthPlan[2];
const exceptionBitWidth = exceptionCount > 0 ? maxBitWidth - bitWidth : 0;
byteContainerPos = writeBlockHeader(workspace, byteContainerPos, bitWidth, exceptionCount, maxBitWidth);
byteContainerPos = recordBlockExceptions(workspace, inValues, tmpInPos, bitWidth, exceptionCount, exceptionBitWidth, byteContainerPos);
packBlock(inValues, tmpInPos, bitWidth, state);
}
const pageEndOutPos = state.outPos;
state.inPos = tmpInPos;
state.out[headerPos] = (pageEndOutPos - headerPos) | 0;
const byteSize = byteContainerPos;
byteContainerPos = padByteContainerToInt32(workspace, byteContainerPos);
state.out = ensureInt32Capacity(state.out, state.outPos + 1);
state.out[state.outPos++] = byteSize | 0;
writeByteContainerInts(workspace, state, byteContainerPos);
writeExceptionStreams(workspace, state);
}
function encodeAlignedPages(inValues, inLength, state, workspace) {
const alignedLength = greatestMultiple(inLength, BLOCK_SIZE);
const finalInPos = state.inPos + alignedLength;
while (state.inPos !== finalInPos) {
const thisSize = Math.min(PAGE_SIZE, finalInPos - state.inPos);
encodePage(inValues, thisSize, state, workspace);
}
}
function encode(inValues, inLength, state, workspace) {
const alignedLength = greatestMultiple(inLength, BLOCK_SIZE);
state.out = ensureInt32Capacity(state.out, state.outPos + 1);
state.out[state.outPos++] = alignedLength;
if (alignedLength === 0)
return;
encodeAlignedPages(inValues, alignedLength, state, workspace);
}
/**
* VByte encoding for FastPFOR tail values (MSB=1 terminator).
* Note: Inverts standard Protobuf Varint (MSB=0 terminator), so we cannot reuse generic methods.
*/
function encodeVByte(inValues, inLength, state, workspace) {
if (inLength === 0)
return;
const requiredBytes = inLength * 5 + 3;
workspace.byteContainer = ensureUint8Capacity(workspace.byteContainer, requiredBytes);
const start = state.inPos;
let bytePos = 0;
for (let k = start; k < start + inLength; k++) {
let value = inValues[k] >>> 0;
while (value >= 0x80) {
workspace.byteContainer[bytePos++] = value & 0x7f;
value >>>= 7;
}
workspace.byteContainer[bytePos++] = (value | 0x80) & 0xff;
}
while ((bytePos & 3) !== 0)
workspace.byteContainer[bytePos++] = 0;
const intsToWrite = bytePos / 4;
state.out = ensureInt32Capacity(state.out, state.outPos + intsToWrite);
let outIdx = state.outPos;
for (let i = 0; i < bytePos; i += 4) {
const packedWord = workspace.byteContainer[i] |
(workspace.byteContainer[i + 1] << 8) |
(workspace.byteContainer[i + 2] << 16) |
(workspace.byteContainer[i + 3] << 24) |
0;
state.out[outIdx++] = packedWord;
}
state.outPos = outIdx;
state.inPos = (state.inPos + inLength) | 0;
}
/**
* Encodes an int32 stream using the FastPFOR wire format (pages + VByte tail).
*/
export function encodeFastPforInt32WithWorkspace(values, workspace) {
const state = { inPos: 0, outPos: 0, out: new Uint32Array(values.length + 1024) };
encode(values, values.length, state, workspace);
const remaining = values.length - state.inPos;
encodeVByte(values, remaining, state, workspace);
return state.out.subarray(0, state.outPos);
}
//# sourceMappingURL=fastPforEncoder.js.map