@kduma-oss/pcf 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +116 -0
  2. package/dist/consts.d.ts +42 -0
  3. package/dist/consts.d.ts.map +1 -0
  4. package/dist/consts.js +44 -0
  5. package/dist/consts.js.map +1 -0
  6. package/dist/container.d.ts +124 -0
  7. package/dist/container.d.ts.map +1 -0
  8. package/dist/container.js +441 -0
  9. package/dist/container.js.map +1 -0
  10. package/dist/crc.d.ts +15 -0
  11. package/dist/crc.d.ts.map +1 -0
  12. package/dist/crc.js +62 -0
  13. package/dist/crc.js.map +1 -0
  14. package/dist/entry.d.ts +44 -0
  15. package/dist/entry.d.ts.map +1 -0
  16. package/dist/entry.js +118 -0
  17. package/dist/entry.js.map +1 -0
  18. package/dist/errors.d.ts +56 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +88 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/hash.d.ts +59 -0
  23. package/dist/hash.d.ts.map +1 -0
  24. package/dist/hash.js +151 -0
  25. package/dist/hash.js.map +1 -0
  26. package/dist/header.d.ts +20 -0
  27. package/dist/header.d.ts.map +1 -0
  28. package/dist/header.js +38 -0
  29. package/dist/header.js.map +1 -0
  30. package/dist/index.d.ts +42 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +42 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/node-storage.d.ts +24 -0
  35. package/dist/node-storage.d.ts.map +1 -0
  36. package/dist/node-storage.js +52 -0
  37. package/dist/node-storage.js.map +1 -0
  38. package/dist/storage.d.ts +35 -0
  39. package/dist/storage.d.ts.map +1 -0
  40. package/dist/storage.js +66 -0
  41. package/dist/storage.js.map +1 -0
  42. package/dist/table.d.ts +28 -0
  43. package/dist/table.d.ts.map +1 -0
  44. package/dist/table.js +48 -0
  45. package/dist/table.js.map +1 -0
  46. package/package.json +60 -0
  47. package/src/consts.ts +50 -0
  48. package/src/container.ts +575 -0
  49. package/src/crc.ts +69 -0
  50. package/src/entry.ts +152 -0
  51. package/src/errors.ts +124 -0
  52. package/src/hash.ts +165 -0
  53. package/src/header.ts +50 -0
  54. package/src/index.ts +68 -0
  55. package/src/node-storage.ts +75 -0
  56. package/src/storage.ts +85 -0
  57. package/src/table.ts +67 -0
