Skip to content

Commit d605cb2

Browse files
committed
Add PHP port of PCF-DCP (kduma/pcf-dcp)
Full library port of the PCF-DCP profile to PHP (>=8.1), modeled 1:1 on the kduma/pcf-sig port and layered strictly above kduma/pcf via a path repository. No crypto dependency (ext-sodium not required) — data/table hashing comes from the base PCF library (ext-hash). - src/{Consts,ErrorKind,PcfDcpException,DcpHeader,FragmentEntry,FragTableHeader, FragmentTable,Chunker,ExtentInfo,InnerInfo}.php: on-disk records + Fragment Table walk/reconstruct. - src/Arena.php: in-memory model with content-defined dedup (intra/inter partition), copy-on-write edits (append/insert/overwrite/delete/truncate), mark-and-sweep compaction normalising the SHARED flag, and a canonical serialiser reproducing the spec Section 17 layout. - src/DcpReader.php: DcpReader over Container (trailer-mode transparent) with full DCP-aware verify. src/DcpWriter.php: whole-file model with promote (a MOVE preserving uid + data_hash), demote, dedup, defrag, trailer. - src/ReferenceVector.php reproduces the byte-exact 700-byte vector (SHA-256 b9bb5979…), identical to the Rust reference and the TS/.NET ports. testdata/canonical.bin shipped. - 28 PHPUnit tests (canonical, spec-compliance, roundtrip, coverage), all green; composer validate --strict passes. Wiring: php.yml test + test-vector(700) matrices include pcf-dcp; php-split.yml splits to PHP-PCF-DCP-lib; release-prepare.yml bumps the pcf-dcp composer pin. https://claude.ai/code/session_01XzcjWWbNiuNX9ZywevfbQu
1 parent d1105f9 commit d605cb2

30 files changed

Lines changed: 2285 additions & 1 deletion

.github/workflows/php-split.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
package:
3232
- { dir: 'implementations/php/pcf', repo: 'PHP-PCF-lib' }
3333
- { dir: 'implementations/php/pcf-sig', repo: 'PHP-PCF-SIG-lib' }
34+
- { dir: 'implementations/php/pcf-dcp', repo: 'PHP-PCF-DCP-lib' }
3435
steps:
3536
- uses: actions/checkout@v4
3637
with:

.github/workflows/php.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
fail-fast: false
1515
matrix:
1616
php: ['8.1', '8.2', '8.3', '8.4']
17-
package: [pcf, pcf-sig]
17+
package: [pcf, pcf-sig, pcf-dcp]
1818
defaults:
1919
run:
2020
working-directory: implementations/php/${{ matrix.package }}
@@ -47,6 +47,7 @@ jobs:
4747
include:
4848
- { package: pcf, output: pcf_testvector.bin, expected: 395 }
4949
- { package: pcf-sig, output: pcfsig_testvector.bin, expected: 966 }
50+
- { package: pcf-dcp, output: pcf_dcp_testvector.bin, expected: 700 }
5051
defaults:
5152
run:
5253
working-directory: implementations/php/${{ matrix.package }}

