alinea
Version:
Headless git-based CMS
197 lines (195 loc) • 5.74 kB
JavaScript
import "../../chunks/chunk-NZLE2WMY.js";
// src/core/source/GitDelta.ts
import { assert } from "../util/Assert.js";
var MIN_MATCH = 4;
var MAX_LITERAL = 127;
var MAX_COPY_SIZE = 16777215;
function createGitDelta(base, target) {
const out = [];
writeVarInt(base.length, out);
writeVarInt(target.length, out);
const index = buildBaseIndex(base);
const literals = [];
let i = 0;
while (i < target.length) {
const best = findBestMatch(base, target, i, index);
if (!best || best.length < MIN_MATCH) {
literals.push(target[i]);
i++;
if (literals.length === MAX_LITERAL) flushLiterals(literals, out);
continue;
}
flushLiterals(literals, out);
let remaining = best.length;
let copyOffset = best.offset;
while (remaining > 0) {
const chunkSize = Math.min(remaining, MAX_COPY_SIZE);
writeCopyInstruction(copyOffset, chunkSize, out);
copyOffset += chunkSize;
remaining -= chunkSize;
}
i += best.length;
}
flushLiterals(literals, out);
return Uint8Array.from(out);
}
function applyGitDelta(base, delta) {
let pos = 0;
const [baseSize, baseSizePos] = readVarInt(delta, pos);
pos = baseSizePos;
const [resultSize, resultSizePos] = readVarInt(delta, pos);
pos = resultSizePos;
assert(baseSize === base.length, "Git delta base size mismatch");
const result = new Uint8Array(resultSize);
let outPos = 0;
while (pos < delta.length) {
const opcode = delta[pos++];
if (opcode & 128) {
let offset = 0;
let size = 0;
if (opcode & 1) offset |= delta[pos++];
if (opcode & 2) offset |= delta[pos++] << 8;
if (opcode & 4) offset |= delta[pos++] << 16;
if (opcode & 8) offset |= delta[pos++] << 24;
if (opcode & 16) size |= delta[pos++];
if (opcode & 32) size |= delta[pos++] << 8;
if (opcode & 64) size |= delta[pos++] << 16;
if (size === 0) size = 65536;
assert(offset >= 0 && offset + size <= base.length, "Invalid delta copy");
assert(outPos + size <= result.length, "Delta expands beyond target size");
result.set(base.subarray(offset, offset + size), outPos);
outPos += size;
continue;
}
assert(opcode !== 0, "Invalid delta opcode 0");
assert(pos + opcode <= delta.length, "Invalid delta insert");
assert(outPos + opcode <= result.length, "Delta expands beyond target size");
result.set(delta.subarray(pos, pos + opcode), outPos);
pos += opcode;
outPos += opcode;
}
assert(outPos === result.length, "Delta result size mismatch");
return result;
}
function writeVarInt(value, out) {
let n = value >>> 0;
while (true) {
let byte = n & 127;
n >>>= 7;
if (n !== 0) byte |= 128;
out.push(byte);
if (n === 0) break;
}
}
function readVarInt(data, start) {
let shift = 0;
let value = 0;
let pos = start;
while (true) {
assert(pos < data.length, "Invalid varint");
const byte = data[pos++];
value |= (byte & 127) << shift;
if ((byte & 128) === 0) break;
shift += 7;
assert(shift <= 28, "Varint too large");
}
return [value >>> 0, pos];
}
function flushLiterals(literals, out) {
if (literals.length === 0) return;
let pos = 0;
while (pos < literals.length) {
const chunk = Math.min(MAX_LITERAL, literals.length - pos);
out.push(chunk);
for (let i = 0; i < chunk; i++) out.push(literals[pos + i]);
pos += chunk;
}
literals.length = 0;
}
function writeCopyInstruction(offset, size, out) {
assert(size > 0, "Invalid copy size");
let opcode = 128;
const params = [];
const offset0 = offset & 255;
const offset1 = offset >>> 8 & 255;
const offset2 = offset >>> 16 & 255;
const offset3 = offset >>> 24 & 255;
if (offset0 !== 0) {
opcode |= 1;
params.push(offset0);
}
if (offset1 !== 0) {
opcode |= 2;
params.push(offset1);
}
if (offset2 !== 0) {
opcode |= 4;
params.push(offset2);
}
if (offset3 !== 0) {
opcode |= 8;
params.push(offset3);
}
const size0 = size & 255;
const size1 = size >>> 8 & 255;
const size2 = size >>> 16 & 255;
if (size !== 65536) {
if (size0 !== 0) {
opcode |= 16;
params.push(size0);
}
if (size1 !== 0) {
opcode |= 32;
params.push(size1);
}
if (size2 !== 0) {
opcode |= 64;
params.push(size2);
}
}
out.push(opcode);
for (const byte of params) out.push(byte);
}
function buildBaseIndex(base) {
const index = /* @__PURE__ */ new Map();
for (let i = 0; i + MIN_MATCH <= base.length; i++) {
const key = key4(base, i);
const list = index.get(key);
if (list) {
if (list.length >= 64) list.shift();
list.push(i);
} else {
index.set(key, [i]);
}
}
return index;
}
function findBestMatch(base, target, targetPos, index) {
if (targetPos + MIN_MATCH > target.length) return void 0;
const key = key4(target, targetPos);
const candidates = index.get(key);
if (!candidates || candidates.length === 0) return void 0;
let bestOffset = 0;
let bestLength = 0;
for (let i = candidates.length - 1; i >= 0; i--) {
const basePos = candidates[i];
let length = 0;
while (basePos + length < base.length && targetPos + length < target.length && base[basePos + length] === target[targetPos + length]) {
length++;
}
if (length > bestLength) {
bestLength = length;
bestOffset = basePos;
if (bestLength >= MAX_COPY_SIZE) break;
}
}
if (bestLength < MIN_MATCH) return void 0;
return { offset: bestOffset, length: bestLength };
}
function key4(data, pos) {
return data[pos] << 24 | data[pos + 1] << 16 | data[pos + 2] << 8 | data[pos + 3];
}
export {
applyGitDelta,
createGitDelta
};