/** * The archive's on-disk block format, written by hand so a test can serve * the bytes `getBlock` returns. */ import { zstdCompressSync } from 'node:zlib'; import { cborEncodeDagCbor } from '@pdsjs/core/repo'; /** * The columnar block body exactly as the archive writes it: an event * count, per-event fixed columns, then the concatenated variable columns. * @param {Array<{seq: number, kind: number, did: string, collection: string, rkey: string, rev: string, payload: Uint8Array}>} rows * @returns {Uint8Array} */ export function encodeBlockColumns(rows) { /** @param {number} value @param {1|2|4|8} bytes */ const le = (value, bytes) => { const out = new Uint8Array(bytes); const view = new DataView(out.buffer); if (bytes === 1) view.setUint8(0, value); if (bytes === 2) view.setUint16(0, value, true); if (bytes === 4) view.setUint32(0, value, true); if (bytes === 8) view.setBigUint64(0, BigInt(value), true); return out; }; /** @param {string} value @returns {Uint8Array} */ const text = (value) => new TextEncoder().encode(value); /** @param {Uint8Array[]} parts @returns {Uint8Array} */ const join = (parts) => { const total = parts.reduce((sum, part) => sum + part.length, 0); const out = new Uint8Array(total); let offset = 0; for (const part of parts) { out.set(part, offset); offset += part.length; } return out; }; return join([ le(rows.length, 4), ...rows.map((row) => le(row.seq, 8)), ...rows.map(() => le(0, 8)), ...rows.map(() => le(0, 8)), ...rows.map((row) => le(row.kind, 1)), ...rows.map((row) => le(row.collection.length, 1)), ...rows.map((row) => le(row.did.length, 2)), ...rows.map((row) => le(row.rkey.length, 1)), ...rows.map((row) => le(row.rev.length, 1)), ...rows.map((row) => le(row.payload.length, 4)), ...rows.map((row) => text(row.collection)), ...rows.map((row) => text(row.did)), ...rows.map((row) => text(row.rkey)), ...rows.map((row) => text(row.rev)), ...rows.map((row) => row.payload), ]); } /** * One row, encoded and zstd-compressed into the frame `getBlock` serves. * @param {Array<{seq: number, kind: number, did: string, collection: string, rkey: string, rev: string, payload?: Record|Uint8Array}>} rows * @returns {Uint8Array} */ export function blockFrame(rows) { const encoded = rows.map((row) => ({ ...row, payload: row.payload instanceof Uint8Array ? row.payload : cborEncodeDagCbor(row.payload ?? {}), })); return zstdCompressSync(Buffer.from(encodeBlockColumns(encoded))); }