|
| 1 | +# pcf-dcp — PCF Dynamic Container Partition (reference implementation) |
| 2 | + |
| 3 | +Reference reader/writer for **PCF-DCP v1.0**, an application-level profile that |
| 4 | +adds *dynamic*, fragmentable, dedup-friendly sub-partitions to the |
| 5 | +[Partitioned Container Format](../PCF-v1.0) without modifying the PCF byte |
| 6 | +container. |
| 7 | + |
| 8 | +This crate mirrors the written specification (`specs/PCF-DCP-spec-v1.0.txt`) |
| 9 | +field-for-field and is intended as the *normative* implementation against which |
| 10 | +language ports are checked. It favours auditability over performance. |
| 11 | + |
| 12 | +## Model at a glance |
| 13 | + |
| 14 | +PCF-DCP defines one new PCF partition type: |
| 15 | + |
| 16 | +| Type | Name | Holds | |
| 17 | +|--------------|-----------------|----------------------------------------------------| |
| 18 | +| `0xAAAC0001` | `DCP_CONTAINER` | An *arena*: a header, an inner partition table, fragment tables, and data extents | |
| 19 | + |
| 20 | +A DCP container's bytes are an **arena** addressed by arena-relative offsets: |
| 21 | + |
| 22 | +``` |
| 23 | +arena: |
| 24 | +[ DCP Header (24 B) | data extents | Fragment Tables | Inner Table Block(s) ] |
| 25 | +``` |
| 26 | + |
| 27 | +* **DCP Header** — `"PDCP"` magic, profile version, `inner_table_offset`, |
| 28 | + `arena_used` (a bump pointer). |
| 29 | +* **Inner Table Block** — a chain of reused PCF Table Blocks (74 B header + |
| 30 | + 141 B entries), byte-for-byte identical to the top-level table, listing the |
| 31 | + *inner* partitions. Two entry fields are reinterpreted: `start_offset` points |
| 32 | + at the partition's Fragment Table, and `max_length` equals `used_bytes`. |
| 33 | +* **Fragment Table** — per inner partition, a chain of 9-byte block headers each |
| 34 | + followed by 18-byte **Fragment Entries**. Each entry names one extent |
| 35 | + `(offset, length, kind, flags)`. The logical content of an inner partition is |
| 36 | + the concatenation of its DATA extents. |
| 37 | + |
| 38 | +A generic PCF reader sees a DCP file as **one opaque partition**; only a |
| 39 | +DCP-aware reader looks inside. A DCP file is always a conforming PCF v1.0 file. |
| 40 | + |
| 41 | +## Why a profile |
| 42 | + |
| 43 | +PCF stores each partition as a contiguous, statically-reserved region. PCF-DCP |
| 44 | +makes each *inner* partition grow, shrink, and be edited in the middle without |
| 45 | +relocating its neighbours, by describing it as a list of extents rather than one |
| 46 | +range. This buys: |
| 47 | + |
| 48 | +* **Fragmentation / random edits** — append, insert, overwrite, delete, and |
| 49 | + truncate are edits of the Fragment Table (copy-on-write for shared bytes); no |
| 50 | + data is moved. |
| 51 | +* **Deduplication** — two extents may name the same arena bytes; identical |
| 52 | + chunks are stored once. The per-extent `SHARED` flag makes safe in-place |
| 53 | + editing explicit. |
| 54 | +* **Hash / signature stability** — an inner partition's `data_hash` covers its |
| 55 | + *logical content*, so fragmentation, dedup, compaction, and promotion all |
| 56 | + leave the hash (and any PCF-SIG signature over it) unchanged. |
| 57 | + |
| 58 | +## Library example |
| 59 | + |
| 60 | +```rust |
| 61 | +use std::io::Cursor; |
| 62 | +use pcf_dcp::{Arena, Chunker, DcpReader, DcpWriter, HashAlgo}; |
| 63 | + |
| 64 | +let mut arena = Arena::new(); |
| 65 | +arena.add_inner(0x10, [0xA1; 16], "A", b"Hello, World!", HashAlgo::Sha256, Chunker::Fixed(7))?; |
| 66 | +arena.add_inner(0x10, [0xB2; 16], "B", b"World!", HashAlgo::Sha256, Chunker::Whole)?; |
| 67 | + |
| 68 | +let mut w = DcpWriter::new(); |
| 69 | +w.add_container([0xDC; 16], "dcp", arena)?; |
| 70 | +let image = w.to_image()?; |
| 71 | + |
| 72 | +let mut r = DcpReader::open(Cursor::new(image))?; |
| 73 | +r.verify()?; |
| 74 | +assert_eq!(r.read_inner(&[0xB2; 16])?, b"World!"); |
| 75 | +# Ok::<(), pcf_dcp::Error>(()) |
| 76 | +``` |
| 77 | + |
| 78 | +## Promotion / demotion |
| 79 | + |
| 80 | +`DcpWriter::promote` moves an inner partition out to a top-level PCF partition |
| 81 | +(dynamic → fixed); `demote` moves a top-level partition into a container |
| 82 | +(fixed → dynamic). Both preserve `uid`, `partition_type`, `label`, |
| 83 | +`data_hash_algo_id`, and `data_hash` — the **promotion invariant**, identical to |
| 84 | +the set of fields a PCF-SIG signature protects. |
| 85 | + |
| 86 | +## Command-line tool |
| 87 | + |
| 88 | +The `dcp` binary inspects and rewrites DCP files; every mutating command |
| 89 | +re-verifies before writing: |
| 90 | + |
| 91 | +``` |
| 92 | +dcp info <file> |
| 93 | +dcp dedup <file> [--fixed N] [--trailer] |
| 94 | +dcp defrag <file> [--trailer] |
| 95 | +dcp promote <file> <container-uid> <inner-uid> [--trailer] |
| 96 | +dcp demote <file> <part-uid> <container-uid> [--trailer] |
| 97 | +``` |
| 98 | + |
| 99 | +UIDs are 32 hex digits, or `0xNN` for a uid of 16 identical bytes (e.g. `0xDC`). |
| 100 | + |
| 101 | +## Build & test |
| 102 | + |
| 103 | +``` |
| 104 | +cargo test -p pcf-dcp |
| 105 | +cargo run -p pcf-dcp --example gen_testvector -- /tmp/dcp.bin # the 700-byte vector |
| 106 | +cargo run -p pcf-dcp --bin dcp -- info /tmp/dcp.bin |
| 107 | +``` |
| 108 | + |
| 109 | +The example reproduces the byte-exact 700-byte test vector from Section 17 of |
| 110 | +the specification. |
| 111 | + |
| 112 | +## Relationship to `pcf` |
| 113 | + |
| 114 | +This crate is layered strictly above [`pcf`](../PCF-v1.0): every container byte |
| 115 | +operation goes through the reference PCF crate, and the arena reuses PCF's Table |
| 116 | +Block, Partition Entry, and table-hash primitives directly. |
| 117 | + |
| 118 | +## Licence |
| 119 | + |
| 120 | +MIT OR Apache-2.0. |
0 commit comments