ts
This commit is contained in:
1 parent
32adb5ddac
commit
1e49010aa9
17 files changed
+1049
No files matched your search
@@ -0,0 +1,95 @@
|
||||
import { LE } from "./buf.ts";
|
||||
|
||||
export const PAGE_SIZE = 4096;
|
||||
const HEADER_SIZE = 40;
|
||||
const ROW_GROUP_SIZE = 36;
|
||||
const ROWS_PER_GROUP = 16;
|
||||
|
||||
function align4(o: number): number { return (o + 3) & ~3; }
|
||||
function numRowGroups(n: number): number { return Math.floor((n + ROWS_PER_GROUP - 1) / ROWS_PER_GROUP); }
|
||||
|
||||
export class PageBuilder {
|
||||
pageIndex: number;
|
||||
pageType: number;
|
||||
nextPage: number;
|
||||
rowBytes: Uint8Array[] = [];
|
||||
rowOffsets: number[] = [];
|
||||
heapCursor = 0;
|
||||
|
||||
constructor(pageIndex: number, pageType: number) {
|
||||
this.pageIndex = pageIndex;
|
||||
this.pageType = pageType;
|
||||
this.nextPage = pageIndex;
|
||||
}
|
||||
|
||||
tryAdd(row: Uint8Array): boolean {
|
||||
const aligned = align4(this.heapCursor);
|
||||
const end = aligned + row.length;
|
||||
const footerAfter = numRowGroups(this.rowBytes.length + 1) * ROW_GROUP_SIZE;
|
||||
if (end + HEADER_SIZE + footerAfter > PAGE_SIZE) return false;
|
||||
this.rowOffsets.push(aligned);
|
||||
this.rowBytes.push(row);
|
||||
this.heapCursor = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
numRows(): number { return this.rowBytes.length; }
|
||||
|
||||
finalize(): Uint8Array {
|
||||
const n = this.rowBytes.length;
|
||||
const numRgs = numRowGroups(n);
|
||||
const footerSize = numRgs * ROW_GROUP_SIZE;
|
||||
const heapPadding = PAGE_SIZE - HEADER_SIZE - footerSize;
|
||||
const usedSize = this.heapCursor;
|
||||
const freeSize = heapPadding - usedSize;
|
||||
if (freeSize < 0) throw new Error("page overflow");
|
||||
|
||||
const pageFlags = n > 0 ? 0x00 : 0x44;
|
||||
|
||||
const h = new LE();
|
||||
h.u32(0); // magic
|
||||
h.u32(this.pageIndex);
|
||||
h.u32(this.pageType);
|
||||
h.u32(this.nextPage);
|
||||
h.u32(0); // unknown1
|
||||
h.u32(0); // unknown2
|
||||
h.u8(n & 0xff); h.u8(0); h.u8(0); h.u8(pageFlags);
|
||||
h.u16(freeSize); h.u16(usedSize);
|
||||
h.u16(0); // unknown5
|
||||
h.u16(n); // num_rows_large
|
||||
h.u16(0); // unknown6
|
||||
h.u16(0); // unknown7
|
||||
const header = h.build();
|
||||
if (header.length !== HEADER_SIZE) throw new Error(`hdr len ${header.length}`);
|
||||
|
||||
const heap = new Uint8Array(heapPadding);
|
||||
for (let i = 0; i < this.rowBytes.length; i++) {
|
||||
heap.set(this.rowBytes[i]!, this.rowOffsets[i]!);
|
||||
}
|
||||
|
||||
const footer = new LE();
|
||||
for (let g = numRgs - 1; g >= 0; g--) {
|
||||
const start = g * ROWS_PER_GROUP;
|
||||
const grpOffsets = this.rowOffsets.slice(start, start + ROWS_PER_GROUP);
|
||||
const grpCount = grpOffsets.length;
|
||||
const padded = grpOffsets.slice();
|
||||
while (padded.length < ROWS_PER_GROUP) padded.push(0);
|
||||
const presence = (1 << grpCount) - 1;
|
||||
const rg = new LE();
|
||||
for (let i = padded.length - 1; i >= 0; i--) rg.u16(padded[i]!);
|
||||
rg.u16(presence);
|
||||
rg.u16(16);
|
||||
const rgBytes = rg.build();
|
||||
if (rgBytes.length !== ROW_GROUP_SIZE) throw new Error("rg size");
|
||||
footer.bytes(rgBytes);
|
||||
}
|
||||
const footerBytes = footer.build();
|
||||
if (footerBytes.length !== footerSize) throw new Error("footer size");
|
||||
|
||||
const out = new Uint8Array(PAGE_SIZE);
|
||||
out.set(header, 0);
|
||||
out.set(heap, HEADER_SIZE);
|
||||
out.set(footerBytes, HEADER_SIZE + heapPadding);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user