.github/workflows/release-prepare.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ jobs:
109109
sed -i 's|"kduma/pcf": "\^[^"]*"|"kduma/pcf": "^'"$NEW"'"|' implementations/php/pcf-sig/composer.json
110110
sed -i 's|"versions": { "kduma/pcf": "[^"]*" }|"versions": { "kduma/pcf": "'"$NEW"'" }|' implementations/php/pcf-sig/composer.json
111111
112+
- name: Bump PHP pcf-dcp dependency on pcf
113+
shell: bash
114+
run: |
115+
NEW='${{ steps.version.outputs.version }}'
116+
sed -i 's|"kduma/pcf": "\^[^"]*"|"kduma/pcf": "^'"$NEW"'"|' implementations/php/pcf-dcp/composer.json
117+
sed -i 's|"versions": { "kduma/pcf": "[^"]*" }|"versions": { "kduma/pcf": "'"$NEW"'" }|' implementations/php/pcf-dcp/composer.json
118+
112119
- name: Bump .NET Directory.Build.props
113120
shell: bash
114121
run: |
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# --- Composer ---
2+
/vendor/
3+
composer.lock
4+
5+
# --- PHPUnit ---
6+
/.phpunit.cache/
7+
.phpunit.result.cache
8+
9+
# --- Generated artifacts ---
10+
*.bin
11+
!testdata/canonical.bin
12+
13+
# --- Editors ---
14+
.idea/
15+
.vscode/
16+
*.swp
17+
*~
18+
19+
# --- macOS ---
20+
.DS_Store
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# kduma/pcf-dcp — PCF Dynamic Container Partition (PHP)
2+
3+
PHP reader/writer for **PCF-DCP v1.0**, an application-level profile that adds
4+
*dynamic*, fragmentable, dedup-friendly sub-partitions to the
5+
[Partitioned Container Format](https://github.com/kduma-OSS/Partitioned-Container-Format)
6+
(`kduma/pcf`) without modifying the PCF byte container.
7+
8+
This package mirrors the written specification (`PCF-DCP-spec-v1.0.txt`) and the
9+
Rust reference implementation field-for-field, and ships the same byte-exact
10+
700-byte canonical test vector as every other port. It has no cryptographic
11+
dependency — data/table hashing comes from `kduma/pcf` (`ext-hash`).
12+
13+
## Model at a glance
14+
15+
One new PCF partition type is defined:
16+
17+
| Type | Name | Holds |
18+
|--------------|-----------------|----------------------------------------------------|
19+
| `0xAAAC0001` | `DCP_CONTAINER` | An *arena*: a header, an inner partition table, fragment tables, and data extents |
20+
21+
```
22+
arena:
23+
[ DCP Header (24 B) | data extents | Fragment Tables | Inner Table Block(s) ]
24+
```
25+
26+
Each inner partition's logical content is the concatenation of its DATA extents;
27+
its data hash covers that logical content, so fragmentation, deduplication,
28+
compaction, and promotion all leave the hash (and any PCF-SIG signature over it)
29+
unchanged. A generic PCF reader sees a DCP file as **one opaque partition**; only
30+
a DCP-aware reader looks inside.
31+
32+
## Example
33+
34+
```php
35+
use Kduma\PCF\HashAlgo;
36+
use Kduma\PCF\Storage\MemoryStorage;
37+
use Kduma\PCFDCP\Arena;
38+
use Kduma\PCFDCP\Chunker;
39+
use Kduma\PCFDCP\DcpReader;
40+
use Kduma\PCFDCP\DcpWriter;
41+
42+
$arena = new Arena();
43+
$arena->addInner(0x10, str_repeat("\xA1", 16), 'A', 'Hello, World!', HashAlgo::Sha256, Chunker::fixed(7));
44+
$arena->addInner(0x10, str_repeat("\xB2", 16), 'B', 'World!', HashAlgo::Sha256, Chunker::whole());
45+
46+
$w = new DcpWriter();
47+
$w->addContainer(str_repeat("\xDC", 16), 'dcp', $arena);
48+
$image = $w->toImage();
49+
50+
$r = DcpReader::open(new MemoryStorage($image));
51+
$r->verify();
52+
echo $r->readInner(str_repeat("\xB2", 16)); // "World!"
53+
```
54+
55+
## Operations
56+
57+
`Arena` supports content-defined deduplication, copy-on-write edits
58+
(`append` / `insert` / `overwrite` / `delete` / `truncate`), and
59+
sharing-preserving `compact`. `DcpWriter` adds **promotion** (`promote`,
60+
dynamic → fixed) and **demotion** (`demote`, fixed → dynamic), each preserving
61+
`uid`, `partitionType`, `label`, `dataHashAlgo`, and `dataHash` — the promotion
62+
invariant, identical to the fields a PCF-SIG signature protects.
63+
64+
## Licence
65+
66+
MIT.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "kduma/pcf-dcp",
3+
"description": "PHP implementation of PCF-DCP v1.0, the PCF Dynamic Container Partition profile",
4+
"type": "library",
5+
"license": "MIT",
6+
"keywords": ["pcf", "pcf-dcp", "container", "deduplication", "fragmentation"],
7+
"homepage": "https://github.com/kduma-OSS/Partitioned-Container-Format",
8+
"support": {
9+
"issues": "https://github.com/kduma-OSS/Partitioned-Container-Format/issues",
10+
"source": "https://github.com/kduma-OSS-splits/PHP-PCF-DCP-lib"
11+
},
12+
"require": {
13+
"php": ">=8.1",
14+
"ext-hash": "*",
15+
"kduma/pcf": "^0.0.9"
16+
},
17+
"require-dev": {
18+
"phpunit/phpunit": "^10.5 || ^11.0"
19+
},
20+
"repositories": [
21+
{
22+
"type": "path",
23+
"url": "../pcf",
24+
"options": {
25+
"symlink": true,
26+
"versions": { "kduma/pcf": "0.0.9" }
27+
}
28+
}
29+
],
30+
"autoload": {
31+
"psr-4": {
32+
"Kduma\\PCFDCP\\": "src/"
33+
}
34+
},
35+
"autoload-dev": {
36+
"psr-4": {
37+
"Kduma\\PCFDCP\\Tests\\": "tests/"
38+
}
39+
},
40+
"scripts": {
41+
"test": "phpunit",
42+
"gen-testvector": "php examples/gen_testvector.php"
43+
},
44+
"config": {
45+
"sort-packages": true
46+
}
47+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Generates the canonical PCF-DCP v1.0 test-vector file (spec Section 17). Run
7+
* with `php examples/gen_testvector.php <output-path>` (defaults to
8+
* ./pcf_dcp_testvector.bin). Everything is fixed and deterministic so that
9+
* independent implementations can reproduce the file byte-for-byte.
10+
*/
11+
12+
require __DIR__ . '/../vendor/autoload.php';
13+
14+
use Kduma\PCF\Container;
15+
use Kduma\PCF\Storage\MemoryStorage;
16+
use Kduma\PCFDCP\DcpReader;
17+
use Kduma\PCFDCP\ReferenceVector;
18+
19+
$path = $argv[1] ?? 'pcf_dcp_testvector.bin';
20+
21+
$image = ReferenceVector::build();
22+
file_put_contents($path, $image);
23+
24+
// It is a conforming PCF v1.0 file ...
25+
Container::open(new MemoryStorage($image))->verify();
26+
27+
// ... and a conforming DCP file.
28+
DcpReader::open(new MemoryStorage($image))->verify();
29+
30+
fwrite(STDERR, sprintf("wrote %s (%d bytes)\n", $path, \strlen($image)));
31+
fwrite(STDERR, 'sha256 = ' . bin2hex(hash('sha256', $image, true)) . "\n");
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
cacheDirectory=".phpunit.cache"
7+
failOnWarning="true"
8+
failOnRisky="true"
9+
beStrictAboutOutputDuringTests="true">
10+
<testsuites>
11+
<testsuite name="PCFDCP">
12+
<directory>tests</directory>
13+
</testsuite>
14+
</testsuites>
15+
<source>
16+
<include>
17+
<directory suffix=".php">src</directory>
18+
</include>
19+
</source>
20+
</phpunit>

0 commit comments

Comments
 (0)