- LZ77由来の前方依存を弱め、GPU解凍の並列性を高める。
- チャンク単位で自己完結する形式にし、チャンク間依存を持たない。
- 既存Deflate互換は目指さず、独自形式としてまず実装可能性を優先する。
- PDeflateは「Deflate系」の設計思想を継承する。
- 差し替える主対象は LZ77 の参照モデル(距離参照 -> 辞書テーブル参照)。
- エントロピー符号化は任意とする。
flagsのHUFFMANbit が立っている場合のみ Huffman を用いる。- Huffman 使用時は、チャンクごとに高速LUT復号テーブル(root table + subtable)を保持し、各セクションは同一チャンク内LUTを参照して復号する。
- 各チャンクに
read-onlyのグローバル辞書テーブル(TABLE_REF用)を持つ。 - Huffman 使用時のみ、各チャンクに
read-onlyの Huffman LUT 群(root table + subtable)を持つ。 - 本文は「参照命令」と「非参照リテラル命令」の論理列で表現する。
- チャンクを固定数セクション(既定 128)に分割し、各セクションは独立して解凍可能にする。
- 参照は「過去出力位置」ではなく「辞書テーブルID」を引く(LZ77距離参照を直接使わない)。
- チャンク: 圧縮/解凍の最小独立単位(既定 4 MiB)。
- テーブルエントリ: 辞書テーブル中のバイト列。
- セクション: チャンク内の独立解凍単位(既定 128 分割)。
- Huffman LUT:
- root table: 先頭
Rbit(例: 9-11bit)で直接引くテーブル - subtable: 長符号語用の2段目テーブル
- root table: 先頭
- PDeflate ストリームは、固定長ヘッダ + 可変個のチャンク frame で構成する。
- 目的:
zero-temp streamingを成立させる- 圧縮時に全チャンク数/元サイズの事前確定を不要にする
- ストリームヘッダ:
magic[4] = "PDS0"version: u16 = 1flags: u16- bit 0:
UNCOMPRESSED_SIZE_PRESENT1: 直後にuncompressed_size: u64が続く0: 追加メタデータなし
- bit 1:
FILE_NAME_PRESENT1:file_name_len: u16と UTF-8 のfile_name[file_name_len]が続く0: ファイル名 metadata なし
- bit 0:
chunk_size: u32uncompressed_size: u64(flags.UNCOMPRESSED_SIZE_PRESENT == 1のときのみ存在)file_name_len: u16(flags.FILE_NAME_PRESENT == 1のときのみ存在)file_name[file_name_len](UTF-8、flags.FILE_NAME_PRESENT == 1のときのみ存在)
- 本文:
chunk_payload_len: u32chunk_payload[chunk_payload_len]- 上記 frame の繰り返し
chunk_countはストリームヘッダに保持しない。uncompressed_sizeは任意メタデータであり、存在しないストリームも正当とする。- 単一ファイル圧縮では、進捗計算や事前容量把握のため
uncompressed_sizeを格納することを推奨する。 - 単一ファイル圧縮では、解凍時の復元ファイル名保持のため
file_nameを格納することを推奨する。 - 単一ファイル解凍で固定長出力領域へ直接並列書き込みする場合、各チャンクの出力先頭位置は
chunk_output_offset(i) = sum(chunk_uncompressed_len(0..i))で決定できる。追加の per-chunk offset メタデータは不要。 - ストリーム終端は、最後のチャンクの
flags.FINAL_STREAM == 1で示す。 - 空ストリームは、チャンク frame を 1 つも持たないヘッダのみのストリームとして表現してよい。
補足:
uncompressed_sizeはストリーム全体の展開後総バイト数である。- 本メタデータは解凍時の進捗率表示や事前出力見積りに使用できる。
- 本メタデータが欠落している場合、解凍は通常どおり可能だが、進捗率は不定扱いとしてよい。
- 整数はすべて Little Endian。
magic[4] = "PDF0"version: u16 = 0flags: u16- bit 0:
HUFFMAN1: セクション本文は Huffman 符号化済みであり、huff_lutを参照して復号する0: セクション本文は Huffman 未使用であり、section_bitstreamは論理命令列をそのまま格納する
- bit 1:
FINAL_STREAM1: 当該チャンクがストリームの最終チャンク0: 後続チャンクが存在しうる
- bit 0:
chunk_uncompressed_len: u32table_count: u16- 有効範囲:
0..=0x0FFF - 1(最大 4095 エントリ)
- 有効範囲:
section_count: u16(既定 128)table_index_offset: u32table_data_offset: u32huff_lut_offset: u32section_index_offset: u32section_bitstream_offset: u32
注: 実装時は u64 オフセット拡張を検討可(v0 は u32 想定)。
- 配列長:
table_count - 各要素:
entry_len: u8(v0 上限 254、0 は無効)
entry_offsetは圧縮ストリームに保持しない。- 解凍時に前処理として
entry_lenの累積和からentry_offsetを生成する。
- テーブルエントリの生バイト列を連結配置。
- v0 制約:
- エントリは生バイト列のみ。
- エントリ同士の参照は禁止(再帰依存禁止)。
huff_lut_offset..section_index_offsetに配置。flags.HUFFMAN == 1のときのみ、チャンク復号に必要な LUT 群を格納する。flags.HUFFMAN == 0のときは空でもよい。- v0 要件:
- 復号時に木構築を行わず、LUT参照のみで復号できること。
root table + subtableの2段参照方式を前提とする。- セクションは本ブロックを共有参照する(read-only)。
- 配列長:
section_count - 各要素:
bit_len: varint(ULEB128)
bit_offset/out_offsetは圧縮ストリームに保持しない。- 解凍時に前処理として:
bit_offset:bit_lenの累積和で生成out_offset/out_len:chunk_uncompressed_lenとsection_countから決定的に生成raw_offset(i) = floor(i * chunk_uncompressed_len / section_count)align_down4(x) = x & ~3out_offset(0) = 0out_offset(i) = align_down4(raw_offset(i))(1 <= i < section_count)out_offset(section_count) = chunk_uncompressed_lenout_len(i) = out_offset(i+1) - out_offset(i)
- 注意:
- 内部セクション境界(
i in [1, section_count-1])は必ず4-byte境界。 - 末尾境界
out_offset(section_count)はchunk_uncompressed_lenを優先し、4-byte境界でなくてもよい。 - 端数(0..3 byte)は末尾側セクション長に集約。
- 内部セクション境界(
section_bitstream_offset..chunk_endに配置。- 各セクションのビット列は
bit_offset/bit_lenで切り出して解釈する。 - セクション間でビット列は論理的に独立し、相互参照しない。
cmd16: u16(Little Endian)- ビット割り当て:
ref_or_tag = cmd16 & 0x0FFF(12 bit)len4 = (cmd16 >> 12) & 0x000F(4 bit)
len4 != 0xFのとき:len = len4len4 == 0xFのとき: 直後にext8: u8を読み、len = 0xF + ext8
ref_or_tag < 0x0FFF:- TABLE_REF
ref_or_tagをテーブルIDとして参照。table[id]の内容を繰り返し利用し、lenバイト出力する。- 参照命令の
lenは>= 3を必須とする。
ref_or_tag == 0x0FFF:- LITERAL_RUN
- 後続に
lenバイトの非圧縮リテラルを格納。
注:
flags.HUFFMAN == 1のとき、上記は Huffman 符号化されたビット列として格納される。flags.HUFFMAN == 0のとき、上記は論理命令列の生バイト列として格納される。
- ストリームラッパーがある場合は
PDS0ヘッダを検証し、frame を順に読む。flags.FINAL_STREAM == 1のチャンクに到達したら終端とみなす。PDS0ヘッダだけで本文 frame が存在しない場合は空出力とみなす。
- チャンクヘッダ検証。
- 辞書テーブル(TABLE_REF)前処理。
flags.HUFFMAN == 1のときのみ Huffman LUT ブロックを読み込み(またはGPU用バッファへ転送)。- セクションインデックス(
bit_len列)からbit_offsetを再構築し、同時にout_offset/out_lenを算出。 - 各セクションを独立復号し、
out_offset..out_offset+out_lenへ出力。
- CPU: チャンク並列(chunk-level parallel)
- GPU: チャンク + セクション並列(chunk-level + section-level parallel)
- LUT はチャンク共有 read-only とし、セクション間同期は不要(チャンク完了同期のみ)。
- セクション独立性を壊す命令は禁止。
- 他セクションの出力を参照する命令は存在しない。
- 参照命令は必ず有効なテーブルIDを指す。
- 参照命令で
len < 3は生成しない。 - セクション出力長
out_lenと実際の命令展開長が一致すること。 - セクション境界は 4-byte ルール(内部境界)に従うこと。
table_count <= 4095entry_len in [1, 254]sum(entry_len)がtable_data実長と一致することsection_count > 0sum(section_bit_len)がsection_bitstream実ビット長と一致することsection_bit_lenの varint 列が正しく終端し、過長/オーバーフローしないこと- 内部セクション境界
out_offset(i)(1 <= i < section_count)が4-byte境界であること flags.HUFFMAN == 1のときの Huffman LUT 整合性:- root/subtable 参照が範囲外にならないこと
- 無効シンボル/無限ループを誘発しないこと
- 解凍時:
max_expand_ratio(例: 64x)max_ops_per_sectionmax_table_bytesmax_section_out_len
- いずれか違反で即エラー(自動フォールバックはラッパー側方針に従う)。
- chunk size: 4 MiB
- section count: 128
- table_count upper bound: 4095
- entry_len upper bound: 254
- len拡張:
len = 0xF + ext8(ext8は 0..255) - Huffman LUT:
flags.HUFFMAN == 1のときのみroot table + subtable(チャンク共有)
- 命令デコードは
TABLE_REFとLITERAL_RUNの2命令に限定。 - 辞書テーブルオフセットは解凍前処理で再構築(prefix-sum)。
- セクション
bit_offsetは varintbit_lenの prefix-sum で再構築。 - セクション
out_offset/out_lenはchunk_uncompressed_lenとsection_countから算出し、内部境界は4-byte整列。 flags.HUFFMAN == 1のとき、Huffman 復号は木走査ではなく LUT 参照(root + subtable)を使う。flags.HUFFMAN == 0のとき、復号側は Huffman 復号をスキップしてsection_bitstreamをそのまま論理命令列として読む。- まず CPU 実装でフォーマット妥当性を固定し、その後 GPU 解凍へ展開する。
- v0 は 新形式専用 とする。
- 旧PDeflateチャンク形式の読み取り互換は v0 の必須要件に含めない。
- 旧形式データを入力した場合は、サイレントフォールバックせず明示エラーを返す。
- 互換読み取りが必要になった場合は、将来の別タスク(互換モード)で
version/flags分岐として導入する。