@fizzyflow/suisql
Version:
SuiSQL is a library and set of tools for working with decentralized SQL databases on the Sui blockchain and Walrus protocol.
109 lines (88 loc) • 2.65 kB
text/typescript
import { compress, decompress } from './SuiSqlUtils.js';
export default class SuiSqlBinaryPatch {
/**
* Applies a binary patch to an original Uint8Array.
* @param {Uint8Array} original - The original array to be patched.
* @param {Uint8Array} binaryPatch - The binary patch to apply.
* @returns {Uint8Array} - The patched array.
*/
static applyPatch(original: Uint8Array, binaryPatch: Uint8Array) {
const result = new Uint8Array(original);
let offset = 0;
while (offset < binaryPatch.length) {
const start = (
(binaryPatch[offset] << 24) |
(binaryPatch[offset + 1] << 16) |
(binaryPatch[offset + 2] << 8) |
binaryPatch[offset + 3]
);
const length = (binaryPatch[offset + 4] << 8) | binaryPatch[offset + 5];
offset += 6;
const newBytes = binaryPatch.slice(offset, offset + length);
result.set(newBytes, start);
offset += length;
}
return result;
}
static async compressedBinaryDiff(a: Uint8Array, b: Uint8Array) {
return compress(this.binaryDiff(a, b));
}
static binaryDiff(a: Uint8Array, b: Uint8Array) {
return this.encodeFixedLengthPatch(this.diff(a, b));
}
static diff(a: Uint8Array, b: Uint8Array) {
if (a.length !== b.length) {
throw new Error("Arrays must be of equal length");
}
const patch = [];
let i = 0;
while (i < a.length) {
if (a[i] === b[i]) {
i++;
continue;
}
const start = i;
while (i < a.length && a[i] !== b[i]) {
i++;
};
// also we can try to skip up to 8 same bytes to make patch smaller overall
let extraI = i;
while (extraI < a.length && a[extraI] === b[extraI] && (extraI - i) < 8) {
extraI++;
};
if (extraI - i > 0 && extraI - i < 8) {
i = extraI; // extend the diff to include these same bytes
}
patch.push({
start,
length: (i - start + 1),
newBytes: b.slice(start, i + 1),
});
}
return patch;
}
static encodeFixedLengthPatch(patch: any[]) {
const chunks = [];
for (const { start, length, newBytes } of patch) {
const header = new Uint8Array(6);
header[0] = (start >>> 24) & 0xff;
header[1] = (start >>> 16) & 0xff;
header[2] = (start >>> 8) & 0xff;
header[3] = start & 0xff;
header[4] = (length >>> 8) & 0xff;
header[5] = length & 0xff;
chunks.push(header, newBytes);
}
return this.concatUint8Arrays(chunks);
}
static concatUint8Arrays(arrays: Uint8Array[]) {
const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
}