@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.
- package/README.md +116 -0
- package/dist/consts.d.ts +42 -0
- package/dist/consts.d.ts.map +1 -0
- package/dist/consts.js +44 -0
- package/dist/consts.js.map +1 -0
- package/dist/container.d.ts +124 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +441 -0
- package/dist/container.js.map +1 -0
- package/dist/crc.d.ts +15 -0
- package/dist/crc.d.ts.map +1 -0
- package/dist/crc.js +62 -0
- package/dist/crc.js.map +1 -0
- package/dist/entry.d.ts +44 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +118 -0
- package/dist/entry.js.map +1 -0
- package/dist/errors.d.ts +56 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +88 -0
- package/dist/errors.js.map +1 -0
- package/dist/hash.d.ts +59 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +151 -0
- package/dist/hash.js.map +1 -0
- package/dist/header.d.ts +20 -0
- package/dist/header.d.ts.map +1 -0
- package/dist/header.js +38 -0
- package/dist/header.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/node-storage.d.ts +24 -0
- package/dist/node-storage.d.ts.map +1 -0
- package/dist/node-storage.js +52 -0
- package/dist/node-storage.js.map +1 -0
- package/dist/storage.d.ts +35 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +66 -0
- package/dist/storage.js.map +1 -0
- package/dist/table.d.ts +28 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +48 -0
- package/dist/table.js.map +1 -0
- package/package.json +60 -0
- package/src/consts.ts +50 -0
- package/src/container.ts +575 -0
- package/src/crc.ts +69 -0
- package/src/entry.ts +152 -0
- package/src/errors.ts +124 -0
- package/src/hash.ts +165 -0
- package/src/header.ts +50 -0
- package/src/index.ts +68 -0
- package/src/node-storage.ts +75 -0
- package/src/storage.ts +85 -0
- package/src/table.ts +67 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # `pcf` — Partitioned Container Format (TypeScript implementation)
|
|
3
|
+
*
|
|
4
|
+
* A language-agnostic binary container that stores multiple independent regions
|
|
5
|
+
* of bytes ("partitions") in a single file. This package mirrors the written
|
|
6
|
+
* specification (`PCF-spec-v1.0.txt`) and the Rust reference implementation
|
|
7
|
+
* field-for-field and favours auditability over performance.
|
|
8
|
+
*
|
|
9
|
+
* ## Layout at a glance
|
|
10
|
+
*
|
|
11
|
+
* ```text
|
|
12
|
+
* [ 20-byte header ] [ table block(s) ] [ partition data regions ]
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* All integers are little-endian. Free space is derived as
|
|
16
|
+
* `maxLength - usedBytes`.
|
|
17
|
+
*
|
|
18
|
+
* ## Example
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { Container, HashAlgo } from "pcf";
|
|
22
|
+
*
|
|
23
|
+
* const c = Container.create();
|
|
24
|
+
* const uid = new Uint8Array(16).fill(1);
|
|
25
|
+
* c.addPartition(0x10, uid, "notes", new TextEncoder().encode("hello world"), 64, HashAlgo.Sha256);
|
|
26
|
+
*
|
|
27
|
+
* c.verify();
|
|
28
|
+
* const entries = c.entries();
|
|
29
|
+
* console.log(new TextDecoder().decode(c.readPartitionData(entries[0]))); // "hello world"
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export * from "./consts.js";
|
|
33
|
+
export { PcfError, PcfErrorKind } from "./errors.js";
|
|
34
|
+
export { HashAlgo, hashAlgoFromId, hashAlgoId, digestLen, verifies, computeHashField, verifyHashField, } from "./hash.js";
|
|
35
|
+
export { crc32, crc32c, crc64 } from "./crc.js";
|
|
36
|
+
export { headerToBytes, headerFromBytes, } from "./header.js";
|
|
37
|
+
export { entryToBytes, entryFromBytes, validateEntry, freeBytes, entryLabelString, encodeLabel, decodeLabel, } from "./entry.js";
|
|
38
|
+
export { tableHeaderToBytes, tableHeaderFromBytes, computeTableHash, } from "./table.js";
|
|
39
|
+
export { MemoryStorage } from "./storage.js";
|
|
40
|
+
export { NodeFileStorage } from "./node-storage.js";
|
|
41
|
+
export { Container } from "./container.js";
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,QAAQ,EACR,cAAc,EACd,UAAU,EACV,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAEL,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAEL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAEL,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAkB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A {@link Storage} implementation backed by a Node file descriptor, the
|
|
3
|
+
* analogue of using `std::fs::File` with the reference implementation.
|
|
4
|
+
*
|
|
5
|
+
* This module imports `node:fs` and is therefore Node-only; the rest of the
|
|
6
|
+
* library is runtime-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
import type { Storage } from "./storage.js";
|
|
9
|
+
/** File-descriptor backed random-access storage. */
|
|
10
|
+
export declare class NodeFileStorage implements Storage {
|
|
11
|
+
private fd;
|
|
12
|
+
private constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Open `path` for reading and writing, creating it if necessary (mode
|
|
15
|
+
* `"r+"`-like with creation). Use `truncate` to start from an empty file.
|
|
16
|
+
*/
|
|
17
|
+
static open(path: string, truncate?: boolean): NodeFileStorage;
|
|
18
|
+
readAt(offset: number, length: number): Uint8Array;
|
|
19
|
+
writeAt(offset: number, data: Uint8Array): void;
|
|
20
|
+
size(): number;
|
|
21
|
+
/** Close the underlying file descriptor. */
|
|
22
|
+
close(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=node-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-storage.d.ts","sourceRoot":"","sources":["../src/node-storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,oDAAoD;AACpD,qBAAa,eAAgB,YAAW,OAAO;IAC7C,OAAO,CAAC,EAAE,CAAS;IAEnB,OAAO;IAIP;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,eAAe;IAO5D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU;IAelD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI;IAc/C,IAAI,IAAI,MAAM;IAId,4CAA4C;IAC5C,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A {@link Storage} implementation backed by a Node file descriptor, the
|
|
3
|
+
* analogue of using `std::fs::File` with the reference implementation.
|
|
4
|
+
*
|
|
5
|
+
* This module imports `node:fs` and is therefore Node-only; the rest of the
|
|
6
|
+
* library is runtime-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
import { closeSync, fstatSync, openSync, readSync, writeSync, } from "node:fs";
|
|
9
|
+
/** File-descriptor backed random-access storage. */
|
|
10
|
+
export class NodeFileStorage {
|
|
11
|
+
fd;
|
|
12
|
+
constructor(fd) {
|
|
13
|
+
this.fd = fd;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Open `path` for reading and writing, creating it if necessary (mode
|
|
17
|
+
* `"r+"`-like with creation). Use `truncate` to start from an empty file.
|
|
18
|
+
*/
|
|
19
|
+
static open(path, truncate = false) {
|
|
20
|
+
// "w+" truncates+creates for read/write; "a+"/"r+" otherwise.
|
|
21
|
+
const flags = truncate ? "w+" : "a+";
|
|
22
|
+
const fd = openSync(path, flags);
|
|
23
|
+
return new NodeFileStorage(fd);
|
|
24
|
+
}
|
|
25
|
+
readAt(offset, length) {
|
|
26
|
+
const buf = new Uint8Array(length);
|
|
27
|
+
let read = 0;
|
|
28
|
+
while (read < length) {
|
|
29
|
+
const n = readSync(this.fd, buf, read, length - read, offset + read);
|
|
30
|
+
if (n === 0) {
|
|
31
|
+
throw new Error(`unexpected EOF: wanted ${length} bytes at offset ${offset}`);
|
|
32
|
+
}
|
|
33
|
+
read += n;
|
|
34
|
+
}
|
|
35
|
+
return buf;
|
|
36
|
+
}
|
|
37
|
+
writeAt(offset, data) {
|
|
38
|
+
let written = 0;
|
|
39
|
+
while (written < data.length) {
|
|
40
|
+
const n = writeSync(this.fd, data, written, data.length - written, offset + written);
|
|
41
|
+
written += n;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
size() {
|
|
45
|
+
return fstatSync(this.fd).size;
|
|
46
|
+
}
|
|
47
|
+
/** Close the underlying file descriptor. */
|
|
48
|
+
close() {
|
|
49
|
+
closeSync(this.fd);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=node-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-storage.js","sourceRoot":"","sources":["../src/node-storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,MAAM,SAAS,CAAC;AAIjB,oDAAoD;AACpD,MAAM,OAAO,eAAe;IAClB,EAAE,CAAS;IAEnB,YAAoB,EAAU;QAC5B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,IAAY,EAAE,QAAQ,GAAG,KAAK;QACxC,8DAA8D;QAC9D,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACrC,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,eAAe,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,MAAc,EAAE,MAAc;QACnC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,OAAO,IAAI,GAAG,MAAM,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;YACrE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,0BAA0B,MAAM,oBAAoB,MAAM,EAAE,CAC7D,CAAC;YACJ,CAAC;YACD,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,IAAgB;QACtC,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,SAAS,CACjB,IAAI,CAAC,EAAE,EACP,IAAI,EACJ,OAAO,EACP,IAAI,CAAC,MAAM,GAAG,OAAO,EACrB,MAAM,GAAG,OAAO,CACjB,CAAC;YACF,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI;QACF,OAAO,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;IACjC,CAAC;IAED,4CAA4C;IAC5C,KAAK;QACH,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
/** Random-access byte store. */
|
|
15
|
+
export interface Storage {
|
|
16
|
+
/** Read exactly `length` bytes starting at `offset`. */
|
|
17
|
+
readAt(offset: number, length: number): Uint8Array;
|
|
18
|
+
/** Write `data` starting at `offset`, growing the store as needed. */
|
|
19
|
+
writeAt(offset: number, data: Uint8Array): void;
|
|
20
|
+
/** Current logical size, in bytes. */
|
|
21
|
+
size(): number;
|
|
22
|
+
}
|
|
23
|
+
/** In-memory growable storage (the `Cursor<Vec<u8>>` analogue). */
|
|
24
|
+
export declare class MemoryStorage implements Storage {
|
|
25
|
+
private buf;
|
|
26
|
+
private len;
|
|
27
|
+
constructor(initial?: Uint8Array);
|
|
28
|
+
private ensureCapacity;
|
|
29
|
+
readAt(offset: number, length: number): Uint8Array;
|
|
30
|
+
writeAt(offset: number, data: Uint8Array): void;
|
|
31
|
+
size(): number;
|
|
32
|
+
/** Return a copy of the logical contents (the analogue of `into_inner`). */
|
|
33
|
+
toUint8Array(): Uint8Array;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,gCAAgC;AAChC,MAAM,WAAW,OAAO;IACtB,wDAAwD;IACxD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IACnD,sEAAsE;IACtE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAChD,sCAAsC;IACtC,IAAI,IAAI,MAAM,CAAC;CAChB;AAED,mEAAmE;AACnE,qBAAa,aAAc,YAAW,OAAO;IAC3C,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,GAAG,CAAS;gBAER,OAAO,CAAC,EAAE,UAAU;IAUhC,OAAO,CAAC,cAAc;IAatB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU;IAUlD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI;IAY/C,IAAI,IAAI,MAAM;IAId,4EAA4E;IAC5E,YAAY,IAAI,UAAU;CAG3B"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
import { PcfError, PcfErrorKind } from "./errors.js";
|
|
15
|
+
/** In-memory growable storage (the `Cursor<Vec<u8>>` analogue). */
|
|
16
|
+
export class MemoryStorage {
|
|
17
|
+
buf;
|
|
18
|
+
len;
|
|
19
|
+
constructor(initial) {
|
|
20
|
+
if (initial && initial.length > 0) {
|
|
21
|
+
this.buf = initial.slice();
|
|
22
|
+
this.len = initial.length;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.buf = new Uint8Array(64);
|
|
26
|
+
this.len = 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
ensureCapacity(needed) {
|
|
30
|
+
if (needed <= this.buf.length) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let cap = this.buf.length === 0 ? 64 : this.buf.length;
|
|
34
|
+
while (cap < needed) {
|
|
35
|
+
cap *= 2;
|
|
36
|
+
}
|
|
37
|
+
const next = new Uint8Array(cap);
|
|
38
|
+
next.set(this.buf.subarray(0, this.len), 0);
|
|
39
|
+
this.buf = next;
|
|
40
|
+
}
|
|
41
|
+
readAt(offset, length) {
|
|
42
|
+
if (offset < 0 || offset + length > this.len) {
|
|
43
|
+
throw new PcfError(PcfErrorKind.Io, `read out of bounds: offset ${offset}, length ${length}, size ${this.len}`);
|
|
44
|
+
}
|
|
45
|
+
return this.buf.slice(offset, offset + length);
|
|
46
|
+
}
|
|
47
|
+
writeAt(offset, data) {
|
|
48
|
+
if (offset < 0) {
|
|
49
|
+
throw new PcfError(PcfErrorKind.Io, `negative write offset ${offset}`);
|
|
50
|
+
}
|
|
51
|
+
const end = offset + data.length;
|
|
52
|
+
this.ensureCapacity(end);
|
|
53
|
+
this.buf.set(data, offset);
|
|
54
|
+
if (end > this.len) {
|
|
55
|
+
this.len = end;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
size() {
|
|
59
|
+
return this.len;
|
|
60
|
+
}
|
|
61
|
+
/** Return a copy of the logical contents (the analogue of `into_inner`). */
|
|
62
|
+
toUint8Array() {
|
|
63
|
+
return this.buf.slice(0, this.len);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAYrD,mEAAmE;AACnE,MAAM,OAAO,aAAa;IAChB,GAAG,CAAa;IAChB,GAAG,CAAS;IAEpB,YAAY,OAAoB;QAC9B,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAEO,cAAc,CAAC,MAAc;QACnC,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;QACvD,OAAO,GAAG,GAAG,MAAM,EAAE,CAAC;YACpB,GAAG,IAAI,CAAC,CAAC;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,MAAc,EAAE,MAAc;QACnC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7C,MAAM,IAAI,QAAQ,CAChB,YAAY,CAAC,EAAE,EACf,8BAA8B,MAAM,YAAY,MAAM,UAAU,IAAI,CAAC,GAAG,EAAE,CAC3E,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,IAAgB;QACtC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,QAAQ,CAAC,YAAY,CAAC,EAAE,EAAE,yBAAyB,MAAM,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QACjC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACjB,CAAC;IACH,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED,4EAA4E;IAC5E,YAAY;QACV,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;CACF"}
|
package/dist/table.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The 74-byte table-block header and table-block hashing
|
|
3
|
+
* (spec sections 5.1, 8.4).
|
|
4
|
+
*/
|
|
5
|
+
import { type PartitionEntry } from "./entry.js";
|
|
6
|
+
import { HashAlgo } from "./hash.js";
|
|
7
|
+
/** Parsed table-block header (the entries follow it on disk). */
|
|
8
|
+
export interface TableBlockHeader {
|
|
9
|
+
/** Number of entries stored in this block (0..=255). */
|
|
10
|
+
partitionCount: number;
|
|
11
|
+
/** Absolute offset of the next block, or 0 for end-of-chain. */
|
|
12
|
+
nextTableOffset: bigint;
|
|
13
|
+
/** Algorithm used for `tableHash`. */
|
|
14
|
+
tableHashAlgo: HashAlgo;
|
|
15
|
+
/** 64-byte table hash field. */
|
|
16
|
+
tableHash: Uint8Array;
|
|
17
|
+
}
|
|
18
|
+
/** Serialise a table-block header to its on-disk 74-byte layout. */
|
|
19
|
+
export declare function tableHeaderToBytes(h: TableBlockHeader): Uint8Array;
|
|
20
|
+
/** Parse a table-block header from its on-disk 74-byte layout. */
|
|
21
|
+
export declare function tableHeaderFromBytes(b: Uint8Array): TableBlockHeader;
|
|
22
|
+
/**
|
|
23
|
+
* Compute the table hash over `[header-with-zeroed-hash || entries]`
|
|
24
|
+
* (spec section 8.4). The `table_hash_algo` byte is included; the 64-byte
|
|
25
|
+
* hash field is treated as zero; trailing reserved space is excluded.
|
|
26
|
+
*/
|
|
27
|
+
export declare function computeTableHash(algo: HashAlgo, nextTableOffset: bigint, entries: readonly PartitionEntry[]): Uint8Array;
|
|
28
|
+
//# sourceMappingURL=table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAoB,QAAQ,EAA8B,MAAM,WAAW,CAAC;AAEnF,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,sCAAsC;IACtC,aAAa,EAAE,QAAQ,CAAC;IACxB,gCAAgC;IAChC,SAAS,EAAE,UAAU,CAAC;CACvB;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAQlE;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,GAAG,gBAAgB,CAOpE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,QAAQ,EACd,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,SAAS,cAAc,EAAE,GACjC,UAAU,CAeZ"}
|
package/dist/table.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The 74-byte table-block header and table-block hashing
|
|
3
|
+
* (spec sections 5.1, 8.4).
|
|
4
|
+
*/
|
|
5
|
+
import { HASH_FIELD_SIZE, TABLE_HEADER_SIZE } from "./consts.js";
|
|
6
|
+
import { entryToBytes } from "./entry.js";
|
|
7
|
+
import { computeHashField, HashAlgo, hashAlgoFromId, hashAlgoId } from "./hash.js";
|
|
8
|
+
/** Serialise a table-block header to its on-disk 74-byte layout. */
|
|
9
|
+
export function tableHeaderToBytes(h) {
|
|
10
|
+
const b = new Uint8Array(TABLE_HEADER_SIZE);
|
|
11
|
+
const view = new DataView(b.buffer);
|
|
12
|
+
b[0] = h.partitionCount & 0xff;
|
|
13
|
+
view.setBigUint64(1, h.nextTableOffset, true);
|
|
14
|
+
b[9] = hashAlgoId(h.tableHashAlgo);
|
|
15
|
+
b.set(h.tableHash, 10);
|
|
16
|
+
return b;
|
|
17
|
+
}
|
|
18
|
+
/** Parse a table-block header from its on-disk 74-byte layout. */
|
|
19
|
+
export function tableHeaderFromBytes(b) {
|
|
20
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
21
|
+
const partitionCount = b[0];
|
|
22
|
+
const nextTableOffset = view.getBigUint64(1, true);
|
|
23
|
+
const tableHashAlgo = hashAlgoFromId(b[9]);
|
|
24
|
+
const tableHash = b.slice(10, 10 + HASH_FIELD_SIZE);
|
|
25
|
+
return { partitionCount, nextTableOffset, tableHashAlgo, tableHash };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Compute the table hash over `[header-with-zeroed-hash || entries]`
|
|
29
|
+
* (spec section 8.4). The `table_hash_algo` byte is included; the 64-byte
|
|
30
|
+
* hash field is treated as zero; trailing reserved space is excluded.
|
|
31
|
+
*/
|
|
32
|
+
export function computeTableHash(algo, nextTableOffset, entries) {
|
|
33
|
+
const header = tableHeaderToBytes({
|
|
34
|
+
partitionCount: entries.length,
|
|
35
|
+
nextTableOffset,
|
|
36
|
+
tableHashAlgo: algo,
|
|
37
|
+
tableHash: new Uint8Array(HASH_FIELD_SIZE), // zeroed for the computation
|
|
38
|
+
});
|
|
39
|
+
const image = new Uint8Array(TABLE_HEADER_SIZE + entries.length * 141);
|
|
40
|
+
image.set(header, 0);
|
|
41
|
+
let off = TABLE_HEADER_SIZE;
|
|
42
|
+
for (const e of entries) {
|
|
43
|
+
image.set(entryToBytes(e), off);
|
|
44
|
+
off += 141;
|
|
45
|
+
}
|
|
46
|
+
return computeHashField(algo, image);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAcnF,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAAC,CAAmB;IACpD,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACvB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,oBAAoB,CAAC,CAAa;IAChD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IAChE,MAAM,cAAc,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IAC7B,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,eAAe,CAAC,CAAC;IACpD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAc,EACd,eAAuB,EACvB,OAAkC;IAElC,MAAM,MAAM,GAAG,kBAAkB,CAAC;QAChC,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,eAAe;QACf,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,6BAA6B;KAC1E,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACvE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACrB,IAAI,GAAG,GAAG,iBAAiB,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChC,GAAG,IAAI,GAAG,CAAC;IACb,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kduma-oss/pcf",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "TypeScript implementation of the Partitioned Container Format (PCF) v1.0",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"author": "Krystian Duma",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/kduma-OSS/Partitioned-Container-Format.git",
|
|
24
|
+
"directory": "implementations/ts"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/kduma-OSS/Partitioned-Container-Format/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/kduma-OSS/Partitioned-Container-Format#readme",
|
|
30
|
+
"keywords": [
|
|
31
|
+
"pcf",
|
|
32
|
+
"partitioned-container-format",
|
|
33
|
+
"binary",
|
|
34
|
+
"container",
|
|
35
|
+
"file-format"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc -p tsconfig.json",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"coverage": "vitest run --coverage",
|
|
48
|
+
"gen-testvector": "tsx examples/gen-testvector.ts"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@noble/hashes": "^1.5.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^22.19.19",
|
|
55
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
56
|
+
"tsx": "^4.19.0",
|
|
57
|
+
"typescript": "^5.6.0",
|
|
58
|
+
"vitest": "^4.1.8"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-disk constants defined by PCF v1.0.
|
|
3
|
+
*
|
|
4
|
+
* Every value here is normative and corresponds directly to a figure in the
|
|
5
|
+
* specification (see Appendix A, "Field Layout Summary").
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** File signature, 8 bytes: `0x89 'K' 'P' 'R' 'T' 0x0D 0x0A 0x1A`. */
|
|
9
|
+
export const MAGIC: Uint8Array = new Uint8Array([
|
|
10
|
+
0x89, 0x4b, 0x50, 0x52, 0x54, 0x0d, 0x0a, 0x1a,
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
/** Major format version implemented by this library. */
|
|
14
|
+
export const VERSION_MAJOR = 1;
|
|
15
|
+
/** Minor format version implemented by this library. */
|
|
16
|
+
export const VERSION_MINOR = 0;
|
|
17
|
+
|
|
18
|
+
/** Fixed size of the file header, in bytes. */
|
|
19
|
+
export const HEADER_SIZE = 20;
|
|
20
|
+
/** Fixed size of a table-block header, in bytes. */
|
|
21
|
+
export const TABLE_HEADER_SIZE = 74;
|
|
22
|
+
/** Fixed size of a single partition entry, in bytes. */
|
|
23
|
+
export const ENTRY_SIZE = 141;
|
|
24
|
+
|
|
25
|
+
/** Size of every hash field, in bytes (large enough for the widest digest). */
|
|
26
|
+
export const HASH_FIELD_SIZE = 64;
|
|
27
|
+
/** Size of the partition label field, in bytes. */
|
|
28
|
+
export const LABEL_SIZE = 32;
|
|
29
|
+
/** Size of the partition UID field, in bytes. */
|
|
30
|
+
export const UID_SIZE = 16;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reserved partition type: invalid / uninitialised. MUST NOT label a live
|
|
34
|
+
* partition.
|
|
35
|
+
*/
|
|
36
|
+
export const TYPE_RESERVED = 0x0000_0000;
|
|
37
|
+
/**
|
|
38
|
+
* Reserved partition type: raw / blob, interpreted entirely by the
|
|
39
|
+
* application.
|
|
40
|
+
*/
|
|
41
|
+
export const TYPE_RAW = 0xffff_ffff;
|
|
42
|
+
|
|
43
|
+
/** The NIL UID (all zero). MUST NOT label a live partition. */
|
|
44
|
+
export const NIL_UID: Uint8Array = new Uint8Array(UID_SIZE);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Maximum number of entries a single table block can hold (`partition_count`
|
|
48
|
+
* is a `u8`).
|
|
49
|
+
*/
|
|
50
|
+
export const MAX_ENTRIES_PER_BLOCK = 255;
|