package/src/storage.ts ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Synchronous random-access storage backing a {@link "./container".Container}.
3
+ *
4
+ * This mirrors the reference implementation's `Read + Write + Seek` bound: the
5
+ * container only needs to read and write byte ranges at absolute offsets and to
6
+ * learn the current size. Two implementations are provided: an in-memory
7
+ * growable buffer (the analogue of the reference's `Cursor<Vec<u8>>`) and a
8
+ * Node file-descriptor backed store.
9
+ *
10
+ * Offsets are plain `number` byte positions. The on-disk format preserves full
11
+ * `u64` fidelity (offsets are encoded as little-endian `u64` via `DataView`);
12
+ * any real file fits comfortably below `Number.MAX_SAFE_INTEGER`.
13
+ */
14
+
15
+ import { PcfError, PcfErrorKind } from "./errors.js";
16
+
17
+ /** Random-access byte store. */
18
+ export interface Storage {
19
+ /** Read exactly `length` bytes starting at `offset`. */
20
+ readAt(offset: number, length: number): Uint8Array;
21
+ /** Write `data` starting at `offset`, growing the store as needed. */
22
+ writeAt(offset: number, data: Uint8Array): void;
23
+ /** Current logical size, in bytes. */
24
+ size(): number;
25
+ }
26
+
27
+ /** In-memory growable storage (the `Cursor<Vec<u8>>` analogue). */
28
+ export class MemoryStorage implements Storage {
29
+ private buf: Uint8Array;
30
+ private len: number;
31
+
32
+ constructor(initial?: Uint8Array) {
33
+ if (initial && initial.length > 0) {
34
+ this.buf = initial.slice();
35
+ this.len = initial.length;
36
+ } else {
37
+ this.buf = new Uint8Array(64);
38
+ this.len = 0;
39
+ }
40
+ }
41
+
42
+ private ensureCapacity(needed: number): void {
43
+ if (needed <= this.buf.length) {
44
+ return;
45
+ }
46
+ let cap = this.buf.length === 0 ? 64 : this.buf.length;
47
+ while (cap < needed) {
48
+ cap *= 2;
49
+ }
50
+ const next = new Uint8Array(cap);
51
+ next.set(this.buf.subarray(0, this.len), 0);
52
+ this.buf = next;
53
+ }
54
+
55
+ readAt(offset: number, length: number): Uint8Array {
56
+ if (offset < 0 || offset + length > this.len) {
57
+ throw new PcfError(
58
+ PcfErrorKind.Io,
59
+ `read out of bounds: offset ${offset}, length ${length}, size ${this.len}`,
60
+ );
61
+ }
62
+ return this.buf.slice(offset, offset + length);
63
+ }
64
+
65
+ writeAt(offset: number, data: Uint8Array): void {
66
+ if (offset < 0) {
67
+ throw new PcfError(PcfErrorKind.Io, `negative write offset ${offset}`);
68
+ }
69
+ const end = offset + data.length;
70
+ this.ensureCapacity(end);
71
+ this.buf.set(data, offset);
72
+ if (end > this.len) {
73
+ this.len = end;
74
+ }
75
+ }
76
+
77
+ size(): number {
78
+ return this.len;
79
+ }
80
+
81
+ /** Return a copy of the logical contents (the analogue of `into_inner`). */
82
+ toUint8Array(): Uint8Array {
83
+ return this.buf.slice(0, this.len);
84
+ }
85
+ }
package/src/table.ts ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The 74-byte table-block header and table-block hashing
3
+ * (spec sections 5.1, 8.4).
4
+ */
5
+
6
+ import { HASH_FIELD_SIZE, TABLE_HEADER_SIZE } from "./consts.js";
7
+ import { entryToBytes, type PartitionEntry } from "./entry.js";
8
+ import { computeHashField, HashAlgo, hashAlgoFromId, hashAlgoId } from "./hash.js";
9
+
10
+ /** Parsed table-block header (the entries follow it on disk). */
11
+ export interface TableBlockHeader {
12
+ /** Number of entries stored in this block (0..=255). */
13
+ partitionCount: number;
14
+ /** Absolute offset of the next block, or 0 for end-of-chain. */
15
+ nextTableOffset: bigint;
16
+ /** Algorithm used for `tableHash`. */
17
+ tableHashAlgo: HashAlgo;
18
+ /** 64-byte table hash field. */
19
+ tableHash: Uint8Array;
20
+ }
21
+
22
+ /** Serialise a table-block header to its on-disk 74-byte layout. */
23
+ export function tableHeaderToBytes(h: TableBlockHeader): Uint8Array {
24
+ const b = new Uint8Array(TABLE_HEADER_SIZE);
25
+ const view = new DataView(b.buffer);
26
+ b[0] = h.partitionCount & 0xff;
27
+ view.setBigUint64(1, h.nextTableOffset, true);
28
+ b[9] = hashAlgoId(h.tableHashAlgo);
29
+ b.set(h.tableHash, 10);
30
+ return b;
31
+ }
32
+
33
+ /** Parse a table-block header from its on-disk 74-byte layout. */
34
+ export function tableHeaderFromBytes(b: Uint8Array): TableBlockHeader {
35
+ const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
36
+ const partitionCount = b[0]!;
37
+ const nextTableOffset = view.getBigUint64(1, true);
38
+ const tableHashAlgo = hashAlgoFromId(b[9]!);
39
+ const tableHash = b.slice(10, 10 + HASH_FIELD_SIZE);
40
+ return { partitionCount, nextTableOffset, tableHashAlgo, tableHash };
41
+ }
42
+
43
+ /**
44
+ * Compute the table hash over `[header-with-zeroed-hash || entries]`
45
+ * (spec section 8.4). The `table_hash_algo` byte is included; the 64-byte
46
+ * hash field is treated as zero; trailing reserved space is excluded.
47
+ */
48
+ export function computeTableHash(
49
+ algo: HashAlgo,
50
+ nextTableOffset: bigint,
51
+ entries: readonly PartitionEntry[],
52
+ ): Uint8Array {
53
+ const header = tableHeaderToBytes({
54
+ partitionCount: entries.length,
55
+ nextTableOffset,
56
+ tableHashAlgo: algo,
57
+ tableHash: new Uint8Array(HASH_FIELD_SIZE), // zeroed for the computation
58
+ });
59
+ const image = new Uint8Array(TABLE_HEADER_SIZE + entries.length * 141);
60
+ image.set(header, 0);
61
+ let off = TABLE_HEADER_SIZE;
62
+ for (const e of entries) {
63
+ image.set(entryToBytes(e), off);
64
+ off += 141;
65
+ }
66
+ return computeHashField(algo, image);
67
+ }