<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xml" href="https://mabbs.github.io/feed.xslt.xml"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mabbs.github.io/atom.xml" rel="self" type="application/atom+xml" /><link href="https://mabbs.github.io/" rel="alternate" type="text/html" hreflang="zh-CN" /><updated>2026-11-01T00:07:39+08:00</updated><id>https://mabbs.github.io/atom.xml</id><title type="html">Mayx的博客</title><subtitle>Mayx&apos;s Home Page</subtitle><author><name>mayx</name></author><entry><title type="html">关于压缩的各种探索</title><link href="https://mabbs.github.io/2026/11/01/compress.html" rel="alternate" type="text/html" title="关于压缩的各种探索" /><published>2026-11-01T00:00:00+08:00</published><updated>2026-11-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/11/01/compress</id><content type="html" xml:base="https://mabbs.github.io/2026/11/01/compress.html"><![CDATA[<p>压缩……在消除冗余的同时，又创造了新的混乱？<!--more--></p>

<h1 id="对压缩比的追求">对压缩比的追求</h1>
<p>上次我在做<a href="/2026/10/01/quine.html">博客的离线模式</a>时，为了降低带宽的消耗，我把我的博客压缩包又用7-Zip额外压缩了一遍，从大约16MiB压缩到了4MiB，效果感觉挺不错。另外我又试了试几个常见的压缩算法，比如Zip、zstd、BZip2等，全部使用最高等级压缩，但压缩效果都不如使用LZMA2的7-Zip好，速度甚至还比7-Zip慢，看来LZMA2还是相当强啊。 <br />
  所以我在想，LZMA2是不是最强的压缩算法呢？虽然不同压缩算法可能对不同类型的数据效果不一样，但目前来看对我的数据来说，LZMA2看起来是最强的啊……不过这些只是常见算法，如果是用那些因为压缩速度慢而不太常见的算法，会不会比LZMA2更强呢？</p>
<h2 id="zpaq">ZPAQ</h2>
<p>我搜了一下，PAQ系列算法似乎是公认最强的，通过类似机器学习的算法花大量的时间达到更高的压缩比。其中比较知名的就是<a href="https://github.com/zpaq/zpaq">ZPAQ</a>，这个程序已经有10年不更新了，当然对于算法来说这点倒也不是什么问题，毕竟LZMA2也是十几年前的算法。最终我试着用它压缩我的博客压缩包，用了一分钟左右的时间，压缩到了3.5MiB，比7-Zip压缩的少了大约10%，感觉效果还不错。</p>
<h2 id="paq8px">PAQ8PX</h2>
<p>当然ZPAQ只是PAQ系列中的其中一个算法，有些人为了追求更高的压缩比，依然在对其进行改进，据我所知，目前仍在更新、压缩效果也非常出色的应该是<a href="https://github.com/hxim/paq8px">PAQ8PX</a>，我试着用v217版本压缩了一下，最终把压缩包压到了3.1MiB，真的是非常强。而代价也是极其高，首先压缩花了一个半小时，而且在这个过程中需要占用30GiB内存，更关键的问题是在解压时同样需要消耗这么多时间和资源，所以说这个算法真的是只能追求压缩比，基本上没办法正常使用……</p>
<h2 id="nncp">NNCP</h2>
<p>除了PAQ系列之外，FFMPEG的作者<a href="https://bellard.org">Fabrice Bellard</a>也做过一个使用机器学习的压缩算法，叫做<a href="https://bellard.org/nncp/">NNCP</a>，使用了和现代LLM同款的Transformer模型。于是我编译了一下，试着在RTX4090上压缩我的压缩包，也差不多用了一个半小时，结果压缩比还没LZMA2高😂，给我压到了4.1MiB，也许是因为我的博客里有些图片，而NNCP貌似是专攻英文文本的，所以效果差了不少……又一次证明了LZMA2真的很强啊～</p>
<h2 id="cmix">cmix</h2>
<p>后来我又听说还有一个叫做<a href="https://github.com/byronknoll/cmix">cmix</a>的压缩算法，一样是基于机器学习，据说效果好像还不错，只不过在我实际用v21版本测试的时候不知道为什么总是压到后面的时候就会崩溃……这个项目也有2年没更新了，可能是代码有什么BUG。而且和PAQ8PX一样内存占用很高，用时非常长，但就从崩溃的位置来看，它压缩一半的结果还是比PAQ8PX更大，看来这个压缩算法的地位有点尴尬啊……不知道它是不是又是针对文本特化过的算法，但目前的结果来看感觉不如PAQ8PX。</p>
<h2 id="总结">总结</h2>
<p>最后PAQ8PX在这次测试中获得了最终的冠军🏆，我的博客压缩包里面包含的数据分布也算是有一定代表性吧，有一定的图片，大量的纯文本网页，和不少网站的结构应该很相似，所以这个压缩结果可能对不少数据都有一定的参考性。不过无论是PAQ8PX、NNCP还是cmix，它们压缩和解压需要的资源实在是太多了，不太适合一般情况下使用，至于ZPAQ虽然也是那种压缩和解压消耗相同的资源的算法，但作为十年前的东西来说，它在现在的速度还算是能在接受范围内，所以说如果想把不算太大的文件压到尽可能小，用ZPAQ看起来是最合适的选择，至于绝大多数的文件，还是用使用LZMA2算法的7-Zip吧～</p>

<h1 id="关于自包含压缩包的继续研究">关于自包含压缩包的继续研究</h1>
<h2 id="zstd-quine">zstd Quine</h2>
<p>在上次我做博客压缩包的时候，AI告诉我LZMA2和zstd都可以做能包含大文件的Quine，但我当时却以“zstd感觉不是很知名”的理由选择了LZMA2。然而实际上zstd甚至都有专门的<a href="https://datatracker.ietf.org/doc/html/rfc8878">RFC 8878</a>，甚至有不少网站都已经选择了zstd作为传输的压缩算法……既然如此，那我就让AI再简单研究一下zstd Quine吧，搜了一下以前也从来没人做过。 <br />
  AI看了一眼规范之后，说这个比7z Quine制作起来在结构上会更简单，但在计算校验值上会更困难，简单是因为zstd支持一种叫做“可跳过帧”的东西，有这个东西就能在构建的时候有更多的自由度，不过zstd用的是一种叫做“有限状态熵”的编码方式，这个要重新研究一下。至于困难是因为zstd默认使用XXH64来校验，相比于7z格式默认使用的CRC32来说，它是非线性的，因此很难像CRC32那样靠几个公式轻松计算出来。不过有一点好处是XXH64的计算速度比CRC32快不少，而且zstd只校验XXH64结果的低32位，因此即使是通过爆破方式，也用不了多长时间就能计算出来。当然这个值和7-Zip一样都是可选的，其实不算也不影响什么，但既然能算的话就算一下呗～ <br />
  然后AI就开始想办法生成了，用了一个小时就做出来了，最开始它是直接做的<code class="language-plaintext highlighter-rouge">.zst</code>格式，但libarchive又开始出幺蛾子，显示<code class="language-plaintext highlighter-rouge">Input requires too many filters for decoding</code>错误，我试了一下<a href="http://swtch.com/r.gz">Gzip Quine</a>用libarchive也有相同的问题……实在是令人难受，所以我又要求它改成<code class="language-plaintext highlighter-rouge">.tar.zst</code>格式，这样直接用Windows资源管理器也能直接打开，还增加了和我之前7z Quine一样的文件夹打包功能，说不定以后会用得上呢。 <br />
  顺便一说，XXH64虽然说是非线性的，但作为非密码学哈希，倒也可以在不爆破的情况下构造值，于是以下就是最终的生成代码：</p>
<details>
  <summary>
Show Code
</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# -*- coding: utf-8 -*-
</span><span class="s">"""Pack files/directories and a byte-identical copy of this .tar.zst archive.

Example: python3 blogquine_tarzstd.py ./site ./site.tar.zst
One directory packs its contents; multiple inputs retain their basenames.
"""</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>

<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span><span class="p">,</span> <span class="n">PurePosixPath</span>
<span class="kn">import</span> <span class="nn">stat</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">tempfile</span>
<span class="kn">import</span> <span class="nn">ctypes</span>
<span class="kn">import</span> <span class="nn">ctypes.util</span>
<span class="kn">import</span> <span class="nn">io</span>
<span class="kn">import</span> <span class="nn">shutil</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">tarfile</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span>

<span class="n">BLOCK</span> <span class="o">=</span> <span class="mi">65536</span>
<span class="n">MASK</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">64</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
<span class="n">P1</span><span class="p">,</span> <span class="n">P2</span> <span class="o">=</span> <span class="mh">0x9E3779B185EBCA87</span><span class="p">,</span> <span class="mh">0xC2B2AE3D27D4EB4F</span>
<span class="n">P3</span><span class="p">,</span> <span class="n">P4</span> <span class="o">=</span> <span class="mh">0x165667B19E3779F9</span><span class="p">,</span> <span class="mh">0x85EBCA77C2B2AE63</span>
<span class="n">I1</span><span class="p">,</span> <span class="n">I2</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">P1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">64</span><span class="p">),</span> <span class="nb">pow</span><span class="p">(</span><span class="n">P2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">64</span><span class="p">)</span>
<span class="n">LL_BASE</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">16</span><span class="p">))</span> <span class="o">+</span> <span class="p">(</span><span class="mi">16</span><span class="p">,</span><span class="mi">18</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">22</span><span class="p">,</span><span class="mi">24</span><span class="p">,</span><span class="mi">28</span><span class="p">,</span><span class="mi">32</span><span class="p">,</span><span class="mi">40</span><span class="p">,</span><span class="mi">48</span><span class="p">,</span><span class="mi">64</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">256</span><span class="p">,</span><span class="mi">512</span><span class="p">,</span><span class="mi">1024</span><span class="p">,</span><span class="mi">2048</span><span class="p">,</span><span class="mi">4096</span><span class="p">,</span><span class="mi">8192</span><span class="p">,</span><span class="mi">16384</span><span class="p">,</span><span class="mi">32768</span><span class="p">,</span><span class="mi">65536</span><span class="p">)</span>
<span class="n">LL_BITS</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,)</span><span class="o">*</span><span class="mi">16</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">11</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">14</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">16</span><span class="p">)</span>
<span class="n">ML_BASE</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">35</span><span class="p">))</span> <span class="o">+</span> <span class="p">(</span><span class="mi">35</span><span class="p">,</span><span class="mi">37</span><span class="p">,</span><span class="mi">39</span><span class="p">,</span><span class="mi">41</span><span class="p">,</span><span class="mi">43</span><span class="p">,</span><span class="mi">47</span><span class="p">,</span><span class="mi">51</span><span class="p">,</span><span class="mi">59</span><span class="p">,</span><span class="mi">67</span><span class="p">,</span><span class="mi">83</span><span class="p">,</span><span class="mi">99</span><span class="p">,</span><span class="mi">131</span><span class="p">,</span><span class="mi">259</span><span class="p">,</span><span class="mi">515</span><span class="p">,</span><span class="mi">1027</span><span class="p">,</span><span class="mi">2051</span><span class="p">,</span><span class="mi">4099</span><span class="p">,</span><span class="mi">8195</span><span class="p">,</span><span class="mi">16387</span><span class="p">,</span><span class="mi">32771</span><span class="p">,</span><span class="mi">65539</span><span class="p">)</span>
<span class="n">ML_BITS</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,)</span><span class="o">*</span><span class="mi">32</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">11</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">14</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">16</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_rol</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">-</span> <span class="n">n</span><span class="p">)))</span> <span class="o">&amp;</span> <span class="n">MASK</span>


<span class="k">def</span> <span class="nf">_ror</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="n">n</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">-</span> <span class="n">n</span><span class="p">)))</span> <span class="o">&amp;</span> <span class="n">MASK</span>


<span class="k">def</span> <span class="nf">_round</span><span class="p">(</span><span class="n">v</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">w</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">_rol</span><span class="p">((</span><span class="n">v</span> <span class="o">+</span> <span class="n">w</span> <span class="o">*</span> <span class="n">P2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK</span><span class="p">,</span> <span class="mi">31</span><span class="p">)</span> <span class="o">*</span> <span class="n">P1</span> <span class="o">&amp;</span> <span class="n">MASK</span>


<span class="k">def</span> <span class="nf">checksum_for_size</span><span class="p">(</span><span class="n">size</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="s">"""XXH64 digest when all four terminal accumulators are zero."""</span>
    <span class="k">if</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="mi">32</span> <span class="ow">or</span> <span class="n">size</span> <span class="o">%</span> <span class="mi">32</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"decoded size must be a positive multiple of 32"</span><span class="p">)</span>
    <span class="n">h</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
        <span class="n">h</span> <span class="o">=</span> <span class="p">(</span><span class="n">h</span> <span class="o">*</span> <span class="n">P1</span> <span class="o">+</span> <span class="n">P4</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK</span>
    <span class="n">h</span> <span class="o">=</span> <span class="p">(</span><span class="n">h</span> <span class="o">+</span> <span class="n">size</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK</span>
    <span class="n">h</span> <span class="o">^=</span> <span class="n">h</span> <span class="o">&gt;&gt;</span> <span class="mi">33</span>
    <span class="n">h</span> <span class="o">=</span> <span class="n">h</span> <span class="o">*</span> <span class="n">P2</span> <span class="o">&amp;</span> <span class="n">MASK</span>
    <span class="n">h</span> <span class="o">^=</span> <span class="n">h</span> <span class="o">&gt;&gt;</span> <span class="mi">29</span>
    <span class="n">h</span> <span class="o">=</span> <span class="n">h</span> <span class="o">*</span> <span class="n">P3</span> <span class="o">&amp;</span> <span class="n">MASK</span>
    <span class="k">return</span> <span class="n">h</span> <span class="o">^</span> <span class="p">(</span><span class="n">h</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">force_words</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span> <span class="o">|</span> <span class="nb">bytearray</span><span class="p">,</span> <span class="n">offset</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="s">"""Force the four final lanes to zero using four unique aligned words."""</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">%</span> <span class="mi">32</span> <span class="ow">or</span> <span class="n">offset</span> <span class="o">%</span> <span class="mi">8</span> <span class="ow">or</span> <span class="ow">not</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">offset</span> <span class="o">&lt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">32</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"invalid checksum patch position"</span><span class="p">)</span>
    <span class="n">before</span> <span class="o">=</span> <span class="p">[(</span><span class="n">P1</span><span class="o">+</span><span class="n">P2</span><span class="p">)</span><span class="o">&amp;</span><span class="n">MASK</span><span class="p">,</span> <span class="n">P2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">(</span><span class="o">-</span><span class="n">P1</span><span class="p">)</span><span class="o">&amp;</span><span class="n">MASK</span><span class="p">]</span>
    <span class="n">after</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
    <span class="n">view</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="mi">8</span><span class="p">):</span>
        <span class="n">lane</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="o">//</span><span class="mi">8</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span>
        <span class="n">before</span><span class="p">[</span><span class="n">lane</span><span class="p">]</span> <span class="o">=</span> <span class="n">_round</span><span class="p">(</span><span class="n">before</span><span class="p">[</span><span class="n">lane</span><span class="p">],</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">view</span><span class="p">[</span><span class="n">p</span><span class="p">:</span><span class="n">p</span><span class="o">+</span><span class="mi">8</span><span class="p">],</span> <span class="s">"little"</span><span class="p">))</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">8</span><span class="p">,</span> <span class="n">offset</span><span class="o">+</span><span class="mi">31</span><span class="p">,</span> <span class="o">-</span><span class="mi">8</span><span class="p">):</span>
        <span class="n">lane</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="o">//</span><span class="mi">8</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span>
        <span class="n">w</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">view</span><span class="p">[</span><span class="n">p</span><span class="p">:</span><span class="n">p</span><span class="o">+</span><span class="mi">8</span><span class="p">],</span> <span class="s">"little"</span><span class="p">)</span>
        <span class="n">after</span><span class="p">[</span><span class="n">lane</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">_ror</span><span class="p">(</span><span class="n">after</span><span class="p">[</span><span class="n">lane</span><span class="p">]</span><span class="o">*</span><span class="n">I1</span> <span class="o">&amp;</span> <span class="n">MASK</span><span class="p">,</span> <span class="mi">31</span><span class="p">)</span> <span class="o">-</span> <span class="n">w</span><span class="o">*</span><span class="n">P2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK</span>
    <span class="n">result</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">offset</span><span class="o">+</span><span class="mi">32</span><span class="p">,</span> <span class="mi">8</span><span class="p">):</span>
        <span class="n">lane</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="o">//</span><span class="mi">8</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span>
        <span class="n">w</span> <span class="o">=</span> <span class="p">((</span><span class="n">_ror</span><span class="p">(</span><span class="n">after</span><span class="p">[</span><span class="n">lane</span><span class="p">]</span><span class="o">*</span><span class="n">I1</span> <span class="o">&amp;</span> <span class="n">MASK</span><span class="p">,</span> <span class="mi">31</span><span class="p">)</span> <span class="o">-</span> <span class="n">before</span><span class="p">[</span><span class="n">lane</span><span class="p">])</span><span class="o">*</span><span class="n">I2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK</span>
        <span class="n">result</span> <span class="o">+=</span> <span class="n">w</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="s">"little"</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_length_code</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">widths</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">symbol</span><span class="p">,</span> <span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">bases</span><span class="p">,</span> <span class="n">widths</span><span class="p">)):</span>
        <span class="k">if</span> <span class="n">base</span> <span class="o">&lt;=</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="n">base</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">width</span><span class="p">):</span>
            <span class="k">return</span> <span class="n">symbol</span><span class="p">,</span> <span class="n">value</span><span class="o">-</span><span class="n">base</span><span class="p">,</span> <span class="n">width</span>
    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"unencodable Zstd length </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_literal_header</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">:</span>
        <span class="k">return</span> <span class="nb">bytes</span><span class="p">((</span><span class="n">n</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">,))</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">4096</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">((</span><span class="n">n</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span><span class="o">|</span><span class="mi">4</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="s">"little"</span><span class="p">)</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">n</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span><span class="o">|</span><span class="mi">12</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="s">"little"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_sequence</span><span class="p">(</span><span class="n">ll</span><span class="p">,</span> <span class="n">ml</span><span class="p">,</span> <span class="n">distance</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">distance</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"back reference must be positive"</span><span class="p">)</span>
    <span class="n">lc</span><span class="p">,</span> <span class="n">lv</span><span class="p">,</span> <span class="n">ln</span> <span class="o">=</span> <span class="n">_length_code</span><span class="p">(</span><span class="n">ll</span><span class="p">,</span> <span class="n">LL_BASE</span><span class="p">,</span> <span class="n">LL_BITS</span><span class="p">)</span>
    <span class="n">mc</span><span class="p">,</span> <span class="n">mv</span><span class="p">,</span> <span class="n">mn</span> <span class="o">=</span> <span class="n">_length_code</span><span class="p">(</span><span class="n">ml</span><span class="p">,</span> <span class="n">ML_BASE</span><span class="p">,</span> <span class="n">ML_BITS</span><span class="p">)</span>
    <span class="n">oc</span> <span class="o">=</span> <span class="p">(</span><span class="n">distance</span><span class="o">+</span><span class="mi">3</span><span class="p">).</span><span class="n">bit_length</span><span class="p">()</span><span class="o">-</span><span class="mi">1</span>
    <span class="k">if</span> <span class="n">oc</span> <span class="o">&gt;</span> <span class="mi">31</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"input needs a Zstd history window larger than 2 GiB"</span><span class="p">)</span>
    <span class="n">ov</span> <span class="o">=</span> <span class="n">distance</span><span class="o">+</span><span class="mi">3</span><span class="o">-</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">oc</span><span class="p">)</span>
    <span class="n">bits</span> <span class="o">=</span> <span class="n">lv</span> <span class="o">|</span> <span class="p">(</span><span class="n">mv</span> <span class="o">&lt;&lt;</span> <span class="n">ln</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">ov</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">ln</span><span class="o">+</span><span class="n">mn</span><span class="p">))</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">ln</span><span class="o">+</span><span class="n">mn</span><span class="o">+</span><span class="n">oc</span><span class="p">))</span>
    <span class="n">stream</span> <span class="o">=</span> <span class="n">bits</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">((</span><span class="n">ln</span><span class="o">+</span><span class="n">mn</span><span class="o">+</span><span class="n">oc</span><span class="o">+</span><span class="mi">8</span><span class="p">)</span><span class="o">//</span><span class="mi">8</span><span class="p">,</span> <span class="s">"little"</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mh">0x54</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">oc</span><span class="p">,</span> <span class="n">mc</span><span class="p">))</span> <span class="o">+</span> <span class="n">stream</span>


<span class="k">def</span> <span class="nf">_copy_block</span><span class="p">(</span><span class="n">literals</span><span class="p">,</span> <span class="n">length</span><span class="p">,</span> <span class="n">distance</span><span class="p">):</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">literals</span><span class="p">)</span><span class="o">+</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mi">131072</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"Zstd decoded block exceeds 128 KiB"</span><span class="p">)</span>
    <span class="n">seq</span> <span class="o">=</span> <span class="n">_sequence</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">literals</span><span class="p">),</span> <span class="n">length</span><span class="p">,</span> <span class="n">distance</span><span class="p">)</span>
    <span class="n">body</span> <span class="o">=</span> <span class="n">_literal_header</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">literals</span><span class="p">))</span> <span class="o">+</span> <span class="n">literals</span> <span class="o">+</span> <span class="n">seq</span>
    <span class="k">return</span> <span class="p">((</span><span class="nb">len</span><span class="p">(</span><span class="n">body</span><span class="p">)</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">)</span><span class="o">|</span><span class="mi">4</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="s">"little"</span><span class="p">)</span> <span class="o">+</span> <span class="n">body</span><span class="p">,</span> <span class="n">seq</span>


<span class="k">def</span> <span class="nf">_raw_header</span><span class="p">(</span><span class="n">size</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s">"little"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_parts</span><span class="p">(</span><span class="n">total</span><span class="p">):</span>
    <span class="s">"""Nonempty pieces with length &gt;=3 and bounded decoded block size."""</span>
    <span class="n">out</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">while</span> <span class="n">total</span> <span class="o">&gt;</span> <span class="n">BLOCK</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">BLOCK</span>
        <span class="k">if</span> <span class="n">total</span><span class="o">-</span><span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span>
            <span class="n">n</span> <span class="o">-=</span> <span class="mi">3</span><span class="o">-</span><span class="p">(</span><span class="n">total</span><span class="o">-</span><span class="n">n</span><span class="p">)</span>
        <span class="n">out</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
        <span class="n">total</span> <span class="o">-=</span> <span class="n">n</span>
    <span class="k">if</span> <span class="n">total</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">total</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"internal block split too short"</span><span class="p">)</span>
        <span class="n">out</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">total</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">out</span>


<span class="k">def</span> <span class="nf">_header</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">mtime</span><span class="p">):</span>
    <span class="n">info</span> <span class="o">=</span> <span class="n">tarfile</span><span class="p">.</span><span class="n">TarInfo</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
    <span class="n">info</span><span class="p">.</span><span class="n">size</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">mode</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">mtime</span> <span class="o">=</span> <span class="n">size</span><span class="p">,</span> <span class="mo">0o644</span><span class="p">,</span> <span class="n">mtime</span>
    <span class="n">info</span><span class="p">.</span><span class="n">uid</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">gid</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">return</span> <span class="n">info</span><span class="p">.</span><span class="n">tobuf</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="n">tarfile</span><span class="p">.</span><span class="n">PAX_FORMAT</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s">"surrogateescape"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_window_descriptor</span><span class="p">(</span><span class="n">required</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">required</span> <span class="o">&gt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">31</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"input needs a history window larger than the supported 2 GiB"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">wd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">256</span><span class="p">):</span>
        <span class="n">base</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">10</span> <span class="o">+</span> <span class="p">(</span><span class="n">wd</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">))</span>
        <span class="n">size</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="p">(</span><span class="n">base</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">wd</span> <span class="o">&amp;</span> <span class="mi">7</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">size</span> <span class="o">&gt;=</span> <span class="n">required</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">wd</span><span class="p">,</span> <span class="n">size</span>
    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"input exceeds the Zstandard frame window format"</span><span class="p">)</span>


<span class="o">@</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_libzstd</span><span class="p">():</span>
    <span class="n">path</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">util</span><span class="p">.</span><span class="n">find_library</span><span class="p">(</span><span class="s">"zstd"</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"libzstd is required (install the Zstandard library)"</span><span class="p">)</span>
    <span class="n">lib</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">CDLL</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_decompress</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">]</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_decompress</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_isError</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">]</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_isError</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_getErrorName</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">]</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_getErrorName</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_char_p</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_compressBound</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">]</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_compressBound</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_compress</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_int</span><span class="p">]</span>
    <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_compress</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span>
    <span class="k">return</span> <span class="n">lib</span>


<span class="k">def</span> <span class="nf">decompress</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">decoded_size</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="s">"""Decode and verify the one frame through the system reference library."""</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">lib</span> <span class="o">=</span> <span class="n">_libzstd</span><span class="p">()</span>
    <span class="k">except</span> <span class="nb">RuntimeError</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="kn">import</span> <span class="nn">zstandard</span>
        <span class="k">except</span> <span class="nb">ImportError</span><span class="p">:</span>
            <span class="n">command</span> <span class="o">=</span> <span class="n">shutil</span><span class="p">.</span><span class="n">which</span><span class="p">(</span><span class="s">"zstd"</span><span class="p">)</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">command</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"verification needs libzstd, Python zstandard, or the zstd executable"</span><span class="p">)</span> <span class="k">from</span> <span class="bp">None</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">([</span><span class="n">command</span><span class="p">,</span><span class="s">"-q"</span><span class="p">,</span><span class="s">"-d"</span><span class="p">,</span><span class="s">"-c"</span><span class="p">,</span><span class="s">"--long=31"</span><span class="p">],</span><span class="nb">input</span><span class="o">=</span><span class="n">data</span><span class="p">,</span><span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="n">returncode</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"Zstandard verification: "</span><span class="o">+</span><span class="n">result</span><span class="p">.</span><span class="n">stderr</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">).</span><span class="n">strip</span><span class="p">())</span>
            <span class="n">raw</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">stdout</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">decoder</span> <span class="o">=</span> <span class="n">zstandard</span><span class="p">.</span><span class="n">ZstdDecompressor</span><span class="p">(</span><span class="n">max_window_size</span><span class="o">=</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">31</span><span class="p">)</span>
            <span class="k">with</span> <span class="n">decoder</span><span class="p">.</span><span class="n">stream_reader</span><span class="p">(</span><span class="n">io</span><span class="p">.</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">data</span><span class="p">),</span><span class="n">read_across_frames</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> <span class="k">as</span> <span class="n">reader</span><span class="p">:</span>
                <span class="n">raw</span> <span class="o">=</span> <span class="n">reader</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">decoded_size</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">reader</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1</span><span class="p">):</span>
                    <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"decoded data exceeds the declared size"</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span> <span class="o">!=</span> <span class="n">decoded_size</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="sa">f</span><span class="s">"decoded size mismatch: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span><span class="si">}</span><span class="s"> != </span><span class="si">{</span><span class="n">decoded_size</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">raw</span>
    <span class="n">dest</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">create_string_buffer</span><span class="p">(</span><span class="n">decoded_size</span><span class="p">)</span>
    <span class="n">n</span> <span class="o">=</span> <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_decompress</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">decoded_size</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_isError</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"Zstandard verification: "</span> <span class="o">+</span> <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_getErrorName</span><span class="p">(</span><span class="n">n</span><span class="p">).</span><span class="n">decode</span><span class="p">())</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">!=</span> <span class="n">decoded_size</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="sa">f</span><span class="s">"decoded size mismatch: </span><span class="si">{</span><span class="n">n</span><span class="si">}</span><span class="s"> != </span><span class="si">{</span><span class="n">decoded_size</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">dest</span><span class="p">.</span><span class="n">raw</span>


<span class="o">@</span><span class="n">dataclass</span>
<span class="k">class</span> <span class="nc">_Chunk</span><span class="p">:</span>
    <span class="n">kind</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">f</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">size</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">decoded</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">content</span><span class="p">:</span> <span class="nb">bytes</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="n">source</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>


<span class="k">def</span> <span class="nf">_build_quine</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">quine_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">compressed</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="s">"""Return (archive, info, decoded TAR), adding only a self member.

    The leading skippable frame is the quotation template. No seed TAR member
    is created. The baseline stores source payloads once as raw Zstd literals.
    """</span>
    <span class="n">template_size</span> <span class="o">=</span> <span class="mi">512</span>
    <span class="n">self_header</span> <span class="o">=</span> <span class="n">_header</span><span class="p">(</span><span class="n">quine_name</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">mtime</span><span class="p">)</span>
    <span class="n">q</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">)</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">self_header</span><span class="p">)</span>
    <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">f</span><span class="p">,</span> <span class="n">o</span> <span class="o">=</span> <span class="n">template_size</span><span class="o">+</span><span class="mi">6</span><span class="p">,</span> <span class="mi">0</span>
    <span class="c1"># Native prefix blocks may use a wider history than the quotation replay.
</span>    <span class="c1"># Also, a block may regenerate at most min(window_size, 128 KiB).
</span>    <span class="n">max_distance</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">)</span> <span class="k">if</span> <span class="n">compressed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">raw_start</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">)</span> <span class="k">if</span> <span class="n">compressed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">prefix_pieces</span> <span class="o">=</span> <span class="n">_parts</span><span class="p">(</span><span class="n">q</span><span class="o">-</span><span class="n">raw_start</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">compressed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">compressed</span><span class="p">),</span><span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">),</span><span class="n">compressed</span><span class="p">))</span>
        <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">compressed</span><span class="p">)</span>
        <span class="n">o</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">)</span>
    <span class="n">template_pieces</span> <span class="o">=</span> <span class="n">_parts</span><span class="p">(</span><span class="n">template_size</span><span class="p">)</span>
    <span class="c1"># First output the actual TAR prefix, then the complete skippable template
</span>    <span class="c1"># as the beginning of the embedded archive itself.
</span>    <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">prefix_pieces</span><span class="p">:</span>
        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"initial"</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="mi">3</span><span class="o">+</span><span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">source</span><span class="o">=</span><span class="n">o</span><span class="p">))</span>
        <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span><span class="o">+</span><span class="n">size</span>
        <span class="n">o</span> <span class="o">+=</span> <span class="n">size</span>
    <span class="n">source</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">template_pieces</span><span class="p">:</span>
        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"template"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="mi">3</span><span class="o">+</span><span class="n">size</span><span class="p">,</span><span class="n">size</span><span class="p">,</span><span class="n">source</span><span class="o">=</span><span class="n">source</span><span class="p">))</span>
        <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span><span class="o">+</span><span class="n">size</span>
        <span class="n">o</span> <span class="o">+=</span> <span class="n">size</span>
        <span class="n">source</span> <span class="o">+=</span> <span class="n">size</span>
    <span class="c1"># Emit the physical frame header, then quote every preceding RAW block.
</span>    <span class="c1"># The tiny header literals are fixed structural bytes, not new payload copies.
</span>    <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"framecopy"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">6</span><span class="p">))</span>
    <span class="n">f</span> <span class="o">+=</span> <span class="mi">9</span>
    <span class="n">o</span> <span class="o">+=</span> <span class="mi">6</span>
    <span class="k">if</span> <span class="n">compressed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">at</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">while</span> <span class="n">at</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">compressed</span><span class="p">):</span>
            <span class="n">literal</span> <span class="o">=</span> <span class="n">compressed</span><span class="p">[</span><span class="n">at</span><span class="p">:</span><span class="n">at</span><span class="o">+</span><span class="n">BLOCK</span><span class="p">]</span>
            <span class="n">code</span> <span class="o">=</span> <span class="n">_raw_header</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">))</span><span class="o">+</span><span class="n">literal</span>
            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">),</span><span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">),</span><span class="n">code</span><span class="p">))</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
            <span class="n">o</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span>
            <span class="n">at</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">source_start</span><span class="p">,</span><span class="n">pieces</span> <span class="ow">in</span> <span class="p">((</span><span class="n">raw_start</span><span class="p">,</span><span class="n">prefix_pieces</span><span class="p">),(</span><span class="n">q</span><span class="p">,</span><span class="n">template_pieces</span><span class="p">)):</span>
        <span class="n">source</span> <span class="o">=</span> <span class="n">source_start</span>
        <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">pieces</span><span class="p">:</span>
            <span class="n">literal</span> <span class="o">=</span> <span class="n">_raw_header</span><span class="p">(</span><span class="n">size</span><span class="p">)</span>
            <span class="n">distance</span> <span class="o">=</span> <span class="n">o</span><span class="o">+</span><span class="mi">3</span><span class="o">-</span><span class="n">source</span>
            <span class="n">code</span><span class="p">,</span><span class="n">_</span> <span class="o">=</span> <span class="n">_copy_block</span><span class="p">(</span><span class="n">literal</span><span class="p">,</span><span class="n">size</span><span class="p">,</span><span class="n">distance</span><span class="p">)</span>
            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">),</span><span class="mi">3</span><span class="o">+</span><span class="n">size</span><span class="p">,</span><span class="n">code</span><span class="p">))</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
            <span class="n">o</span> <span class="o">+=</span> <span class="mi">3</span><span class="o">+</span><span class="n">size</span>
            <span class="n">source</span> <span class="o">+=</span> <span class="n">size</span>
            <span class="n">max_distance</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">max_distance</span><span class="p">,</span><span class="n">distance</span><span class="p">)</span>
    <span class="c1"># A quote/replay gear may itself use multiple RAW chunks. Its quoted source
</span>    <span class="c1"># ends just before the FIRST new RAW payload, so no circular data is read.
</span>    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">64</span><span class="p">):</span>
        <span class="n">gap</span> <span class="o">=</span> <span class="n">f</span><span class="o">-</span><span class="p">(</span><span class="n">o</span><span class="o">-</span><span class="n">q</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">gap</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"invalid forward quotation gap"</span><span class="p">)</span>
        <span class="n">start_o</span> <span class="o">=</span> <span class="n">o</span>
        <span class="n">group</span> <span class="o">=</span> <span class="n">_parts</span><span class="p">(</span><span class="n">gap</span><span class="o">+</span><span class="mi">3</span><span class="p">)</span>
        <span class="n">source</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">group</span><span class="p">:</span>
            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"gear"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="mi">3</span><span class="o">+</span><span class="n">size</span><span class="p">,</span><span class="n">size</span><span class="p">,</span><span class="n">source</span><span class="o">=</span><span class="n">o</span><span class="o">-</span><span class="n">q</span><span class="p">))</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span><span class="o">+</span><span class="n">size</span>
            <span class="n">o</span> <span class="o">+=</span> <span class="n">size</span>
        <span class="k">for</span> <span class="n">index</span><span class="p">,</span><span class="n">size</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">group</span><span class="p">):</span>
            <span class="n">literal</span> <span class="o">=</span> <span class="n">_raw_header</span><span class="p">(</span><span class="n">size</span><span class="p">)</span> <span class="k">if</span> <span class="n">index</span> <span class="k">else</span> <span class="sa">b</span><span class="s">""</span>
            <span class="n">distance</span> <span class="o">=</span> <span class="n">o</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span><span class="o">-</span><span class="p">(</span><span class="n">start_o</span><span class="o">+</span><span class="n">source</span><span class="p">)</span>
            <span class="n">code</span><span class="p">,</span><span class="n">_</span> <span class="o">=</span> <span class="n">_copy_block</span><span class="p">(</span><span class="n">literal</span><span class="p">,</span><span class="n">size</span><span class="p">,</span><span class="n">distance</span><span class="p">)</span>
            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">),</span><span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span><span class="o">+</span><span class="n">size</span><span class="p">,</span><span class="n">code</span><span class="p">))</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
            <span class="n">o</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span><span class="o">+</span><span class="n">size</span>
            <span class="n">source</span> <span class="o">+=</span> <span class="n">size</span>
            <span class="n">max_distance</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">max_distance</span><span class="p">,</span><span class="n">distance</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">f</span><span class="o">-</span><span class="p">(</span><span class="n">o</span><span class="o">-</span><span class="n">q</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">24</span><span class="p">:</span>
            <span class="k">break</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"quotation gear normalization did not converge"</span><span class="p">)</span>
    <span class="n">plant_pos</span><span class="p">,</span><span class="n">tail_pos</span> <span class="o">=</span> <span class="n">q</span><span class="o">+</span><span class="mi">32</span><span class="p">,</span><span class="n">q</span><span class="o">+</span><span class="mi">256</span>
    <span class="n">choice</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">for</span> <span class="n">empty</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">):</span>
        <span class="n">jf</span> <span class="o">=</span> <span class="n">f</span><span class="o">+</span><span class="mi">3</span><span class="o">*</span><span class="n">empty</span>
        <span class="n">gap</span> <span class="o">=</span> <span class="n">jf</span><span class="o">-</span><span class="p">(</span><span class="n">o</span><span class="o">-</span><span class="n">q</span><span class="p">)</span>
        <span class="n">distance</span> <span class="o">=</span> <span class="n">o</span><span class="o">-</span><span class="n">plant_pos</span>
        <span class="k">for</span> <span class="n">length</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="n">gap</span><span class="o">+</span><span class="mi">5</span><span class="p">),</span><span class="nb">min</span><span class="p">(</span><span class="mi">220</span><span class="p">,</span><span class="n">gap</span><span class="o">+</span><span class="mi">40</span><span class="p">)):</span>
            <span class="n">jump</span><span class="p">,</span><span class="n">_</span> <span class="o">=</span> <span class="n">_copy_block</span><span class="p">(</span><span class="sa">b</span><span class="s">""</span><span class="p">,</span><span class="n">length</span><span class="p">,</span><span class="n">distance</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">length</span> <span class="o">!=</span> <span class="n">gap</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">)</span><span class="o">+</span><span class="mi">5</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">patch_f</span> <span class="o">=</span> <span class="n">jf</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">)</span><span class="o">+</span><span class="mi">5</span>
            <span class="k">if</span> <span class="n">patch_f</span> <span class="o">%</span> <span class="mi">8</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">xo</span> <span class="o">=</span> <span class="n">o</span><span class="o">+</span><span class="n">length</span>
            <span class="n">match</span> <span class="o">=</span> <span class="mi">15</span>
            <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
                <span class="n">xcode</span><span class="p">,</span><span class="n">sx</span> <span class="o">=</span> <span class="n">_copy_block</span><span class="p">(</span><span class="nb">bytes</span><span class="p">(</span><span class="mi">32</span><span class="p">),</span><span class="n">match</span><span class="p">,</span><span class="n">xo</span><span class="o">+</span><span class="mi">32</span><span class="o">-</span><span class="n">tail_pos</span><span class="p">)</span>
                <span class="n">new_match</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">sx</span><span class="p">)</span><span class="o">+</span><span class="mi">8</span>
                <span class="k">if</span> <span class="n">new_match</span> <span class="o">==</span> <span class="n">match</span><span class="p">:</span>
                    <span class="k">break</span>
                <span class="n">match</span> <span class="o">=</span> <span class="n">new_match</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">total</span> <span class="o">=</span> <span class="n">jf</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">)</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">xcode</span><span class="p">)</span><span class="o">+</span><span class="mi">8</span>
            <span class="n">choice</span> <span class="o">=</span> <span class="p">(</span><span class="n">total</span><span class="p">,</span><span class="n">empty</span><span class="p">,</span><span class="n">length</span><span class="p">,</span><span class="n">jump</span><span class="p">,</span><span class="n">xcode</span><span class="p">,</span><span class="n">sx</span><span class="p">,</span><span class="n">patch_f</span><span class="p">,</span><span class="n">xo</span><span class="p">)</span>
            <span class="k">break</span>
        <span class="k">if</span> <span class="n">choice</span><span class="p">:</span>
            <span class="k">break</span>
    <span class="k">if</span> <span class="n">choice</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"no aligned terminal block layout"</span><span class="p">)</span>
    <span class="n">total</span><span class="p">,</span><span class="n">empty</span><span class="p">,</span><span class="n">length</span><span class="p">,</span><span class="n">jump</span><span class="p">,</span><span class="n">xcode</span><span class="p">,</span><span class="n">sx</span><span class="p">,</span><span class="n">patch_f</span><span class="p">,</span><span class="n">xo</span> <span class="o">=</span> <span class="n">choice</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">empty</span><span class="p">):</span>
        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="nb">bytes</span><span class="p">(</span><span class="mi">3</span><span class="p">)))</span>
        <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span>
    <span class="n">jump_output</span> <span class="o">=</span> <span class="n">o</span>
    <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">),</span><span class="n">length</span><span class="p">,</span><span class="n">jump</span><span class="p">))</span>
    <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">)</span>
    <span class="n">o</span> <span class="o">+=</span> <span class="n">length</span>
    <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">_Chunk</span><span class="p">(</span><span class="s">"code"</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">xcode</span><span class="p">),</span><span class="mi">32</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">sx</span><span class="p">)</span><span class="o">+</span><span class="mi">8</span><span class="p">,</span><span class="n">xcode</span><span class="p">))</span>
    <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">xcode</span><span class="p">)</span>
    <span class="n">o</span> <span class="o">+=</span> <span class="mi">32</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">sx</span><span class="p">)</span><span class="o">+</span><span class="mi">8</span>
    <span class="n">padding</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="n">total</span><span class="p">)</span><span class="o">%</span><span class="mi">512</span><span class="o">+</span><span class="mi">1024</span>
    <span class="n">decoded_size</span> <span class="o">=</span> <span class="n">q</span><span class="o">+</span><span class="n">total</span><span class="o">+</span><span class="n">padding</span>
    <span class="k">if</span> <span class="n">o</span> <span class="o">!=</span> <span class="n">q</span><span class="o">+</span><span class="n">total</span> <span class="ow">or</span> <span class="n">f</span><span class="o">+</span><span class="mi">8</span> <span class="o">!=</span> <span class="n">total</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"terminal self-copy equation did not close"</span><span class="p">)</span>
    <span class="n">checksum</span> <span class="o">=</span> <span class="n">checksum_for_size</span><span class="p">(</span><span class="n">decoded_size</span><span class="p">)</span>
    <span class="n">checksum_bytes</span> <span class="o">=</span> <span class="p">(</span><span class="n">checksum</span> <span class="o">&amp;</span> <span class="mh">0xffffffff</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="s">"little"</span><span class="p">)</span>
    <span class="n">rle</span> <span class="o">=</span> <span class="p">((</span><span class="n">padding</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span><span class="o">|</span><span class="mi">3</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="s">"little"</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\0</span><span class="s">"</span>
    <span class="n">max_distance</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">max_distance</span><span class="p">,</span><span class="n">jump_output</span><span class="o">-</span><span class="n">plant_pos</span><span class="p">,</span><span class="n">xo</span><span class="o">+</span><span class="mi">32</span><span class="o">-</span><span class="n">tail_pos</span><span class="p">)</span>
    <span class="n">wd</span><span class="p">,</span><span class="n">window_size</span> <span class="o">=</span> <span class="n">_window_descriptor</span><span class="p">(</span><span class="n">max_distance</span><span class="p">)</span>
    <span class="n">frame_header</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s">"28b52ffd04"</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">((</span><span class="n">wd</span><span class="p">,))</span>
    <span class="n">out</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">total</span><span class="p">)</span>
    <span class="n">out</span><span class="p">[:</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s">"502a4d18"</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="n">template_size</span><span class="o">-</span><span class="mi">8</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="s">"little"</span><span class="p">)</span>
    <span class="n">out</span><span class="p">[</span><span class="n">template_size</span><span class="p">:</span><span class="n">template_size</span><span class="o">+</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="n">frame_header</span>
    <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">chunks</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">chunk</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"code"</span><span class="p">:</span>
            <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">size</span><span class="p">]</span> <span class="o">=</span> <span class="n">chunk</span><span class="p">.</span><span class="n">content</span>
        <span class="k">elif</span> <span class="n">chunk</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"framecopy"</span><span class="p">:</span>
            <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">size</span><span class="p">]</span> <span class="o">=</span> <span class="n">_raw_header</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">+</span><span class="n">frame_header</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">_raw_header</span><span class="p">(</span><span class="n">chunk</span><span class="p">.</span><span class="n">decoded</span><span class="p">)</span>
    <span class="n">out</span><span class="p">[</span><span class="o">-</span><span class="mi">8</span><span class="p">:</span><span class="o">-</span><span class="mi">4</span><span class="p">],</span><span class="n">out</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">:]</span> <span class="o">=</span> <span class="n">rle</span><span class="p">,</span><span class="n">checksum_bytes</span>
    <span class="n">template</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">out</span><span class="p">[:</span><span class="n">template_size</span><span class="p">])</span>
    <span class="n">plant</span> <span class="o">=</span> <span class="n">out</span><span class="p">[</span><span class="n">jump_output</span><span class="o">-</span><span class="n">q</span><span class="p">:</span><span class="n">jump_output</span><span class="o">-</span><span class="n">q</span><span class="o">+</span><span class="n">length</span><span class="p">]</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">plant</span><span class="p">)</span> <span class="o">!=</span> <span class="n">length</span> <span class="ow">or</span> <span class="mi">32</span><span class="o">+</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mi">256</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"skippable jump template overflow"</span><span class="p">)</span>
    <span class="n">template</span><span class="p">[</span><span class="mi">32</span><span class="p">:</span><span class="mi">32</span><span class="o">+</span><span class="n">length</span><span class="p">]</span> <span class="o">=</span> <span class="n">plant</span>
    <span class="n">tail</span> <span class="o">=</span> <span class="n">sx</span><span class="o">+</span><span class="n">rle</span><span class="o">+</span><span class="n">checksum_bytes</span>
    <span class="n">template</span><span class="p">[</span><span class="mi">256</span><span class="p">:</span><span class="mi">256</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">tail</span><span class="p">)]</span> <span class="o">=</span> <span class="n">tail</span>
    <span class="n">note</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\n</span><span class="s">Zstandard quine template; checksum solved by lane inversion.</span><span class="se">\n</span><span class="s">"</span>
    <span class="n">template</span><span class="p">[</span><span class="mi">320</span><span class="p">:]</span> <span class="o">=</span> <span class="p">(</span><span class="n">note</span><span class="o">*</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">192</span><span class="p">]</span>
    <span class="n">out</span><span class="p">[:</span><span class="n">template_size</span><span class="p">]</span> <span class="o">=</span> <span class="n">template</span>
    <span class="n">self_header</span> <span class="o">=</span> <span class="n">_header</span><span class="p">(</span><span class="n">quine_name</span><span class="p">,</span><span class="n">total</span><span class="p">,</span><span class="n">mtime</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">)</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">self_header</span><span class="p">)</span> <span class="o">!=</span> <span class="n">q</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"self header changed serialized length; input too large for this layout"</span><span class="p">)</span>
    <span class="n">tar_prefix</span> <span class="o">=</span> <span class="n">prefix_tar</span><span class="o">+</span><span class="n">self_header</span>
    <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">chunks</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">chunk</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"initial"</span><span class="p">:</span>
            <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="mi">3</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">size</span><span class="p">]</span> <span class="o">=</span> <span class="n">tar_prefix</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">source</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">source</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">decoded</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">chunk</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"template"</span><span class="p">:</span>
            <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="mi">3</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">size</span><span class="p">]</span> <span class="o">=</span> <span class="n">template</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">source</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">source</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">decoded</span><span class="p">]</span>
        <span class="k">elif</span> <span class="n">chunk</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"gear"</span><span class="p">:</span>
            <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="mi">3</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">f</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">size</span><span class="p">]</span> <span class="o">=</span> <span class="n">out</span><span class="p">[</span><span class="n">chunk</span><span class="p">.</span><span class="n">source</span><span class="p">:</span><span class="n">chunk</span><span class="p">.</span><span class="n">source</span><span class="o">+</span><span class="n">chunk</span><span class="p">.</span><span class="n">decoded</span><span class="p">]</span>
    <span class="n">raw</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">decoded_size</span><span class="p">)</span>
    <span class="n">raw</span><span class="p">[:</span><span class="n">q</span><span class="p">]</span> <span class="o">=</span> <span class="n">tar_prefix</span>
    <span class="n">raw</span><span class="p">[</span><span class="n">q</span><span class="p">:</span><span class="n">q</span><span class="o">+</span><span class="n">total</span><span class="p">]</span> <span class="o">=</span> <span class="n">out</span>
    <span class="n">patch</span> <span class="o">=</span> <span class="n">force_words</span><span class="p">(</span><span class="n">raw</span><span class="p">,</span><span class="n">q</span><span class="o">+</span><span class="n">patch_f</span><span class="p">)</span>
    <span class="n">out</span><span class="p">[</span><span class="n">patch_f</span><span class="p">:</span><span class="n">patch_f</span><span class="o">+</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="n">patch</span>
    <span class="n">raw</span><span class="p">[</span><span class="n">q</span><span class="o">+</span><span class="n">patch_f</span><span class="p">:</span><span class="n">q</span><span class="o">+</span><span class="n">patch_f</span><span class="o">+</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="n">patch</span>
    <span class="n">archive</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
    <span class="n">actual</span> <span class="o">=</span> <span class="n">decompress</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span><span class="n">decoded_size</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">actual</span> <span class="o">!=</span> <span class="n">raw</span><span class="p">:</span>
        <span class="n">mismatch</span> <span class="o">=</span> <span class="nb">next</span><span class="p">((</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span><span class="p">,(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span><span class="n">raw</span><span class="p">))</span> <span class="k">if</span> <span class="n">a</span> <span class="o">!=</span> <span class="n">b</span><span class="p">),</span><span class="nb">min</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">actual</span><span class="p">),</span><span class="nb">len</span><span class="p">(</span><span class="n">raw</span><span class="p">)))</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="sa">f</span><span class="s">"self reproduction mismatch at decoded offset </span><span class="si">{</span><span class="n">mismatch</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="n">info</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">quine_name</span><span class="o">=</span><span class="n">quine_name</span><span class="p">,</span><span class="n">decoded_size</span><span class="o">=</span><span class="n">decoded_size</span><span class="p">,</span><span class="n">quine_offset</span><span class="o">=</span><span class="n">q</span><span class="p">,</span>
                <span class="n">frame_count</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">skippable_frames</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">compressed_size</span><span class="o">=</span><span class="n">total</span><span class="p">,</span>
                <span class="n">checksum</span><span class="o">=</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">checksum</span> <span class="o">&amp;</span> <span class="mh">0xffffffff</span><span class="si">:</span><span class="mi">08</span><span class="n">x</span><span class="si">}</span><span class="s">"</span><span class="p">,</span><span class="n">xxh64</span><span class="o">=</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">checksum</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
                <span class="n">window_size</span><span class="o">=</span><span class="n">window_size</span><span class="p">,</span><span class="n">patch_offset</span><span class="o">=</span><span class="n">patch_f</span><span class="p">,</span><span class="n">block_count</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">chunks</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span>
                <span class="n">prefix_size</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">),</span><span class="n">template_size</span><span class="o">=</span><span class="n">template_size</span><span class="p">,</span>
                <span class="n">payload_mode</span><span class="o">=</span><span class="s">"compressed"</span> <span class="k">if</span> <span class="n">compressed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="s">"store"</span><span class="p">,</span>
                <span class="n">compressed_prefix_size</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">compressed</span><span class="p">)</span> <span class="k">if</span> <span class="n">compressed</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="bp">None</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">archive</span><span class="p">,</span><span class="n">info</span><span class="p">,</span><span class="n">actual</span>


<span class="k">def</span> <span class="nf">_compress_blocks</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">level</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="s">"""Independent source-prefix blocks, ready to execute after our frame header."""</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">lib</span> <span class="o">=</span> <span class="n">_libzstd</span><span class="p">()</span>
    <span class="k">except</span> <span class="nb">RuntimeError</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="kn">import</span> <span class="nn">zstandard</span>
        <span class="k">except</span> <span class="nb">ImportError</span><span class="p">:</span>
            <span class="n">command</span> <span class="o">=</span> <span class="n">shutil</span><span class="p">.</span><span class="n">which</span><span class="p">(</span><span class="s">"zstd"</span><span class="p">)</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">command</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"compression needs libzstd, Python zstandard, or the zstd executable"</span><span class="p">)</span> <span class="k">from</span> <span class="bp">None</span>
            <span class="n">option</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"--fast=</span><span class="si">{</span><span class="o">-</span><span class="n">level</span><span class="si">}</span><span class="s">"</span> <span class="k">if</span> <span class="n">level</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="sa">f</span><span class="s">"-</span><span class="si">{</span><span class="n">level</span> <span class="ow">or</span> <span class="mi">3</span><span class="si">}</span><span class="s">"</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">([</span><span class="n">command</span><span class="p">,</span><span class="s">"-q"</span><span class="p">,</span><span class="s">"--no-check"</span><span class="p">,</span><span class="s">"--ultra"</span><span class="p">,</span><span class="n">option</span><span class="p">,</span><span class="s">"-c"</span><span class="p">],</span><span class="nb">input</span><span class="o">=</span><span class="n">data</span><span class="p">,</span><span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="n">returncode</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"Zstandard prefix compression: "</span><span class="o">+</span><span class="n">result</span><span class="p">.</span><span class="n">stderr</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">).</span><span class="n">strip</span><span class="p">())</span>
            <span class="n">frame</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">stdout</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">frame</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">zstandard</span><span class="p">.</span><span class="n">ZstdCompressor</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">level</span><span class="p">,</span><span class="n">write_checksum</span><span class="o">=</span><span class="bp">False</span><span class="p">).</span><span class="n">compress</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">capacity</span> <span class="o">=</span> <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_compressBound</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
        <span class="n">dest</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">create_string_buffer</span><span class="p">(</span><span class="n">capacity</span><span class="p">)</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_compress</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span><span class="n">capacity</span><span class="p">,</span><span class="n">data</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span><span class="n">level</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_isError</span><span class="p">(</span><span class="n">result</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"Zstandard prefix compression: "</span><span class="o">+</span><span class="n">lib</span><span class="p">.</span><span class="n">ZSTD_getErrorName</span><span class="p">(</span><span class="n">result</span><span class="p">).</span><span class="n">decode</span><span class="p">())</span>
        <span class="n">frame</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">dest</span><span class="p">.</span><span class="n">raw</span><span class="p">[:</span><span class="n">result</span><span class="p">])</span>
    <span class="n">descriptor</span> <span class="o">=</span> <span class="n">frame</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
    <span class="n">single</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">descriptor</span> <span class="o">&amp;</span> <span class="mi">32</span><span class="p">)</span>
    <span class="n">dictionary_size</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">)[</span><span class="n">descriptor</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">]</span>
    <span class="n">fcs_flag</span> <span class="o">=</span> <span class="n">descriptor</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span>
    <span class="n">content_size</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="k">if</span> <span class="n">single</span> <span class="k">else</span> <span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">8</span><span class="p">)[</span><span class="n">fcs_flag</span><span class="p">]</span>
    <span class="n">header_size</span> <span class="o">=</span> <span class="mi">5</span><span class="o">+</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">single</span> <span class="k">else</span> <span class="mi">1</span><span class="p">)</span><span class="o">+</span><span class="n">dictionary_size</span><span class="o">+</span><span class="n">content_size</span>
    <span class="n">at</span> <span class="o">=</span> <span class="n">header_size</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">at</span><span class="o">+</span><span class="mi">3</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">frame</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"truncated native prefix frame"</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">frame</span><span class="p">[</span><span class="n">at</span><span class="p">:</span><span class="n">at</span><span class="o">+</span><span class="mi">3</span><span class="p">],</span><span class="s">"little"</span><span class="p">)</span>
        <span class="n">last</span><span class="p">,</span><span class="n">kind</span><span class="p">,</span><span class="n">size</span> <span class="o">=</span> <span class="n">h</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">,(</span><span class="n">h</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">,</span><span class="n">h</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span>
        <span class="k">if</span> <span class="n">kind</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"invalid native prefix block"</span><span class="p">)</span>
        <span class="n">frame</span><span class="p">[</span><span class="n">at</span><span class="p">]</span> <span class="o">&amp;=</span> <span class="mi">254</span>
        <span class="n">at</span> <span class="o">+=</span> <span class="mi">3</span><span class="o">+</span><span class="p">(</span><span class="mi">1</span> <span class="k">if</span> <span class="n">kind</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="n">size</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">last</span><span class="p">:</span>
            <span class="k">break</span>
    <span class="k">if</span> <span class="n">at</span><span class="o">+</span><span class="p">(</span><span class="mi">4</span> <span class="k">if</span> <span class="n">descriptor</span> <span class="o">&amp;</span> <span class="mi">4</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">frame</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"unexpected native prefix frame suffix"</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">frame</span><span class="p">[</span><span class="n">header_size</span><span class="p">:</span><span class="n">at</span><span class="p">])</span>


<span class="k">def</span> <span class="nf">build_quine</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">quine_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">level</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span>
                <span class="n">verify_decoded</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="s">"""Build a seedless single-data-frame TAR quine.

    level=None stores the source prefix once verbatim. An integer enables native
    prefix compression when its three encoded copies plus overhead are smaller
    than the raw prefix. This avoids expanding incompressible payloads. Both
    paths use the same algebraic checksum construction and contain no seed file.
    After standard decoding and self-copy verification, an optional callback
    receives (archive, decoded_tar) for additional checks without decoding again.
    """</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">)</span> <span class="o">%</span> <span class="mi">512</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"prefix_tar must end on a 512-byte TAR boundary"</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">quine_name</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"self member name must be nonempty"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">level</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="p">(</span><span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">level</span><span class="p">,</span><span class="nb">int</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span> <span class="o">-</span><span class="mi">131072</span> <span class="o">&lt;=</span> <span class="n">level</span> <span class="o">&lt;=</span> <span class="mi">22</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"compression level must be between -131072 and 22"</span><span class="p">)</span>
    <span class="n">compressed</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">if</span> <span class="n">level</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="n">prefix_tar</span><span class="p">:</span>
        <span class="n">candidate</span> <span class="o">=</span> <span class="n">_compress_blocks</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">,</span><span class="n">level</span><span class="p">)</span>
        <span class="k">if</span> <span class="mi">3</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">candidate</span><span class="p">)</span><span class="o">+</span><span class="mi">2048</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">):</span>
            <span class="n">compressed</span> <span class="o">=</span> <span class="n">candidate</span>
    <span class="n">result</span><span class="p">,</span><span class="n">info</span><span class="p">,</span><span class="n">decoded</span> <span class="o">=</span> <span class="n">_build_quine</span><span class="p">(</span><span class="n">prefix_tar</span><span class="p">,</span><span class="n">quine_name</span><span class="p">,</span><span class="n">mtime</span><span class="o">=</span><span class="n">mtime</span><span class="p">,</span><span class="n">compressed</span><span class="o">=</span><span class="n">compressed</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">verify_decoded</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">verify_decoded</span><span class="p">(</span><span class="n">result</span><span class="p">,</span><span class="n">decoded</span><span class="p">)</span>
    <span class="n">info</span><span class="p">[</span><span class="s">"compression_level"</span><span class="p">]</span> <span class="o">=</span> <span class="n">level</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">,</span><span class="n">info</span>


<span class="o">@</span><span class="n">dataclass</span>
<span class="k">class</span> <span class="nc">Entry</span><span class="p">:</span>
    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">kind</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">mode</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">path</span><span class="p">:</span> <span class="n">Path</span> <span class="o">|</span> <span class="bp">None</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">link</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">digest</span><span class="p">:</span> <span class="nb">bytes</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>


<span class="k">def</span> <span class="nf">archive_name</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">empty</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="s">"""Validate a relative POSIX member path without silently truncating it."""</span>
    <span class="k">if</span> <span class="s">"</span><span class="se">\0</span><span class="s">"</span> <span class="ow">in</span> <span class="n">value</span> <span class="ow">or</span> <span class="n">value</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span> <span class="ow">or</span> <span class="s">".."</span> <span class="ow">in</span> <span class="n">value</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"/"</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"unsafe archive path: </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">2</span> <span class="ow">and</span> <span class="n">value</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">":"</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"archive path must be relative: </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s">"</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">PurePosixPath</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">"."</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">empty</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">""</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"archive member name cannot be empty"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">name</span>


<span class="k">def</span> <span class="nf">join_name</span><span class="p">(</span><span class="n">prefix</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">archive_name</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">prefix</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">"</span> <span class="k">if</span> <span class="n">prefix</span> <span class="k">else</span> <span class="n">name</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">collect_entries</span><span class="p">(</span><span class="n">inputs</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Path</span><span class="p">],</span> <span class="n">output</span><span class="p">:</span> <span class="n">Path</span><span class="p">,</span> <span class="n">prefix</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
                    <span class="n">reserved</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="n">Entry</span><span class="p">],</span> <span class="nb">bool</span><span class="p">]:</span>
    <span class="n">entries</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Entry</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">names</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Entry</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="n">excluded</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="n">output_absolute</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">==</span> <span class="n">output_absolute</span> <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">inputs</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"output cannot also be an explicit input"</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">entry</span><span class="p">:</span> <span class="n">Entry</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">entry</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
        <span class="n">old</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">old</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">old</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"directory"</span> <span class="ow">and</span> <span class="n">old</span><span class="p">.</span><span class="n">path</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="n">old</span><span class="p">.</span><span class="n">path</span><span class="p">,</span> <span class="n">old</span><span class="p">.</span><span class="n">mode</span> <span class="o">=</span> <span class="n">entry</span><span class="p">.</span><span class="n">path</span><span class="p">,</span> <span class="n">entry</span><span class="p">.</span><span class="n">mode</span>
                <span class="k">return</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"duplicate archive path: </span><span class="si">{</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">names</span><span class="p">[</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">entry</span>
        <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">parents</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">parts</span> <span class="o">=</span> <span class="n">PurePosixPath</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="n">parts</span>
        <span class="k">for</span> <span class="n">end</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">parts</span><span class="p">)):</span>
            <span class="n">parent</span> <span class="o">=</span> <span class="s">"/"</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">parts</span><span class="p">[:</span><span class="n">end</span><span class="p">])</span>
            <span class="n">existing</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">existing</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">existing</span><span class="p">.</span><span class="n">kind</span> <span class="o">!=</span> <span class="s">"directory"</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"archive parent is not a directory: </span><span class="si">{</span><span class="n">parent</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">add</span><span class="p">(</span><span class="n">Entry</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s">"directory"</span><span class="p">,</span> <span class="mo">0o755</span><span class="p">))</span>

    <span class="k">def</span> <span class="nf">walk</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">Path</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">nonlocal</span> <span class="n">excluded</span>
        <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">==</span> <span class="n">output_absolute</span><span class="p">:</span>
            <span class="n">excluded</span> <span class="o">=</span> <span class="bp">True</span>
            <span class="k">return</span>
        <span class="n">st</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="n">lstat</span><span class="p">()</span>
        <span class="n">parents</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
        <span class="n">mode</span> <span class="o">=</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IMODE</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISLNK</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="n">add</span><span class="p">(</span><span class="n">Entry</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">"symlink"</span><span class="p">,</span> <span class="n">mode</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">readlink</span><span class="p">(</span><span class="n">path</span><span class="p">)))</span>
        <span class="k">elif</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISDIR</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="n">add</span><span class="p">(</span><span class="n">Entry</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">"directory"</span><span class="p">,</span> <span class="n">mode</span><span class="p">,</span> <span class="n">path</span><span class="p">))</span>
            <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">iterdir</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">name</span><span class="p">):</span>
                <span class="n">walk</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="n">join_name</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">child</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
        <span class="k">elif</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISREG</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="n">add</span><span class="p">(</span><span class="n">Entry</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">"file"</span><span class="p">,</span> <span class="n">mode</span><span class="p">,</span> <span class="n">path</span><span class="p">))</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"unsupported special file (not read): </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="c1"># Explicit parents also cover empty sources and custom self locations.
</span>    <span class="n">parents</span><span class="p">(</span><span class="n">reserved</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">prefix</span><span class="p">:</span>
        <span class="n">parents</span><span class="p">(</span><span class="n">prefix</span> <span class="o">+</span> <span class="s">"/placeholder"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">inputs</span><span class="p">:</span>
        <span class="n">st</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="n">lstat</span><span class="p">()</span>  <span class="c1"># Fail for nonexistent inputs, including broken parents.
</span>        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">inputs</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISDIR</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">iterdir</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">name</span><span class="p">):</span>
                <span class="n">walk</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="n">join_name</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">child</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">walk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">join_name</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">path</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">reserved</span> <span class="ow">in</span> <span class="n">names</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"source collides with reserved archive member: </span><span class="si">{</span><span class="n">reserved</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">names</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">name</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">reserved</span> <span class="o">+</span> <span class="s">"/"</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"reserved file would replace a directory: </span><span class="si">{</span><span class="n">reserved</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">entries</span><span class="p">,</span> <span class="n">excluded</span>


<span class="k">def</span> <span class="nf">encode_entries</span><span class="p">(</span><span class="n">entries</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Entry</span><span class="p">],</span> <span class="n">mtime</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="n">out</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">:</span>
        <span class="n">member</span> <span class="o">=</span> <span class="n">tarfile</span><span class="p">.</span><span class="n">TarInfo</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
        <span class="n">member</span><span class="p">.</span><span class="n">mode</span><span class="p">,</span> <span class="n">member</span><span class="p">.</span><span class="n">mtime</span> <span class="o">=</span> <span class="n">entry</span><span class="p">.</span><span class="n">mode</span><span class="p">,</span> <span class="n">mtime</span>
        <span class="n">data</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
        <span class="k">if</span> <span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"directory"</span><span class="p">:</span>
            <span class="n">member</span><span class="p">.</span><span class="nb">type</span> <span class="o">=</span> <span class="n">tarfile</span><span class="p">.</span><span class="n">DIRTYPE</span>
        <span class="k">elif</span> <span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"symlink"</span><span class="p">:</span>
            <span class="n">member</span><span class="p">.</span><span class="nb">type</span><span class="p">,</span> <span class="n">member</span><span class="p">.</span><span class="n">linkname</span> <span class="o">=</span> <span class="n">tarfile</span><span class="p">.</span><span class="n">SYMTYPE</span><span class="p">,</span> <span class="n">entry</span><span class="p">.</span><span class="n">link</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">flags</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">O_RDONLY</span> <span class="o">|</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">os</span><span class="p">,</span> <span class="s">"O_BINARY"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">|</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">os</span><span class="p">,</span> <span class="s">"O_NOFOLLOW"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
            <span class="k">assert</span> <span class="n">entry</span><span class="p">.</span><span class="n">path</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span>
            <span class="k">with</span> <span class="n">os</span><span class="p">.</span><span class="n">fdopen</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">path</span><span class="p">,</span> <span class="n">flags</span><span class="p">),</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">source</span><span class="p">:</span>
                <span class="n">before</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">fstat</span><span class="p">(</span><span class="n">source</span><span class="p">.</span><span class="n">fileno</span><span class="p">())</span>
                <span class="k">if</span> <span class="ow">not</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISREG</span><span class="p">(</span><span class="n">before</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
                    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"input is no longer a regular file: </span><span class="si">{</span><span class="n">entry</span><span class="p">.</span><span class="n">path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
                <span class="n">data</span> <span class="o">=</span> <span class="n">source</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
                <span class="n">after</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">fstat</span><span class="p">(</span><span class="n">source</span><span class="p">.</span><span class="n">fileno</span><span class="p">())</span>
            <span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">!=</span> <span class="n">before</span><span class="p">.</span><span class="n">st_size</span> <span class="ow">or</span> <span class="n">before</span><span class="p">.</span><span class="n">st_size</span> <span class="o">!=</span> <span class="n">after</span><span class="p">.</span><span class="n">st_size</span> <span class="ow">or</span>
                    <span class="n">before</span><span class="p">.</span><span class="n">st_mtime_ns</span> <span class="o">!=</span> <span class="n">after</span><span class="p">.</span><span class="n">st_mtime_ns</span><span class="p">):</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"input changed while reading: </span><span class="si">{</span><span class="n">entry</span><span class="p">.</span><span class="n">path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="n">entry</span><span class="p">.</span><span class="n">size</span><span class="p">,</span> <span class="n">entry</span><span class="p">.</span><span class="n">digest</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">data</span><span class="p">).</span><span class="n">digest</span><span class="p">()</span>
            <span class="n">member</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">entry</span><span class="p">.</span><span class="n">size</span>
        <span class="n">out</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">member</span><span class="p">.</span><span class="n">tobuf</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="n">tarfile</span><span class="p">.</span><span class="n">PAX_FORMAT</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">,</span>
                                <span class="n">errors</span><span class="o">=</span><span class="s">"surrogateescape"</span><span class="p">))</span>
        <span class="n">out</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
        <span class="n">out</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="nb">bytes</span><span class="p">((</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span> <span class="o">%</span> <span class="mi">512</span><span class="p">))</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">verify_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">raw</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">entries</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Entry</span><span class="p">],</span>
                   <span class="n">quine_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="s">"""Check members using the core's already checksum-verified decoded bytes."""</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span> <span class="o">%</span> <span class="mi">512</span> <span class="ow">or</span> <span class="n">raw</span><span class="p">[</span><span class="o">-</span><span class="mi">1024</span><span class="p">:]</span> <span class="o">!=</span> <span class="nb">bytes</span><span class="p">(</span><span class="mi">1024</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"TAR is missing its complete zero end records"</span><span class="p">)</span>
    <span class="k">with</span> <span class="n">tarfile</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">fileobj</span><span class="o">=</span><span class="n">io</span><span class="p">.</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">raw</span><span class="p">),</span> <span class="n">mode</span><span class="o">=</span><span class="s">"r:"</span><span class="p">)</span> <span class="k">as</span> <span class="n">tar</span><span class="p">:</span>
        <span class="n">members</span> <span class="o">=</span> <span class="n">tar</span><span class="p">.</span><span class="n">getmembers</span><span class="p">()</span>
        <span class="n">by_name</span> <span class="o">=</span> <span class="p">{</span><span class="n">member</span><span class="p">.</span><span class="n">name</span><span class="p">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s">"/"</span><span class="p">):</span> <span class="n">member</span> <span class="k">for</span> <span class="n">member</span> <span class="ow">in</span> <span class="n">members</span><span class="p">}</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">by_name</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">members</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"duplicate member in generated TAR"</span><span class="p">)</span>
        <span class="n">expected</span> <span class="o">=</span> <span class="p">{</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">}</span> <span class="o">|</span> <span class="p">{</span><span class="n">quine_name</span><span class="p">}</span>
        <span class="k">if</span> <span class="nb">set</span><span class="p">(</span><span class="n">by_name</span><span class="p">)</span> <span class="o">!=</span> <span class="n">expected</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"TAR member list does not match the input snapshot"</span><span class="p">)</span>
        <span class="n">member</span> <span class="o">=</span> <span class="n">by_name</span><span class="p">[</span><span class="n">quine_name</span><span class="p">]</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">member</span><span class="p">.</span><span class="n">isfile</span><span class="p">()</span> <span class="ow">or</span> <span class="n">tar</span><span class="p">.</span><span class="n">extractfile</span><span class="p">(</span><span class="n">member</span><span class="p">).</span><span class="n">read</span><span class="p">()</span> <span class="o">!=</span> <span class="n">archive</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"Quine member is not byte-identical to the archive"</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">:</span>
            <span class="n">member</span> <span class="o">=</span> <span class="n">by_name</span><span class="p">[</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"directory"</span><span class="p">:</span>
                <span class="n">ok</span> <span class="o">=</span> <span class="n">member</span><span class="p">.</span><span class="n">isdir</span><span class="p">()</span>
            <span class="k">elif</span> <span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"symlink"</span><span class="p">:</span>
                <span class="n">ok</span> <span class="o">=</span> <span class="n">member</span><span class="p">.</span><span class="n">issym</span><span class="p">()</span> <span class="ow">and</span> <span class="n">member</span><span class="p">.</span><span class="n">linkname</span> <span class="o">==</span> <span class="n">entry</span><span class="p">.</span><span class="n">link</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">ok</span> <span class="o">=</span> <span class="p">(</span><span class="n">member</span><span class="p">.</span><span class="n">isfile</span><span class="p">()</span> <span class="ow">and</span> <span class="n">member</span><span class="p">.</span><span class="n">size</span> <span class="o">==</span> <span class="n">entry</span><span class="p">.</span><span class="n">size</span> <span class="ow">and</span>
                      <span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">tar</span><span class="p">.</span><span class="n">extractfile</span><span class="p">(</span><span class="n">member</span><span class="p">).</span><span class="n">read</span><span class="p">()).</span><span class="n">digest</span><span class="p">()</span> <span class="o">==</span> <span class="n">entry</span><span class="p">.</span><span class="n">digest</span><span class="p">)</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">ok</span> <span class="ow">or</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IMODE</span><span class="p">(</span><span class="n">member</span><span class="p">.</span><span class="n">mode</span><span class="p">)</span> <span class="o">!=</span> <span class="n">entry</span><span class="p">.</span><span class="n">mode</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"input entry did not survive packing: </span><span class="si">{</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">atomic_write</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">Path</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">force</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">force</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">FileExistsError</span><span class="p">(</span><span class="sa">f</span><span class="s">"output exists; use --force to replace it: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="n">temporary</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">with</span> <span class="n">tempfile</span><span class="p">.</span><span class="n">NamedTemporaryFile</span><span class="p">(</span><span class="n">prefix</span><span class="o">=</span><span class="s">".zstd-quine-"</span><span class="p">,</span> <span class="nb">dir</span><span class="o">=</span><span class="n">path</span><span class="p">.</span><span class="n">parent</span><span class="p">,</span>
                                         <span class="n">delete</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> <span class="k">as</span> <span class="n">output</span><span class="p">:</span>
            <span class="n">temporary</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">output</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
            <span class="n">output</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
            <span class="n">output</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>
            <span class="n">os</span><span class="p">.</span><span class="n">fsync</span><span class="p">(</span><span class="n">output</span><span class="p">.</span><span class="n">fileno</span><span class="p">())</span>
        <span class="k">if</span> <span class="n">force</span><span class="p">:</span>
            <span class="n">os</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="n">temporary</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c1"># link is atomic and cannot overwrite a file created after the check.
</span>            <span class="n">os</span><span class="p">.</span><span class="n">link</span><span class="p">(</span><span class="n">temporary</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
            <span class="n">temporary</span><span class="p">.</span><span class="n">unlink</span><span class="p">()</span>
        <span class="n">temporary</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">finally</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">temporary</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">temporary</span><span class="p">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">missing_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">|</span> <span class="bp">None</span> <span class="o">=</span> <span class="bp">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="n">__doc__</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"inputs"</span><span class="p">,</span> <span class="n">metavar</span><span class="o">=</span><span class="s">"INPUT"</span><span class="p">,</span> <span class="n">nargs</span><span class="o">=</span><span class="s">"+"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"output"</span><span class="p">,</span> <span class="n">metavar</span><span class="o">=</span><span class="s">"OUTPUT"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--quine-name"</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"self member name (default: output basename)"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--src-dir"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">""</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"archive prefix for source entries"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--quine-dir"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">""</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"archive prefix for the self member"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--mtime"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"fixed TAR timestamp (default: 0)"</span><span class="p">)</span>
    <span class="n">compression</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">add_mutually_exclusive_group</span><span class="p">()</span>
    <span class="n">compression</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--level"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span>
                             <span class="n">help</span><span class="o">=</span><span class="s">"Zstd source compression level (default: 9; auto-falls back to storage)"</span><span class="p">)</span>
    <span class="n">compression</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--store"</span><span class="p">,</span> <span class="n">dest</span><span class="o">=</span><span class="s">"level"</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s">"store_const"</span><span class="p">,</span> <span class="n">const</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                             <span class="n">help</span><span class="o">=</span><span class="s">"store the source prefix without trying compression"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--force"</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"replace an existing output after verification"</span><span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">is_symlink</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"output must not be a symbolic link"</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">is_file</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"output must be a regular file path"</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">force</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">FileExistsError</span><span class="p">(</span><span class="sa">f</span><span class="s">"output exists; use --force to replace it: </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">parent</span><span class="p">.</span><span class="n">is_dir</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"output directory does not exist: </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">parent</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">prefix</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">src_dir</span><span class="p">,</span> <span class="n">empty</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">quine_dir</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">quine_dir</span><span class="p">,</span> <span class="n">empty</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">quine</span> <span class="o">=</span> <span class="n">join_name</span><span class="p">(</span><span class="n">quine_dir</span><span class="p">,</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">quine_name</span> <span class="ow">or</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
        <span class="n">entries</span><span class="p">,</span> <span class="n">excluded</span> <span class="o">=</span> <span class="n">collect_entries</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">inputs</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="n">prefix</span><span class="p">,</span> <span class="n">quine</span><span class="p">)</span>
        <span class="n">prefix_tar</span> <span class="o">=</span> <span class="n">encode_entries</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">mtime</span><span class="p">)</span>
        <span class="n">archive</span><span class="p">,</span> <span class="n">info</span> <span class="o">=</span> <span class="n">build_quine</span><span class="p">(</span>
            <span class="n">prefix_tar</span><span class="p">,</span> <span class="n">quine</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">mtime</span><span class="p">,</span> <span class="n">level</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">level</span><span class="p">,</span>
            <span class="n">verify_decoded</span><span class="o">=</span><span class="k">lambda</span> <span class="n">data</span><span class="p">,</span> <span class="n">raw</span><span class="p">:</span> <span class="n">verify_archive</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">raw</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span> <span class="n">quine</span><span class="p">),</span>
        <span class="p">)</span>
        <span class="n">atomic_write</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="n">archive</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">force</span><span class="p">)</span>
        <span class="n">files</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"file"</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="n">dirs</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"directory"</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="n">links</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="s">"symlink"</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[tar.zst quine] </span><span class="si">{</span><span class="n">files</span><span class="si">}</span><span class="s"> files, </span><span class="si">{</span><span class="n">dirs</span><span class="si">}</span><span class="s"> directories, </span><span class="si">{</span><span class="n">links</span><span class="si">}</span><span class="s"> symlinks"</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">excluded</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[tar.zst quine] excluded the output path from source traversal"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[tar.zst quine] wrote </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[layout] </span><span class="si">{</span><span class="n">info</span><span class="p">[</span><span class="s">'frame_count'</span><span class="p">]</span><span class="si">}</span><span class="s"> checked data frame; payload mode: </span><span class="si">{</span><span class="n">info</span><span class="p">[</span><span class="s">'payload_mode'</span><span class="p">]</span><span class="si">}</span><span class="s">; "</span>
              <span class="sa">f</span><span class="s">"window: </span><span class="si">{</span><span class="n">info</span><span class="p">[</span><span class="s">'window_size'</span><span class="p">]</span><span class="si">}</span><span class="s"> bytes; XXH64: </span><span class="si">{</span><span class="n">info</span><span class="p">[</span><span class="s">'checksum'</span><span class="p">]</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] standard Zstd checksum, TAR contents and exact self: PASS"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[self] </span><span class="si">{</span><span class="n">quine</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span>
    <span class="k">except</span> <span class="p">(</span><span class="nb">OSError</span><span class="p">,</span> <span class="nb">ValueError</span><span class="p">,</span> <span class="nb">RuntimeError</span><span class="p">,</span> <span class="n">tarfile</span><span class="p">.</span><span class="n">TarError</span><span class="p">)</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"error: </span><span class="si">{</span><span class="n">error</span><span class="si">}</span><span class="s">"</span><span class="p">,</span> <span class="nb">file</span><span class="o">=</span><span class="n">sys</span><span class="p">.</span><span class="n">stderr</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">1</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div>  </div>
</details>

<p>现在AI写的代码真的是越来越复杂了，真的是根本看不懂啊……但就是这样，很神奇的就把问题解决了。总之，zstd Quine也轻松实现了，也算是给自包含压缩包再提供一种新的格式吧。</p>

<h2 id="zip-quine">ZIP Quine</h2>
<p>后来闲的没事又让AI继续研究有没有可能做出大文件的ZIP Quine，结果它研究了一段时间还真让它研究出来了，当然<a href="https://github.com/ruvmello">Ruben Van Mello</a>所说的窗口大小的问题依然存在，只不过AI用了某种方式绕过去了，而代价则是会存在成员数据有重叠的部分。比如在Python的标准库zipfile中，在<a href="https://github.com/python/cpython/blob/3fb88e94db547e9dbed97de68da87bcf9335a53a/Lib/zipfile/__init__.py#L2276">这里</a>有检查自身成员压缩数据是不是越过了后续成员的局部头，导致我这种方法生成的ZIP Quine无法被解压，然而7-Zip和libarchive都能正常解压……总之这种方式生成的有一点点兼容性问题，但问题不是很严重，那么以下就是这种生成器的代码：</p>
<details>
  <summary>
Show Code
</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# -*- coding: utf-8 -*-
</span><span class="s">"""Pack files/directories plus an exact copy of this ZIP archive.

Example: python zip_quine.py ./site ./site.zip
One directory packs its contents; multiple inputs retain their basenames.
Python 3.10+; standard library only. Extract with 7-Zip or libarchive.
The archive deliberately shares physical bytes between overlapping members.
"""</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">io</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">stat</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">tempfile</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">zipfile</span>
<span class="kn">import</span> <span class="nn">zlib</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span><span class="p">,</span> <span class="n">PurePosixPath</span>

<span class="n">LBASE</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">11</span><span class="p">))</span> <span class="o">+</span> <span class="p">[</span><span class="mi">11</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">17</span><span class="p">,</span><span class="mi">19</span><span class="p">,</span><span class="mi">23</span><span class="p">,</span><span class="mi">27</span><span class="p">,</span><span class="mi">31</span><span class="p">,</span><span class="mi">35</span><span class="p">,</span><span class="mi">43</span><span class="p">,</span><span class="mi">51</span><span class="p">,</span><span class="mi">59</span><span class="p">,</span><span class="mi">67</span><span class="p">,</span><span class="mi">83</span><span class="p">,</span><span class="mi">99</span><span class="p">,</span><span class="mi">115</span><span class="p">,</span><span class="mi">131</span><span class="p">,</span><span class="mi">163</span><span class="p">,</span><span class="mi">195</span><span class="p">,</span><span class="mi">227</span><span class="p">,</span><span class="mi">258</span><span class="p">]</span>
<span class="n">LBITS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="mi">8</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">DBASE</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">17</span><span class="p">,</span><span class="mi">25</span><span class="p">,</span><span class="mi">33</span><span class="p">,</span><span class="mi">49</span><span class="p">,</span><span class="mi">65</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">129</span><span class="p">,</span><span class="mi">193</span><span class="p">,</span><span class="mi">257</span><span class="p">,</span><span class="mi">385</span><span class="p">,</span><span class="mi">513</span><span class="p">,</span><span class="mi">769</span><span class="p">,</span><span class="mi">1025</span><span class="p">,</span><span class="mi">1537</span><span class="p">,</span><span class="mi">2049</span><span class="p">,</span><span class="mi">3073</span><span class="p">,</span><span class="mi">4097</span><span class="p">,</span><span class="mi">6145</span><span class="p">,</span><span class="mi">8193</span><span class="p">,</span><span class="mi">12289</span><span class="p">,</span><span class="mi">16385</span><span class="p">,</span><span class="mi">24577</span><span class="p">]</span>
<span class="n">DBITS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="p">[</span><span class="n">v</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">14</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="p">[</span><span class="n">n</span><span class="p">,</span><span class="n">n</span><span class="p">]]</span>

<span class="n">CL_ORDER</span> <span class="o">=</span> <span class="p">[</span><span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">CL_LEN</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">17</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">18</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
<span class="c1"># CL codes in stream order (MSB of the Huffman code first):
#   0 -&gt; "0", 18 -&gt; "10", 1 -&gt; "110", 17 -&gt; "111"
</span><span class="n">CL_CODE</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span> <span class="p">(</span><span class="mb">0b0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="mi">18</span><span class="p">:</span> <span class="p">(</span><span class="mb">0b1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="mi">1</span><span class="p">:</span> <span class="p">(</span><span class="mb">0b011</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="mi">17</span><span class="p">:</span> <span class="p">(</span><span class="mb">0b111</span><span class="p">,</span> <span class="mi">3</span><span class="p">)}</span>

<span class="c1"># (cell length, zero-length prefix, code lengths per unit copy).
# Exhaustively found for the fixed distance-8 match used by unit_bytes().
</span><span class="n">CONNECTORS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">(</span><span class="mi">576</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">129</span><span class="p">),</span> <span class="p">(</span><span class="mi">639</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">129</span><span class="p">),</span> <span class="p">(</span><span class="mi">927</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">32872</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">128</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">33772</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">39936</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">39951</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">39953</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">124</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">39999</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">40732</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">108</span><span class="p">),</span> <span class="p">(</span><span class="mi">40944</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">45559</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">124</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">45560</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">49300</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">134</span><span class="p">),</span> <span class="p">(</span><span class="mi">49934</span><span class="p">,</span> <span class="mi">57</span><span class="p">,</span> <span class="mi">107</span><span class="p">),</span> <span class="p">(</span><span class="mi">50078</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">107</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">57356</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">57373</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">129</span><span class="p">),</span> <span class="p">(</span><span class="mi">57374</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">117</span><span class="p">),</span> <span class="p">(</span><span class="mi">57572</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">133</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">57599</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">57854</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">117</span><span class="p">),</span> <span class="p">(</span><span class="mi">58126</span><span class="p">,</span> <span class="mi">57</span><span class="p">,</span> <span class="mi">107</span><span class="p">),</span> <span class="p">(</span><span class="mi">58319</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">107</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">59264</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">117</span><span class="p">),</span> <span class="p">(</span><span class="mi">59390</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">117</span><span class="p">),</span> <span class="p">(</span><span class="mi">60536</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">60540</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">61540</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">129</span><span class="p">),</span> <span class="p">(</span><span class="mi">61903</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">107</span><span class="p">),</span> <span class="p">(</span><span class="mi">63495</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">63544</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">63608</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">117</span><span class="p">),</span> <span class="p">(</span><span class="mi">63743</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">63943</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">110</span><span class="p">),</span> <span class="p">(</span><span class="mi">63984</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">64423</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">124</span><span class="p">),</span> <span class="p">(</span><span class="mi">64551</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span> <span class="p">(</span><span class="mi">65048</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">117</span><span class="p">),</span> <span class="p">(</span><span class="mi">65311</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span>
    <span class="p">(</span><span class="mi">65420</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">118</span><span class="p">),</span> <span class="p">(</span><span class="mi">65436</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">120</span><span class="p">),</span>
<span class="p">]</span>


<span class="k">class</span> <span class="nc">Bits</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">width</span><span class="p">):</span>
        <span class="k">assert</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">width</span><span class="p">),</span> <span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">value</span> <span class="o">|=</span> <span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="bp">self</span><span class="p">.</span><span class="n">count</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">count</span> <span class="o">+=</span> <span class="n">width</span>

    <span class="k">def</span> <span class="nf">align</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="bp">self</span><span class="p">.</span><span class="n">count</span> <span class="o">%</span> <span class="mi">8</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">bytes</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">//</span> <span class="mi">8</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">reverse</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">width</span><span class="p">):</span>
    <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">value</span><span class="si">:</span><span class="mi">0</span><span class="si">{</span><span class="n">width</span><span class="si">}</span><span class="n">b</span><span class="si">}</span><span class="s">'</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="mi">2</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">symbol</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">s</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">s</span> <span class="o">&lt;=</span> <span class="mi">143</span><span class="p">:</span>
        <span class="n">code</span><span class="p">,</span> <span class="n">width</span> <span class="o">=</span> <span class="mh">0x30</span><span class="o">+</span><span class="n">s</span><span class="p">,</span> <span class="mi">8</span>
    <span class="k">elif</span> <span class="n">s</span> <span class="o">&lt;=</span> <span class="mi">255</span><span class="p">:</span>
        <span class="n">code</span><span class="p">,</span> <span class="n">width</span> <span class="o">=</span> <span class="mh">0x190</span><span class="o">+</span><span class="n">s</span><span class="o">-</span><span class="mi">144</span><span class="p">,</span> <span class="mi">9</span>
    <span class="k">elif</span> <span class="n">s</span> <span class="o">&lt;=</span> <span class="mi">279</span><span class="p">:</span>
        <span class="n">code</span><span class="p">,</span> <span class="n">width</span> <span class="o">=</span> <span class="n">s</span><span class="o">-</span><span class="mi">256</span><span class="p">,</span> <span class="mi">7</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">code</span><span class="p">,</span> <span class="n">width</span> <span class="o">=</span> <span class="mh">0xc0</span><span class="o">+</span><span class="n">s</span><span class="o">-</span><span class="mi">280</span><span class="p">,</span> <span class="mi">8</span>
    <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">reverse</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">width</span><span class="p">),</span> <span class="n">width</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">number</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">widths</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">bases</span><span class="p">,</span> <span class="n">widths</span><span class="p">)):</span>
        <span class="k">if</span> <span class="n">base</span> <span class="o">&lt;=</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="n">base</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">width</span><span class="p">):</span>
            <span class="k">return</span> <span class="n">i</span><span class="p">,</span> <span class="n">value</span> <span class="o">-</span> <span class="n">base</span><span class="p">,</span> <span class="n">width</span>
    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">copy_code</span><span class="p">(</span><span class="n">length</span><span class="p">,</span> <span class="n">distance</span><span class="p">):</span>
    <span class="k">assert</span> <span class="mi">3</span> <span class="o">&lt;=</span> <span class="n">length</span> <span class="ow">and</span> <span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">distance</span> <span class="o">&lt;=</span> <span class="mi">32768</span>
    <span class="n">bits</span> <span class="o">=</span> <span class="n">Bits</span><span class="p">()</span>
    <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
    <span class="n">remaining</span> <span class="o">=</span> <span class="n">length</span>
    <span class="k">while</span> <span class="n">remaining</span><span class="p">:</span>
        <span class="n">size</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="mi">258</span><span class="p">,</span> <span class="n">remaining</span><span class="p">)</span>
        <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;</span> <span class="n">remaining</span> <span class="o">-</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span>
            <span class="n">size</span> <span class="o">-=</span> <span class="mi">3</span> <span class="o">-</span> <span class="p">(</span><span class="n">remaining</span> <span class="o">-</span> <span class="n">size</span><span class="p">)</span>
        <span class="n">slot</span><span class="p">,</span> <span class="n">extra</span><span class="p">,</span> <span class="n">width</span> <span class="o">=</span> <span class="n">number</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">LBASE</span><span class="p">,</span> <span class="n">LBITS</span><span class="p">)</span>
        <span class="n">symbol</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="mi">257</span> <span class="o">+</span> <span class="n">slot</span><span class="p">)</span>
        <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">extra</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span>
        <span class="n">slot</span><span class="p">,</span> <span class="n">extra</span><span class="p">,</span> <span class="n">width</span> <span class="o">=</span> <span class="n">number</span><span class="p">(</span><span class="n">distance</span><span class="p">,</span> <span class="n">DBASE</span><span class="p">,</span> <span class="n">DBITS</span><span class="p">)</span>
        <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">reverse</span><span class="p">(</span><span class="n">slot</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="mi">5</span><span class="p">)</span>
        <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">extra</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span>
        <span class="n">remaining</span> <span class="o">-=</span> <span class="n">size</span>
    <span class="n">symbol</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="mi">256</span><span class="p">)</span>
    <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
    <span class="n">bits</span><span class="p">.</span><span class="n">align</span><span class="p">()</span>
    <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
    <span class="n">bits</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mh">0xffff</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">bits</span><span class="p">.</span><span class="nb">bytes</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">raw_header</span><span class="p">(</span><span class="n">size</span><span class="p">):</span>
    <span class="k">assert</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">size</span> <span class="o">&lt;=</span> <span class="mi">65535</span>
    <span class="k">return</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span> <span class="o">^</span> <span class="mh">0xffff</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">crc</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">zlib</span><span class="p">.</span><span class="n">crc32</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>


<span class="c1"># --------------------------------------------------------------- connector
</span><span class="k">def</span> <span class="nf">unit_bytes</span><span class="p">(</span><span class="n">lc</span><span class="p">,</span> <span class="n">final</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
    <span class="n">code</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x82\xd1\x80\0</span><span class="s">'</span> <span class="k">if</span> <span class="n">final</span> <span class="k">else</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x82\xd1\0\0</span><span class="s">'</span>
    <span class="k">return</span> <span class="n">code</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">lc</span> <span class="o">^</span> <span class="mh">0xffff</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">connector_tail</span><span class="p">(</span><span class="n">unit</span><span class="p">,</span> <span class="n">m</span><span class="p">):</span>
    <span class="n">w</span> <span class="o">=</span> <span class="n">Bits</span><span class="p">()</span>
    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>          <span class="c1"># BFINAL=0, BTYPE=10
</span>    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>          <span class="c1"># HLIT=0 -&gt; 257 literal lengths
</span>    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">29</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>         <span class="c1"># HDIST=29 -&gt; 30 distance codes
</span>    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">14</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>         <span class="c1"># HCLEN=14 -&gt; 18 CL lengths
</span>    <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">CL_ORDER</span><span class="p">:</span>
        <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">CL_LEN</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">w</span><span class="p">.</span><span class="n">count</span> <span class="o">==</span> <span class="mi">71</span>
    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span>          <span class="c1"># m zero lengths (m == 1 mod 8)
</span>    <span class="k">assert</span> <span class="n">w</span><span class="p">.</span><span class="n">count</span> <span class="o">%</span> <span class="mi">8</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">unit</span><span class="p">:</span>
        <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">w</span><span class="p">.</span><span class="nb">bytes</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">payload_head</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">c1</span><span class="p">,</span> <span class="n">final</span><span class="p">):</span>
    <span class="n">window_end</span> <span class="o">=</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">c1</span>
    <span class="n">first_free</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">257</span><span class="p">,</span> <span class="n">window_end</span><span class="p">)</span>
    <span class="n">count</span> <span class="o">=</span> <span class="mi">287</span> <span class="o">-</span> <span class="n">first_free</span>
    <span class="k">if</span> <span class="n">count</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'no free distance codes'</span><span class="p">)</span>
    <span class="n">w</span> <span class="o">=</span> <span class="n">Bits</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">count</span><span class="p">):</span>
        <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">CL_CODE</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="k">else</span> <span class="n">CL_CODE</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>                      <span class="c1"># EOB (literal code 1)
</span>    <span class="n">w</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="mi">1</span> <span class="k">if</span> <span class="n">final</span> <span class="k">else</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>      <span class="c1"># stored block header
</span>    <span class="n">w</span><span class="p">.</span><span class="n">align</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">w</span><span class="p">.</span><span class="nb">bytes</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">geometry</span><span class="p">(</span><span class="n">lc</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">c1</span><span class="p">):</span>
    <span class="n">head</span> <span class="o">=</span> <span class="n">payload_head</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">c1</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">lc</span><span class="o">=</span><span class="n">lc</span><span class="p">,</span> <span class="n">m</span><span class="o">=</span><span class="n">m</span><span class="p">,</span> <span class="n">unit</span><span class="o">=</span><span class="n">unit_bytes</span><span class="p">(</span><span class="n">lc</span><span class="p">),</span> <span class="n">p_head</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">head</span><span class="p">)</span><span class="o">+</span><span class="mi">4</span><span class="p">,</span>
                <span class="n">k</span><span class="o">=</span><span class="mi">8</span><span class="o">+</span><span class="p">(</span><span class="mi">71</span><span class="o">+</span><span class="n">m</span><span class="p">)</span><span class="o">//</span><span class="mi">8</span><span class="p">,</span> <span class="n">head</span><span class="o">=</span><span class="n">head</span><span class="p">,</span>
                <span class="n">head_final</span><span class="o">=</span><span class="n">payload_head</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">c1</span><span class="p">,</span> <span class="bp">True</span><span class="p">))</span>


<span class="n">UINT32</span> <span class="o">=</span> <span class="mh">0xffffffff</span>
<span class="n">UTF8_FLAG</span> <span class="o">=</span> <span class="mh">0x800</span>


<span class="k">def</span> <span class="nf">_stamp</span><span class="p">(</span><span class="n">epoch</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">epoch</span> <span class="o">&lt;=</span> <span class="mi">315532800</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">33</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">gmtime</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">epoch</span><span class="p">,</span> <span class="mi">4354819198</span><span class="p">))</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">t</span><span class="p">.</span><span class="n">tm_hour</span> <span class="o">&lt;&lt;</span> <span class="mi">11</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">tm_min</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">tm_sec</span> <span class="o">//</span> <span class="mi">2</span><span class="p">),</span>
            <span class="p">((</span><span class="n">t</span><span class="p">.</span><span class="n">tm_year</span> <span class="o">-</span> <span class="mi">1980</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">9</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">tm_mon</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">)</span> <span class="o">|</span> <span class="n">t</span><span class="p">.</span><span class="n">tm_mday</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_extra_padding</span><span class="p">(</span><span class="n">size</span><span class="p">):</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">size</span><span class="p">:</span>
        <span class="k">return</span> <span class="sa">b</span><span class="s">''</span>
    <span class="k">if</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'extra padding needs at least four bytes'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="mh">0xca05</span><span class="p">,</span> <span class="n">size</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">size</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_local</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="sa">b</span><span class="s">''</span><span class="p">):</span>
    <span class="n">tm</span><span class="p">,</span> <span class="n">dt</span> <span class="o">=</span> <span class="n">_stamp</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'mtime'</span><span class="p">])</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;IHHHHHIIIHH'</span><span class="p">,</span> <span class="mh">0x04034b50</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">UTF8_FLAG</span><span class="p">,</span>
                        <span class="n">e</span><span class="p">[</span><span class="s">'method'</span><span class="p">],</span> <span class="n">tm</span><span class="p">,</span> <span class="n">dt</span><span class="p">,</span> <span class="n">e</span><span class="p">[</span><span class="s">'crc'</span><span class="p">],</span> <span class="n">e</span><span class="p">[</span><span class="s">'csize'</span><span class="p">],</span>
                        <span class="n">e</span><span class="p">[</span><span class="s">'size'</span><span class="p">],</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">]),</span> <span class="nb">len</span><span class="p">(</span><span class="n">extra</span><span class="p">))</span>
            <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">]</span> <span class="o">+</span> <span class="n">extra</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_central</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="sa">b</span><span class="s">''</span><span class="p">,</span> <span class="n">extra_size</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">tm</span><span class="p">,</span> <span class="n">dt</span> <span class="o">=</span> <span class="n">_stamp</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'mtime'</span><span class="p">])</span>
    <span class="n">attrs</span> <span class="o">=</span> <span class="p">((</span><span class="n">e</span><span class="p">[</span><span class="s">'mode'</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0xffff</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="mh">0x10</span> <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;IHHHHHHIIIHHHHHII'</span><span class="p">,</span> <span class="mh">0x02014b50</span><span class="p">,</span>
                        <span class="p">(</span><span class="mi">3</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">UTF8_FLAG</span><span class="p">,</span> <span class="n">e</span><span class="p">[</span><span class="s">'method'</span><span class="p">],</span> <span class="n">tm</span><span class="p">,</span> <span class="n">dt</span><span class="p">,</span>
                        <span class="n">e</span><span class="p">[</span><span class="s">'crc'</span><span class="p">],</span> <span class="n">e</span><span class="p">[</span><span class="s">'csize'</span><span class="p">],</span> <span class="n">e</span><span class="p">[</span><span class="s">'size'</span><span class="p">],</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">]),</span>
                        <span class="nb">len</span><span class="p">(</span><span class="n">extra</span><span class="p">)</span> <span class="k">if</span> <span class="n">extra_size</span> <span class="ow">is</span> <span class="bp">None</span> <span class="k">else</span> <span class="n">extra_size</span><span class="p">,</span>
                        <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">attrs</span><span class="p">,</span> <span class="n">e</span><span class="p">[</span><span class="s">'local_offset'</span><span class="p">])</span>
            <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">]</span> <span class="o">+</span> <span class="n">extra</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_entry</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">mtime</span><span class="p">):</span>
    <span class="n">e</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
    <span class="n">e</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'is_dir'</span><span class="p">,</span> <span class="bp">False</span><span class="p">))</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'name'</span><span class="p">])</span> <span class="o">+</span> <span class="p">(</span><span class="s">'/'</span> <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="k">else</span> <span class="s">''</span><span class="p">)</span>
    <span class="n">encoded</span> <span class="o">=</span> <span class="n">name</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf8'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">encoded</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">60000</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'UTF-8 archive path exceeds 60000 bytes: </span><span class="si">{</span><span class="n">name</span><span class="si">!r}</span><span class="s">'</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'data'</span><span class="p">,</span> <span class="sa">b</span><span class="s">''</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="ow">and</span> <span class="n">data</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'directory entries cannot have data'</span><span class="p">)</span>
    <span class="n">e</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">,</span> <span class="n">encoded</span><span class="o">=</span><span class="n">encoded</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span>
             <span class="n">mtime</span><span class="o">=</span><span class="n">e</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'mtime'</span><span class="p">,</span> <span class="n">mtime</span><span class="p">),</span> <span class="n">crc</span><span class="o">=</span><span class="n">crc</span><span class="p">(</span><span class="n">data</span><span class="p">),</span> <span class="n">method</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
             <span class="n">mode</span><span class="o">=</span><span class="n">e</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'mode'</span><span class="p">,</span> <span class="p">(</span><span class="n">stat</span><span class="p">.</span><span class="n">S_IFDIR</span> <span class="o">|</span> <span class="mo">0o755</span><span class="p">)</span> <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span>
                        <span class="k">else</span> <span class="p">(</span><span class="n">stat</span><span class="p">.</span><span class="n">S_IFREG</span> <span class="o">|</span> <span class="mo">0o644</span><span class="p">)),</span> <span class="n">csize</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">UINT32</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'ZIP64 is not implemented: a member is too large'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">e</span>


<span class="k">def</span> <span class="nf">_large_plan</span><span class="p">(</span><span class="n">e</span><span class="p">):</span>
    <span class="n">best</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">base</span> <span class="o">=</span> <span class="mi">30</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span>
    <span class="k">for</span> <span class="n">values</span> <span class="ow">in</span> <span class="n">CONNECTORS</span><span class="p">:</span>
        <span class="n">g</span> <span class="o">=</span> <span class="n">geometry</span><span class="p">(</span><span class="o">*</span><span class="n">values</span><span class="p">)</span>
        <span class="n">lc</span><span class="p">,</span> <span class="n">ph</span><span class="p">,</span> <span class="n">k</span> <span class="o">=</span> <span class="n">g</span><span class="p">[</span><span class="s">'lc'</span><span class="p">],</span> <span class="n">g</span><span class="p">[</span><span class="s">'p_head'</span><span class="p">],</span> <span class="n">g</span><span class="p">[</span><span class="s">'k'</span><span class="p">]</span>
        <span class="n">first_cap</span><span class="p">,</span> <span class="n">middle_cap</span><span class="p">,</span> <span class="n">last_cap</span> <span class="o">=</span> <span class="n">lc</span> <span class="o">-</span> <span class="n">base</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">-</span> <span class="n">k</span><span class="p">,</span> <span class="n">lc</span> <span class="o">-</span> <span class="n">ph</span> <span class="o">-</span> <span class="n">k</span><span class="p">,</span> <span class="n">lc</span> <span class="o">-</span> <span class="n">ph</span> <span class="o">-</span> <span class="mi">8</span>
        <span class="k">if</span> <span class="nb">min</span><span class="p">(</span><span class="n">first_cap</span><span class="p">,</span> <span class="n">middle_cap</span><span class="p">,</span> <span class="n">last_cap</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">count</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">+</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'size'</span><span class="p">]</span> <span class="o">-</span> <span class="n">first_cap</span> <span class="o">-</span> <span class="n">last_cap</span> <span class="o">+</span> <span class="n">middle_cap</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="n">middle_cap</span><span class="p">)</span>
        <span class="n">left</span> <span class="o">=</span> <span class="n">e</span><span class="p">[</span><span class="s">'size'</span><span class="p">]</span> <span class="o">-</span> <span class="p">(</span><span class="n">count</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">middle_cap</span>
        <span class="n">first</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">first_cap</span><span class="p">,</span> <span class="n">left</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">first</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">extra</span> <span class="o">=</span> <span class="n">first_cap</span> <span class="o">-</span> <span class="n">first</span>
        <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;</span> <span class="n">extra</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">:</span>
            <span class="n">first</span> <span class="o">-=</span> <span class="mi">4</span> <span class="o">-</span> <span class="n">extra</span>
            <span class="n">extra</span> <span class="o">=</span> <span class="mi">4</span>
        <span class="n">last</span> <span class="o">=</span> <span class="n">left</span> <span class="o">-</span> <span class="n">first</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">last</span> <span class="o">&lt;=</span> <span class="n">last_cap</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">physical</span> <span class="o">=</span> <span class="n">count</span> <span class="o">*</span> <span class="n">lc</span> <span class="o">+</span> <span class="p">(</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span>
        <span class="n">candidate</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">count</span><span class="o">=</span><span class="n">count</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="n">first</span><span class="p">,</span> <span class="n">middle</span><span class="o">=</span><span class="n">middle_cap</span><span class="p">,</span>
                         <span class="n">last</span><span class="o">=</span><span class="n">last</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="n">extra</span><span class="p">,</span> <span class="n">physical</span><span class="o">=</span><span class="n">physical</span><span class="p">,</span>
                         <span class="n">csize</span><span class="o">=</span><span class="n">e</span><span class="p">[</span><span class="s">'size'</span><span class="p">]</span> <span class="o">+</span> <span class="mi">5</span> <span class="o">+</span> <span class="p">(</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">8</span> <span class="o">+</span> <span class="n">ph</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">best</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">or</span> <span class="n">physical</span> <span class="o">&lt;</span> <span class="n">best</span><span class="p">[</span><span class="s">'physical'</span><span class="p">]:</span>
            <span class="n">best</span> <span class="o">=</span> <span class="n">candidate</span>
    <span class="k">if</span> <span class="n">best</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'cannot fit payload layout for </span><span class="si">{</span><span class="n">e</span><span class="p">[</span><span class="s">"name"</span><span class="p">]</span><span class="si">!r}</span><span class="s">'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">best</span>


<span class="k">def</span> <span class="nf">_solve_patch</span><span class="p">(</span><span class="n">baseline</span><span class="p">):</span>
    <span class="c1"># The final four bytes are a unique free field in the archive comment.
</span>    <span class="c1"># CRC differences do not depend on the prefix bytes, so this needs only
</span>    <span class="c1"># two full-archive CRC scans and 32 tiny four-byte computations.
</span>    <span class="n">zero</span> <span class="o">=</span> <span class="n">crc</span><span class="p">(</span><span class="nb">bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
    <span class="n">cols</span> <span class="o">=</span> <span class="p">[</span><span class="n">crc</span><span class="p">((</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span> <span class="o">^</span> <span class="n">zero</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">)]</span>
    <span class="n">rows</span> <span class="o">=</span> <span class="p">[</span><span class="nb">sum</span><span class="p">(((</span><span class="n">value</span> <span class="o">&gt;&gt;</span> <span class="n">bit</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">col</span> <span class="k">for</span> <span class="n">col</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">cols</span><span class="p">))</span>
            <span class="o">|</span> <span class="p">(((</span><span class="n">baseline</span> <span class="o">&gt;&gt;</span> <span class="n">bit</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">32</span><span class="p">)</span> <span class="k">for</span> <span class="n">bit</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">)]</span>
    <span class="k">for</span> <span class="n">col</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">):</span>
        <span class="n">pivot</span> <span class="o">=</span> <span class="nb">next</span><span class="p">((</span><span class="n">r</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span> <span class="k">if</span> <span class="n">rows</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">col</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">),</span> <span class="bp">None</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">pivot</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'CRC patch matrix unexpectedly singular'</span><span class="p">)</span>
        <span class="n">rows</span><span class="p">[</span><span class="n">col</span><span class="p">],</span> <span class="n">rows</span><span class="p">[</span><span class="n">pivot</span><span class="p">]</span> <span class="o">=</span> <span class="n">rows</span><span class="p">[</span><span class="n">pivot</span><span class="p">],</span> <span class="n">rows</span><span class="p">[</span><span class="n">col</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">r</span> <span class="o">!=</span> <span class="n">col</span> <span class="ow">and</span> <span class="n">rows</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">col</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
                <span class="n">rows</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">^=</span> <span class="n">rows</span><span class="p">[</span><span class="n">col</span><span class="p">]</span>
    <span class="k">return</span> <span class="nb">sum</span><span class="p">(((</span><span class="n">rows</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">)).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">self_name</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
    <span class="s">"""Return ZIP bytes containing the inputs and a byte-identical self member.

    Each entry has name, data, is_dir, mode and optional mtime. Unix type bits
    in mode are preserved, including symlinks whose data is their target text.
    """</span>
    <span class="n">items</span> <span class="o">=</span> <span class="p">[</span><span class="n">_entry</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">mtime</span><span class="p">)</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">]</span>
    <span class="n">me</span> <span class="o">=</span> <span class="n">_entry</span><span class="p">(</span><span class="nb">dict</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">self_name</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="sa">b</span><span class="s">''</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="n">stat</span><span class="p">.</span><span class="n">S_IFREG</span> <span class="o">|</span> <span class="mo">0o644</span><span class="p">),</span> <span class="n">mtime</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">me</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span> <span class="o">&gt;</span> <span class="mi">32000</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'self member name is too long for the bootstrap window'</span><span class="p">)</span>
    <span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="n">me</span><span class="p">[</span><span class="s">'name'</span><span class="p">].</span><span class="n">rstrip</span><span class="p">(</span><span class="s">'/'</span><span class="p">).</span><span class="n">casefold</span><span class="p">()]</span> <span class="o">+</span> <span class="p">[</span><span class="n">e</span><span class="p">[</span><span class="s">'name'</span><span class="p">].</span><span class="n">rstrip</span><span class="p">(</span><span class="s">'/'</span><span class="p">).</span><span class="n">casefold</span><span class="p">()</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">items</span><span class="p">]</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">names</span><span class="p">))</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">names</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'duplicate or case-colliding member names'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">65535</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'ZIP64 is not implemented: too many members'</span><span class="p">)</span>
    <span class="n">me</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">crc</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">local_offset</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">h</span> <span class="o">=</span> <span class="mi">30</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">me</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span> <span class="o">+</span> <span class="mi">260</span>
    <span class="n">p0</span> <span class="o">=</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">5</span>
    <span class="n">plant_at</span> <span class="o">=</span> <span class="mi">30</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">me</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span> <span class="o">+</span> <span class="mi">12</span>
    <span class="n">first</span> <span class="o">=</span> <span class="n">copy_code</span><span class="p">(</span><span class="n">p0</span><span class="p">,</span> <span class="n">p0</span><span class="p">)</span>
    <span class="n">jump</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">for</span> <span class="n">length</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4096</span><span class="p">):</span>
        <span class="n">code</span> <span class="o">=</span> <span class="n">copy_code</span><span class="p">(</span><span class="n">length</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">p0</span> <span class="o">-</span> <span class="n">plant_at</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">length</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">code</span><span class="p">)</span> <span class="o">+</span> <span class="mi">5</span><span class="p">:</span>
            <span class="n">jump</span> <span class="o">=</span> <span class="n">code</span>
            <span class="k">break</span>
    <span class="k">if</span> <span class="n">jump</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'no bootstrap layout for this self name'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">)</span> <span class="o">+</span> <span class="mi">5</span> <span class="o">&gt;</span> <span class="mi">248</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'self member name needs a bootstrap template larger than 248 bytes; use a shorter name'</span><span class="p">)</span>
    <span class="n">prefix_size</span> <span class="o">=</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">5</span> <span class="o">+</span> <span class="n">p0</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">jump</span><span class="p">)</span> <span class="o">+</span> <span class="mi">5</span>
    <span class="n">cells</span><span class="p">,</span> <span class="n">pending</span><span class="p">,</span> <span class="n">pending_entries</span> <span class="o">=</span> <span class="p">[],</span> <span class="nb">bytearray</span><span class="p">(),</span> <span class="p">[]</span>

    <span class="k">def</span> <span class="nf">flush_small</span><span class="p">():</span>
        <span class="k">nonlocal</span> <span class="n">pending</span><span class="p">,</span> <span class="n">pending_entries</span>
        <span class="k">if</span> <span class="n">pending_entries</span><span class="p">:</span>
            <span class="k">for</span> <span class="n">e</span><span class="p">,</span> <span class="n">relative</span> <span class="ow">in</span> <span class="n">pending_entries</span><span class="p">:</span>
                <span class="n">e</span><span class="p">[</span><span class="s">'cell_index'</span><span class="p">],</span> <span class="n">e</span><span class="p">[</span><span class="s">'cell_relative'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">),</span> <span class="n">relative</span>
            <span class="n">cells</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">bytearray</span><span class="p">(</span><span class="n">pending</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span>
            <span class="n">pending</span><span class="p">,</span> <span class="n">pending_entries</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(),</span> <span class="p">[]</span>

    <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">items</span><span class="p">:</span>
        <span class="n">record_size</span> <span class="o">=</span> <span class="mi">30</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'data'</span><span class="p">])</span>
        <span class="k">if</span> <span class="n">record_size</span> <span class="o">&lt;=</span> <span class="mi">65527</span><span class="p">:</span>
            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">pending</span><span class="p">)</span> <span class="o">+</span> <span class="n">record_size</span> <span class="o">&gt;</span> <span class="mi">65527</span><span class="p">:</span>
                <span class="n">flush_small</span><span class="p">()</span>
            <span class="n">pending_entries</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">e</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">pending</span><span class="p">)))</span>
            <span class="n">pending</span> <span class="o">+=</span> <span class="n">_local</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="s">'data'</span><span class="p">]</span>
            <span class="k">continue</span>
        <span class="n">flush_small</span><span class="p">()</span>
        <span class="n">g</span> <span class="o">=</span> <span class="n">_large_plan</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
        <span class="n">e</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">csize</span><span class="o">=</span><span class="n">g</span><span class="p">[</span><span class="s">'csize'</span><span class="p">],</span> <span class="n">cell_index</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">),</span> <span class="n">cell_relative</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">tail</span><span class="p">,</span> <span class="n">at</span> <span class="o">=</span> <span class="n">connector_tail</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="s">'unit'</span><span class="p">],</span> <span class="n">g</span><span class="p">[</span><span class="s">'m'</span><span class="p">]),</span> <span class="n">g</span><span class="p">[</span><span class="s">'first'</span><span class="p">]</span>
        <span class="n">cell</span> <span class="o">=</span> <span class="p">(</span><span class="n">_local</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="n">_extra_padding</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="s">'extra'</span><span class="p">]))</span>
                <span class="o">+</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="n">at</span><span class="p">,</span> <span class="n">at</span> <span class="o">^</span> <span class="mi">65535</span><span class="p">)</span>
                <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="s">'data'</span><span class="p">][:</span><span class="n">at</span><span class="p">]</span> <span class="o">+</span> <span class="n">tail</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cell</span><span class="p">)</span> <span class="o">!=</span> <span class="n">g</span><span class="p">[</span><span class="s">'lc'</span><span class="p">]:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'first payload cell length mismatch'</span><span class="p">)</span>
        <span class="n">cells</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">bytearray</span><span class="p">(</span><span class="n">cell</span><span class="p">))</span>
        <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">g</span><span class="p">[</span><span class="s">'count'</span><span class="p">]):</span>
            <span class="n">last</span> <span class="o">=</span> <span class="n">index</span> <span class="o">==</span> <span class="n">g</span><span class="p">[</span><span class="s">'count'</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span>
            <span class="n">size</span> <span class="o">=</span> <span class="n">g</span><span class="p">[</span><span class="s">'last'</span><span class="p">]</span> <span class="k">if</span> <span class="n">last</span> <span class="k">else</span> <span class="n">g</span><span class="p">[</span><span class="s">'middle'</span><span class="p">]</span>
            <span class="n">cell</span> <span class="o">=</span> <span class="p">((</span><span class="n">g</span><span class="p">[</span><span class="s">'head_final'</span><span class="p">]</span> <span class="k">if</span> <span class="n">last</span> <span class="k">else</span> <span class="n">g</span><span class="p">[</span><span class="s">'head'</span><span class="p">])</span>
                    <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span> <span class="o">^</span> <span class="mi">65535</span><span class="p">)</span>
                    <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="s">'data'</span><span class="p">][</span><span class="n">at</span><span class="p">:</span><span class="n">at</span> <span class="o">+</span> <span class="n">size</span><span class="p">])</span>
            <span class="n">at</span> <span class="o">+=</span> <span class="n">size</span>
            <span class="n">cell</span> <span class="o">+=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="s">'lc'</span><span class="p">]</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">cell</span><span class="p">))</span> <span class="k">if</span> <span class="n">last</span> <span class="k">else</span> <span class="n">tail</span>
            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cell</span><span class="p">)</span> <span class="o">!=</span> <span class="n">g</span><span class="p">[</span><span class="s">'lc'</span><span class="p">]:</span>
                <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'payload cell length mismatch'</span><span class="p">)</span>
            <span class="n">cells</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">bytearray</span><span class="p">(</span><span class="n">cell</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">at</span> <span class="o">!=</span> <span class="n">e</span><span class="p">[</span><span class="s">'size'</span><span class="p">]:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'payload split length mismatch'</span><span class="p">)</span>
    <span class="n">flush_small</span><span class="p">()</span>
    <span class="n">offsets</span><span class="p">,</span> <span class="n">position</span> <span class="o">=</span> <span class="p">[],</span> <span class="n">prefix_size</span>
    <span class="k">for</span> <span class="n">cell</span> <span class="ow">in</span> <span class="n">cells</span><span class="p">:</span>
        <span class="n">offsets</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">position</span><span class="p">)</span>
        <span class="n">position</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">cell</span><span class="p">)</span> <span class="o">+</span> <span class="mi">8</span>
    <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">items</span><span class="p">:</span>
        <span class="n">e</span><span class="p">[</span><span class="s">'local_offset'</span><span class="p">]</span> <span class="o">=</span> <span class="n">offsets</span><span class="p">[</span><span class="n">e</span><span class="p">[</span><span class="s">'cell_index'</span><span class="p">]]</span> <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="s">'cell_relative'</span><span class="p">]</span>

    <span class="c1"># Directory cell boundaries are hidden inside a valid custom extra TLV.
</span>    <span class="c1"># Its data is the 8-byte seed in the cell plus the next actual 8-byte unit.
</span>    <span class="n">all_entries</span> <span class="o">=</span> <span class="p">[</span><span class="n">me</span><span class="p">]</span> <span class="o">+</span> <span class="n">items</span>
    <span class="n">batches</span><span class="p">,</span> <span class="n">current</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">[],</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">all_entries</span><span class="p">:</span>
        <span class="n">record_size</span> <span class="o">=</span> <span class="mi">46</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span>
        <span class="k">if</span> <span class="n">size</span> <span class="o">+</span> <span class="n">record_size</span> <span class="o">&gt;</span> <span class="mi">65000</span> <span class="ow">and</span> <span class="n">current</span><span class="p">:</span>
            <span class="n">batches</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">current</span><span class="p">)</span>
            <span class="n">current</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="p">[],</span> <span class="mi">0</span>
        <span class="n">current</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
        <span class="n">size</span> <span class="o">+=</span> <span class="n">record_size</span>
    <span class="n">batches</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">current</span><span class="p">)</span>
    <span class="n">cd_start</span> <span class="o">=</span> <span class="n">position</span> <span class="k">if</span> <span class="n">cells</span> <span class="k">else</span> <span class="n">prefix_size</span>
    <span class="n">cd_size</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="mi">46</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">all_entries</span><span class="p">)</span> <span class="o">+</span> <span class="mi">20</span> <span class="o">*</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">batches</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">directory_sizes</span> <span class="o">=</span> <span class="p">[</span><span class="nb">sum</span><span class="p">(</span><span class="mi">46</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'encoded'</span><span class="p">])</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">batch</span><span class="p">)</span>
                       <span class="o">+</span> <span class="p">(</span><span class="mi">12</span> <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">batches</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="mi">26</span><span class="p">)</span>
                       <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">batch</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">batches</span><span class="p">)]</span>
    <span class="n">total</span> <span class="o">=</span> <span class="p">(</span><span class="n">prefix_size</span> <span class="o">+</span> <span class="nb">sum</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">len</span><span class="p">,</span> <span class="n">cells</span><span class="p">))</span> <span class="o">+</span> <span class="nb">sum</span><span class="p">(</span><span class="n">directory_sizes</span><span class="p">)</span>
             <span class="o">+</span> <span class="mi">8</span> <span class="o">*</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">batches</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span>
    <span class="k">if</span> <span class="nb">max</span><span class="p">(</span><span class="n">total</span><span class="p">,</span> <span class="n">cd_start</span><span class="p">,</span> <span class="n">cd_size</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">UINT32</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'ZIP64 is not implemented: archive exceeds ZIP32 limits'</span><span class="p">)</span>
    <span class="n">me</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="n">total</span><span class="p">,</span> <span class="n">csize</span><span class="o">=</span><span class="n">total</span> <span class="o">-</span> <span class="n">h</span><span class="p">)</span>
    <span class="n">eocd</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;IHHHHIIH'</span><span class="p">,</span> <span class="mh">0x06054b50</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">all_entries</span><span class="p">),</span>
                       <span class="nb">len</span><span class="p">(</span><span class="n">all_entries</span><span class="p">),</span> <span class="n">cd_size</span><span class="p">,</span> <span class="n">cd_start</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">batch</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">batches</span><span class="p">):</span>
        <span class="n">buf</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">e</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">batch</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">batches</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">j</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">batch</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
                <span class="n">partial</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="mh">0xca04</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
                <span class="n">buf</span> <span class="o">+=</span> <span class="n">_central</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="n">partial</span><span class="p">,</span> <span class="n">extra_size</span><span class="o">=</span><span class="mi">20</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">buf</span> <span class="o">+=</span> <span class="n">_central</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">batches</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">buf</span> <span class="o">+=</span> <span class="n">eocd</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">!=</span> <span class="n">directory_sizes</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'directory cell length mismatch'</span><span class="p">)</span>
        <span class="n">cells</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="ow">not</span> <span class="mi">1</span> <span class="o">&lt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">cell</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">65535</span> <span class="k">for</span> <span class="n">cell</span> <span class="ow">in</span> <span class="n">cells</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'cell exceeds Deflate stored-block bounds'</span><span class="p">)</span>
    <span class="n">units</span> <span class="o">=</span> <span class="p">[</span><span class="n">unit_bytes</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cell</span><span class="p">),</span> <span class="n">final</span><span class="o">=</span><span class="n">i</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
             <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">cell</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">cells</span><span class="p">)]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
        <span class="n">cells</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="o">-</span><span class="mi">8</span><span class="p">:]</span> <span class="o">=</span> <span class="n">units</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
    <span class="n">extra</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="mi">256</span><span class="p">)</span>
    <span class="n">extra</span><span class="p">[:</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'VACUUM04'</span>
    <span class="n">first_raw</span> <span class="o">=</span> <span class="n">raw_header</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">first_raw</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x01</span><span class="s">'</span> <span class="o">+</span> <span class="n">first_raw</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="n">plant</span> <span class="o">=</span> <span class="n">first</span> <span class="o">+</span> <span class="n">jump</span> <span class="o">+</span> <span class="n">first_raw</span>
    <span class="n">extra</span><span class="p">[</span><span class="mi">8</span><span class="p">:</span><span class="mi">8</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">plant</span><span class="p">)]</span> <span class="o">=</span> <span class="n">plant</span>
    <span class="n">header</span> <span class="o">=</span> <span class="n">_local</span><span class="p">(</span><span class="n">me</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;HH'</span><span class="p">,</span> <span class="mh">0xca03</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">extra</span><span class="p">))</span> <span class="o">+</span> <span class="n">extra</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">header</span><span class="p">)</span> <span class="o">!=</span> <span class="n">h</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'self header length mismatch'</span><span class="p">)</span>
    <span class="n">archive</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">header</span> <span class="o">+</span> <span class="n">raw_header</span><span class="p">(</span><span class="n">p0</span><span class="p">)</span> <span class="o">+</span> <span class="n">header</span> <span class="o">+</span> <span class="n">raw_header</span><span class="p">(</span><span class="n">p0</span><span class="p">)</span>
                        <span class="o">+</span> <span class="n">first</span> <span class="o">+</span> <span class="n">jump</span> <span class="o">+</span> <span class="n">first_raw</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">cell</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">cells</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">i</span><span class="p">:</span>
            <span class="n">archive</span> <span class="o">+=</span> <span class="n">units</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">archive</span> <span class="o">+=</span> <span class="n">cell</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span> <span class="o">!=</span> <span class="n">total</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'archive length mismatch'</span><span class="p">)</span>
    <span class="n">patch</span> <span class="o">=</span> <span class="n">_solve_patch</span><span class="p">(</span><span class="n">crc</span><span class="p">(</span><span class="n">archive</span><span class="p">))</span>
    <span class="n">archive</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">:]</span> <span class="o">=</span> <span class="n">patch</span>
    <span class="k">if</span> <span class="n">crc</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'archive CRC patch failed'</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">archive_name</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">empty</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
    <span class="n">value</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'</span><span class="se">\\</span><span class="s">'</span><span class="p">,</span> <span class="s">'/'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">value</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span> <span class="ow">or</span> <span class="s">'</span><span class="se">\0</span><span class="s">'</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'invalid archive path: </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s">'</span><span class="p">)</span>
    <span class="n">parts</span> <span class="o">=</span> <span class="n">value</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">part</span> <span class="o">==</span> <span class="s">'..'</span> <span class="ow">or</span> <span class="s">':'</span> <span class="ow">in</span> <span class="n">part</span> <span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">parts</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'archive paths must be relative, without .. or drive names: </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s">'</span><span class="p">)</span>
    <span class="n">parts</span> <span class="o">=</span> <span class="p">[</span><span class="n">part</span> <span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">parts</span> <span class="k">if</span> <span class="n">part</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s">''</span><span class="p">,</span> <span class="s">'.'</span><span class="p">)]</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">'/'</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">name</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">empty</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'archive member name must not be empty'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">name</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">65535</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'UTF-8 archive path exceeds the ZIP filename limit'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">name</span>


<span class="k">def</span> <span class="nf">join_name</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">archive_name</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">prefix</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">'</span> <span class="k">if</span> <span class="n">prefix</span> <span class="k">else</span> <span class="n">name</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">path_key</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">normcase</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="n">path</span><span class="p">).</span><span class="n">resolve</span><span class="p">()))</span>


<span class="k">def</span> <span class="nf">read_snapshot</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
    <span class="n">flags</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">O_RDONLY</span> <span class="o">|</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">os</span><span class="p">,</span> <span class="s">'O_BINARY'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">|</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">os</span><span class="p">,</span> <span class="s">'O_NOFOLLOW'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    <span class="k">with</span> <span class="n">os</span><span class="p">.</span><span class="n">fdopen</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">flags</span><span class="p">),</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">source</span><span class="p">:</span>
        <span class="n">before</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">fstat</span><span class="p">(</span><span class="n">source</span><span class="p">.</span><span class="n">fileno</span><span class="p">())</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISREG</span><span class="p">(</span><span class="n">before</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'input is no longer a regular file: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">before</span><span class="p">.</span><span class="n">st_size</span> <span class="o">&gt;=</span> <span class="mh">0xffffffff</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'file exceeds this generator</span><span class="se">\'</span><span class="s">s ZIP32 size limit: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">source</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
        <span class="n">after</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">fstat</span><span class="p">(</span><span class="n">source</span><span class="p">.</span><span class="n">fileno</span><span class="p">())</span>
    <span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">!=</span> <span class="n">before</span><span class="p">.</span><span class="n">st_size</span> <span class="ow">or</span> <span class="n">before</span><span class="p">.</span><span class="n">st_size</span> <span class="o">!=</span> <span class="n">after</span><span class="p">.</span><span class="n">st_size</span>
            <span class="ow">or</span> <span class="n">before</span><span class="p">.</span><span class="n">st_mtime_ns</span> <span class="o">!=</span> <span class="n">after</span><span class="p">.</span><span class="n">st_mtime_ns</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'input changed while reading: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">data</span>


<span class="k">def</span> <span class="nf">collect_entries</span><span class="p">(</span><span class="n">inputs</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">prefix</span><span class="p">,</span> <span class="n">reserved</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
    <span class="n">entries</span><span class="p">,</span> <span class="n">names</span><span class="p">,</span> <span class="n">case_names</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">{},</span> <span class="p">{}</span>
    <span class="n">excluded</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="n">output_key</span> <span class="o">=</span> <span class="n">path_key</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">path_key</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">==</span> <span class="n">output_key</span> <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">inputs</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'output cannot also be an explicit input'</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">mode</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="n">name</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
        <span class="n">old</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">old</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">kind</span> <span class="o">==</span> <span class="s">'directory'</span> <span class="ow">and</span> <span class="n">old</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="ow">and</span> <span class="n">old</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'_path'</span><span class="p">)</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="n">old</span><span class="p">[</span><span class="s">'_path'</span><span class="p">],</span> <span class="n">old</span><span class="p">[</span><span class="s">'mode'</span><span class="p">]</span> <span class="o">=</span> <span class="n">path</span><span class="p">,</span> <span class="n">mode</span>
                <span class="k">return</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'duplicate archive path: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">name</span><span class="p">.</span><span class="n">casefold</span><span class="p">()</span> <span class="ow">in</span> <span class="n">case_names</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'archive paths collide on case-insensitive systems: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="n">entry</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="sa">b</span><span class="s">''</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="n">mode</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="n">mtime</span><span class="p">,</span>
                     <span class="n">is_dir</span><span class="o">=</span><span class="n">kind</span> <span class="o">==</span> <span class="s">'directory'</span><span class="p">,</span> <span class="n">kind</span><span class="o">=</span><span class="n">kind</span><span class="p">,</span> <span class="n">_path</span><span class="o">=</span><span class="n">path</span><span class="p">)</span>
        <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>
        <span class="n">names</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">entry</span>
        <span class="n">case_names</span><span class="p">[</span><span class="n">name</span><span class="p">.</span><span class="n">casefold</span><span class="p">()]</span> <span class="o">=</span> <span class="n">name</span>

    <span class="k">def</span> <span class="nf">parents</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
        <span class="n">parts</span> <span class="o">=</span> <span class="n">PurePosixPath</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="n">parts</span>
        <span class="k">for</span> <span class="n">end</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">parts</span><span class="p">)):</span>
            <span class="n">parent</span> <span class="o">=</span> <span class="s">'/'</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">parts</span><span class="p">[:</span><span class="n">end</span><span class="p">])</span>
            <span class="n">existing</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">existing</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                <span class="k">if</span> <span class="ow">not</span> <span class="n">existing</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]:</span>
                    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'archive parent is not a directory: </span><span class="si">{</span><span class="n">parent</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">add</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s">'directory'</span><span class="p">,</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IFDIR</span> <span class="o">|</span> <span class="mo">0o755</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">walk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="k">nonlocal</span> <span class="n">excluded</span>
        <span class="k">if</span> <span class="n">path_key</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">==</span> <span class="n">output_key</span><span class="p">:</span>
            <span class="n">excluded</span> <span class="o">=</span> <span class="bp">True</span>
            <span class="k">return</span>
        <span class="n">st</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="n">lstat</span><span class="p">()</span>
        <span class="n">parents</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISLNK</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="n">add</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">'symlink'</span><span class="p">,</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IFLNK</span> <span class="o">|</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IMODE</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">),</span> <span class="n">path</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISDIR</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">'is_junction'</span><span class="p">,</span> <span class="k">lambda</span><span class="p">:</span> <span class="bp">False</span><span class="p">)():</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'directory junctions are not followed: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="n">add</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">'directory'</span><span class="p">,</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IFDIR</span> <span class="o">|</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IMODE</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">),</span> <span class="n">path</span><span class="p">)</span>
            <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">iterdir</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">name</span><span class="p">):</span>
                <span class="n">walk</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="n">join_name</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">child</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
        <span class="k">elif</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISREG</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="n">add</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">'file'</span><span class="p">,</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IFREG</span> <span class="o">|</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_IMODE</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">),</span> <span class="n">path</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'unsupported special file: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

    <span class="n">parents</span><span class="p">(</span><span class="n">reserved</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">prefix</span><span class="p">:</span>
        <span class="n">parents</span><span class="p">(</span><span class="n">prefix</span> <span class="o">+</span> <span class="s">'/placeholder'</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">inputs</span><span class="p">:</span>
        <span class="n">st</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="n">lstat</span><span class="p">()</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">inputs</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">stat</span><span class="p">.</span><span class="n">S_ISDIR</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">st_mode</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">iterdir</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">name</span><span class="p">):</span>
                <span class="n">walk</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="n">join_name</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">child</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">walk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">join_name</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">path</span><span class="p">.</span><span class="n">name</span> <span class="ow">or</span> <span class="n">path</span><span class="p">.</span><span class="n">resolve</span><span class="p">().</span><span class="n">name</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">reserved</span><span class="p">.</span><span class="n">casefold</span><span class="p">()</span> <span class="ow">in</span> <span class="n">case_names</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'source collides with reserved archive member: </span><span class="si">{</span><span class="n">reserved</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">name</span><span class="p">.</span><span class="n">casefold</span><span class="p">().</span><span class="n">startswith</span><span class="p">(</span><span class="n">reserved</span><span class="p">.</span><span class="n">casefold</span><span class="p">()</span><span class="o">+</span><span class="s">'/'</span><span class="p">)</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">names</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'reserved self member would replace a directory: </span><span class="si">{</span><span class="n">reserved</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">:</span>
        <span class="n">path</span> <span class="o">=</span> <span class="n">entry</span><span class="p">.</span><span class="n">pop</span><span class="p">(</span><span class="s">'_path'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">entry</span><span class="p">[</span><span class="s">'kind'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'file'</span><span class="p">:</span>
            <span class="n">entry</span><span class="p">[</span><span class="s">'data'</span><span class="p">]</span> <span class="o">=</span> <span class="n">read_snapshot</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">entry</span><span class="p">[</span><span class="s">'kind'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'symlink'</span><span class="p">:</span>
            <span class="n">entry</span><span class="p">[</span><span class="s">'data'</span><span class="p">]</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">readlink</span><span class="p">(</span><span class="n">path</span><span class="p">).</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">entries</span><span class="p">,</span> <span class="n">excluded</span>


<span class="k">def</span> <span class="nf">verify_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span> <span class="n">self_name</span><span class="p">):</span>
    <span class="n">expected</span> <span class="o">=</span> <span class="p">{</span><span class="n">entry</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span><span class="o">+</span><span class="p">(</span><span class="s">'/'</span> <span class="k">if</span> <span class="n">entry</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="k">else</span> <span class="s">''</span><span class="p">):</span><span class="n">entry</span><span class="p">[</span><span class="s">'data'</span><span class="p">]</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">}</span>
    <span class="n">expected</span><span class="p">[</span><span class="n">self_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">archive</span>
    <span class="k">with</span> <span class="n">zipfile</span><span class="p">.</span><span class="n">ZipFile</span><span class="p">(</span><span class="n">io</span><span class="p">.</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">archive</span><span class="p">))</span> <span class="k">as</span> <span class="n">reader</span><span class="p">:</span>
        <span class="n">infos</span> <span class="o">=</span> <span class="n">reader</span><span class="p">.</span><span class="n">infolist</span><span class="p">()</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">infos</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">expected</span><span class="p">)</span> <span class="ow">or</span> <span class="p">{</span><span class="n">i</span><span class="p">.</span><span class="n">filename</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">infos</span><span class="p">}</span> <span class="o">!=</span> <span class="nb">set</span><span class="p">(</span><span class="n">expected</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'generated member list does not match the input snapshot'</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">member</span> <span class="ow">in</span> <span class="n">infos</span><span class="p">:</span>
            <span class="n">local</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack_from</span><span class="p">(</span><span class="s">'&lt;IHHHHHIIIHH'</span><span class="p">,</span> <span class="n">archive</span><span class="p">,</span> <span class="n">member</span><span class="p">.</span><span class="n">header_offset</span><span class="p">)</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">local</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="mh">0x04034b50</span> <span class="ow">or</span> <span class="n">local</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">!=</span> <span class="n">member</span><span class="p">.</span><span class="n">compress_type</span>
                    <span class="ow">or</span> <span class="n">local</span><span class="p">[</span><span class="mi">6</span><span class="p">:</span><span class="mi">9</span><span class="p">]</span> <span class="o">!=</span> <span class="p">(</span><span class="n">member</span><span class="p">.</span><span class="n">CRC</span><span class="p">,</span> <span class="n">member</span><span class="p">.</span><span class="n">compress_size</span><span class="p">,</span> <span class="n">member</span><span class="p">.</span><span class="n">file_size</span><span class="p">)):</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'inconsistent ZIP local header: </span><span class="si">{</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="n">at</span> <span class="o">=</span> <span class="n">member</span><span class="p">.</span><span class="n">header_offset</span> <span class="o">+</span> <span class="mi">30</span> <span class="o">+</span> <span class="n">local</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">local</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
            <span class="n">wire</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">archive</span><span class="p">)[</span><span class="n">at</span><span class="p">:</span><span class="n">at</span><span class="o">+</span><span class="n">member</span><span class="p">.</span><span class="n">compress_size</span><span class="p">]</span>
            <span class="n">original</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">expected</span><span class="p">[</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="p">])</span>
            <span class="k">if</span> <span class="n">member</span><span class="p">.</span><span class="n">file_size</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">original</span><span class="p">):</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'wrong member size: </span><span class="si">{</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="n">checksum</span><span class="p">,</span> <span class="n">position</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
            <span class="k">def</span> <span class="nf">check_chunk</span><span class="p">(</span><span class="n">decoded</span><span class="p">):</span>
                <span class="k">nonlocal</span> <span class="n">checksum</span><span class="p">,</span> <span class="n">position</span>
                <span class="k">if</span> <span class="n">decoded</span> <span class="o">!=</span> <span class="n">original</span><span class="p">[</span><span class="n">position</span><span class="p">:</span><span class="n">position</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">decoded</span><span class="p">)]:</span>
                    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'member data mismatch: </span><span class="si">{</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
                <span class="n">checksum</span> <span class="o">=</span> <span class="n">zlib</span><span class="p">.</span><span class="n">crc32</span><span class="p">(</span><span class="n">decoded</span><span class="p">,</span> <span class="n">checksum</span><span class="p">)</span>
                <span class="n">position</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">decoded</span><span class="p">)</span>
            <span class="n">inflater</span> <span class="o">=</span> <span class="n">zlib</span><span class="p">.</span><span class="n">decompressobj</span><span class="p">(</span><span class="o">-</span><span class="mi">15</span><span class="p">)</span> <span class="k">if</span> <span class="n">member</span><span class="p">.</span><span class="n">compress_type</span> <span class="o">==</span> <span class="mi">8</span> <span class="k">else</span> <span class="bp">None</span>
            <span class="k">if</span> <span class="n">member</span><span class="p">.</span><span class="n">compress_type</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">):</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'unexpected ZIP compression method: </span><span class="si">{</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="k">for</span> <span class="n">start</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">wire</span><span class="p">),</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">):</span>
                <span class="n">pending</span> <span class="o">=</span> <span class="n">wire</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">start</span><span class="o">+</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">)]</span>
                <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">pending</span><span class="p">):</span>
                    <span class="n">decoded</span> <span class="o">=</span> <span class="n">inflater</span><span class="p">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">pending</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">)</span> <span class="k">if</span> <span class="n">inflater</span> <span class="k">else</span> <span class="n">pending</span>
                    <span class="n">check_chunk</span><span class="p">(</span><span class="n">decoded</span><span class="p">)</span>
                    <span class="n">pending</span> <span class="o">=</span> <span class="n">inflater</span><span class="p">.</span><span class="n">unconsumed_tail</span> <span class="k">if</span> <span class="n">inflater</span> <span class="k">else</span> <span class="sa">b</span><span class="s">''</span>
            <span class="k">if</span> <span class="n">inflater</span><span class="p">:</span>
                <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
                    <span class="n">decoded</span> <span class="o">=</span> <span class="n">inflater</span><span class="p">.</span><span class="n">decompress</span><span class="p">(</span><span class="sa">b</span><span class="s">''</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">)</span>
                    <span class="k">if</span> <span class="ow">not</span> <span class="n">decoded</span><span class="p">:</span>
                        <span class="k">break</span>
                    <span class="n">check_chunk</span><span class="p">(</span><span class="n">decoded</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">inflater</span> <span class="ow">and</span> <span class="p">(</span><span class="ow">not</span> <span class="n">inflater</span><span class="p">.</span><span class="n">eof</span> <span class="ow">or</span> <span class="n">inflater</span><span class="p">.</span><span class="n">unused_data</span> <span class="ow">or</span> <span class="n">inflater</span><span class="p">.</span><span class="n">unconsumed_tail</span><span class="p">):</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'incomplete or trailing Deflate data: </span><span class="si">{</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">position</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">original</span><span class="p">)</span> <span class="ow">or</span> <span class="n">checksum</span> <span class="o">!=</span> <span class="n">member</span><span class="p">.</span><span class="n">CRC</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'member length or CRC mismatch: </span><span class="si">{</span><span class="n">member</span><span class="p">.</span><span class="n">filename</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">atomic_write</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">force</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">force</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">FileExistsError</span><span class="p">(</span><span class="sa">f</span><span class="s">'output exists; use --force to replace it: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
    <span class="n">temporary</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">with</span> <span class="n">tempfile</span><span class="p">.</span><span class="n">NamedTemporaryFile</span><span class="p">(</span><span class="n">prefix</span><span class="o">=</span><span class="s">'.zip-quine-'</span><span class="p">,</span> <span class="nb">dir</span><span class="o">=</span><span class="n">path</span><span class="p">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">delete</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> <span class="k">as</span> <span class="n">output</span><span class="p">:</span>
            <span class="n">temporary</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">output</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
            <span class="n">output</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
            <span class="n">output</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>
            <span class="n">os</span><span class="p">.</span><span class="n">fsync</span><span class="p">(</span><span class="n">output</span><span class="p">.</span><span class="n">fileno</span><span class="p">())</span>
        <span class="k">if</span> <span class="n">force</span><span class="p">:</span>
            <span class="n">os</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="n">temporary</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">os</span><span class="p">.</span><span class="n">link</span><span class="p">(</span><span class="n">temporary</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
            <span class="n">temporary</span><span class="p">.</span><span class="n">unlink</span><span class="p">()</span>
        <span class="n">temporary</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">finally</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">temporary</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">temporary</span><span class="p">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">missing_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="n">__doc__</span><span class="p">,</span>
                                     <span class="n">formatter_class</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">RawDescriptionHelpFormatter</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'inputs'</span><span class="p">,</span> <span class="n">metavar</span><span class="o">=</span><span class="s">'INPUT'</span><span class="p">,</span> <span class="n">nargs</span><span class="o">=</span><span class="s">'+'</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'output'</span><span class="p">,</span> <span class="n">metavar</span><span class="o">=</span><span class="s">'OUTPUT'</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--quine-name'</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'self member name (default: output basename)'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--src-dir'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">''</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'archive prefix for source entries'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--quine-dir'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">''</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'archive prefix for the self member'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--mtime'</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'fixed timestamp; ZIP dates clamp to 1980..2107 (default: 0)'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--force'</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s">'store_true'</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'replace an existing output after verification'</span><span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">is_symlink</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'output must not be a symbolic link'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">is_file</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'output must be a regular file path'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">force</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">FileExistsError</span><span class="p">(</span><span class="sa">f</span><span class="s">'output exists; use --force to replace it: </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">parent</span><span class="p">.</span><span class="n">is_dir</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">'output directory does not exist: </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">parent</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="n">prefix</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">src_dir</span><span class="p">,</span> <span class="n">empty</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">self_dir</span> <span class="o">=</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">quine_dir</span><span class="p">,</span> <span class="n">empty</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">self_name</span> <span class="o">=</span> <span class="n">join_name</span><span class="p">(</span><span class="n">self_dir</span><span class="p">,</span> <span class="n">archive_name</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">quine_name</span> <span class="ow">or</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">.</span><span class="n">name</span><span class="p">))</span>
        <span class="n">entries</span><span class="p">,</span> <span class="n">excluded</span> <span class="o">=</span> <span class="n">collect_entries</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">inputs</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="n">prefix</span><span class="p">,</span> <span class="n">self_name</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">mtime</span><span class="p">)</span>
        <span class="n">archive</span> <span class="o">=</span> <span class="n">build</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">self_name</span><span class="p">,</span> <span class="n">mtime</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">mtime</span><span class="p">)</span>
        <span class="n">verify_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span> <span class="n">self_name</span><span class="p">)</span>
        <span class="n">atomic_write</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="n">archive</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">force</span><span class="p">)</span>
        <span class="n">files</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'kind'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'file'</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="n">dirs</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'is_dir'</span><span class="p">]</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="n">links</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">'kind'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'symlink'</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'[ZIP quine] </span><span class="si">{</span><span class="n">files</span><span class="si">}</span><span class="s"> files, </span><span class="si">{</span><span class="n">dirs</span><span class="si">}</span><span class="s"> directories, </span><span class="si">{</span><span class="n">links</span><span class="si">}</span><span class="s"> symlinks'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">excluded</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">'[ZIP quine] excluded the output path from source traversal'</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'[ZIP quine] wrote </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes'</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'[verify] member contents, CRC32 and exact self: PASS'</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'[self] </span><span class="si">{</span><span class="n">self_name</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span>
    <span class="k">except</span> <span class="p">(</span><span class="nb">OSError</span><span class="p">,</span> <span class="nb">ValueError</span><span class="p">,</span> <span class="nb">RuntimeError</span><span class="p">,</span> <span class="nb">OverflowError</span><span class="p">,</span> <span class="n">zipfile</span><span class="p">.</span><span class="n">BadZipFile</span><span class="p">,</span>
            <span class="n">zlib</span><span class="p">.</span><span class="n">error</span><span class="p">,</span> <span class="n">struct</span><span class="p">.</span><span class="n">error</span><span class="p">)</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'error: </span><span class="si">{</span><span class="n">error</span><span class="si">}</span><span class="s">'</span><span class="p">,</span> <span class="nb">file</span><span class="o">=</span><span class="n">sys</span><span class="p">.</span><span class="n">stderr</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">1</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div>  </div>
</details>

<h2 id="更多格式rar-quine">更多格式——RAR Quine</h2>
<p>其实大多数常用的压缩算法都和ZIP差不多，核心思想基本上都是LZ77那套用距离和长度来表示重复内容，然后再用各种熵编码来尽可能用少的比特存储数据……所以说RAR Quine理论上也可以存在，毕竟原理都差不多。同样，我也试着让AI做了一下，依旧轻轻松松就做出来了。不过毕竟RAR不是开源软件，生成的代码我就不放出来了，这里有一份生成后的RAR Quine：<a href="/assets/quine.rar">quine.rar</a>，如果有人有兴趣，也可以研究一下这个档案。</p>

<h1 id="感想">感想</h1>
<p>我明明对压缩的原理也只是了解点皮毛，但是有了AI之后能做的事情真的是太可怕了，各种只能在想象中的东西都被轻松实现……真的是越来越离不开AI了，再看看最近的AI又发现了大量基础设施的高危漏洞，很明显，人被AI淘汰的时代可能越来越近了，到时候……还有继续记录的意义吗？</p>]]></content><author><name>mayx</name></author><category term="压缩" /><category term="探索" /><category term="Quine" /><category term="zstd" /><summary type="html"><![CDATA[压缩……在消除冗余的同时，又创造了新的混乱？]]></summary></entry><entry><title type="html">如何制作一个“完整”的博客压缩包？</title><link href="https://mabbs.github.io/2026/10/01/quine.html" rel="alternate" type="text/html" title="如何制作一个“完整”的博客压缩包？" /><published>2026-10-01T00:00:00+08:00</published><updated>2026-10-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/10/01/quine</id><content type="html" xml:base="https://mabbs.github.io/2026/10/01/quine.html"><![CDATA[<p>让AI做出真正的“完整”将不再是难事……<!--more--></p>

<h1 id="起因">起因</h1>
<p>在上次<a href="/2026/09/01/vibe-coding2.html">用AI制作了各种东西</a>之后，我已经完全理解了AI确实是无所不能的。既然如此，那就让它帮我解决曾经未能解决的事情吧？ <br />
  去年，我为了让下载全站压缩包的按钮不断链，让这个压缩包也包含它本身而研究了<a href="/2025/09/01/quine.html">ZIP Quine</a>，但限于DEFLATE的回溯窗口大小没能做到……但那是人做的东西，人还是太弱小了，现在换AI来试试，也许一切将变得不一样？</p>

<h1 id="制作基于lzma2的博客压缩包">制作基于LZMA2的博客压缩包</h1>
<p>首先，我把<a href="https://github.com/ruvmello">Ruben Van Mello</a>写的那篇论文《<a href="https://www.mdpi.com/2076-3417/14/21/9797">A Generator for Recursive Zip Files</a>》以及生成器<a href="https://github.com/ruvmello/zip-quine-generator">zip-quine-generator</a>发给了AI，问它这个限制是不是真的，是不是真的无法创造出超过32KiB的ZIP Quine？没过多久它分析完了，告诉我确实存在这样的问题，但并不是无法解决的，最简单的办法就是换个回溯窗口更大的压缩算法。我看了一下它给我列出的几个算法，看起来LZMA2和zstd比较符合要求。不过zstd感觉不是很知名，一般的解压软件应该处理不了，所以我就选择让它基于LZMA2来制作了。 <br />
  不过LZMA2只是算法，还得选个容器，考虑到7z算是最出名，而且LZMA2本来就是7-Zip的作者开发的，所以就直接让它做7z版本的了。另外因为<a href="https://github.com/NateChoe1">Nate Choe</a>做了通过扩展欧几里得求逆元来计算CRC32的<a href="https://github.com/ruvmello/zip-quine-generator/pull/3">PR</a>，所以我也告诉它要用这种方法在多项式时间内解出CRC32的值，而不是爆破。就这样AI花了两个小时左右成功把代码写出来了，效果非常完美，另外它还偷懒地将计算CRC32的算法改成了高斯消元法😆，因为7z格式有3个相互影响的CRC32值，用扩展欧几里得会更复杂一点，不过对7z来说不写入文件的CRC32也不影响，只是会不校验罢了，至少它还是按我要求做了……但其实这个问题也并不是完全不能用扩展欧几里得算法，后来我又强烈要求了一下，AI还是给我写出来了。以下是代码片段，有兴趣的人可以参考一下（不过这个看起来写的就是很复杂，所以我自己只用了高斯消元法）：</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">RU_POLY</span> <span class="o">=</span> <span class="mh">0x104C11DB7</span>          <span class="c1"># x^32+x^26+x^23+...+1（含 x^32 首项）
</span>

<span class="k">def</span> <span class="nf">_find_probe</span><span class="p">(</span><span class="n">p</span><span class="p">):</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">64</span><span class="p">):</span>
        <span class="k">if</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">p</span><span class="p">:</span>
            <span class="n">ret</span> <span class="o">=</span> <span class="n">i</span>
    <span class="k">return</span> <span class="n">ret</span>


<span class="k">def</span> <span class="nf">_mul_raw</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">):</span>
    <span class="s">"""多项式乘法（无模）。"""</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">probe</span> <span class="o">=</span> <span class="n">_find_probe</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">64</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">p2</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">):</span>
            <span class="k">assert</span> <span class="n">probe</span> <span class="o">+</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">64</span><span class="p">,</span> <span class="s">"多项式乘法溢出"</span>
            <span class="n">ret</span> <span class="o">^=</span> <span class="n">p1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
    <span class="k">return</span> <span class="n">ret</span>


<span class="k">def</span> <span class="nf">_poly_divmod</span><span class="p">(</span><span class="n">dividend</span><span class="p">,</span> <span class="n">divisor</span><span class="p">):</span>
    <span class="s">"""多项式长除法，返回 (商, 余)。"""</span>
    <span class="n">probe</span> <span class="o">=</span> <span class="n">_find_probe</span><span class="p">(</span><span class="n">divisor</span><span class="p">)</span>
    <span class="n">probe_bit</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">probe</span>
    <span class="n">quot</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">rem</span> <span class="o">=</span> <span class="n">dividend</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">63</span> <span class="o">-</span> <span class="n">probe</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">rem</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">probe_bit</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">):</span>
            <span class="n">quot</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
            <span class="n">rem</span> <span class="o">^=</span> <span class="n">divisor</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
    <span class="k">return</span> <span class="n">quot</span><span class="p">,</span> <span class="n">rem</span>


<span class="k">def</span> <span class="nf">_mul</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">mod</span><span class="p">):</span>
    <span class="s">"""p1*p2 % mod。"""</span>
    <span class="k">return</span> <span class="n">_poly_divmod</span><span class="p">(</span><span class="n">_mul_raw</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">),</span> <span class="n">mod</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">_xgcd</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">):</span>
    <span class="s">"""扩展欧几里得：返回 (k1,k2,gcd)，满足 p1*k1+p2*k2=gcd。"""</span>
    <span class="k">if</span> <span class="n">_find_probe</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">_find_probe</span><span class="p">(</span><span class="n">p2</span><span class="p">):</span>
        <span class="n">k1</span><span class="p">,</span> <span class="n">k2</span><span class="p">,</span> <span class="n">g</span> <span class="o">=</span> <span class="n">_xgcd</span><span class="p">(</span><span class="n">p2</span><span class="p">,</span> <span class="n">p1</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">k2</span><span class="p">,</span> <span class="n">k1</span><span class="p">,</span> <span class="n">g</span>
    <span class="k">if</span> <span class="n">p2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">p1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">p1</span>
    <span class="n">q</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="n">_poly_divmod</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">)</span>
    <span class="n">c1</span><span class="p">,</span> <span class="n">c2</span><span class="p">,</span> <span class="n">g</span> <span class="o">=</span> <span class="n">_xgcd</span><span class="p">(</span><span class="n">p2</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">c2</span><span class="p">,</span> <span class="n">c1</span> <span class="o">^</span> <span class="n">_mul_raw</span><span class="p">(</span><span class="n">c2</span><span class="p">,</span> <span class="n">q</span><span class="p">),</span> <span class="n">g</span>


<span class="k">def</span> <span class="nf">_minv</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">mod</span><span class="p">):</span>
    <span class="s">"""p 在环上的乘法逆元；不存在时返回 0。"""</span>
    <span class="n">k1</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">g</span> <span class="o">=</span> <span class="n">_xgcd</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">mod</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">g</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">0</span>
    <span class="k">return</span> <span class="n">_poly_divmod</span><span class="p">(</span><span class="n">k1</span><span class="p">,</span> <span class="n">mod</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">_bitrev32</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="s">"""32 位比特反转。CRC 的输出循环冗余（binascii 反射）↔ 本环（非反射）互为 bit-reverse。"""</span>
    <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="nb">format</span><span class="p">(</span><span class="n">x</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">,</span> <span class="s">"032b"</span><span class="p">)[::</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="mi">2</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_ring_pow_x</span><span class="p">(</span><span class="n">k</span><span class="p">):</span>
    <span class="s">"""环内 x^k mod P（P=RU_POLY），二进制幂 O(log k)。变量列就是单个环幂。"""</span>
    <span class="n">res</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="n">mod</span><span class="p">,</span> <span class="n">e</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">RU_POLY</span><span class="p">,</span> <span class="n">k</span>
    <span class="k">while</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">e</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">res</span> <span class="o">=</span> <span class="n">_mul</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="n">mod</span><span class="p">)</span>
        <span class="n">base</span> <span class="o">=</span> <span class="n">_mul</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="n">mod</span><span class="p">)</span>
        <span class="n">e</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">res</span>


<span class="k">def</span> <span class="nf">solve_crc_system</span><span class="p">(</span><span class="n">files</span><span class="p">):</span>
    <span class="s">"""解析解 CRC 定点系统

    - 常数列     ：对“变量清零”的数据做一次 binascii.crc32，再 bit-reverse 32 位
                    （binascii 反射 CRC 与本环非反射 CRC 互为 bit-reverse，见 _bitrev32）。
    - 变量列     ：单个环幂 x^(32 + 8*(len-pos-4))，二进制幂 O(log N) 一步算出；
                    同一变量出现在多处时按位异或累加。
    各自 O(#rows) 次 CRC + 少量环幂即可建出整矩阵，Gauss 消元不变。
    """</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">files</span><span class="p">)</span>
    <span class="n">mod</span> <span class="o">=</span> <span class="n">RU_POLY</span>
    <span class="n">matrix</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>

    <span class="k">for</span> <span class="nb">file</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">files</span><span class="p">[</span><span class="nb">file</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">offsets</span> <span class="o">=</span> <span class="n">files</span><span class="p">[</span><span class="nb">file</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
        <span class="n">N</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
        <span class="n">zero</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">pos</span> <span class="ow">in</span> <span class="n">offsets</span><span class="p">:</span>                      <span class="c1"># 未知量占 4 字节，清零后求常数项
</span>            <span class="n">zero</span><span class="p">[</span><span class="n">pos</span><span class="p">:</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00\x00\x00</span><span class="s">"</span>
        <span class="n">matrix</span><span class="p">[</span><span class="nb">file</span><span class="p">][</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">_bitrev32</span><span class="p">(</span><span class="n">crc32</span><span class="p">(</span><span class="nb">bytes</span><span class="p">(</span><span class="n">zero</span><span class="p">)))</span>
        <span class="k">for</span> <span class="n">pos</span><span class="p">,</span> <span class="n">vid</span> <span class="ow">in</span> <span class="n">offsets</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
            <span class="n">rest</span> <span class="o">=</span> <span class="n">N</span> <span class="o">-</span> <span class="n">pos</span> <span class="o">-</span> <span class="mi">4</span>                    <span class="c1"># 变量被 4 字节（x^32）+ 其后字节（x^8/个）推到底
</span>            <span class="n">matrix</span><span class="p">[</span><span class="nb">file</span><span class="p">][</span><span class="n">vid</span><span class="p">]</span> <span class="o">^=</span> <span class="n">_ring_pow_x</span><span class="p">(</span><span class="mi">32</span> <span class="o">+</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">rest</span><span class="p">)</span>
        <span class="n">matrix</span><span class="p">[</span><span class="nb">file</span><span class="p">][</span><span class="nb">file</span><span class="p">]</span> <span class="o">^=</span> <span class="mi">1</span>

    <span class="k">for</span> <span class="n">sr</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
        <span class="n">ex</span> <span class="o">=</span> <span class="n">sr</span>
        <span class="k">while</span> <span class="n">ex</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="ow">and</span> <span class="n">matrix</span><span class="p">[</span><span class="n">ex</span><span class="p">][</span><span class="n">sr</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">ex</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">if</span> <span class="n">ex</span> <span class="o">==</span> <span class="n">n</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"CRC 多项式系统奇异 @row%d"</span> <span class="o">%</span> <span class="n">sr</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">ex</span> <span class="o">!=</span> <span class="n">sr</span><span class="p">:</span>
            <span class="n">matrix</span><span class="p">[</span><span class="n">sr</span><span class="p">],</span> <span class="n">matrix</span><span class="p">[</span><span class="n">ex</span><span class="p">]</span> <span class="o">=</span> <span class="n">matrix</span><span class="p">[</span><span class="n">ex</span><span class="p">],</span> <span class="n">matrix</span><span class="p">[</span><span class="n">sr</span><span class="p">]</span>
        <span class="n">inv</span> <span class="o">=</span> <span class="n">_minv</span><span class="p">(</span><span class="n">matrix</span><span class="p">[</span><span class="n">sr</span><span class="p">][</span><span class="n">sr</span><span class="p">],</span> <span class="n">mod</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">er</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">sr</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
            <span class="n">mult</span> <span class="o">=</span> <span class="n">_mul</span><span class="p">(</span><span class="n">inv</span><span class="p">,</span> <span class="n">matrix</span><span class="p">[</span><span class="n">er</span><span class="p">][</span><span class="n">sr</span><span class="p">],</span> <span class="n">mod</span><span class="p">)</span>
            <span class="k">for</span> <span class="n">ec</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
                <span class="n">matrix</span><span class="p">[</span><span class="n">er</span><span class="p">][</span><span class="n">ec</span><span class="p">]</span> <span class="o">^=</span> <span class="n">_mul</span><span class="p">(</span><span class="n">mult</span><span class="p">,</span> <span class="n">matrix</span><span class="p">[</span><span class="n">sr</span><span class="p">][</span><span class="n">ec</span><span class="p">],</span> <span class="n">mod</span><span class="p">)</span>

    <span class="n">res</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">n</span>
    <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
        <span class="n">v</span> <span class="o">=</span> <span class="n">matrix</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">n</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">col</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
            <span class="n">v</span> <span class="o">^=</span> <span class="n">_mul</span><span class="p">(</span><span class="n">res</span><span class="p">[</span><span class="n">col</span><span class="p">],</span> <span class="n">matrix</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">col</span><span class="p">],</span> <span class="n">mod</span><span class="p">)</span>
        <span class="n">res</span><span class="p">[</span><span class="n">row</span><span class="p">]</span> <span class="o">=</span> <span class="n">_mul</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">_minv</span><span class="p">(</span><span class="n">matrix</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">row</span><span class="p">],</span> <span class="n">mod</span><span class="p">),</span> <span class="n">mod</span><span class="p">)</span>

    <span class="k">return</span> <span class="p">[</span><span class="n">_impl_val</span><span class="p">(</span><span class="n">res</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>

<span class="k">def</span> <span class="nf">_impl_val</span><span class="p">(</span><span class="n">math_val</span><span class="p">):</span>
    <span class="n">imp</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">bit</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">math_val</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bit</span><span class="p">):</span>
            <span class="n">imp</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">31</span> <span class="o">-</span> <span class="n">bit</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">imp</span>

<span class="k">class</span> <span class="nc">BlogQuine</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">solve_crc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">seed</span><span class="p">):</span>
        <span class="n">T</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"T"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"h"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"d"</span><span class="p">]</span>
        <span class="n">k</span><span class="p">,</span> <span class="n">n_sub</span><span class="p">,</span> <span class="n">crc_base</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"k"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n_sub"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"crc_base"</span><span class="p">]</span>
        <span class="n">hoff</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"total"</span><span class="p">]</span> <span class="o">-</span> <span class="n">h</span>
        <span class="n">vout_f</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"total"</span><span class="p">]</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">T</span>
        <span class="n">hcopy</span> <span class="o">=</span> <span class="n">vout_f</span> <span class="o">+</span> <span class="p">(</span><span class="n">T</span> <span class="o">-</span> <span class="n">h</span><span class="p">)</span>
        <span class="c1"># 已知 CRC：seed + 各文件（quine 的是未知量 D）
</span>        <span class="n">known</span> <span class="o">=</span> <span class="p">[</span><span class="n">crc32</span><span class="p">(</span><span class="n">seed</span><span class="p">)]</span> <span class="o">+</span> <span class="p">[</span><span class="n">crc32</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">]</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">known</span><span class="p">)</span> <span class="o">==</span> <span class="n">n_sub</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">e</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">known</span><span class="p">):</span>
            <span class="n">struct</span><span class="p">.</span><span class="n">pack_into</span><span class="p">(</span><span class="s">"&lt;I"</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">hoff</span> <span class="o">+</span> <span class="n">crc_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">i</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
            <span class="n">struct</span><span class="p">.</span><span class="n">pack_into</span><span class="p">(</span><span class="s">"&lt;I"</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">hcopy</span> <span class="o">+</span> <span class="n">crc_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">i</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
        <span class="c1"># 未知量 D N S：D=整个文件、N=头部 F[hoff:hoff+h]、S=签名 F[12:32]
</span>        <span class="n">dpos</span> <span class="o">=</span> <span class="n">crc_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">n_sub</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
        <span class="n">ncopy</span> <span class="o">=</span> <span class="mi">32</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">k</span> <span class="o">+</span> <span class="n">d</span>                            <span class="c1"># C1k 载荷里 F[0:35) 副本起点
</span>        <span class="n">groups</span> <span class="o">=</span> <span class="p">[[</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">dpos</span><span class="p">,</span> <span class="n">hcopy</span> <span class="o">+</span> <span class="n">dpos</span><span class="p">],</span>            <span class="c1"># D 出现两处
</span>                  <span class="p">[</span><span class="mi">28</span><span class="p">,</span> <span class="n">ncopy</span> <span class="o">+</span> <span class="mi">28</span><span class="p">],</span>                        <span class="c1"># N
</span>                  <span class="p">[</span><span class="mi">8</span><span class="p">,</span> <span class="n">ncopy</span> <span class="o">+</span> <span class="mi">8</span><span class="p">]]</span>                          <span class="c1"># S
</span>
        <span class="c1"># 构造 3 个“文件”喂给解析解系统：
</span>        <span class="c1">#   文件0 = 整个 F，三个未知量各自出现在 groups 两处
</span>        <span class="c1">#   文件1 = 头部块，只有 D 出现在相对 dpos（另一处 hcopy+dpos 在头外）
</span>        <span class="c1">#   文件2 = 签名块，只有 N 出现在相对 16（28-12）
</span>        <span class="n">whole</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">)</span>
        <span class="n">hdr</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">h</span><span class="p">])</span>
        <span class="n">sig</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="mi">12</span><span class="p">:</span><span class="mi">32</span><span class="p">])</span>
        <span class="n">f0_off</span> <span class="o">=</span> <span class="p">{</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">dpos</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">hcopy</span> <span class="o">+</span> <span class="n">dpos</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
                  <span class="mi">28</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ncopy</span> <span class="o">+</span> <span class="mi">28</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
                  <span class="mi">8</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">ncopy</span> <span class="o">+</span> <span class="mi">8</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
        <span class="n">f1_off</span> <span class="o">=</span> <span class="p">{</span><span class="n">dpos</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span>
        <span class="n">f2_off</span> <span class="o">=</span> <span class="p">{</span><span class="mi">28</span> <span class="o">-</span> <span class="mi">12</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>
        <span class="n">vals</span> <span class="o">=</span> <span class="n">solve_crc_system</span><span class="p">([(</span><span class="n">whole</span><span class="p">,</span> <span class="n">f0_off</span><span class="p">),</span> <span class="p">(</span><span class="n">hdr</span><span class="p">,</span> <span class="n">f1_off</span><span class="p">),</span> <span class="p">(</span><span class="n">sig</span><span class="p">,</span> <span class="n">f2_off</span><span class="p">)])</span>

        <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">groups</span><span class="p">[</span><span class="n">g</span><span class="p">]:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">p</span><span class="p">:</span><span class="n">p</span> <span class="o">+</span> <span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"&lt;I"</span><span class="p">,</span> <span class="n">vals</span><span class="p">[</span><span class="n">g</span><span class="p">])</span>
        <span class="n">real</span> <span class="o">=</span> <span class="p">(</span><span class="n">crc32</span><span class="p">(</span><span class="n">F</span><span class="p">),</span> <span class="n">crc32</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">h</span><span class="p">]),</span> <span class="n">crc32</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="mi">12</span><span class="p">:</span><span class="mi">32</span><span class="p">]))</span>
        <span class="k">assert</span> <span class="n">real</span> <span class="o">==</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">vals</span><span class="p">),</span> <span class="s">"CRC 定点失败: %s != %s"</span> <span class="o">%</span> <span class="p">(</span><span class="n">real</span><span class="p">,</span> <span class="n">vals</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">vals</span>
</code></pre></div></div>

<p>于是我第一时间就把原来的TGZ压缩命令换掉，换成了AI给我写的<a href="https://github.com/Mabbs/mabbs.github.io/blob/master/_tools/blogquine.py">blogquine.py</a>，现在就可以通过<a href="https://mayx.eu.org/MayxBlog.7z">这里</a>下载到“完整”包含我博客所有内容的压缩包了。 <br />
  不过唯一的问题就是这样做出来的压缩包并没有压缩😂，相当于给做成了普通的归档了。当然我的博客本身倒是不大，没压缩也多不了多少空间，但相比于能做出“完整”的效果来说，这点浪费的空间也是小问题了。</p>
<h2 id="基于博客压缩包的离线模式20261003更新">基于博客压缩包的离线模式（2026.10.03更新）</h2>
<p>既然现在的AI什么都能做了，那上次我因为技术不足仅仅只是用<a href="/2025/08/01/sw-proxy.html">Service Worker做了个代理</a>，现在也可以升级成下载全站之后让全站离线的功能吧？不过之前做的压缩包并没有压缩，导致整个压缩包有接近20MiB的大小，后来我仔细想了一下我的需求，我只是想让下载全站压缩包的按钮不断链，并没有说解压后的文件一定就必须是它本身，所以我只要用相同的文件名再压缩一次应该就可以了，这样既能满足我的想法，又可以让其他人下载的时候不会花费太多的时间。 <br />
  在我直接压缩之后，整个压缩包占用大概9MiB的空间，其实我已经很满意了，不过压之前是18MiB，也就是说压缩率是50%……虽然也不是不能接受，但我在想这是不是合理的结果呢？我让AI分析了一下50%的压缩率正不正常，它看了一眼之后告诉我文本部分的10MiB压缩完之后有1MiB，图片因为压不动所以直接占用了8MiB😅，这时候我才意识到原来我一直存着一堆占着大量空间的垃圾，而且还是用的PNG/JPG这种压缩率很低的格式……考虑到兼容性和压缩效果，我把占用比较大的图片转换为了WebP格式，最终整个压缩包就只占用了4MiB的空间，一口气又节省了50%的空间。 <br />
  最后做完对压缩包的处理之后，我直接让AI给我基于那个压缩包制作离线模式，它轻轻松松就<a href="https://mayx.eu.org/offline.html">做出来</a>了，最终使用的是<a href="https://github.com/nika-begiashvili/libarchivejs">libarchive库</a>，在启用离线模式之后整个网站的速度简直就是0延迟，点击就能瞬间跳转，效果算是相当不错了。</p>
<h2 id="受大佬启发制作了能压缩的7z-quine20261005更新">受大佬启发制作了能压缩的7z Quine（2026.10.05更新）</h2>
<p>做完博客压缩包之后我突发奇想，虽然东西是AI做的，但也是在我的手中做出了世界上第一个LZMA2 Quine，那么如果我向全世界第一个制作ZIP Quine的人展示一下我的作品会怎么样呢？目前可以知道，世界上第一个制作ZIP Quine的人是<a href="https://infosec.exchange/@steike">Erling Ellingsen</a>，所以我向他发了个帖子<a href="https://infosec.exchange/@mayx/117385801954308033">展示了一下</a>。大佬不愧是大佬，一眼就能看出理论上能压缩的7z Quine是可以做出来的，对我来说，我是完全不知道7-Zip居然还支持链式编解码，能可以通过“double lzma2”的方式做到既可以自包含、又能真正被压缩的压缩包。既然大佬说理论上可以，那我自然是相信的，于是就让AI给我使劲折腾想办法把它做出来。在折腾了一晚上之后AI终于做出来了，真是强得可怕：</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ============================================================
# 双层 LZMA2 的 7z quine（"double lzma2"）
#
# F = S32 ‖ P ‖ H，folder 的 coder 链 = [LZMA2(外层), LZMA2(内层)]，
# 解码管线：P --外层--&gt; I --内层--&gt; U。
#
#   I = Cseg ‖ LBseg ‖ Mseg ‖ 0x00
#     Cseg : 真实 LZMA2 对 content 的压缩流（内容真正的压缩数据 C）
#     LBseg: 内层 store 链，载荷 = seed（seed 内含一份 C 的参照副本）
#     Mseg : 内层 match 链，输出 quine 区（= F 的逐字节副本）
#   U = content ‖ seed ‖ F
#
# 外层把 I 再压一遍：Cseg 用 store 链原样承载；seed 里的 C 参照副本与 Cseg
# 逐字节相同，被外层的 rep0 回溯匹配消掉，于是成品里只剩一份压缩数据。
# quine 区回引 P 的字节时，源全在 seed 里（C 副本 / 头样本表 / 机械串）。
# ============================================================
</span><span class="k">def</span> <span class="nf">lzma_chunk</span><span class="p">(</span><span class="n">tokens</span><span class="p">,</span> <span class="n">out_pos</span><span class="p">):</span>
    <span class="s">"""编一个 LZMA chunk（0xC0: state+props reset，无 dict reset，无 end marker）。
    match token 的编码字节只取决于 (dist,len,pos)，与历史内容无关（不维护输出历史）。
    解压长度用 21 位（控制字节低 5 位存高 5 位），故单 chunk 输出上限 2 MiB。"""</span>
    <span class="n">enc</span> <span class="o">=</span> <span class="n">LzmaEncoder</span><span class="p">()</span>
    <span class="n">enc</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">out_pos</span>
    <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">tokens</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">t</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"m"</span><span class="p">:</span>
            <span class="n">enc</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
            <span class="n">total</span> <span class="o">+=</span> <span class="n">t</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">enc</span><span class="p">.</span><span class="n">rep_match</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
            <span class="n">total</span> <span class="o">+=</span> <span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">enc</span><span class="p">.</span><span class="n">finish</span><span class="p">()</span>
    <span class="n">u</span> <span class="o">=</span> <span class="n">total</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">c</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="k">assert</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">u</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">21</span><span class="p">)</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">c</span> <span class="o">&lt;</span> <span class="mi">65536</span><span class="p">,</span> <span class="p">(</span><span class="n">total</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
    <span class="n">hdr</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xC0</span> <span class="o">|</span> <span class="p">((</span><span class="n">u</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x1F</span><span class="p">)])</span> <span class="o">+</span> <span class="p">(</span><span class="n">u</span> <span class="o">&amp;</span> <span class="mh">0xFFFF</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"big"</span><span class="p">)</span> <span class="o">+</span> \
          <span class="p">(</span><span class="n">c</span> <span class="o">&amp;</span> <span class="mh">0xFFFF</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"big"</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">PROPS_BYTE</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">hdr</span> <span class="o">+</span> <span class="n">data</span><span class="p">,</span> <span class="n">total</span>

<span class="n">LZ2_MAXU</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">21</span>                       <span class="c1"># LZMA2 单个 LZMA chunk 的解压上限
</span>

<span class="k">def</span> <span class="nf">lzma2_raw</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">dict_size</span><span class="p">,</span> <span class="n">preset</span><span class="o">=</span><span class="mi">6</span><span class="p">):</span>
    <span class="s">"""FORMAT_RAW + LZMA2 压缩；去掉结尾 0x00 便于后面继续追加 chunk。"""</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">lzma</span><span class="p">.</span><span class="n">LZMACompressor</span><span class="p">(</span>
        <span class="nb">format</span><span class="o">=</span><span class="n">lzma</span><span class="p">.</span><span class="n">FORMAT_RAW</span><span class="p">,</span>
        <span class="n">filters</span><span class="o">=</span><span class="p">[{</span><span class="s">"id"</span><span class="p">:</span> <span class="n">lzma</span><span class="p">.</span><span class="n">FILTER_LZMA2</span><span class="p">,</span> <span class="s">"dict_size"</span><span class="p">:</span> <span class="n">dict_size</span><span class="p">,</span> <span class="s">"preset"</span><span class="p">:</span> <span class="n">preset</span><span class="p">}])</span>
    <span class="n">out</span> <span class="o">=</span> <span class="n">c</span><span class="p">.</span><span class="n">compress</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">+</span> <span class="n">c</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>
    <span class="k">assert</span> <span class="n">out</span> <span class="ow">and</span> <span class="n">out</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x00</span><span class="p">,</span> <span class="s">"LZMA2 流应以 0x00 结尾"</span>
    <span class="k">return</span> <span class="n">out</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">split_len</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">cap</span><span class="p">):</span>
    <span class="s">"""按 cap 上限切分 n 字节。末片若为 1 字节则从前一片借 1（match 最短为 2）。"""</span>
    <span class="n">Ls</span><span class="p">,</span> <span class="n">rem</span> <span class="o">=</span> <span class="p">[],</span> <span class="n">n</span>
    <span class="k">while</span> <span class="n">rem</span> <span class="o">&gt;</span> <span class="n">cap</span><span class="p">:</span>
        <span class="n">Ls</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">cap</span><span class="p">)</span>
        <span class="n">rem</span> <span class="o">-=</span> <span class="n">cap</span>
    <span class="k">if</span> <span class="n">rem</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">Ls</span><span class="p">:</span>
        <span class="n">Ls</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-=</span> <span class="mi">1</span>
        <span class="n">rem</span> <span class="o">=</span> <span class="mi">2</span>
    <span class="n">Ls</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">rem</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">Ls</span>


<span class="k">class</span> <span class="nc">DoubleQuine</span><span class="p">(</span><span class="n">BlogQuine</span><span class="p">):</span>
    <span class="s">"""双层 LZMA2 版本：folder = [LZMA2(外层), LZMA2(内层)]。"""</span>

    <span class="k">def</span> <span class="nf">entries</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="s">"""U 的顺序是 内容文件… ‖ seed ‖ quine，条目顺序必须与之对应。"""</span>
        <span class="n">ent</span> <span class="o">=</span> <span class="p">[{</span><span class="s">"name"</span><span class="p">:</span> <span class="n">r</span><span class="p">,</span> <span class="s">"dir"</span><span class="p">:</span> <span class="n">isd</span><span class="p">}</span> <span class="k">for</span> <span class="n">r</span><span class="p">,</span> <span class="n">isd</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span><span class="p">]</span>
        <span class="n">ent</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">seed_name</span><span class="p">,</span> <span class="s">"dir"</span><span class="p">:</span> <span class="bp">False</span><span class="p">})</span>
        <span class="n">ent</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">quine_name</span><span class="p">,</span> <span class="s">"dir"</span><span class="p">:</span> <span class="bp">False</span><span class="p">})</span>
        <span class="k">return</span> <span class="n">ent</span>

    <span class="c1"># ---------- 头部：双 coder + BindPair ----------
</span>    <span class="k">def</span> <span class="nf">build_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">total</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span> <span class="n">sub_sizes</span><span class="p">,</span> <span class="n">size_I</span><span class="p">):</span>
        <span class="n">n_sub</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">sub_sizes</span><span class="p">)</span>
        <span class="n">N</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">entries</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x01\x04</span><span class="s">"</span>                                   <span class="c1"># kHeader, kMainStreamsInfo
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x06</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x09</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x07</span><span class="s">"</span>                                       <span class="c1"># kUnpackInfo
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x0b</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span>                 <span class="c1"># kFolder: 1, local
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="n">varint</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>                                     <span class="c1"># NumCoders = 2
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x21\x21</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="bp">self</span><span class="p">.</span><span class="n">dict_prop_out</span><span class="p">])</span>   <span class="c1"># coder0 = 外层
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x21\x21</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="bp">self</span><span class="p">.</span><span class="n">dict_prop_in</span><span class="p">])</span>    <span class="c1"># coder1 = 内层
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="n">varint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>             <span class="c1"># BindPair = (InIndex=1, OutIndex=0)
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x0c</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="n">size_I</span><span class="p">)</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="n">d</span> <span class="o">+</span> <span class="n">total</span><span class="p">)</span>  <span class="c1"># 两个 coder 的输出大小
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x08</span><span class="s">"</span>                                       <span class="c1"># SubStreamsInfo
</span>        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x0d</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="n">n_sub</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x09</span><span class="s">"</span> <span class="o">+</span> <span class="sa">b</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">varint</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">sub_sizes</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x0a</span><span class="s">"</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x01</span><span class="s">"</span>
        <span class="n">crc_base</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="p">(</span><span class="mi">4</span> <span class="o">*</span> <span class="n">n_sub</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00</span><span class="s">"</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x05</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="n">N</span><span class="p">)</span>                           <span class="c1"># FilesInfo
</span>        <span class="n">bits</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">((</span><span class="n">N</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">//</span> <span class="mi">8</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">e</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">entries</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">"dir"</span><span class="p">]:</span>
                <span class="n">bits</span><span class="p">[</span><span class="n">i</span> <span class="o">//</span> <span class="mi">8</span><span class="p">]</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">7</span> <span class="o">-</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">8</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x0e</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">bits</span><span class="p">))</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">bits</span><span class="p">)</span>
        <span class="n">mt</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x01\x00</span><span class="s">"</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"&lt;Q"</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">mtime_ft</span><span class="p">)</span> <span class="o">*</span> <span class="n">N</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x14</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">mt</span><span class="p">))</span> <span class="o">+</span> <span class="n">mt</span>
        <span class="n">names</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">:</span>
            <span class="n">names</span> <span class="o">+=</span> <span class="n">e</span><span class="p">[</span><span class="s">"name"</span><span class="p">].</span><span class="n">encode</span><span class="p">(</span><span class="s">"utf-16-le"</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00</span><span class="s">"</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x11</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">names</span><span class="p">))</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">names</span><span class="p">)</span>
        <span class="n">attrs</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"&lt;I"</span><span class="p">,</span> <span class="mh">0x10</span> <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">"dir"</span><span class="p">]</span> <span class="k">else</span> <span class="mh">0x20</span><span class="p">)</span>
                         <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">)</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x15</span><span class="s">"</span> <span class="o">+</span> <span class="n">varint</span><span class="p">(</span><span class="mi">2</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">attrs</span><span class="p">))</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x01\x00</span><span class="s">"</span> <span class="o">+</span> <span class="n">attrs</span>
        <span class="n">h</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00</span><span class="s">"</span>
        <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">h</span><span class="p">),</span> <span class="n">crc_base</span>

    <span class="c1"># ---------- 布局：三层坐标的不动点迭代 ----------
</span>    <span class="k">def</span> <span class="nf">layout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">content</span>
        <span class="n">dc</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
        <span class="n">dict_c</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="nb">max</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="n">dc</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()))</span>
        <span class="n">C</span> <span class="o">=</span> <span class="n">lzma2_raw</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">dict_c</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">_C</span> <span class="o">=</span> <span class="n">C</span>                         <span class="c1"># 内容压缩只做一次，finalize 复用
</span>        <span class="n">lc</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
        <span class="c1"># 迭代变量：Y = plant 长度, H = 头部长度, M = Mseg 字节数
</span>        <span class="n">Y</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span> <span class="o">=</span> <span class="mi">96</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mi">256</span>
        <span class="n">seen</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">200</span><span class="p">):</span>
            <span class="n">ds</span> <span class="o">=</span> <span class="n">lc</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span> <span class="o">+</span> <span class="n">Y</span>            <span class="c1"># seed = C副本‖S32‖H‖plant
</span>            <span class="n">d</span> <span class="o">=</span> <span class="n">dc</span> <span class="o">+</span> <span class="n">ds</span>
            <span class="n">Ls_LB</span> <span class="o">=</span> <span class="n">split_len</span><span class="p">(</span><span class="n">ds</span><span class="p">,</span> <span class="n">CHUNK</span><span class="p">)</span>
            <span class="n">lLB</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">Ls_LB</span><span class="p">)</span> <span class="o">+</span> <span class="n">ds</span>
            <span class="n">off_in</span> <span class="o">=</span> <span class="n">lc</span> <span class="o">+</span> <span class="n">lLB</span>
            <span class="n">M1</span> <span class="o">=</span> <span class="n">M</span> <span class="o">+</span> <span class="mi">1</span>                      <span class="c1"># Mseg ‖ 内层流终止符 0x00
</span>            <span class="n">Ls_M</span> <span class="o">=</span> <span class="n">split_len</span><span class="p">(</span><span class="n">M1</span><span class="p">,</span> <span class="n">CHUNK</span><span class="p">)</span>
            <span class="n">Li</span> <span class="o">=</span> <span class="n">off_in</span> <span class="o">+</span> <span class="n">M1</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">dict_prop_out</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">dict_prop_for</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">Li</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">))</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">dict_prop_in</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">dict_prop_for</span><span class="p">(</span><span class="n">d</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span> <span class="o">+</span> <span class="mi">8192</span><span class="p">)</span>
            <span class="n">st</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_build_streams</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">ds</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">Ls_LB</span><span class="p">,</span> <span class="n">lLB</span><span class="p">,</span> <span class="n">off_in</span><span class="p">,</span> <span class="n">Ls_M</span><span class="p">,</span>
                                     <span class="n">M1</span><span class="p">,</span> <span class="n">H</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">st</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="n">Y</span> <span class="o">+=</span> <span class="mi">8</span>
                <span class="k">continue</span>
            <span class="n">mseg</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_build_mseg</span><span class="p">(</span><span class="n">st</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">ds</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">mseg</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="n">Y</span> <span class="o">+=</span> <span class="mi">8</span>
                <span class="k">continue</span>
            <span class="n">M_new</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">mseg</span><span class="p">)</span>
            <span class="n">Y_new</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"hdr_bytes"</span><span class="p">])</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"mech"</span><span class="p">])</span> <span class="o">+</span> <span class="n">M_new</span> <span class="o">+</span> <span class="mi">2</span>
            <span class="n">total</span> <span class="o">=</span> <span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span>
            <span class="n">file_sizes</span> <span class="o">=</span> <span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">]</span>
            <span class="n">sub_sizes</span> <span class="o">=</span> <span class="n">file_sizes</span> <span class="o">+</span> <span class="p">[</span><span class="n">ds</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">total</span><span class="p">]</span>
            <span class="n">entries</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">entries</span><span class="p">()</span>
            <span class="n">hdr</span><span class="p">,</span> <span class="n">crc_base</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">build_header</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">],</span> <span class="n">total</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span>
                                              <span class="n">sub_sizes</span><span class="p">,</span> <span class="n">Li</span><span class="p">)</span>
            <span class="n">H_new</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">hdr</span><span class="p">)</span>
            <span class="n">key</span> <span class="o">=</span> <span class="p">(</span><span class="n">Y_new</span><span class="p">,</span> <span class="n">H_new</span><span class="p">,</span> <span class="n">M_new</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">seen</span><span class="p">:</span>                 <span class="c1"># 再次见到同一组值 = 已收敛
</span>                <span class="n">Y</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span> <span class="o">=</span> <span class="n">Y_new</span><span class="p">,</span> <span class="n">H_new</span><span class="p">,</span> <span class="n">M_new</span>
                <span class="k">break</span>
            <span class="n">seen</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
            <span class="n">Y</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span> <span class="o">=</span> <span class="n">Y_new</span><span class="p">,</span> <span class="n">H_new</span><span class="p">,</span> <span class="n">M_new</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"double layout 不收敛"</span><span class="p">)</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">"Y"</span><span class="p">:</span> <span class="n">Y</span><span class="p">,</span> <span class="s">"H"</span><span class="p">:</span> <span class="n">H</span><span class="p">,</span> <span class="s">"M"</span><span class="p">:</span> <span class="n">M</span><span class="p">}</span>

    <span class="c1"># ---------- 定稿：用收敛后的 (Y, H, M) 重算全部结构 ----------
</span>    <span class="k">def</span> <span class="nf">finalize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">Y</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span><span class="p">):</span>
        <span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">content</span>
        <span class="n">dc</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
        <span class="n">C</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_C</span>                         <span class="c1"># layout 里已压缩过
</span>        <span class="n">lc</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
        <span class="n">ds</span> <span class="o">=</span> <span class="n">lc</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span> <span class="o">+</span> <span class="n">Y</span>
        <span class="n">d</span> <span class="o">=</span> <span class="n">dc</span> <span class="o">+</span> <span class="n">ds</span>
        <span class="n">Ls_LB</span> <span class="o">=</span> <span class="n">split_len</span><span class="p">(</span><span class="n">ds</span><span class="p">,</span> <span class="n">CHUNK</span><span class="p">)</span>
        <span class="n">lLB</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">Ls_LB</span><span class="p">)</span> <span class="o">+</span> <span class="n">ds</span>
        <span class="n">off_in</span> <span class="o">=</span> <span class="n">lc</span> <span class="o">+</span> <span class="n">lLB</span>
        <span class="n">M1</span> <span class="o">=</span> <span class="n">M</span> <span class="o">+</span> <span class="mi">1</span>
        <span class="n">Ls_M</span> <span class="o">=</span> <span class="n">split_len</span><span class="p">(</span><span class="n">M1</span><span class="p">,</span> <span class="n">CHUNK</span><span class="p">)</span>
        <span class="n">Li</span> <span class="o">=</span> <span class="n">off_in</span> <span class="o">+</span> <span class="n">M1</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dict_prop_out</span><span class="p">,</span> <span class="n">ds_out</span> <span class="o">=</span> <span class="n">dict_prop_for</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">Li</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">))</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dict_prop_in</span><span class="p">,</span> <span class="n">ds_in</span> <span class="o">=</span> <span class="n">dict_prop_for</span><span class="p">(</span><span class="n">d</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span> <span class="o">+</span> <span class="mi">8192</span><span class="p">)</span>
        <span class="n">st</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_build_streams</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">ds</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">Ls_LB</span><span class="p">,</span> <span class="n">lLB</span><span class="p">,</span> <span class="n">off_in</span><span class="p">,</span> <span class="n">Ls_M</span><span class="p">,</span> <span class="n">M1</span><span class="p">,</span> <span class="n">H</span><span class="p">)</span>
        <span class="n">mseg</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_build_mseg</span><span class="p">(</span><span class="n">st</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">ds</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span><span class="p">)</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">mseg</span><span class="p">)</span> <span class="o">==</span> <span class="n">M</span><span class="p">,</span> <span class="s">"Mseg %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">mseg</span><span class="p">),</span> <span class="n">M</span><span class="p">)</span>
        <span class="n">total</span> <span class="o">=</span> <span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span>
        <span class="n">file_sizes</span> <span class="o">=</span> <span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">]</span>
        <span class="n">sub_sizes</span> <span class="o">=</span> <span class="n">file_sizes</span> <span class="o">+</span> <span class="p">[</span><span class="n">ds</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">total</span><span class="p">]</span>
        <span class="n">entries</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">entries</span><span class="p">()</span>
        <span class="n">hdr</span><span class="p">,</span> <span class="n">crc_base</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">build_header</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">],</span> <span class="n">total</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span>
                                          <span class="n">sub_sizes</span><span class="p">,</span> <span class="n">Li</span><span class="p">)</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">hdr</span><span class="p">)</span> <span class="o">==</span> <span class="n">H</span><span class="p">,</span> <span class="s">"header %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">hdr</span><span class="p">),</span> <span class="n">H</span><span class="p">)</span>
        <span class="n">plant</span> <span class="o">=</span> <span class="n">st</span><span class="p">[</span><span class="s">"hdr_bytes"</span><span class="p">]</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"mech"</span><span class="p">])</span> <span class="o">+</span> <span class="n">mseg</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00</span><span class="s">"</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">plant</span><span class="p">)</span> <span class="o">==</span> <span class="n">Y</span><span class="p">,</span> <span class="s">"plant %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">plant</span><span class="p">),</span> <span class="n">Y</span><span class="p">)</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">"C"</span><span class="p">:</span> <span class="n">C</span><span class="p">,</span> <span class="s">"lc"</span><span class="p">:</span> <span class="n">lc</span><span class="p">,</span> <span class="s">"dc"</span><span class="p">:</span> <span class="n">dc</span><span class="p">,</span> <span class="s">"ds"</span><span class="p">:</span> <span class="n">ds</span><span class="p">,</span> <span class="s">"d"</span><span class="p">:</span> <span class="n">d</span><span class="p">,</span> <span class="s">"Li"</span><span class="p">:</span> <span class="n">Li</span><span class="p">,</span>
                <span class="s">"ochunks"</span><span class="p">:</span> <span class="n">st</span><span class="p">[</span><span class="s">"ochunks"</span><span class="p">],</span> <span class="s">"hdr_tab"</span><span class="p">:</span> <span class="n">st</span><span class="p">[</span><span class="s">"hdr_tab"</span><span class="p">],</span>
                <span class="s">"mseg"</span><span class="p">:</span> <span class="n">mseg</span><span class="p">,</span> <span class="s">"M"</span><span class="p">:</span> <span class="n">M</span><span class="p">,</span> <span class="s">"H"</span><span class="p">:</span> <span class="n">H</span><span class="p">,</span> <span class="s">"n"</span><span class="p">:</span> <span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">],</span> <span class="s">"total"</span><span class="p">:</span> <span class="n">total</span><span class="p">,</span>
                <span class="s">"header"</span><span class="p">:</span> <span class="n">hdr</span><span class="p">,</span> <span class="s">"crc_base"</span><span class="p">:</span> <span class="n">crc_base</span><span class="p">,</span> <span class="s">"plant"</span><span class="p">:</span> <span class="n">plant</span><span class="p">,</span>
                <span class="s">"dict_size_in"</span><span class="p">:</span> <span class="n">ds_in</span><span class="p">,</span> <span class="s">"dict_size_out"</span><span class="p">:</span> <span class="n">ds_out</span><span class="p">,</span>
                <span class="s">"n_sub"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">sub_sizes</span><span class="p">)}</span>

    <span class="c1"># ---------- 外层流 P 的构造 ----------
</span>    <span class="k">def</span> <span class="nf">_build_streams</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">ds</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">Ls_LB</span><span class="p">,</span> <span class="n">lLB</span><span class="p">,</span> <span class="n">off_in</span><span class="p">,</span> <span class="n">Ls_M</span><span class="p">,</span> <span class="n">M1</span><span class="p">,</span> <span class="n">H</span><span class="p">):</span>
        <span class="n">hdr_tab</span><span class="p">,</span> <span class="n">hdr_idx</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">{}</span>

        <span class="k">def</span> <span class="nf">hidx</span><span class="p">(</span><span class="n">b</span><span class="p">):</span>
            <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span> <span class="ow">and</span> <span class="n">b</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="p">(</span><span class="mh">0x01</span><span class="p">,</span> <span class="mh">0x02</span><span class="p">),</span> <span class="n">b</span><span class="p">.</span><span class="nb">hex</span><span class="p">()</span>
            <span class="k">if</span> <span class="n">b</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">hdr_idx</span><span class="p">:</span>
                <span class="n">hdr_idx</span><span class="p">[</span><span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">hdr_tab</span><span class="p">)</span>
                <span class="n">hdr_tab</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">hdr_idx</span><span class="p">[</span><span class="n">b</span><span class="p">]</span>

        <span class="n">ochunks</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">f</span><span class="p">,</span> <span class="n">io</span> <span class="o">=</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">0</span>
        <span class="n">mech</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>

        <span class="k">def</span> <span class="nf">put_lzma</span><span class="p">(</span><span class="n">tokens</span><span class="p">,</span> <span class="n">ioff</span><span class="p">):</span>
            <span class="n">blk</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">(</span><span class="n">tokens</span><span class="p">,</span> <span class="n">ioff</span><span class="p">)</span>
            <span class="n">ochunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"lzma"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">blk</span><span class="p">),</span>
                            <span class="s">"ioff"</span><span class="p">:</span> <span class="n">ioff</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">t</span><span class="p">,</span> <span class="s">"bytes"</span><span class="p">:</span> <span class="n">blk</span><span class="p">,</span>
                            <span class="s">"mech"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">mech</span><span class="p">)})</span>
            <span class="n">mech</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">blk</span><span class="p">)</span>
            <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">blk</span><span class="p">),</span> <span class="n">t</span>

        <span class="k">def</span> <span class="nf">put_store</span><span class="p">(</span><span class="n">ioff</span><span class="p">,</span> <span class="n">olen</span><span class="p">,</span> <span class="n">hi</span><span class="p">,</span> <span class="n">src</span><span class="p">):</span>
            <span class="n">ochunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"store"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">olen</span><span class="p">,</span>
                            <span class="s">"ioff"</span><span class="p">:</span> <span class="n">ioff</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">olen</span><span class="p">,</span> <span class="s">"hdr"</span><span class="p">:</span> <span class="n">hi</span><span class="p">,</span> <span class="s">"src"</span><span class="p">:</span> <span class="n">src</span><span class="p">})</span>
            <span class="k">return</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">olen</span>

        <span class="c1"># 段 1：store 链承载 Cseg（I[0:lc] = C 的字节；源 = seed 的 C 参照副本）
</span>        <span class="n">Ls_C</span> <span class="o">=</span> <span class="n">split_len</span><span class="p">(</span><span class="n">lc</span><span class="p">,</span> <span class="n">CHUNK</span><span class="p">)</span>
        <span class="n">c_off</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">L</span> <span class="ow">in</span> <span class="n">Ls_C</span><span class="p">:</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="n">put_store</span><span class="p">(</span><span class="n">io</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">hidx</span><span class="p">(</span><span class="n">store_hdr</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="p">(</span><span class="n">c_off</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))),</span>
                           <span class="p">(</span><span class="s">"seed"</span><span class="p">,</span> <span class="n">c_off</span><span class="p">))</span>
            <span class="n">io</span> <span class="o">+=</span> <span class="n">L</span>
            <span class="n">c_off</span> <span class="o">+=</span> <span class="n">L</span>
        <span class="k">assert</span> <span class="n">io</span> <span class="o">==</span> <span class="n">lc</span>

        <span class="c1"># 段 2：LBseg（I[lc : lc+lLB]）—— 内层 store 链，载荷 = seed
</span>        <span class="n">lb_off</span> <span class="o">=</span> <span class="n">lc</span>
        <span class="n">seed_off</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">L</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">Ls_LB</span><span class="p">):</span>
            <span class="n">lb_hdr</span> <span class="o">=</span> <span class="n">store_hdr</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">j</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">L</span> <span class="o">!=</span> <span class="n">CHUNK</span><span class="p">:</span>
                <span class="c1"># 没有可回引的相同样本，用外层 store chunk 原样携带这 3 字节
</span>                <span class="n">f</span> <span class="o">+=</span> <span class="n">put_store</span><span class="p">(</span><span class="n">lb_off</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">hidx</span><span class="p">(</span><span class="n">store_hdr</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">)),</span>
                               <span class="p">(</span><span class="s">"tab"</span><span class="p">,</span> <span class="n">hidx</span><span class="p">(</span><span class="n">lb_hdr</span><span class="p">)))</span>
                <span class="n">io</span> <span class="o">+=</span> <span class="mi">3</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="c1"># 与 chunk0 的头逐字节相同，直接从 I[lc:lc+3] 复制
</span>                <span class="n">n</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">put_lzma</span><span class="p">([(</span><span class="s">"m"</span><span class="p">,</span> <span class="n">lb_off</span> <span class="o">-</span> <span class="n">lc</span><span class="p">,</span> <span class="mi">3</span><span class="p">)],</span> <span class="n">lb_off</span><span class="p">)</span>
                <span class="k">assert</span> <span class="n">t</span> <span class="o">==</span> <span class="mi">3</span>
                <span class="n">f</span> <span class="o">+=</span> <span class="n">n</span>
                <span class="n">io</span> <span class="o">+=</span> <span class="mi">3</span>
            <span class="c1"># 载荷：C 参照副本区走 rep0 回溯匹配（去重发生处），其余走 store
</span>            <span class="n">pay</span> <span class="o">=</span> <span class="n">lb_off</span> <span class="o">+</span> <span class="mi">3</span>
            <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
            <span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">L</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">seed_off</span> <span class="o">+</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">lc</span><span class="p">:</span>
                    <span class="n">a</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">L</span> <span class="o">-</span> <span class="n">i</span><span class="p">,</span> <span class="n">lc</span> <span class="o">-</span> <span class="p">(</span><span class="n">seed_off</span> <span class="o">+</span> <span class="n">i</span><span class="p">),</span> <span class="n">LZ2_MAXU</span><span class="p">)</span>
                    <span class="n">dist</span> <span class="o">=</span> <span class="p">(</span><span class="n">pay</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">seed_off</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span>
                    <span class="n">n</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">put_lzma</span><span class="p">(</span><span class="n">matches_for</span><span class="p">(</span><span class="n">dist</span><span class="p">,</span> <span class="n">a</span><span class="p">),</span> <span class="n">pay</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span>
                    <span class="k">assert</span> <span class="n">t</span> <span class="o">==</span> <span class="n">a</span>
                    <span class="n">f</span> <span class="o">+=</span> <span class="n">n</span>
                    <span class="n">io</span> <span class="o">+=</span> <span class="n">a</span>
                    <span class="n">i</span> <span class="o">+=</span> <span class="n">a</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">a</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">L</span> <span class="o">-</span> <span class="n">i</span><span class="p">,</span> <span class="n">LZ2_MAXU</span><span class="p">)</span>
                    <span class="n">f</span> <span class="o">+=</span> <span class="n">put_store</span><span class="p">(</span><span class="n">pay</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">hidx</span><span class="p">(</span><span class="n">store_hdr</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">)),</span>
                                   <span class="p">(</span><span class="s">"seed"</span><span class="p">,</span> <span class="n">seed_off</span> <span class="o">+</span> <span class="n">i</span><span class="p">))</span>
                    <span class="n">io</span> <span class="o">+=</span> <span class="n">a</span>
                    <span class="n">i</span> <span class="o">+=</span> <span class="n">a</span>
            <span class="n">lb_off</span> <span class="o">+=</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">L</span>
            <span class="n">seed_off</span> <span class="o">+=</span> <span class="n">L</span>
        <span class="k">assert</span> <span class="n">lb_off</span> <span class="o">==</span> <span class="n">lc</span> <span class="o">+</span> <span class="n">lLB</span> <span class="ow">and</span> <span class="n">io</span> <span class="o">==</span> <span class="n">lc</span> <span class="o">+</span> <span class="n">lLB</span>

        <span class="c1"># 段 3：store 链承载 Mseg ‖ 0x00（0x00 是内层流的终止符）
</span>        <span class="n">m_off</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">L</span> <span class="ow">in</span> <span class="n">Ls_M</span><span class="p">:</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="n">put_store</span><span class="p">(</span><span class="n">off_in</span> <span class="o">+</span> <span class="n">m_off</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span>
                           <span class="n">hidx</span><span class="p">(</span><span class="n">store_hdr</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">)),</span> <span class="p">(</span><span class="s">"mseg"</span><span class="p">,</span> <span class="n">m_off</span><span class="p">))</span>
            <span class="n">io</span> <span class="o">+=</span> <span class="n">L</span>
            <span class="n">m_off</span> <span class="o">+=</span> <span class="n">L</span>
        <span class="c1"># 外层流自己的终止符 0x00
</span>        <span class="n">ochunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"term"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"ioff"</span><span class="p">:</span> <span class="n">io</span><span class="p">,</span>
                        <span class="s">"olen"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"bytes"</span><span class="p">:</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">})</span>
        <span class="n">f</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">"ochunks"</span><span class="p">:</span> <span class="n">ochunks</span><span class="p">,</span> <span class="s">"mech"</span><span class="p">:</span> <span class="n">mech</span><span class="p">,</span> <span class="s">"hdr_tab"</span><span class="p">:</span> <span class="n">hdr_tab</span><span class="p">,</span>
                <span class="s">"hdr_bytes"</span><span class="p">:</span> <span class="sa">b</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">hdr_tab</span><span class="p">),</span> <span class="s">"n"</span><span class="p">:</span> <span class="n">f</span> <span class="o">-</span> <span class="mi">32</span><span class="p">}</span>

    <span class="c1"># ---------- 内层 Mseg：把 quine 区的每个 F 区段映射成 match 链 ----------
</span>    <span class="k">def</span> <span class="nf">_build_mseg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">st</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="n">ds</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">M</span><span class="p">):</span>
        <span class="n">dc</span> <span class="o">=</span> <span class="n">d</span> <span class="o">-</span> <span class="n">ds</span>
        <span class="n">s_S32</span> <span class="o">=</span> <span class="n">dc</span> <span class="o">+</span> <span class="n">lc</span>                                  <span class="c1"># seed 里 S32 副本
</span>        <span class="n">s_H</span> <span class="o">=</span> <span class="n">dc</span> <span class="o">+</span> <span class="n">lc</span> <span class="o">+</span> <span class="mi">32</span>                               <span class="c1"># seed 里 H 副本
</span>        <span class="n">s_tab</span> <span class="o">=</span> <span class="n">dc</span> <span class="o">+</span> <span class="n">lc</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span>                         <span class="c1"># seed 里头样本表
</span>        <span class="n">s_mech</span> <span class="o">=</span> <span class="n">s_tab</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"hdr_bytes"</span><span class="p">])</span>            <span class="c1"># seed 里机械串
</span>        <span class="n">s_mseg</span> <span class="o">=</span> <span class="n">s_mech</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">st</span><span class="p">[</span><span class="s">"mech"</span><span class="p">])</span>                <span class="c1"># seed 里 Mseg 副本
</span>        <span class="n">total</span> <span class="o">=</span> <span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">H</span>
        <span class="n">hoff</span> <span class="o">=</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">st</span><span class="p">[</span><span class="s">"n"</span><span class="p">]</span>
        <span class="n">segs</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="n">s_S32</span><span class="p">)]</span>                          <span class="c1"># F[0:32) 签名头
</span>        <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">st</span><span class="p">[</span><span class="s">"ochunks"</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">==</span> <span class="s">"store"</span><span class="p">:</span>
                <span class="n">segs</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">],</span> <span class="mi">3</span><span class="p">,</span> <span class="n">s_tab</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">c</span><span class="p">[</span><span class="s">"hdr"</span><span class="p">]))</span>
                <span class="n">src</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"src"</span><span class="p">]</span>
                <span class="k">if</span> <span class="n">src</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"seed"</span><span class="p">:</span>
                    <span class="n">u</span> <span class="o">=</span> <span class="n">dc</span> <span class="o">+</span> <span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
                <span class="k">elif</span> <span class="n">src</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"tab"</span><span class="p">:</span>
                    <span class="n">u</span> <span class="o">=</span> <span class="n">s_tab</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
                <span class="k">else</span><span class="p">:</span>                                    <span class="c1"># ("mseg", off)
</span>                    <span class="n">u</span> <span class="o">=</span> <span class="n">s_mseg</span> <span class="o">+</span> <span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
                <span class="n">segs</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span><span class="p">,</span> <span class="n">c</span><span class="p">[</span><span class="s">"olen"</span><span class="p">],</span> <span class="n">u</span><span class="p">))</span>
            <span class="k">elif</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">==</span> <span class="s">"lzma"</span><span class="p">:</span>
                <span class="n">segs</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">],</span> <span class="n">c</span><span class="p">[</span><span class="s">"size"</span><span class="p">],</span> <span class="n">s_mech</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="s">"mech"</span><span class="p">]))</span>
            <span class="k">else</span><span class="p">:</span>                                        <span class="c1"># term：并入前一个载荷段
</span>                <span class="n">pf</span><span class="p">,</span> <span class="n">pl</span><span class="p">,</span> <span class="n">pu</span> <span class="o">=</span> <span class="n">segs</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
                <span class="n">segs</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">pf</span><span class="p">,</span> <span class="n">pl</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">pu</span><span class="p">)</span>
        <span class="n">segs</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">hoff</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">s_H</span><span class="p">))</span>                      <span class="c1"># F[hoff:] 头部
</span>        <span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">fo</span><span class="p">,</span> <span class="n">ln</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span> <span class="ow">in</span> <span class="n">segs</span><span class="p">:</span>
            <span class="k">assert</span> <span class="n">fo</span> <span class="o">==</span> <span class="n">pos</span><span class="p">,</span> <span class="s">"段不连续: %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">fo</span><span class="p">,</span> <span class="n">pos</span><span class="p">)</span>
            <span class="k">assert</span> <span class="n">ln</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">,</span> <span class="s">"段太短 %d @%d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">ln</span><span class="p">,</span> <span class="n">fo</span><span class="p">)</span>
            <span class="n">pos</span> <span class="o">+=</span> <span class="n">ln</span>
        <span class="k">assert</span> <span class="n">pos</span> <span class="o">==</span> <span class="n">total</span><span class="p">,</span> <span class="s">"段总长 %d != total %d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span>
        <span class="c1"># 生成 match 链，按 2 MiB 上限切成多个 0xC0 chunk
</span>        <span class="n">mseg</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
        <span class="n">out_pos</span> <span class="o">=</span> <span class="n">d</span>
        <span class="n">cur</span><span class="p">,</span> <span class="n">cur_len</span> <span class="o">=</span> <span class="p">[],</span> <span class="mi">0</span>
        <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">fo</span><span class="p">,</span> <span class="n">ln</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span> <span class="ow">in</span> <span class="n">segs</span><span class="p">:</span>
            <span class="n">dist</span> <span class="o">=</span> <span class="p">(</span><span class="n">d</span> <span class="o">+</span> <span class="n">fo</span><span class="p">)</span> <span class="o">-</span> <span class="n">u</span>
            <span class="k">assert</span> <span class="mi">0</span> <span class="o">&lt;</span> <span class="n">dist</span><span class="p">,</span> <span class="s">"dist=%d @%d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">dist</span><span class="p">,</span> <span class="n">fo</span><span class="p">)</span>
            <span class="n">toks</span> <span class="o">=</span> <span class="n">matches_for</span><span class="p">(</span><span class="n">dist</span><span class="p">,</span> <span class="n">ln</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">cur_len</span> <span class="o">+</span> <span class="n">ln</span> <span class="o">&gt;</span> <span class="n">LZ2_MAXU</span> <span class="o">-</span> <span class="n">MAX_MATCH</span><span class="p">:</span>
                <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">cur</span><span class="p">,</span> <span class="n">out_pos</span><span class="p">,</span> <span class="n">cur_len</span><span class="p">))</span>
                <span class="n">out_pos</span> <span class="o">+=</span> <span class="n">cur_len</span>
                <span class="n">cur</span><span class="p">,</span> <span class="n">cur_len</span> <span class="o">=</span> <span class="p">[],</span> <span class="mi">0</span>
            <span class="n">cur</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">toks</span><span class="p">)</span>
            <span class="n">cur_len</span> <span class="o">+=</span> <span class="n">ln</span>
        <span class="k">if</span> <span class="n">cur</span><span class="p">:</span>
            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">cur</span><span class="p">,</span> <span class="n">out_pos</span><span class="p">,</span> <span class="n">cur_len</span><span class="p">))</span>
            <span class="n">out_pos</span> <span class="o">+=</span> <span class="n">cur_len</span>
        <span class="k">assert</span> <span class="n">out_pos</span> <span class="o">==</span> <span class="n">d</span> <span class="o">+</span> <span class="n">total</span><span class="p">,</span> <span class="s">"Mseg 输出 %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">out_pos</span><span class="p">,</span> <span class="n">d</span> <span class="o">+</span> <span class="n">total</span><span class="p">)</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">toks</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">cl</span><span class="p">)</span> <span class="ow">in</span> <span class="n">chunks</span><span class="p">:</span>
            <span class="n">blk</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">(</span><span class="n">toks</span><span class="p">,</span> <span class="n">op</span><span class="p">)</span>
            <span class="k">assert</span> <span class="n">t</span> <span class="o">==</span> <span class="n">cl</span>
            <span class="n">mseg</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">blk</span><span class="p">)</span>
        <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">mseg</span><span class="p">)</span>

    <span class="c1"># ---------- F 中承载 seed[soff] 的那个字节的偏移 ----------
</span>    <span class="k">def</span> <span class="nf">_f_off_of_seed</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">soff</span><span class="p">):</span>
        <span class="s">"""返回 (F 偏移, 该 store 载荷内从 soff 起还剩多少字节)。
        seed_head 超过 64 KiB 时会被切成多片，所以调用方要循环推进。"""</span>
        <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ochunks"</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">==</span> <span class="s">"store"</span> <span class="ow">and</span> <span class="n">c</span><span class="p">[</span><span class="s">"src"</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"seed"</span><span class="p">:</span>
                <span class="n">s0</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"src"</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
                <span class="k">if</span> <span class="n">s0</span> <span class="o">&lt;=</span> <span class="n">soff</span> <span class="o">&lt;</span> <span class="n">s0</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="s">"olen"</span><span class="p">]:</span>
                    <span class="k">return</span> <span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="p">(</span><span class="n">soff</span> <span class="o">-</span> <span class="n">s0</span><span class="p">),</span> <span class="n">s0</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="s">"olen"</span><span class="p">]</span> <span class="o">-</span> <span class="n">soff</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"seed 偏移 %d 未被 store 载荷覆盖"</span> <span class="o">%</span> <span class="n">soff</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">_f_pos_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">soff</span><span class="p">,</span> <span class="n">length</span><span class="p">):</span>
        <span class="s">"""seed[soff:soff+length] 在 F 中对应的字节位置列表（可能跨多片）。"""</span>
        <span class="n">res</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">while</span> <span class="n">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">p</span><span class="p">,</span> <span class="n">avail</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_f_off_of_seed</span><span class="p">(</span><span class="n">lay</span><span class="p">,</span> <span class="n">soff</span><span class="p">)</span>
            <span class="n">a</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">length</span><span class="p">,</span> <span class="n">avail</span><span class="p">)</span>
            <span class="n">res</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">p</span> <span class="o">+</span> <span class="n">a</span><span class="p">))</span>
            <span class="n">soff</span> <span class="o">+=</span> <span class="n">a</span>
            <span class="n">length</span> <span class="o">-=</span> <span class="n">a</span>
        <span class="k">return</span> <span class="n">res</span>

    <span class="c1"># ---------- 装配 ----------
</span>    <span class="k">def</span> <span class="nf">assemble</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lay</span><span class="p">):</span>
        <span class="n">total</span><span class="p">,</span> <span class="n">H</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"total"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"H"</span><span class="p">]</span>
        <span class="n">hoff</span> <span class="o">=</span> <span class="n">total</span> <span class="o">-</span> <span class="n">H</span>
        <span class="n">C</span><span class="p">,</span> <span class="n">plant</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"plant"</span><span class="p">]</span>
        <span class="n">F</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">total</span><span class="p">)</span>
        <span class="c1"># pass A: 签名头 + 所有非 store 载荷字节
</span>        <span class="n">F</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="n">SIG</span>
        <span class="n">F</span><span class="p">[</span><span class="mi">6</span><span class="p">:</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="n">VER</span>
        <span class="n">struct</span><span class="p">.</span><span class="n">pack_into</span><span class="p">(</span><span class="s">"&lt;Q"</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n"</span><span class="p">])</span>
        <span class="n">struct</span><span class="p">.</span><span class="n">pack_into</span><span class="p">(</span><span class="s">"&lt;Q"</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">H</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ochunks"</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">==</span> <span class="s">"store"</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]:</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"hdr_tab"</span><span class="p">][</span><span class="n">c</span><span class="p">[</span><span class="s">"hdr"</span><span class="p">]]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]:</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="s">"size"</span><span class="p">]]</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"bytes"</span><span class="p">]</span>
        <span class="n">F</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">H</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"header"</span><span class="p">]</span>
        <span class="c1"># pass B: seed = C副本 ‖ S32副本 ‖ H副本 ‖ plant
</span>        <span class="n">seed</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
        <span class="n">seed</span> <span class="o">+=</span> <span class="n">C</span>
        <span class="n">seed</span> <span class="o">+=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">32</span><span class="p">])</span>
        <span class="n">seed</span> <span class="o">+=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">H</span><span class="p">])</span>
        <span class="n">seed</span> <span class="o">+=</span> <span class="n">plant</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span> <span class="o">==</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ds"</span><span class="p">],</span> <span class="s">"seed %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">seed</span><span class="p">),</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ds"</span><span class="p">])</span>
        <span class="c1"># pass C: 各 store 的载荷
</span>        <span class="n">msegz</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"mseg"</span><span class="p">]</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span>
        <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ochunks"</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">!=</span> <span class="s">"store"</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">p</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span>
            <span class="n">src</span><span class="p">,</span> <span class="n">L</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"src"</span><span class="p">],</span> <span class="n">c</span><span class="p">[</span><span class="s">"olen"</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">src</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"seed"</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">p</span><span class="p">:</span><span class="n">p</span> <span class="o">+</span> <span class="n">L</span><span class="p">]</span> <span class="o">=</span> <span class="n">seed</span><span class="p">[</span><span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span><span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">L</span><span class="p">]</span>
            <span class="k">elif</span> <span class="n">src</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"tab"</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">p</span><span class="p">:</span><span class="n">p</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"hdr_tab"</span><span class="p">][</span><span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">p</span><span class="p">:</span><span class="n">p</span> <span class="o">+</span> <span class="n">L</span><span class="p">]</span> <span class="o">=</span> <span class="n">msegz</span><span class="p">[</span><span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span><span class="n">src</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">L</span><span class="p">]</span>
        <span class="k">return</span> <span class="n">F</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>

    <span class="c1"># ---------- CRC 定点（128x128 GF(2)：seed/quine/Next/Start 四个 CRC） ----------
</span>    <span class="k">def</span> <span class="nf">solve_crc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">plant</span><span class="p">):</span>
        <span class="n">H</span><span class="p">,</span> <span class="n">hoff</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"H"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"total"</span><span class="p">]</span> <span class="o">-</span> <span class="n">lay</span><span class="p">[</span><span class="s">"H"</span><span class="p">]</span>
        <span class="n">n_sub</span><span class="p">,</span> <span class="n">crc_base</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n_sub"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"crc_base"</span><span class="p">]</span>
        <span class="n">lc</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"lc"</span><span class="p">]</span>
        <span class="c1"># seed 里 S32 / H 副本在 F 中的镜像（可能跨多片，逐字节列出）
</span>        <span class="n">scopy0</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_f_pos_list</span><span class="p">(</span><span class="n">lay</span><span class="p">,</span> <span class="n">lc</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span>
        <span class="n">Fv</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">F</span><span class="p">)</span>

        <span class="k">def</span> <span class="nf">seed_crc</span><span class="p">():</span>
            <span class="n">v</span> <span class="o">=</span> <span class="n">crc32</span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
            <span class="n">v</span> <span class="o">=</span> <span class="n">crc32</span><span class="p">(</span><span class="n">Fv</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">32</span><span class="p">],</span> <span class="n">v</span><span class="p">)</span>                    <span class="c1"># S32 副本
</span>            <span class="n">v</span> <span class="o">=</span> <span class="n">crc32</span><span class="p">(</span><span class="n">Fv</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">H</span><span class="p">],</span> <span class="n">v</span><span class="p">)</span>           <span class="c1"># H 副本
</span>            <span class="k">return</span> <span class="n">crc32</span><span class="p">(</span><span class="n">plant</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>

        <span class="k">def</span> <span class="nf">targets</span><span class="p">():</span>
            <span class="k">return</span> <span class="p">(</span><span class="n">seed_crc</span><span class="p">(),</span> <span class="n">crc32</span><span class="p">(</span><span class="n">Fv</span><span class="p">),</span> <span class="n">crc32</span><span class="p">(</span><span class="n">Fv</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">H</span><span class="p">]),</span>
                    <span class="n">crc32</span><span class="p">(</span><span class="n">Fv</span><span class="p">[</span><span class="mi">12</span><span class="p">:</span><span class="mi">32</span><span class="p">]))</span>

        <span class="c1"># 已知：各内容文件的 CRC，写本体 + seed 里 H 副本的镜像两处
</span>        <span class="n">known</span> <span class="o">=</span> <span class="p">[</span><span class="n">crc32</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">e</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">known</span><span class="p">):</span>
            <span class="n">slot</span> <span class="o">=</span> <span class="n">crc_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">i</span>
            <span class="n">b</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"&lt;I"</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
            <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
                <span class="n">F</span><span class="p">[</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">slot</span> <span class="o">+</span> <span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">b</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
            <span class="n">mir</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_f_pos_list</span><span class="p">(</span><span class="n">lay</span><span class="p">,</span> <span class="n">lc</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">slot</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
            <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">mir</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span>
            <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
                <span class="n">F</span><span class="p">[</span><span class="n">mir</span><span class="p">[</span><span class="n">k</span><span class="p">]]</span> <span class="o">=</span> <span class="n">b</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
        <span class="c1"># 未知量：D_seed(seed 的 CRC)、D(quine 的 CRC)、N、S
</span>        <span class="c1"># 每个未知量 4 字节，每字节有"本体 + seed 镜像"两个落点
</span>        <span class="n">qs</span> <span class="o">=</span> <span class="n">crc_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">known</span><span class="p">)</span>       <span class="c1"># seed 子流的 CRC 槽
</span>        <span class="n">q</span> <span class="o">=</span> <span class="n">crc_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">n_sub</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>       <span class="c1"># quine 子流的 CRC 槽
</span>        <span class="n">groups</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="n">slot</span> <span class="ow">in</span> <span class="p">(</span><span class="n">qs</span><span class="p">,</span> <span class="n">q</span><span class="p">):</span>
            <span class="n">groups</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">slot</span><span class="p">,</span> <span class="n">hoff</span> <span class="o">+</span> <span class="n">slot</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)),</span>
                           <span class="bp">self</span><span class="p">.</span><span class="n">_f_pos_list</span><span class="p">(</span><span class="n">lay</span><span class="p">,</span> <span class="n">lc</span> <span class="o">+</span> <span class="mi">32</span> <span class="o">+</span> <span class="n">slot</span><span class="p">,</span> <span class="mi">4</span><span class="p">)])</span>
        <span class="n">groups</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">28</span><span class="p">,</span> <span class="mi">32</span><span class="p">)),</span> <span class="n">scopy0</span><span class="p">[</span><span class="mi">28</span><span class="p">:</span><span class="mi">32</span><span class="p">]])</span>   <span class="c1"># NextHeaderCRC
</span>        <span class="n">groups</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">12</span><span class="p">)),</span> <span class="n">scopy0</span><span class="p">[</span><span class="mi">8</span><span class="p">:</span><span class="mi">12</span><span class="p">]])</span>     <span class="c1"># StartHeaderCRC
</span>        <span class="n">nb</span> <span class="o">=</span> <span class="mi">32</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">groups</span><span class="p">)</span>
        <span class="n">base</span> <span class="o">=</span> <span class="n">targets</span><span class="p">()</span>
        <span class="n">basevec</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">base</span><span class="p">):</span>
            <span class="n">basevec</span> <span class="o">|=</span> <span class="n">v</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">32</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span>
        <span class="n">cols</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">groups</span><span class="p">)):</span>
            <span class="n">own</span><span class="p">,</span> <span class="n">mir</span> <span class="o">=</span> <span class="n">groups</span><span class="p">[</span><span class="n">g</span><span class="p">]</span>
            <span class="k">for</span> <span class="n">kb</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">32</span><span class="p">):</span>
                <span class="n">bi</span><span class="p">,</span> <span class="n">bb</span> <span class="o">=</span> <span class="n">kb</span> <span class="o">//</span> <span class="mi">8</span><span class="p">,</span> <span class="n">kb</span> <span class="o">%</span> <span class="mi">8</span>
                <span class="n">poss</span> <span class="o">=</span> <span class="p">[</span><span class="n">own</span><span class="p">[</span><span class="n">bi</span><span class="p">],</span> <span class="n">mir</span><span class="p">[</span><span class="n">bi</span><span class="p">]]</span>
                <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">poss</span><span class="p">:</span>
                    <span class="n">F</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">^=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bb</span><span class="p">)</span>
                <span class="n">t</span> <span class="o">=</span> <span class="n">targets</span><span class="p">()</span>
                <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">poss</span><span class="p">:</span>
                    <span class="n">F</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">^=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bb</span><span class="p">)</span>
                <span class="n">delta</span> <span class="o">=</span> <span class="mi">0</span>
                <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
                    <span class="n">delta</span> <span class="o">|=</span> <span class="p">(</span><span class="n">v</span> <span class="o">^</span> <span class="n">base</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">32</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span>
                <span class="n">cols</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
        <span class="n">eqs</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nb</span><span class="p">):</span>
            <span class="n">coeff</span> <span class="o">=</span> <span class="mi">0</span>
            <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nb</span><span class="p">):</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">cols</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
                    <span class="n">coeff</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">j</span>
            <span class="n">coeff</span> <span class="o">^=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
            <span class="n">eqs</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="n">coeff</span><span class="p">,</span> <span class="p">(</span><span class="n">basevec</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">])</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nb</span><span class="p">):</span>
            <span class="n">p</span> <span class="o">=</span> <span class="nb">next</span><span class="p">((</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">j</span><span class="p">,</span> <span class="n">nb</span><span class="p">)</span> <span class="k">if</span> <span class="p">(</span><span class="n">eqs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">j</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">),</span> <span class="bp">None</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">p</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"GF(2) 奇异 @bit%d"</span> <span class="o">%</span> <span class="n">j</span><span class="p">)</span>
            <span class="n">eqs</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">eqs</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">eqs</span><span class="p">[</span><span class="n">p</span><span class="p">],</span> <span class="n">eqs</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
            <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nb</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">j</span> <span class="ow">and</span> <span class="p">((</span><span class="n">eqs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">j</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">):</span>
                    <span class="n">eqs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">^=</span> <span class="n">eqs</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
                    <span class="n">eqs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">^=</span> <span class="n">eqs</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
        <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nb</span><span class="p">):</span>
            <span class="k">assert</span> <span class="n">eqs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">eqs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]:</span>
                <span class="n">x</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
        <span class="n">vals</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="mi">32</span> <span class="o">*</span> <span class="n">i</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">groups</span><span class="p">))]</span>
        <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">groups</span><span class="p">)):</span>
            <span class="n">own</span><span class="p">,</span> <span class="n">mir</span> <span class="o">=</span> <span class="n">groups</span><span class="p">[</span><span class="n">g</span><span class="p">]</span>
            <span class="n">b</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"&lt;I"</span><span class="p">,</span> <span class="n">vals</span><span class="p">[</span><span class="n">g</span><span class="p">])</span>
            <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
                <span class="n">F</span><span class="p">[</span><span class="n">own</span><span class="p">[</span><span class="n">k</span><span class="p">]]</span> <span class="o">=</span> <span class="n">b</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
                <span class="n">F</span><span class="p">[</span><span class="n">mir</span><span class="p">[</span><span class="n">k</span><span class="p">]]</span> <span class="o">=</span> <span class="n">b</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
        <span class="n">t</span> <span class="o">=</span> <span class="n">targets</span><span class="p">()</span>
        <span class="k">assert</span> <span class="n">t</span> <span class="o">==</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">vals</span><span class="p">),</span> <span class="s">"CRC 定点失败: %s != %s"</span> <span class="o">%</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">vals</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">vals</span>

    <span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">l0</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">layout</span><span class="p">()</span>
        <span class="n">lay</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">finalize</span><span class="p">(</span><span class="n">l0</span><span class="p">[</span><span class="s">"Y"</span><span class="p">],</span> <span class="n">l0</span><span class="p">[</span><span class="s">"H"</span><span class="p">],</span> <span class="n">l0</span><span class="p">[</span><span class="s">"M"</span><span class="p">])</span>
        <span class="n">F</span><span class="p">,</span> <span class="n">seed</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">assemble</span><span class="p">(</span><span class="n">lay</span><span class="p">)</span>
        <span class="n">crcs</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">solve_crc</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">lay</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"plant"</span><span class="p">])</span>
        <span class="c1"># CRC 定点会改写 H / S32，而 seed 是它们的镜像，定点后需重建
</span>        <span class="n">hoff</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"total"</span><span class="p">]</span> <span class="o">-</span> <span class="n">lay</span><span class="p">[</span><span class="s">"H"</span><span class="p">]</span>
        <span class="n">seed</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"C"</span><span class="p">]</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">32</span><span class="p">])</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="n">hoff</span><span class="p">:</span><span class="n">hoff</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"H"</span><span class="p">]])</span> \
            <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"plant"</span><span class="p">]</span>
        <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">),</span> <span class="n">lay</span><span class="p">,</span> <span class="n">seed</span><span class="p">,</span> <span class="n">crcs</span>


<span class="k">def</span> <span class="nf">verify_double</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="n">seed</span><span class="p">,</span> <span class="n">content</span><span class="p">):</span>
    <span class="s">"""串联两个原始 LZMA2 解码器还原 U，逐段校验。"""</span>
    <span class="n">d</span><span class="p">,</span> <span class="n">total</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"d"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"total"</span><span class="p">]</span>
    <span class="n">dec_o</span> <span class="o">=</span> <span class="n">lzma</span><span class="p">.</span><span class="n">LZMADecompressor</span><span class="p">(</span>
        <span class="nb">format</span><span class="o">=</span><span class="n">lzma</span><span class="p">.</span><span class="n">FORMAT_RAW</span><span class="p">,</span>
        <span class="n">filters</span><span class="o">=</span><span class="p">[{</span><span class="s">"id"</span><span class="p">:</span> <span class="n">lzma</span><span class="p">.</span><span class="n">FILTER_LZMA2</span><span class="p">,</span> <span class="s">"dict_size"</span><span class="p">:</span> <span class="n">lay</span><span class="p">[</span><span class="s">"dict_size_out"</span><span class="p">]}])</span>
    <span class="n">I</span> <span class="o">=</span> <span class="n">dec_o</span><span class="p">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="mi">32</span><span class="p">:</span><span class="mi">32</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n"</span><span class="p">]])</span>
    <span class="n">ok</span> <span class="o">=</span> <span class="bp">True</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">I</span><span class="p">)</span> <span class="o">!=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"Li"</span><span class="p">]:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 外层输出 %d != I %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">I</span><span class="p">),</span> <span class="n">lay</span><span class="p">[</span><span class="s">"Li"</span><span class="p">]))</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="n">dec_i</span> <span class="o">=</span> <span class="n">lzma</span><span class="p">.</span><span class="n">LZMADecompressor</span><span class="p">(</span>
        <span class="nb">format</span><span class="o">=</span><span class="n">lzma</span><span class="p">.</span><span class="n">FORMAT_RAW</span><span class="p">,</span>
        <span class="n">filters</span><span class="o">=</span><span class="p">[{</span><span class="s">"id"</span><span class="p">:</span> <span class="n">lzma</span><span class="p">.</span><span class="n">FILTER_LZMA2</span><span class="p">,</span> <span class="s">"dict_size"</span><span class="p">:</span> <span class="n">lay</span><span class="p">[</span><span class="s">"dict_size_in"</span><span class="p">]}])</span>
    <span class="n">U</span> <span class="o">=</span> <span class="n">dec_i</span><span class="p">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">I</span><span class="p">)</span>
    <span class="n">exp</span> <span class="o">=</span> <span class="n">d</span> <span class="o">+</span> <span class="n">total</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">U</span><span class="p">)</span> <span class="o">!=</span> <span class="n">exp</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 内层输出 %d != %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">U</span><span class="p">),</span> <span class="n">exp</span><span class="p">))</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="n">dc</span><span class="p">,</span> <span class="n">ds</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"dc"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ds"</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">U</span><span class="p">[:</span><span class="n">dc</span><span class="p">]</span> <span class="o">!=</span> <span class="n">content</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 内容不匹配"</span><span class="p">)</span>
        <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">rel</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>                   <span class="c1"># 内容文件排在 U 的最前面
</span>        <span class="k">if</span> <span class="n">U</span><span class="p">[</span><span class="n">pos</span><span class="p">:</span><span class="n">pos</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)]</span> <span class="o">!=</span> <span class="n">data</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 文件不匹配:"</span><span class="p">,</span> <span class="n">rel</span><span class="p">)</span>
            <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="n">pos</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">pos</span> <span class="o">==</span> <span class="n">dc</span>
    <span class="k">if</span> <span class="n">U</span><span class="p">[</span><span class="n">dc</span><span class="p">:</span><span class="n">dc</span> <span class="o">+</span> <span class="n">ds</span><span class="p">]</span> <span class="o">!=</span> <span class="n">seed</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] seed 不匹配"</span><span class="p">)</span>
        <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="n">U</span><span class="p">[</span><span class="n">d</span><span class="p">:]</span> <span class="o">!=</span> <span class="n">F</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] quine 自复制不匹配"</span><span class="p">)</span>
        <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 自解码: |I|=%d, |U|=%d, seed/文件/quine 全部匹配: %s"</span>
          <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">I</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">U</span><span class="p">),</span> <span class="n">ok</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">ok</span>
</code></pre></div></div>
<p>仅仅是大佬的一句话，AI就能做出来，实在是太可怕了。最终我试了一下，官方的7-Zip可以正常解压，但是Windows资源管理器不行……Windows资源管理器使用的也是<a href="https://github.com/libarchive/libarchive">libarchive</a>，似乎是因为libarchive只支持2个解码器，并且<a href="https://github.com/libarchive/libarchive/blob/8bb3bbdc7b117a1e22086a2260f2087aafa90687/libarchive/archive_read_support_format_7zip.c#L1475">第二个解码器只能是像BCJ这样的过滤器</a>……明明7-Zip官方是可以解压的，不知道如果把这个问题当作BUG报告给libarchive他们会不会解决？但目前存在兼容性问题的话我博客压缩包就先不用这个方案了吧🥲……</p>
<h2 id="对txz格式的尝试">对TXZ格式的尝试</h2>
<p>在做完7z格式的压缩包之后，我发现了一个问题，虽然7z确实很流行，但是在Linux下解压起来有点麻烦，7-Zip历史上主要面向Windows，Linux上长期以来更多依赖p7zip等第三方移植，因此生态集成度不如tar.xz，想要解压7z文件还得额外安装。 <br />
  不过Linux下也有个支持LZMA2算法的压缩软件，那就是前些年出过<a href="https://tukaani.org/xz-backdoor/">后门</a>的XZ Utils，配合tar就可以做出TXZ（tar.xz）文件，甚至用我<a href="https://mabbs.github.io/linux/">博客终端</a>中的BusyBox也能解压。我想了一下反正有AI，干脆一句话让AI帮我把blogquine.py改成tar.xz格式的好了，结果倒也没费多少功夫，AI就这样写出来了：</p>

<details>
  <summary>
Show Code
</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# -*- coding: utf-8 -*-
</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">binascii</span>
<span class="kn">import</span> <span class="nn">io</span>
<span class="kn">import</span> <span class="nn">lzma</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">tarfile</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">ctypes</span>
<span class="kn">import</span> <span class="nn">ctypes.util</span>

<span class="c1"># ============================================================
# Part 1: 最小 LZMA1 range coder（只做编码）
# ============================================================
</span>
<span class="n">kNumBitModelTotalBits</span> <span class="o">=</span> <span class="mi">11</span>
<span class="n">kBitModelTotal</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumBitModelTotalBits</span>          <span class="c1"># 2048
</span><span class="n">kNumMoveBits</span> <span class="o">=</span> <span class="mi">5</span>
<span class="n">kTopValue</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">24</span>                                   <span class="c1"># 0x1000000
</span>
<span class="n">kNumStates</span> <span class="o">=</span> <span class="mi">12</span>
<span class="n">kNumLitStates</span> <span class="o">=</span> <span class="mi">7</span>
<span class="n">kNumPosBitsMax</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">kNumLenToPosStates</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">kNumAlignBits</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">kEndPosModelIndex</span> <span class="o">=</span> <span class="mi">14</span>
<span class="n">kNumFullDistances</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">kEndPosModelIndex</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span>     <span class="c1"># 128
</span><span class="n">kMatchMinLen</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">kNumLowLenBits</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">kNumMidLenBits</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">kNumHighLenBits</span> <span class="o">=</span> <span class="mi">8</span>
<span class="n">kNumLowLenSymbols</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumLowLenBits</span>               <span class="c1"># 8
</span><span class="n">kNumMidLenSymbols</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumMidLenBits</span>               <span class="c1"># 8
</span><span class="n">kNumPosSlotBits</span> <span class="o">=</span> <span class="mi">6</span>

<span class="n">PROB_INIT</span> <span class="o">=</span> <span class="n">kBitModelTotal</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span>                       <span class="c1"># 1024
</span>

<span class="k">class</span> <span class="nc">RangeEncoder</span><span class="p">:</span>
    <span class="s">"""LZMA 的区间编码器。low 是 64 位（要容纳进位），range 是 32 位。"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">=</span> <span class="mh">0xFFFFFFFF</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cache</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cache_size</span> <span class="o">=</span> <span class="mi">1</span>          <span class="c1"># 初值 1 -&gt; 第一个输出字节恒为 0x00
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">buf</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">_shift_low</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mh">0xFF000000</span> <span class="ow">or</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">temp</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">cache</span>
            <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">buf</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">temp</span> <span class="o">+</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">)</span>
                <span class="n">temp</span> <span class="o">=</span> <span class="mh">0xFF</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">cache_size</span> <span class="o">-=</span> <span class="mi">1</span>
                <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">cache_size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
                    <span class="k">break</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">&gt;&gt;</span> <span class="mi">24</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cache_size</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">=</span> <span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>

    <span class="k">def</span> <span class="nf">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">probs</span><span class="p">,</span> <span class="n">idx</span><span class="p">,</span> <span class="n">bit</span><span class="p">):</span>
        <span class="n">p</span> <span class="o">=</span> <span class="n">probs</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
        <span class="n">bound</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">&gt;&gt;</span> <span class="n">kNumBitModelTotalBits</span><span class="p">)</span> <span class="o">*</span> <span class="n">p</span>
        <span class="k">if</span> <span class="n">bit</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">=</span> <span class="n">bound</span>
            <span class="n">probs</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span> <span class="o">+</span> <span class="p">((</span><span class="n">kBitModelTotal</span> <span class="o">-</span> <span class="n">p</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">kNumMoveBits</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">+=</span> <span class="n">bound</span>
            <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">-=</span> <span class="n">bound</span>
            <span class="n">probs</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span> <span class="o">-</span> <span class="p">(</span><span class="n">p</span> <span class="o">&gt;&gt;</span> <span class="n">kNumMoveBits</span><span class="p">)</span>
        <span class="k">while</span> <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">&lt;</span> <span class="n">kTopValue</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">_shift_low</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">encode_direct_bits</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">num_bits</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_bits</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
            <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">value</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">low</span> <span class="o">+=</span> <span class="bp">self</span><span class="p">.</span><span class="nb">range</span>
            <span class="k">while</span> <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">&lt;</span> <span class="n">kTopValue</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="nb">range</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">_shift_low</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">bittree_encode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">probs</span><span class="p">,</span> <span class="n">off</span><span class="p">,</span> <span class="n">num_bits</span><span class="p">,</span> <span class="n">symbol</span><span class="p">):</span>
        <span class="n">m</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_bits</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
            <span class="n">bit</span> <span class="o">=</span> <span class="p">(</span><span class="n">symbol</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="n">probs</span><span class="p">,</span> <span class="n">off</span> <span class="o">+</span> <span class="n">m</span><span class="p">,</span> <span class="n">bit</span><span class="p">)</span>
            <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">m</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="n">bit</span>

    <span class="k">def</span> <span class="nf">bittree_reverse_encode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">probs</span><span class="p">,</span> <span class="n">off</span><span class="p">,</span> <span class="n">num_bits</span><span class="p">,</span> <span class="n">symbol</span><span class="p">):</span>
        <span class="n">m</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_bits</span><span class="p">):</span>
            <span class="n">bit</span> <span class="o">=</span> <span class="n">symbol</span> <span class="o">&amp;</span> <span class="mi">1</span>
            <span class="n">symbol</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="n">probs</span><span class="p">,</span> <span class="n">off</span> <span class="o">+</span> <span class="n">m</span><span class="p">,</span> <span class="n">bit</span><span class="p">)</span>
            <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">m</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="n">bit</span>

    <span class="k">def</span> <span class="nf">finish</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">_shift_low</span><span class="p">()</span>
        <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">buf</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_pos_slot_and_bits</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">d</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">d</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">slot</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">64</span><span class="p">):</span>
        <span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="n">slot</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">base</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span> <span class="o">|</span> <span class="p">(</span><span class="n">slot</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="n">n</span>
        <span class="k">if</span> <span class="n">d</span> <span class="o">&lt;</span> <span class="n">base</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">):</span>
            <span class="k">return</span> <span class="n">slot</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span> <span class="o">-</span> <span class="n">base</span>
    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"distance too large: %d"</span> <span class="o">%</span> <span class="n">d</span><span class="p">)</span>


<span class="c1"># pb 必须与写进每个 LZMA2 chunk 头的 PROPS_BYTE(0x5D) 一致。
# lc/lp 只影响 literal 编码的概率表索引，而本编码器不实现 literal 路径，故无需常量。
</span><span class="n">PB</span> <span class="o">=</span> <span class="mi">2</span>


<span class="k">class</span> <span class="nc">LzmaEncoder</span><span class="p">:</span>
    <span class="s">"""只实现 quine 用到的两条路径：match 与 rep0-match。

    literal / rep-g1 / rep-g2 三条分支从未被构造，因此它们对应的概率表
    （p_lit、p_is_rep_g1、p_is_rep_g2）以及 prev_byte 都不必存在。
    reps 同理：编码器从不回读（rep0 的距离由解码器自行维护），故不保存。
    """</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pos_mask</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">PB</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span> <span class="o">=</span> <span class="n">RangeEncoder</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">PROB_INIT</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_is_match</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">kNumStates</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosBitsMax</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_is_rep</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="n">kNumStates</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_is_rep_g0</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="n">kNumStates</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_rep0_long</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">kNumStates</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosBitsMax</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_pos_slot</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">kNumLenToPosStates</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosSlotBits</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_spec_pos</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">kNumFullDistances</span> <span class="o">-</span> <span class="n">kEndPosModelIndex</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_align</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumAlignBits</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_len_choice</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="mi">2</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_len_low</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">16</span> <span class="o">*</span> <span class="n">kNumLowLenSymbols</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_len_mid</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">16</span> <span class="o">*</span> <span class="n">kNumMidLenSymbols</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_len_high</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumHighLenBits</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_choice</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="mi">2</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_low</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">16</span> <span class="o">*</span> <span class="n">kNumLowLenSymbols</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_mid</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">16</span> <span class="o">*</span> <span class="n">kNumMidLenSymbols</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_high</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumHighLenBits</span><span class="p">)</span>

    <span class="o">@</span><span class="nb">property</span>
    <span class="k">def</span> <span class="nf">pos_state</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">pos</span> <span class="o">&amp;</span> <span class="bp">self</span><span class="p">.</span><span class="n">pos_mask</span>

    <span class="k">def</span> <span class="nf">_encode_len</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">choice</span><span class="p">,</span> <span class="n">low</span><span class="p">,</span> <span class="n">mid</span><span class="p">,</span> <span class="n">high</span><span class="p">,</span> <span class="n">length</span><span class="p">):</span>
        <span class="n">ps</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">pos_state</span>
        <span class="n">l</span> <span class="o">=</span> <span class="n">length</span> <span class="o">-</span> <span class="n">kMatchMinLen</span>
        <span class="k">if</span> <span class="n">l</span> <span class="o">&lt;</span> <span class="n">kNumLowLenSymbols</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="n">choice</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">bittree_encode</span><span class="p">(</span><span class="n">low</span><span class="p">,</span> <span class="n">ps</span> <span class="o">&lt;&lt;</span> <span class="n">kNumLowLenBits</span><span class="p">,</span> <span class="n">kNumLowLenBits</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="n">choice</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            <span class="n">l</span> <span class="o">-=</span> <span class="n">kNumLowLenSymbols</span>
            <span class="k">if</span> <span class="n">l</span> <span class="o">&lt;</span> <span class="n">kNumMidLenSymbols</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="n">choice</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">bittree_encode</span><span class="p">(</span><span class="n">mid</span><span class="p">,</span> <span class="n">ps</span> <span class="o">&lt;&lt;</span> <span class="n">kNumMidLenBits</span><span class="p">,</span> <span class="n">kNumMidLenBits</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="n">choice</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">bittree_encode</span><span class="p">(</span><span class="n">high</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">kNumHighLenBits</span><span class="p">,</span> <span class="n">l</span> <span class="o">-</span> <span class="n">kNumMidLenSymbols</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">_write_dist</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dist</span><span class="p">,</span> <span class="n">lts</span><span class="p">):</span>
        <span class="n">d</span> <span class="o">=</span> <span class="n">dist</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">slot</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">low_bits</span> <span class="o">=</span> <span class="n">_pos_slot_and_bits</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">bittree_encode</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_pos_slot</span><span class="p">,</span> <span class="n">lts</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosSlotBits</span><span class="p">,</span>
                               <span class="n">kNumPosSlotBits</span><span class="p">,</span> <span class="n">slot</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">slot</span> <span class="o">&gt;=</span> <span class="mi">4</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">slot</span> <span class="o">&lt;</span> <span class="n">kEndPosModelIndex</span><span class="p">:</span>
                <span class="n">base</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span> <span class="o">|</span> <span class="p">(</span><span class="n">slot</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="n">n</span>
                <span class="n">off</span> <span class="o">=</span> <span class="n">base</span> <span class="o">-</span> <span class="n">slot</span> <span class="o">-</span> <span class="mi">1</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">bittree_reverse_encode</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_spec_pos</span><span class="p">,</span> <span class="n">off</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">low_bits</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_direct_bits</span><span class="p">(</span><span class="n">low_bits</span> <span class="o">&gt;&gt;</span> <span class="n">kNumAlignBits</span><span class="p">,</span> <span class="n">n</span> <span class="o">-</span> <span class="n">kNumAlignBits</span><span class="p">)</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">bittree_reverse_encode</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_align</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">kNumAlignBits</span><span class="p">,</span>
                                               <span class="n">low_bits</span> <span class="o">&amp;</span> <span class="p">((</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">kNumAlignBits</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span>

    <span class="k">def</span> <span class="nf">match</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dist</span><span class="p">,</span> <span class="n">length</span><span class="p">):</span>
        <span class="k">assert</span> <span class="n">kMatchMinLen</span> <span class="o">&lt;=</span> <span class="n">length</span> <span class="o">&lt;=</span> <span class="mi">273</span><span class="p">,</span> <span class="n">length</span>
        <span class="n">ps</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">pos_state</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_is_match</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosBitsMax</span><span class="p">)</span> <span class="o">+</span> <span class="n">ps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_is_rep</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">lts</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">length</span> <span class="o">-</span> <span class="n">kMatchMinLen</span><span class="p">,</span> <span class="n">kNumLenToPosStates</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">_encode_len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_len_choice</span><span class="p">,</span>
                         <span class="bp">self</span><span class="p">.</span><span class="n">p_len_low</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">p_len_mid</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">p_len_high</span><span class="p">,</span> <span class="n">length</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">_write_dist</span><span class="p">(</span><span class="n">dist</span><span class="p">,</span> <span class="n">lts</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="mi">7</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">&lt;</span> <span class="n">kNumLitStates</span> <span class="k">else</span> <span class="mi">10</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pos</span> <span class="o">+=</span> <span class="n">length</span>

    <span class="k">def</span> <span class="nf">rep_match</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">length</span><span class="p">):</span>
        <span class="k">assert</span> <span class="n">kMatchMinLen</span> <span class="o">&lt;=</span> <span class="n">length</span> <span class="o">&lt;=</span> <span class="mi">273</span><span class="p">,</span> <span class="n">length</span>
        <span class="n">ps</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">pos_state</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_is_match</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosBitsMax</span><span class="p">)</span> <span class="o">+</span> <span class="n">ps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_is_rep</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_is_rep_g0</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">encode_bit</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_rep0_long</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">&lt;&lt;</span> <span class="n">kNumPosBitsMax</span><span class="p">)</span> <span class="o">+</span> <span class="n">ps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">_encode_len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_choice</span><span class="p">,</span>
                         <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_low</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_mid</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">p_rep_len_high</span><span class="p">,</span> <span class="n">length</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="mi">8</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">&lt;</span> <span class="n">kNumLitStates</span> <span class="k">else</span> <span class="mi">11</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pos</span> <span class="o">+=</span> <span class="n">length</span>

    <span class="k">def</span> <span class="nf">finish</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">rc</span><span class="p">.</span><span class="n">finish</span><span class="p">()</span>


<span class="c1"># ============================================================
# Part 2: tar + xz 格式原语
# ============================================================
</span>
<span class="n">MAX_MATCH</span> <span class="o">=</span> <span class="mi">273</span>                       <span class="c1"># LZMA1 单个 match 长度上限
</span><span class="n">CHUNK</span> <span class="o">=</span> <span class="mi">65536</span>                          <span class="c1"># LZMA2 单 chunk 解压上限
</span><span class="n">PROPS_BYTE</span> <span class="o">=</span> <span class="mh">0x5D</span>                      <span class="c1"># lc=3, lp=0, pb=2
</span><span class="n">SEED_NOTE</span> <span class="o">=</span> <span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Mayx's Blog!"</span><span class="p">).</span><span class="n">encode</span><span class="p">(</span><span class="s">"utf-8"</span><span class="p">)</span>

<span class="c1"># xz 格式常量
</span><span class="n">XZ_MAGIC</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\xFD</span><span class="s">7zXZ</span><span class="se">\x00</span><span class="s">"</span>
<span class="n">XZ_FOOTER_MAGIC</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"YZ"</span>
<span class="n">XZ_CHECK_CRC64</span> <span class="o">=</span> <span class="mh">0x04</span>
<span class="n">LZMA2_FILTER_ID</span> <span class="o">=</span> <span class="mh">0x21</span>
<span class="n">CHECK_SIZE</span> <span class="o">=</span> <span class="mi">8</span>  <span class="c1"># CRC64 占 8 字节
</span><span class="n">_XZ_FLAGS</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0x00</span><span class="p">,</span> <span class="n">XZ_CHECK_CRC64</span><span class="p">])</span>   <span class="c1"># Stream Flags: reserved + check_type
</span>
<span class="c1"># tar 格式常量
</span><span class="n">TAR_BLOCK</span> <span class="o">=</span> <span class="mi">512</span>


<span class="k">def</span> <span class="nf">round_up_512</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="s">"""长度按 tar 块 512 字节向上取整。"""</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="n">TAR_BLOCK</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="n">TAR_BLOCK</span> <span class="o">*</span> <span class="n">TAR_BLOCK</span>


<span class="k">def</span> <span class="nf">crc32</span><span class="p">(</span><span class="n">b</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">binascii</span><span class="p">.</span><span class="n">crc32</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>


<span class="c1"># ---------- CRC64 (ECMA-182, reflected) ----------
</span>
<span class="n">_CRC64_POLY</span> <span class="o">=</span> <span class="mh">0xC96C5795D7870F42</span>  <span class="c1"># reflected ECMA-182
</span><span class="n">_CRC64_INIT</span> <span class="o">=</span> <span class="mh">0xFFFFFFFFFFFFFFFF</span>
<span class="n">_CRC64_XOROUT</span> <span class="o">=</span> <span class="mh">0xFFFFFFFFFFFFFFFF</span>
<span class="n">_CRC64_NON_REFLECTED</span> <span class="o">=</span> <span class="mh">0x142F0E1EBA9EA3693</span>  <span class="c1"># 非反射多项式 (65 bit)
</span><span class="n">_CRC64_MOD_BIT</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">64</span>  <span class="c1"># x^64 对应的 bit
</span>

<span class="n">_liblzma</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">try</span><span class="p">:</span>
    <span class="n">_lzma_name</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">util</span><span class="p">.</span><span class="n">find_library</span><span class="p">(</span><span class="s">'lzma'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">_lzma_name</span><span class="p">:</span>
        <span class="n">_liblzma</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">CDLL</span><span class="p">(</span><span class="n">_lzma_name</span><span class="p">)</span>
        <span class="n">_liblzma</span><span class="p">.</span><span class="n">lzma_crc64</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_char_p</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint64</span><span class="p">]</span>
        <span class="n">_liblzma</span><span class="p">.</span><span class="n">lzma_crc64</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint64</span>
        <span class="c1"># 验证：CRC64("123456789") 应为 0x995dc9bbdf1939fa
</span>        <span class="k">if</span> <span class="n">_liblzma</span><span class="p">.</span><span class="n">lzma_crc64</span><span class="p">(</span><span class="sa">b</span><span class="s">'123456789'</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">!=</span> <span class="mh">0x995dc9bbdf1939fa</span><span class="p">:</span>
            <span class="n">_liblzma</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">except</span> <span class="p">(</span><span class="nb">OSError</span><span class="p">,</span> <span class="nb">AttributeError</span><span class="p">):</span>
    <span class="n">_liblzma</span> <span class="o">=</span> <span class="bp">None</span>


<span class="k">def</span> <span class="nf">_make_crc64_table</span><span class="p">():</span>
    <span class="n">table</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">256</span><span class="p">):</span>
        <span class="n">crc</span> <span class="o">=</span> <span class="n">i</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">):</span>
            <span class="n">crc</span> <span class="o">=</span> <span class="p">(</span><span class="n">crc</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">^</span> <span class="n">_CRC64_POLY</span> <span class="k">if</span> <span class="p">(</span><span class="n">crc</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="k">else</span> <span class="n">crc</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span>
        <span class="n">table</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">crc</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">table</span>


<span class="n">_CRC64_TABLE</span> <span class="o">=</span> <span class="n">_make_crc64_table</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">_crc64_pure</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="s">"""纯 Python CRC64（ECMA-182 反射，init/xorout = 0xFFFF...FFFF）。"""</span>
    <span class="n">crc</span> <span class="o">=</span> <span class="n">_CRC64_INIT</span>
    <span class="k">for</span> <span class="n">byte</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="n">crc</span> <span class="o">=</span> <span class="p">(</span><span class="n">crc</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">^</span> <span class="n">_CRC64_TABLE</span><span class="p">[(</span><span class="n">crc</span> <span class="o">^</span> <span class="n">byte</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">crc</span> <span class="o">^</span> <span class="n">_CRC64_XOROUT</span>


<span class="k">def</span> <span class="nf">_crc64_lib</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="s">"""liblzma CRC64（init=0，内部处理 init/xorout）。"""</span>
    <span class="k">return</span> <span class="n">_liblzma</span><span class="p">.</span><span class="n">lzma_crc64</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>


<span class="n">crc64</span> <span class="o">=</span> <span class="n">_crc64_lib</span> <span class="k">if</span> <span class="n">_liblzma</span> <span class="k">else</span> <span class="n">_crc64_pure</span>


<span class="c1"># ---------- GF(2^64) 多项式运算 ----------
# 用于 CRC64 自引用求解。CRC64 的线性贡献可表示为
# bit_reverse(contribution(D)) = D_poly * P (mod G_non)
# 其中 D_poly = bit_reverse(D)，P 是位置决定的多项式。
# 方程 D_poly * (1 ^ P1 ^ P2) = bit_reverse(CRC64(W)) 通过多项式逆元求解。
</span>
<span class="k">def</span> <span class="nf">_bit_reverse64</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
    <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">64</span><span class="p">):</span>
        <span class="n">r</span> <span class="o">=</span> <span class="p">(</span><span class="n">r</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">v</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span>
        <span class="n">v</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">r</span>


<span class="k">def</span> <span class="nf">_poly_mul_mod</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
    <span class="s">"""GF(2) 多项式乘法 mod G（非反射）。"""</span>
    <span class="n">result</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="n">b</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">b</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">result</span> <span class="o">^=</span> <span class="n">a</span>
        <span class="n">b</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
        <span class="n">a</span> <span class="o">&lt;&lt;=</span> <span class="mi">1</span>
        <span class="k">if</span> <span class="n">a</span> <span class="o">&amp;</span> <span class="n">_CRC64_MOD_BIT</span><span class="p">:</span>
            <span class="n">a</span> <span class="o">^=</span> <span class="n">_CRC64_NON_REFLECTED</span>
    <span class="k">return</span> <span class="n">result</span>


<span class="k">def</span> <span class="nf">_poly_pow</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">exp</span><span class="p">):</span>
    <span class="s">"""多项式幂：base^exp mod G。"""</span>
    <span class="n">result</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">while</span> <span class="n">exp</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">exp</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">_poly_mul_mod</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">base</span><span class="p">)</span>
        <span class="n">base</span> <span class="o">=</span> <span class="n">_poly_mul_mod</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">base</span><span class="p">)</span>
        <span class="n">exp</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">result</span>


<span class="k">def</span> <span class="nf">_poly_minv</span><span class="p">(</span><span class="n">p</span><span class="p">):</span>
    <span class="s">"""多项式逆元 p^(-1) mod G，用扩展欧几里得算法。"""</span>
    <span class="k">if</span> <span class="n">p</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">0</span>
    <span class="n">old_r</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="n">p</span><span class="p">,</span> <span class="n">_CRC64_NON_REFLECTED</span>
    <span class="n">old_s</span><span class="p">,</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="n">r</span><span class="p">:</span>
        <span class="c1"># poly divmod(old_r, r) -&gt; (q, rem)
</span>        <span class="n">probe_r</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">rem</span> <span class="o">=</span> <span class="n">old_r</span>
        <span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">while</span> <span class="n">rem</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="n">probe_r</span> <span class="ow">and</span> <span class="n">rem</span><span class="p">:</span>
            <span class="n">shift</span> <span class="o">=</span> <span class="n">rem</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">probe_r</span>
            <span class="n">q</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">shift</span>
            <span class="n">rem</span> <span class="o">^=</span> <span class="n">r</span> <span class="o">&lt;&lt;</span> <span class="n">shift</span>
        <span class="n">old_r</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="n">r</span><span class="p">,</span> <span class="n">rem</span>
        <span class="c1"># s = old_s ^ q*s (raw 多项式乘法)
</span>        <span class="n">prod</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">q</span><span class="p">,</span> <span class="n">s</span>
        <span class="k">while</span> <span class="n">b</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">b</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">:</span>
                <span class="n">prod</span> <span class="o">^=</span> <span class="n">a</span>
            <span class="n">b</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span>
            <span class="n">a</span> <span class="o">&lt;&lt;=</span> <span class="mi">1</span>
        <span class="n">old_s</span><span class="p">,</span> <span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="p">,</span> <span class="n">old_s</span> <span class="o">^</span> <span class="n">prod</span>
    <span class="k">if</span> <span class="n">old_r</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">0</span>
    <span class="c1"># old_s mod G
</span>    <span class="n">probe_m</span> <span class="o">=</span> <span class="n">_CRC64_NON_REFLECTED</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">rem</span> <span class="o">=</span> <span class="n">old_s</span>
    <span class="k">while</span> <span class="n">rem</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="n">probe_m</span> <span class="ow">and</span> <span class="n">rem</span><span class="p">:</span>
        <span class="n">shift</span> <span class="o">=</span> <span class="n">rem</span><span class="p">.</span><span class="n">bit_length</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">probe_m</span>
        <span class="n">rem</span> <span class="o">^=</span> <span class="n">_CRC64_NON_REFLECTED</span> <span class="o">&lt;&lt;</span> <span class="n">shift</span>
    <span class="k">return</span> <span class="n">rem</span>


<span class="k">def</span> <span class="nf">store_hdr</span><span class="p">(</span><span class="n">payload_len</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
    <span class="s">"""LZMA2 uncompressed chunk 头（3 字节，大端 size-1）。"""</span>
    <span class="k">assert</span> <span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">payload_len</span> <span class="o">&lt;=</span> <span class="mi">65536</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0x01</span> <span class="k">if</span> <span class="n">first</span> <span class="k">else</span> <span class="mh">0x02</span><span class="p">])</span> <span class="o">+</span> <span class="p">(</span><span class="n">payload_len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"big"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">matches_for</span><span class="p">(</span><span class="n">dist</span><span class="p">,</span> <span class="n">total</span><span class="p">):</span>
    <span class="s">"""dist 固定、总长 total 的 token 序列：首个 match + 后续 rep0。"""</span>
    <span class="k">assert</span> <span class="n">total</span> <span class="o">&gt;=</span> <span class="mi">2</span>
    <span class="n">toks</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">first</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">MAX_MATCH</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">total</span> <span class="o">-</span> <span class="n">first</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">first</span> <span class="o">-=</span> <span class="mi">1</span>
    <span class="n">toks</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="s">"m"</span><span class="p">,</span> <span class="n">dist</span><span class="p">,</span> <span class="n">first</span><span class="p">))</span>
    <span class="n">total</span> <span class="o">-=</span> <span class="n">first</span>
    <span class="k">while</span> <span class="n">total</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">l</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">MAX_MATCH</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">total</span> <span class="o">-</span> <span class="n">l</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">l</span> <span class="o">-=</span> <span class="mi">1</span>
        <span class="n">toks</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="s">"r0"</span><span class="p">,</span> <span class="n">l</span><span class="p">))</span>
        <span class="n">total</span> <span class="o">-=</span> <span class="n">l</span>
    <span class="k">return</span> <span class="n">toks</span>


<span class="k">def</span> <span class="nf">_add_gear</span><span class="p">(</span><span class="n">chunks</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">q</span><span class="p">):</span>
    <span class="s">"""追加一组标准 gear：store 载荷 x = slip+3，紧跟一个 lzma 把那 x 字节复制一遍。

    lzma chunk 解压出的字节数正好等于 x，所以齿轮转完之后 slip (f - (o-q)) 精确
    归约为 len(cb)（很小的正数，x 被 CHUNK 截断时则按 CHUNK 缩减）。
    _layout_iterate 反复套用把 slip 压到 &lt;= 16，_trim_layouts 用它做相位微调。
    返回追加后的 (o, f)。
    """</span>
    <span class="n">x</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">f</span> <span class="o">-</span> <span class="p">(</span><span class="n">o</span> <span class="o">-</span> <span class="n">q</span><span class="p">)</span> <span class="o">+</span> <span class="mi">3</span><span class="p">,</span> <span class="n">CHUNK</span><span class="p">)</span>
    <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"store"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span>
                   <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">x</span><span class="p">})</span>
    <span class="n">o</span> <span class="o">+=</span> <span class="n">x</span>
    <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">x</span>
    <span class="n">cb</span><span class="p">,</span> <span class="n">cu</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">(</span><span class="n">matches_for</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="p">),</span> <span class="n">o</span><span class="p">)</span>
    <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"lzma"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">cb</span><span class="p">),</span>
                   <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">cu</span><span class="p">,</span> <span class="s">"bytes"</span><span class="p">:</span> <span class="n">cb</span><span class="p">})</span>
    <span class="k">return</span> <span class="n">o</span> <span class="o">+</span> <span class="n">cu</span><span class="p">,</span> <span class="n">f</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">cb</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_forward_vac</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">vac</span><span class="p">,</span> <span class="n">q</span><span class="p">):</span>
    <span class="s">"""vac_store：把紧随其后的等长字节块前移覆盖自身。"""</span>
    <span class="n">fo</span> <span class="o">=</span> <span class="n">vac</span><span class="p">[</span><span class="s">"ooff"</span><span class="p">]</span> <span class="o">-</span> <span class="n">q</span>
    <span class="n">olen</span> <span class="o">=</span> <span class="n">vac</span><span class="p">[</span><span class="s">"olen"</span><span class="p">]</span>
    <span class="n">F</span><span class="p">[</span><span class="n">fo</span><span class="p">:</span><span class="n">fo</span> <span class="o">+</span> <span class="n">olen</span><span class="p">]</span> <span class="o">=</span> <span class="n">F</span><span class="p">[</span><span class="n">fo</span> <span class="o">+</span> <span class="n">olen</span><span class="p">:</span><span class="n">fo</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">olen</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">dict_prop_for</span><span class="p">(</span><span class="n">maxdist</span><span class="p">):</span>
    <span class="s">"""选最小的 LZMA2 dict prop 使字典 &gt;= maxdist。"""</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">41</span><span class="p">):</span>
        <span class="k">if</span> <span class="p">(</span><span class="mi">2</span> <span class="o">|</span> <span class="p">(</span><span class="n">p</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">p</span> <span class="o">//</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">11</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">maxdist</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">p</span><span class="p">,</span> <span class="p">(</span><span class="mi">2</span> <span class="o">|</span> <span class="p">(</span><span class="n">p</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">p</span> <span class="o">//</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">11</span><span class="p">)</span>
    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"distance too large"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">lzma_chunk</span><span class="p">(</span><span class="n">tokens</span><span class="p">,</span> <span class="n">out_pos</span><span class="p">):</span>
    <span class="s">"""编一个 LZMA chunk（0xC0: state+props reset，无 dict reset，无 end marker）。"""</span>
    <span class="n">enc</span> <span class="o">=</span> <span class="n">LzmaEncoder</span><span class="p">()</span>
    <span class="n">enc</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">out_pos</span>
    <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">tokens</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">t</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">"m"</span><span class="p">:</span>
            <span class="n">enc</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
            <span class="n">total</span> <span class="o">+=</span> <span class="n">t</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">enc</span><span class="p">.</span><span class="n">rep_match</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
            <span class="n">total</span> <span class="o">+=</span> <span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">enc</span><span class="p">.</span><span class="n">finish</span><span class="p">()</span>
    <span class="k">assert</span> <span class="n">total</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="mi">65536</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="mi">65536</span>
    <span class="n">hdr</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xC0</span><span class="p">])</span> <span class="o">+</span> <span class="p">(</span><span class="n">total</span> <span class="o">-</span> <span class="mi">1</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"big"</span><span class="p">)</span> <span class="o">+</span> \
          <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"big"</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">PROPS_BYTE</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">hdr</span> <span class="o">+</span> <span class="n">data</span><span class="p">,</span> <span class="n">total</span>


<span class="c1"># ---------- tar 原语 ----------
</span>
<span class="k">def</span> <span class="nf">tar_header</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">mtime</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="mo">0o644</span><span class="p">,</span> <span class="n">typeflag</span><span class="o">=</span><span class="sa">b</span><span class="s">'0'</span><span class="p">,</span> <span class="n">uid</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">gid</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
    <span class="s">"""构建 512 字节 POSIX ustar tar header。
    注意：tar 数值字段包含 null 终止符，slice 范围必须精确匹配字段宽度。
    mode/uid/gid 各 8 字节（7 octal + NUL），size/mtime 各 12 字节（11 octal + NUL）。
    """</span>
    <span class="n">h</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="mi">512</span><span class="p">)</span>
    <span class="n">name_bytes</span> <span class="o">=</span> <span class="n">name</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)[:</span><span class="mi">100</span><span class="p">]</span>
    <span class="n">h</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nb">len</span><span class="p">(</span><span class="n">name_bytes</span><span class="p">)]</span> <span class="o">=</span> <span class="n">name_bytes</span>
    <span class="n">h</span><span class="p">[</span><span class="mi">100</span><span class="p">:</span><span class="mi">108</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'%07o</span><span class="se">\x00</span><span class="s">'</span> <span class="o">%</span> <span class="n">mode</span>       <span class="c1"># 8 bytes (field 100-107)
</span>    <span class="n">h</span><span class="p">[</span><span class="mi">108</span><span class="p">:</span><span class="mi">116</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'%07o</span><span class="se">\x00</span><span class="s">'</span> <span class="o">%</span> <span class="n">uid</span>        <span class="c1"># 8 bytes (field 108-115)
</span>    <span class="n">h</span><span class="p">[</span><span class="mi">116</span><span class="p">:</span><span class="mi">124</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'%07o</span><span class="se">\x00</span><span class="s">'</span> <span class="o">%</span> <span class="n">gid</span>        <span class="c1"># 8 bytes (field 116-123)
</span>    <span class="n">h</span><span class="p">[</span><span class="mi">124</span><span class="p">:</span><span class="mi">136</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'%011o</span><span class="se">\x00</span><span class="s">'</span> <span class="o">%</span> <span class="n">size</span>      <span class="c1"># 12 bytes (field 124-135)
</span>    <span class="n">h</span><span class="p">[</span><span class="mi">136</span><span class="p">:</span><span class="mi">148</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'%011o</span><span class="se">\x00</span><span class="s">'</span> <span class="o">%</span> <span class="n">mtime</span>     <span class="c1"># 12 bytes (field 136-147)
</span>    <span class="n">h</span><span class="p">[</span><span class="mi">148</span><span class="p">:</span><span class="mi">156</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'        '</span>              <span class="c1"># checksum placeholder (8 spaces)
</span>    <span class="n">h</span><span class="p">[</span><span class="mi">156</span><span class="p">:</span><span class="mi">157</span><span class="p">]</span> <span class="o">=</span> <span class="n">typeflag</span>
    <span class="n">h</span><span class="p">[</span><span class="mi">257</span><span class="p">:</span><span class="mi">263</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'ustar</span><span class="se">\x00</span><span class="s">'</span>
    <span class="n">h</span><span class="p">[</span><span class="mi">263</span><span class="p">:</span><span class="mi">265</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'00'</span>
    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="o">==</span> <span class="mi">512</span><span class="p">,</span> <span class="s">"header length %d != 512"</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
    <span class="c1"># 计算校验和：所有字节之和，checksum 字段视为空格
</span>    <span class="n">chksum</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mo">0o7777777</span>
    <span class="n">h</span><span class="p">[</span><span class="mi">148</span><span class="p">:</span><span class="mi">156</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'%06o</span><span class="se">\x00</span><span class="s"> '</span> <span class="o">%</span> <span class="n">chksum</span>    <span class="c1"># 6 octal + NUL + space = 8 bytes
</span>    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="o">==</span> <span class="mi">512</span><span class="p">,</span> <span class="s">"header length %d != 512 after checksum"</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>


<span class="c1"># ---------- xz 原语 ----------
</span>
<span class="k">def</span> <span class="nf">xz_varint</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
    <span class="s">"""xz 变长整数编码：每字节 7 位数据（LSB first），MSB 为续位。"""</span>
    <span class="n">b</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">while</span> <span class="n">v</span> <span class="o">&gt;=</span> <span class="mh">0x80</span><span class="p">:</span>
        <span class="n">b</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">v</span> <span class="o">&amp;</span> <span class="mh">0x7F</span><span class="p">)</span> <span class="o">|</span> <span class="mh">0x80</span><span class="p">)</span>
        <span class="n">v</span> <span class="o">&gt;&gt;=</span> <span class="mi">7</span>
    <span class="n">b</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">v</span> <span class="o">&amp;</span> <span class="mh">0x7F</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">xz_stream_header</span><span class="p">():</span>
    <span class="s">"""12 字节 xz stream header (CHECK_CRC64)。
    Stream Flags = [reserved(0x00), check_type(CRC64=0x04)]。
    """</span>
    <span class="n">flags</span> <span class="o">=</span> <span class="n">_XZ_FLAGS</span>
    <span class="n">crc</span> <span class="o">=</span> <span class="n">crc32</span><span class="p">(</span><span class="n">flags</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">XZ_MAGIC</span> <span class="o">+</span> <span class="n">flags</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;I'</span><span class="p">,</span> <span class="n">crc</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">xz_block_header</span><span class="p">(</span><span class="n">dict_size</span><span class="p">):</span>
    <span class="s">"""xz block header（单 LZMA2 filter）。
    返回 (header_bytes, 实际字典大小)；header 长度即 block header 总长度。
    """</span>
    <span class="n">prop</span><span class="p">,</span> <span class="n">real_dict</span> <span class="o">=</span> <span class="n">dict_prop_for</span><span class="p">(</span><span class="n">dict_size</span><span class="p">)</span>
    <span class="c1"># filter flags: filter_id(1) + props_size(1) + props(1=prop byte)
</span>    <span class="n">filter_flags</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">LZMA2_FILTER_ID</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">prop</span><span class="p">])</span>
    <span class="c1"># block header content: flags(1, 0x00=1 filter) + filter_flags
</span>    <span class="n">content</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0x00</span><span class="p">])</span> <span class="o">+</span> <span class="n">filter_flags</span>
    <span class="c1"># 填充到 (1 + len(content) + 4) 是 4 的倍数
</span>    <span class="n">total</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">content</span><span class="p">)</span> <span class="o">+</span> <span class="mi">4</span>
    <span class="n">pad</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4</span> <span class="o">-</span> <span class="n">total</span> <span class="o">%</span> <span class="mi">4</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span>
    <span class="n">content</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="n">pad</span>
    <span class="n">bh</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">content</span><span class="p">)</span> <span class="o">+</span> <span class="mi">4</span>
    <span class="k">assert</span> <span class="n">bh</span> <span class="o">%</span> <span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"bh not 4-aligned: %d"</span> <span class="o">%</span> <span class="n">bh</span>
    <span class="n">crc</span> <span class="o">=</span> <span class="n">crc32</span><span class="p">(</span><span class="nb">bytes</span><span class="p">([</span><span class="n">bh</span> <span class="o">//</span> <span class="mi">4</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="n">content</span><span class="p">)</span>
    <span class="n">header</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">bh</span> <span class="o">//</span> <span class="mi">4</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="n">content</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;I'</span><span class="p">,</span> <span class="n">crc</span><span class="p">)</span>
    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">header</span><span class="p">)</span> <span class="o">==</span> <span class="n">bh</span>
    <span class="k">return</span> <span class="n">header</span><span class="p">,</span> <span class="n">real_dict</span>


<span class="k">def</span> <span class="nf">xz_index</span><span class="p">(</span><span class="n">records</span><span class="p">):</span>
    <span class="s">"""xz index。records = [(unpadded_size, uncompressed_size), ...]。"""</span>
    <span class="n">data</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0x00</span><span class="p">])</span>  <span class="c1"># index indicator
</span>    <span class="n">data</span> <span class="o">+=</span> <span class="n">xz_varint</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">records</span><span class="p">))</span>
    <span class="k">for</span> <span class="n">unpadded</span><span class="p">,</span> <span class="n">uncompressed</span> <span class="ow">in</span> <span class="n">records</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">+=</span> <span class="n">xz_varint</span><span class="p">(</span><span class="n">unpadded</span><span class="p">)</span>
        <span class="n">data</span> <span class="o">+=</span> <span class="n">xz_varint</span><span class="p">(</span><span class="n">uncompressed</span><span class="p">)</span>
    <span class="c1"># 填充到 (len(data) + 4) 是 4 的倍数
</span>    <span class="n">pad</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4</span> <span class="o">-</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span>
    <span class="n">data</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="n">pad</span>
    <span class="k">return</span> <span class="n">data</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;I'</span><span class="p">,</span> <span class="n">crc32</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>


<span class="k">def</span> <span class="nf">xz_stream_footer</span><span class="p">(</span><span class="n">backward_size</span><span class="p">):</span>
    <span class="s">"""12 字节 xz stream footer (CHECK_CRC64)。
    backward_size = (index_size / 4) - 1。
    Stream Flags = [reserved(0x00), check_type(CRC64=0x04)]。
    CRC32 覆盖 Backward Size + Stream Flags（6 字节，不含 CRC32 和 Footer Magic）。
    """</span>
    <span class="n">flags</span> <span class="o">=</span> <span class="n">_XZ_FLAGS</span>
    <span class="n">crc_data</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;I'</span><span class="p">,</span> <span class="n">backward_size</span><span class="p">)</span> <span class="o">+</span> <span class="n">flags</span>  <span class="c1"># 6 bytes, no magic
</span>    <span class="n">crc</span> <span class="o">=</span> <span class="n">crc32</span><span class="p">(</span><span class="n">crc_data</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;I'</span><span class="p">,</span> <span class="n">crc</span><span class="p">)</span> <span class="o">+</span> <span class="n">crc_data</span> <span class="o">+</span> <span class="n">XZ_FOOTER_MAGIC</span>


<span class="c1"># ============================================================
# Part 3: 多文件 tar.xz quine 构造器
# ============================================================
</span>
<span class="k">class</span> <span class="nc">TarXzQuine</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">root</span><span class="p">,</span> <span class="n">quine_name</span><span class="o">=</span><span class="s">"quine.tar.xz"</span><span class="p">,</span> <span class="n">seed_name</span><span class="o">=</span><span class="s">".this_is_mayx_blog"</span><span class="p">,</span>
                 <span class="n">src_dir</span><span class="o">=</span><span class="s">""</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">quine_name</span> <span class="o">=</span> <span class="n">quine_name</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">seed_name</span> <span class="o">=</span> <span class="n">seed_name</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">src_dir</span> <span class="o">=</span> <span class="n">src_dir</span><span class="p">.</span><span class="n">strip</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">mtime</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">())</span>
        <span class="c1"># walk 源目录
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">files</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">src_dir</span><span class="p">:</span>
            <span class="n">parts</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">src_dir</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
            <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="s">"/"</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">parts</span><span class="p">[:</span><span class="n">i</span><span class="p">]),</span> <span class="bp">True</span><span class="p">))</span>
        <span class="k">for</span> <span class="n">dirpath</span><span class="p">,</span> <span class="n">dirnames</span><span class="p">,</span> <span class="n">filenames</span> <span class="ow">in</span> <span class="n">os</span><span class="p">.</span><span class="n">walk</span><span class="p">(</span><span class="n">root</span><span class="p">):</span>
            <span class="n">dirnames</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>
            <span class="k">for</span> <span class="n">dn</span> <span class="ow">in</span> <span class="n">dirnames</span><span class="p">:</span>
                <span class="n">rel</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">relpath</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">dirpath</span><span class="p">,</span> <span class="n">dn</span><span class="p">),</span> <span class="n">root</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">sep</span><span class="p">,</span> <span class="s">"/"</span><span class="p">)</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">_prefixed</span><span class="p">(</span><span class="n">rel</span><span class="p">),</span> <span class="bp">True</span><span class="p">))</span>
            <span class="k">for</span> <span class="n">fn</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">filenames</span><span class="p">):</span>
                <span class="n">p</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">dirpath</span><span class="p">,</span> <span class="n">fn</span><span class="p">)</span>
                <span class="n">rel</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">relpath</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">root</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">sep</span><span class="p">,</span> <span class="s">"/"</span><span class="p">)</span>
                <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">fp</span><span class="p">:</span>
                    <span class="n">data</span> <span class="o">=</span> <span class="n">fp</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">_prefixed</span><span class="p">(</span><span class="n">rel</span><span class="p">),</span> <span class="bp">False</span><span class="p">))</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">_prefixed</span><span class="p">(</span><span class="n">rel</span><span class="p">),</span> <span class="n">data</span><span class="p">))</span>
        <span class="c1"># rel -&gt; data 映射，避免下面三处按 rel 回头线性搜索整个 files 列表
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">file_map</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dirs</span> <span class="o">=</span> <span class="p">[</span><span class="n">r</span> <span class="k">for</span> <span class="n">r</span><span class="p">,</span> <span class="n">isd</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span> <span class="k">if</span> <span class="n">isd</span><span class="p">]</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">content</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">data</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">_prefixed</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">rel</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">src_dir</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="n">rel</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">src_dir</span> <span class="k">else</span> <span class="n">rel</span>

    <span class="k">def</span> <span class="nf">tar_entries</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">,</span> <span class="n">xz_size</span><span class="p">):</span>
        <span class="s">"""计算 tar 归档中每个条目的 (header, data_size, is_dir)。
        顺序：seed, walk_entries (interleaved files+dirs), quine。
        """</span>
        <span class="n">entries</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="c1"># seed 文件
</span>        <span class="n">seed_hdr</span> <span class="o">=</span> <span class="n">tar_header</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">seed_name</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">mtime</span><span class="p">)</span>
        <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">seed_name</span><span class="p">,</span> <span class="s">"hdr"</span><span class="p">:</span> <span class="n">seed_hdr</span><span class="p">,</span>
                        <span class="s">"data_size"</span><span class="p">:</span> <span class="n">d_seed</span><span class="p">,</span> <span class="s">"is_dir"</span><span class="p">:</span> <span class="bp">False</span><span class="p">})</span>
        <span class="c1"># walk 顺序的文件和目录
</span>        <span class="k">for</span> <span class="n">rel</span><span class="p">,</span> <span class="n">isd</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">isd</span><span class="p">:</span>
                <span class="n">hdr</span> <span class="o">=</span> <span class="n">tar_header</span><span class="p">(</span><span class="n">rel</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">mtime</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="mo">0o755</span><span class="p">,</span> <span class="n">typeflag</span><span class="o">=</span><span class="sa">b</span><span class="s">'5'</span><span class="p">)</span>
                <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"name"</span><span class="p">:</span> <span class="n">rel</span><span class="p">,</span> <span class="s">"hdr"</span><span class="p">:</span> <span class="n">hdr</span><span class="p">,</span>
                                <span class="s">"data_size"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"is_dir"</span><span class="p">:</span> <span class="bp">True</span><span class="p">})</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="c1"># file_map 与 walk_entries 在 __init__ 里同步构造，这里必有
</span>                <span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">file_map</span><span class="p">[</span><span class="n">rel</span><span class="p">]</span>
                <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"name"</span><span class="p">:</span> <span class="n">rel</span><span class="p">,</span> <span class="s">"hdr"</span><span class="p">:</span> <span class="n">tar_header</span><span class="p">(</span><span class="n">rel</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span> <span class="bp">self</span><span class="p">.</span><span class="n">mtime</span><span class="p">),</span>
                                <span class="s">"data_size"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span> <span class="s">"is_dir"</span><span class="p">:</span> <span class="bp">False</span><span class="p">})</span>
        <span class="c1"># quine 文件（放在最后）
</span>        <span class="n">qhdr</span> <span class="o">=</span> <span class="n">tar_header</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">quine_name</span><span class="p">,</span> <span class="n">xz_size</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">mtime</span><span class="p">)</span>
        <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">quine_name</span><span class="p">,</span> <span class="s">"hdr"</span><span class="p">:</span> <span class="n">qhdr</span><span class="p">,</span>
                        <span class="s">"data_size"</span><span class="p">:</span> <span class="n">xz_size</span><span class="p">,</span> <span class="s">"is_dir"</span><span class="p">:</span> <span class="bp">False</span><span class="p">})</span>
        <span class="k">return</span> <span class="n">entries</span>

    <span class="c1"># ---------- 结构计算 ----------
</span>    <span class="k">def</span> <span class="nf">_compute_q</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">):</span>
        <span class="s">"""计算 xz_bytes 在 tar 中的偏移 q。
        q = seed + files + dirs 的 tar 占用 + quine tar header。
        q 不依赖 xz_size（quine 的 data 在 q 之后）。"""</span>
        <span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="c1"># seed 文件
</span>        <span class="n">q</span> <span class="o">+=</span> <span class="n">TAR_BLOCK</span>  <span class="c1"># seed tar header
</span>        <span class="n">q</span> <span class="o">+=</span> <span class="n">round_up_512</span><span class="p">(</span><span class="n">d_seed</span><span class="p">)</span>      <span class="c1"># seed data + pad
</span>        <span class="c1"># walk 顺序的文件和目录
</span>        <span class="k">for</span> <span class="n">rel</span><span class="p">,</span> <span class="n">isd</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">walk_entries</span><span class="p">:</span>
            <span class="n">q</span> <span class="o">+=</span> <span class="n">TAR_BLOCK</span>  <span class="c1"># tar header
</span>            <span class="k">if</span> <span class="ow">not</span> <span class="n">isd</span><span class="p">:</span>
                <span class="n">q</span> <span class="o">+=</span> <span class="n">round_up_512</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">file_map</span><span class="p">[</span><span class="n">rel</span><span class="p">]))</span>
        <span class="c1"># quine tar header
</span>        <span class="n">q</span> <span class="o">+=</span> <span class="n">TAR_BLOCK</span>
        <span class="k">return</span> <span class="n">q</span>

    <span class="k">def</span> <span class="nf">_trim_layouts</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="n">o_g</span><span class="p">,</span> <span class="n">f_g</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">w_plant_off</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">):</span>
        <span class="s">"""枚举 trim 配置，产出一批「相位」不同的候选布局。

        trim = [可选的裸 store chunk，载荷长度 x0] + 一组标准 gear
               （store 载荷 x = s+3，紧接着一个 lzma 复制这 x 字节）。

        裸 store 把 slip 抬高 3、把 f 抬高 3+x0；随后这组 gear 把 slip 重新归一到
        len(cb)，jump 再归一到 -3。净效果是 (o_pre, f_pre) 被整体平移
        δ ≈ s_g + 6 + len(cb) + 3 + x0，而 δ 决定了自洽方程的模 4 相位。
        旧实现 δ 恒为 0，相位一旦不对就永远无解 —— 而 d_seed += 1 根本不改变
        布局（q 只在跨 512 字节块时才变），所以那 80 次重试是纯粹的无效空转。

        注意：不能只插一个裸的 lzma 复制块。lzma 复制块的输出必须等于
        F[o-q : o-q+olen]，而这只能靠前面那个「载荷 = F[o-q : o-q+x] 且 x = s+3」
        的 store chunk 把字节搬到正确位置来实现；裸插复制块解压出来和 F 对不上。

        返回 [(chunks, o_before_jump, f_before_jump, y, jb), ...]，按文件体积升序。
        """</span>
        <span class="n">s_g</span> <span class="o">=</span> <span class="n">f_g</span> <span class="o">-</span> <span class="p">(</span><span class="n">o_g</span> <span class="o">-</span> <span class="n">q</span><span class="p">)</span>
        <span class="n">out</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">seen</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">x0</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">s_g</span> <span class="o">+</span> <span class="mi">4</span><span class="p">):</span>
            <span class="n">chunks</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">base</span><span class="p">)</span>
            <span class="n">o</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">o_g</span><span class="p">,</span> <span class="n">f_g</span>
            <span class="k">if</span> <span class="n">x0</span><span class="p">:</span>
                <span class="c1"># 裸 store：载荷 = F[o-q : o-q+x0]，要求 x0 &lt;= s+3 以免读到尚未定稿的字节
</span>                <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"store"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">x0</span><span class="p">,</span>
                               <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">x0</span><span class="p">})</span>
                <span class="n">o</span> <span class="o">+=</span> <span class="n">x0</span>
                <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">x0</span>
            <span class="n">o</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">_add_gear</span><span class="p">(</span><span class="n">chunks</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">q</span><span class="p">)</span>   <span class="c1"># 一组标准 gear
</span>            <span class="c1"># jump 不动点：y = s + 3 + len(jb)
</span>            <span class="n">s</span> <span class="o">=</span> <span class="n">f</span> <span class="o">-</span> <span class="p">(</span><span class="n">o</span> <span class="o">-</span> <span class="n">q</span><span class="p">)</span>
            <span class="n">y</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">s</span> <span class="o">+</span> <span class="mi">17</span><span class="p">)</span>
            <span class="n">jb</span> <span class="o">=</span> <span class="n">ju</span> <span class="o">=</span> <span class="bp">None</span>
            <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">64</span><span class="p">):</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="n">jb</span><span class="p">,</span> <span class="n">ju</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">([(</span><span class="s">"m"</span><span class="p">,</span> <span class="n">o</span> <span class="o">-</span> <span class="n">w_plant_off</span><span class="p">,</span> <span class="n">y</span><span class="p">)],</span> <span class="n">o</span><span class="p">)</span>
                <span class="k">except</span> <span class="nb">AssertionError</span><span class="p">:</span>
                    <span class="n">jb</span> <span class="o">=</span> <span class="bp">None</span>
                    <span class="k">break</span>
                <span class="n">y2</span> <span class="o">=</span> <span class="n">s</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">jb</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">y2</span> <span class="o">==</span> <span class="n">y</span><span class="p">:</span>
                    <span class="k">break</span>
                <span class="k">if</span> <span class="n">y2</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">y2</span> <span class="o">&gt;</span> <span class="mi">273</span><span class="p">:</span>
                    <span class="n">jb</span> <span class="o">=</span> <span class="bp">None</span>
                    <span class="k">break</span>
                <span class="n">y</span> <span class="o">=</span> <span class="n">y2</span>
            <span class="k">if</span> <span class="n">jb</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">or</span> <span class="n">ju</span> <span class="o">!=</span> <span class="n">y</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="k">if</span> <span class="n">y</span> <span class="o">+</span> <span class="n">w_plant_off</span> <span class="o">-</span> <span class="n">TAR_BLOCK</span> <span class="o">&gt;</span> <span class="n">d_seed</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">key</span> <span class="o">=</span> <span class="p">(</span><span class="n">o</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span> <span class="n">f</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">jb</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">seen</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">seen</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
            <span class="n">out</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">chunks</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">jb</span><span class="p">))</span>

        <span class="n">out</span><span class="p">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">t</span><span class="p">:</span> <span class="n">t</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">4</span><span class="p">]))</span>
        <span class="k">return</span> <span class="n">out</span>

    <span class="k">def</span> <span class="nf">_solve_gadget</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">chunks_pre</span><span class="p">,</span> <span class="n">o_j</span><span class="p">,</span> <span class="n">f_j</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">jb</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">bh</span><span class="p">,</span> <span class="n">FEW</span><span class="p">,</span>
                      <span class="n">block_hdr</span><span class="p">,</span> <span class="n">dict_size</span><span class="p">,</span> <span class="n">hdrA</span><span class="p">,</span> <span class="n">hdrB</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
        <span class="s">"""在一个给定相位的布局上求解 gadget（T / gb / suffix / tz / k）。

        关键简化：tz 只依赖 u = gb + suffix 与 k；T = u + tz + 4 也只依赖 (u, k)；
        而 gb 又由 T 唯一确定（len(lzma_chunk(matches_for(T, T))))。
        因此只需一维枚举 u 即可遍历全部解，既不漏解也远快于原来的三重暴力。
        """</span>
        <span class="n">o_pre</span> <span class="o">=</span> <span class="n">o_j</span> <span class="o">+</span> <span class="n">y</span>
        <span class="n">f_pre</span> <span class="o">=</span> <span class="n">f_j</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">jb</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">f_pre</span> <span class="o">!=</span> <span class="p">(</span><span class="n">o_pre</span> <span class="o">-</span> <span class="n">q</span><span class="p">)</span> <span class="o">-</span> <span class="mi">3</span><span class="p">:</span>      <span class="c1"># jump 应把 slip 归一到 -3
</span>            <span class="k">return</span> <span class="bp">None</span>

        <span class="n">chunks</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">chunks_pre</span><span class="p">)</span>
        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"lzma"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f_j</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">jb</span><span class="p">),</span>
                       <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o_j</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">y</span><span class="p">,</span> <span class="s">"bytes"</span><span class="p">:</span> <span class="n">jb</span><span class="p">,</span> <span class="s">"jump"</span><span class="p">:</span> <span class="bp">True</span><span class="p">})</span>

        <span class="n">best</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="k">for</span> <span class="n">u</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">512</span><span class="p">):</span>
            <span class="c1"># 512k 必须落在 [f_pre + 2u + 2059, f_pre + 2u + 3595)
</span>            <span class="n">lo</span> <span class="o">=</span> <span class="n">f_pre</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">u</span> <span class="o">+</span> <span class="mi">2059</span>
            <span class="n">hi</span> <span class="o">=</span> <span class="n">f_pre</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">u</span> <span class="o">+</span> <span class="mi">3595</span>
            <span class="k">for</span> <span class="n">k_cand</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="n">lo</span> <span class="o">-</span> <span class="mi">512</span><span class="p">)</span> <span class="o">//</span> <span class="mi">512</span><span class="p">),</span> <span class="p">(</span><span class="n">hi</span> <span class="o">+</span> <span class="mi">512</span><span class="p">)</span> <span class="o">//</span> <span class="mi">512</span> <span class="o">+</span> <span class="mi">2</span><span class="p">):</span>
                <span class="n">num</span> <span class="o">=</span> <span class="n">k_cand</span> <span class="o">*</span> <span class="mi">512</span> <span class="o">-</span> <span class="p">(</span><span class="n">o_pre</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">u</span> <span class="o">+</span> <span class="mi">8</span> <span class="o">-</span> <span class="n">q</span> <span class="o">-</span> <span class="mi">1024</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">num</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
                    <span class="k">continue</span>
                <span class="n">tz_cand</span> <span class="o">=</span> <span class="n">num</span> <span class="o">//</span> <span class="mi">3</span>
                <span class="k">if</span> <span class="n">tz_cand</span> <span class="o">&lt;</span> <span class="mi">1024</span><span class="p">:</span>
                    <span class="k">continue</span>
                <span class="n">T</span> <span class="o">=</span> <span class="n">u</span> <span class="o">+</span> <span class="n">tz_cand</span> <span class="o">+</span> <span class="mi">4</span>
                <span class="k">if</span> <span class="n">T</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
                    <span class="k">continue</span>
                <span class="n">gb</span><span class="p">,</span> <span class="n">gu</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">(</span><span class="n">matches_for</span><span class="p">(</span><span class="n">T</span><span class="p">,</span> <span class="n">T</span><span class="p">),</span> <span class="n">o_pre</span> <span class="o">+</span> <span class="n">T</span><span class="p">)</span>
                <span class="n">gb_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">gb</span><span class="p">)</span>
                <span class="n">suffix</span> <span class="o">=</span> <span class="n">u</span> <span class="o">-</span> <span class="n">gb_len</span>
                <span class="k">if</span> <span class="n">suffix</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
                    <span class="k">continue</span>

                <span class="c1"># 精确自检（不再用带 9 字节偏差的预筛公式）
</span>                <span class="n">f_final</span> <span class="o">=</span> <span class="n">f_pre</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">T</span> <span class="o">+</span> <span class="n">gb_len</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">tz_cand</span> <span class="o">+</span> <span class="mi">1</span>
                <span class="n">n</span> <span class="o">=</span> <span class="n">f_final</span> <span class="o">-</span> <span class="p">(</span><span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span><span class="p">)</span>
                <span class="n">bp</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4</span> <span class="o">-</span> <span class="p">(</span><span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="n">n</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span>
                <span class="n">unpadded</span> <span class="o">=</span> <span class="n">bh</span> <span class="o">+</span> <span class="n">n</span> <span class="o">+</span> <span class="n">CHECK_SIZE</span>
                <span class="n">idx</span> <span class="o">=</span> <span class="n">xz_index</span><span class="p">([(</span><span class="n">unpadded</span><span class="p">,</span> <span class="n">q</span> <span class="o">+</span> <span class="n">k_cand</span> <span class="o">*</span> <span class="mi">512</span> <span class="o">+</span> <span class="mi">1024</span><span class="p">)])</span>
                <span class="n">ix</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span>
                <span class="n">suffix_actual</span> <span class="o">=</span> <span class="n">bp</span> <span class="o">+</span> <span class="n">CHECK_SIZE</span> <span class="o">+</span> <span class="n">ix</span> <span class="o">+</span> <span class="mi">12</span>
                <span class="k">if</span> <span class="n">suffix_actual</span> <span class="o">!=</span> <span class="n">suffix</span><span class="p">:</span>
                    <span class="k">continue</span>
                <span class="n">xz_size</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="n">n</span> <span class="o">+</span> <span class="n">bp</span> <span class="o">+</span> <span class="n">CHECK_SIZE</span> <span class="o">+</span> <span class="n">ix</span> <span class="o">+</span> <span class="mi">12</span>
                <span class="n">xz_pad_actual</span> <span class="o">=</span> <span class="n">round_up_512</span><span class="p">(</span><span class="n">xz_size</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">xz_pad_actual</span> <span class="o">!=</span> <span class="n">k_cand</span> <span class="o">*</span> <span class="mi">512</span><span class="p">:</span>
                    <span class="k">continue</span>
                <span class="n">total_w_actual</span> <span class="o">=</span> <span class="n">q</span> <span class="o">+</span> <span class="n">xz_pad_actual</span> <span class="o">+</span> <span class="mi">1024</span>
                <span class="n">o_final</span> <span class="o">=</span> <span class="n">o_pre</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">T</span> <span class="o">+</span> <span class="n">tz_cand</span>
                <span class="k">if</span> <span class="n">o_final</span> <span class="o">!=</span> <span class="n">total_w_actual</span><span class="p">:</span>
                    <span class="k">continue</span>

                <span class="k">if</span> <span class="n">best</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="n">xz_size</span> <span class="o">&gt;=</span> <span class="n">best</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]:</span>
                    <span class="k">continue</span>          <span class="c1"># 已找到更小的解，跳过
</span>
                <span class="n">backward_size</span> <span class="o">=</span> <span class="n">ix</span> <span class="o">//</span> <span class="mi">4</span> <span class="o">-</span> <span class="mi">1</span>
                <span class="n">final</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">chunks</span><span class="p">)</span>
                <span class="n">final</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"store"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f_pre</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">T</span><span class="p">,</span>
                              <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o_pre</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span> <span class="s">"vac"</span><span class="p">:</span> <span class="bp">True</span><span class="p">})</span>
                <span class="n">final</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"lzma"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f_pre</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">T</span><span class="p">,</span>
                              <span class="s">"size"</span><span class="p">:</span> <span class="n">gb_len</span><span class="p">,</span> <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o_pre</span> <span class="o">+</span> <span class="n">T</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">gu</span><span class="p">,</span>
                              <span class="s">"bytes"</span><span class="p">:</span> <span class="n">gb</span><span class="p">})</span>
                <span class="n">final</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"store"</span><span class="p">,</span>
                              <span class="s">"foff"</span><span class="p">:</span> <span class="n">f_pre</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">T</span> <span class="o">+</span> <span class="n">gb_len</span><span class="p">,</span>
                              <span class="s">"size"</span><span class="p">:</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">tz_cand</span><span class="p">,</span> <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o_pre</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">T</span><span class="p">,</span>
                              <span class="s">"olen"</span><span class="p">:</span> <span class="n">tz_cand</span><span class="p">,</span> <span class="s">"trailing"</span><span class="p">:</span> <span class="bp">True</span><span class="p">})</span>
                <span class="n">final</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"term"</span><span class="p">,</span>
                              <span class="s">"foff"</span><span class="p">:</span> <span class="n">f_pre</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">T</span> <span class="o">+</span> <span class="n">gb_len</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">tz_cand</span><span class="p">,</span>
                              <span class="s">"size"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o_final</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
                              <span class="s">"bytes"</span><span class="p">:</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">})</span>
                <span class="n">best</span> <span class="o">=</span> <span class="p">{</span>
                    <span class="s">"q"</span><span class="p">:</span> <span class="n">q</span><span class="p">,</span> <span class="s">"d_seed"</span><span class="p">:</span> <span class="n">d_seed</span><span class="p">,</span> <span class="s">"k"</span><span class="p">:</span> <span class="n">k</span><span class="p">,</span>
                    <span class="s">"chunks"</span><span class="p">:</span> <span class="n">final</span><span class="p">,</span> <span class="s">"block_hdr"</span><span class="p">:</span> <span class="n">block_hdr</span><span class="p">,</span> <span class="s">"bh"</span><span class="p">:</span> <span class="n">bh</span><span class="p">,</span>
                    <span class="s">"n"</span><span class="p">:</span> <span class="n">n</span><span class="p">,</span> <span class="s">"xz_size"</span><span class="p">:</span> <span class="n">xz_size</span><span class="p">,</span>
                    <span class="s">"T"</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span> <span class="s">"suffix"</span><span class="p">:</span> <span class="n">suffix_actual</span><span class="p">,</span>
                    <span class="s">"bp"</span><span class="p">:</span> <span class="n">bp</span><span class="p">,</span> <span class="s">"ix"</span><span class="p">:</span> <span class="n">ix</span><span class="p">,</span>
                    <span class="s">"dict_size"</span><span class="p">:</span> <span class="n">dict_size</span><span class="p">,</span>
                    <span class="s">"FEW"</span><span class="p">:</span> <span class="n">FEW</span><span class="p">,</span> <span class="s">"hdrA"</span><span class="p">:</span> <span class="n">hdrA</span><span class="p">,</span>
                    <span class="s">"hdrB"</span><span class="p">:</span> <span class="n">hdrB</span><span class="p">,</span> <span class="s">"jump_y"</span><span class="p">:</span> <span class="n">y</span><span class="p">,</span> <span class="s">"index"</span><span class="p">:</span> <span class="n">idx</span><span class="p">,</span>
                    <span class="s">"stream_hdr"</span><span class="p">:</span> <span class="n">xz_stream_header</span><span class="p">(),</span>
                    <span class="s">"footer"</span><span class="p">:</span> <span class="n">xz_stream_footer</span><span class="p">(</span><span class="n">backward_size</span><span class="p">),</span>
                    <span class="s">"entries"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">tar_entries</span><span class="p">(</span><span class="n">d_seed</span><span class="p">,</span> <span class="n">xz_size</span><span class="p">),</span>
                <span class="p">}</span>
        <span class="k">return</span> <span class="n">best</span>

    <span class="k">def</span> <span class="nf">_layout_iterate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">,</span> <span class="n">maxdist_hint</span><span class="p">):</span>
        <span class="s">"""核心布局。

        阶段 1a：C1 / repro / gears（只依赖 q，所有 trim 配置共用，只算一次）
        阶段 1b：插入 trim 微调块，平移 (o_pre, f_pre) 改变模 4 相位
        阶段 2  ：按 u = gb + suffix 一维搜索自洽解
        """</span>
        <span class="n">block_hdr</span><span class="p">,</span> <span class="n">dict_size</span> <span class="o">=</span> <span class="n">xz_block_header</span><span class="p">(</span><span class="n">maxdist_hint</span><span class="p">)</span>
        <span class="n">bh</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">block_hdr</span><span class="p">)</span>
        <span class="n">FEW</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="mi">3</span>
        <span class="n">w_hdrA_off</span> <span class="o">=</span> <span class="n">TAR_BLOCK</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span>
        <span class="n">w_hdrB_off</span> <span class="o">=</span> <span class="n">TAR_BLOCK</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="mi">3</span>
        <span class="n">w_plant_off</span> <span class="o">=</span> <span class="n">TAR_BLOCK</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="mi">6</span>

        <span class="n">q</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_compute_q</span><span class="p">(</span><span class="n">d_seed</span><span class="p">)</span>

        <span class="c1"># ---------- 阶段 1a：C1 + repro + gears ----------
</span>        <span class="n">base</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">o</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span>
        <span class="n">Ls</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">rem</span> <span class="o">=</span> <span class="n">q</span> <span class="o">+</span> <span class="n">FEW</span>
        <span class="k">while</span> <span class="n">rem</span> <span class="o">&gt;</span> <span class="n">CHUNK</span><span class="p">:</span>
            <span class="n">Ls</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">CHUNK</span><span class="p">)</span>
            <span class="n">rem</span> <span class="o">-=</span> <span class="n">CHUNK</span>
        <span class="n">Ls</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">rem</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">Ls</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">,</span> <span class="s">"末 C1 chunk 载荷 %d 太小"</span> <span class="o">%</span> <span class="n">Ls</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
        <span class="n">k</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">Ls</span><span class="p">)</span>
        <span class="n">hdrA</span> <span class="o">=</span> <span class="n">store_hdr</span><span class="p">(</span><span class="n">CHUNK</span><span class="p">,</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
        <span class="n">hdrB</span> <span class="o">=</span> <span class="n">store_hdr</span><span class="p">(</span><span class="n">Ls</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">first</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>

        <span class="n">S</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">L</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">Ls</span><span class="p">):</span>
            <span class="n">base</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"store"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">L</span><span class="p">,</span>
                         <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">L</span><span class="p">,</span> <span class="s">"first"</span><span class="p">:</span> <span class="n">j</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span>
                         <span class="s">"c1"</span><span class="p">:</span> <span class="bp">True</span><span class="p">,</span> <span class="s">"S"</span><span class="p">:</span> <span class="n">S</span><span class="p">})</span>
            <span class="n">o</span> <span class="o">+=</span> <span class="n">L</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">L</span>
            <span class="n">S</span> <span class="o">+=</span> <span class="n">L</span>
        <span class="k">assert</span> <span class="n">o</span> <span class="o">==</span> <span class="n">q</span> <span class="o">+</span> <span class="n">FEW</span>

        <span class="n">S</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">L</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">Ls</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">j</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">srcpos</span> <span class="o">=</span> <span class="n">w_hdrA_off</span> <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">&lt;</span> <span class="n">k</span> <span class="o">-</span> <span class="mi">1</span> <span class="ow">or</span> <span class="n">L</span> <span class="o">==</span> <span class="n">CHUNK</span><span class="p">)</span> <span class="k">else</span> <span class="n">w_hdrB_off</span>
                <span class="n">dist_h</span> <span class="o">=</span> <span class="p">(</span><span class="n">q</span> <span class="o">+</span> <span class="n">FEW</span> <span class="o">+</span> <span class="n">S</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="p">(</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">-</span> <span class="n">srcpos</span>
                <span class="n">hb</span><span class="p">,</span> <span class="n">hu</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">([(</span><span class="s">"m"</span><span class="p">,</span> <span class="n">dist_h</span><span class="p">,</span> <span class="mi">3</span><span class="p">)],</span> <span class="n">o</span><span class="p">)</span>
                <span class="n">base</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"lzma"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">hb</span><span class="p">),</span>
                             <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">hu</span><span class="p">,</span> <span class="s">"bytes"</span><span class="p">:</span> <span class="n">hb</span><span class="p">})</span>
                <span class="n">o</span> <span class="o">+=</span> <span class="n">hu</span>
                <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">hb</span><span class="p">)</span>
            <span class="n">cb</span><span class="p">,</span> <span class="n">cu</span> <span class="o">=</span> <span class="n">lzma_chunk</span><span class="p">(</span><span class="n">matches_for</span><span class="p">(</span><span class="n">q</span> <span class="o">+</span> <span class="n">FEW</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">j</span><span class="p">,</span> <span class="n">L</span><span class="p">),</span> <span class="n">o</span><span class="p">)</span>
            <span class="n">base</span><span class="p">.</span><span class="n">append</span><span class="p">({</span><span class="s">"kind"</span><span class="p">:</span> <span class="s">"lzma"</span><span class="p">,</span> <span class="s">"foff"</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="s">"size"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">cb</span><span class="p">),</span>
                         <span class="s">"ooff"</span><span class="p">:</span> <span class="n">o</span><span class="p">,</span> <span class="s">"olen"</span><span class="p">:</span> <span class="n">cu</span><span class="p">,</span> <span class="s">"bytes"</span><span class="p">:</span> <span class="n">cb</span><span class="p">})</span>
            <span class="n">o</span> <span class="o">+=</span> <span class="n">cu</span>
            <span class="n">f</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">cb</span><span class="p">)</span>
            <span class="n">S</span> <span class="o">+=</span> <span class="n">L</span>

        <span class="c1"># gears：把 slip 压到 &lt;= 16
</span>        <span class="k">while</span> <span class="n">f</span> <span class="o">-</span> <span class="p">(</span><span class="n">o</span> <span class="o">-</span> <span class="n">q</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">16</span><span class="p">:</span>
            <span class="n">o</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">_add_gear</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">q</span><span class="p">)</span>

        <span class="n">o_g</span><span class="p">,</span> <span class="n">f_g</span> <span class="o">=</span> <span class="n">o</span><span class="p">,</span> <span class="n">f</span>

        <span class="c1"># ---------- 阶段 1b + 阶段 2 ----------
</span>        <span class="c1"># 相位按文件体积升序枚举；第一个成功的相位通常已经足够好，但不同相位的
</span>        <span class="c1"># (u, tz) 不同，xz_size 仍可能相差几百字节，所以再多看几个取最小的。
</span>        <span class="n">tried</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">best</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="n">since_hit</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">chunks_pre</span><span class="p">,</span> <span class="n">o_j</span><span class="p">,</span> <span class="n">f_j</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">jb</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">_trim_layouts</span><span class="p">(</span>
                <span class="n">base</span><span class="p">,</span> <span class="n">o_g</span><span class="p">,</span> <span class="n">f_g</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">w_plant_off</span><span class="p">,</span> <span class="n">d_seed</span><span class="p">):</span>
            <span class="n">lay</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_solve_gadget</span><span class="p">(</span><span class="n">chunks_pre</span><span class="p">,</span> <span class="n">o_j</span><span class="p">,</span> <span class="n">f_j</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">jb</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">bh</span><span class="p">,</span> <span class="n">FEW</span><span class="p">,</span>
                                     <span class="n">block_hdr</span><span class="p">,</span> <span class="n">dict_size</span><span class="p">,</span> <span class="n">hdrA</span><span class="p">,</span> <span class="n">hdrB</span><span class="p">,</span>
                                     <span class="n">d_seed</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
            <span class="n">tried</span> <span class="o">+=</span> <span class="mi">1</span>
            <span class="k">if</span> <span class="n">lay</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="p">(</span><span class="n">best</span> <span class="ow">is</span> <span class="bp">None</span>
                                    <span class="ow">or</span> <span class="n">lay</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">best</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]):</span>
                <span class="n">best</span> <span class="o">=</span> <span class="n">lay</span>
            <span class="k">if</span> <span class="n">best</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                <span class="n">since_hit</span> <span class="o">+=</span> <span class="mi">1</span>
                <span class="k">if</span> <span class="n">since_hit</span> <span class="o">&gt;=</span> <span class="mi">10</span><span class="p">:</span>
                    <span class="k">break</span>
            <span class="k">elif</span> <span class="n">tried</span> <span class="o">&gt;=</span> <span class="mi">80</span><span class="p">:</span>        <span class="c1"># 一直无解，放弃后续相位
</span>                <span class="k">break</span>
        <span class="k">if</span> <span class="n">best</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">best</span>

        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"layout 不收敛: 无自洽解 (q=%d, f_pre=%d, o_pre=%d)"</span>
                           <span class="o">%</span> <span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">f_g</span><span class="p">,</span> <span class="n">o_g</span><span class="p">))</span>

    <span class="c1"># ---------- 装配 ----------
</span>    <span class="k">def</span> <span class="nf">assemble</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lay</span><span class="p">):</span>
        <span class="s">"""构建 xz 文件 F 和 seed。"""</span>
        <span class="n">F</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">lay</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">])</span>
        <span class="n">q</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">]</span>
        <span class="n">d_seed</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"d_seed"</span><span class="p">]</span>
        <span class="n">bh</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"bh"</span><span class="p">]</span>
        <span class="n">FEW</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"FEW"</span><span class="p">]</span>

        <span class="c1"># bytearray 已全零初始化：block padding、CRC64 占位、trailing-zero 载荷
</span>        <span class="c1"># 都不必再显式写 0（后面几处赋值只会写到各自独立的区间，不会交叉覆盖）。
</span>
        <span class="c1"># pass A: 写 xz stream header + block header + 所有 chunk bytes + suffix
</span>        <span class="n">F</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"stream_hdr"</span><span class="p">]</span>
        <span class="n">F</span><span class="p">[</span><span class="mi">12</span><span class="p">:</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"block_hdr"</span><span class="p">]</span>

        <span class="n">jump_c</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">lay</span><span class="p">[</span><span class="s">"chunks"</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">==</span> <span class="s">"store"</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]:</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">store_hdr</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="s">"olen"</span><span class="p">],</span>
                                                       <span class="n">first</span><span class="o">=</span><span class="n">c</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"first"</span><span class="p">,</span> <span class="bp">False</span><span class="p">))</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">F</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]:</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="s">"size"</span><span class="p">]]</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"bytes"</span><span class="p">]</span>
                <span class="k">if</span> <span class="n">c</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"jump"</span><span class="p">):</span>
                    <span class="n">jump_c</span> <span class="o">=</span> <span class="n">c</span>

        <span class="c1"># block padding + CRC64(pass D 填) + index + footer
</span>        <span class="n">bp_off</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n"</span><span class="p">]</span>
        <span class="n">ix_off</span> <span class="o">=</span> <span class="n">bp_off</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"bp"</span><span class="p">]</span> <span class="o">+</span> <span class="n">CHECK_SIZE</span>
        <span class="n">F</span><span class="p">[</span><span class="n">ix_off</span><span class="p">:</span><span class="n">ix_off</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ix"</span><span class="p">]]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"index"</span><span class="p">]</span>
        <span class="n">sf_off</span> <span class="o">=</span> <span class="n">ix_off</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ix"</span><span class="p">]</span>
        <span class="n">F</span><span class="p">[</span><span class="n">sf_off</span><span class="p">:</span><span class="n">sf_off</span> <span class="o">+</span> <span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"footer"</span><span class="p">]</span>

        <span class="k">assert</span> <span class="n">jump_c</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span>

        <span class="c1"># pass B: 构建 seed
</span>        <span class="n">y</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"jump_y"</span><span class="p">]</span>
        <span class="n">plant</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="n">jump_c</span><span class="p">[</span><span class="s">"ooff"</span><span class="p">]</span> <span class="o">-</span> <span class="n">q</span><span class="p">:</span> <span class="n">jump_c</span><span class="p">[</span><span class="s">"ooff"</span><span class="p">]</span> <span class="o">-</span> <span class="n">q</span> <span class="o">+</span> <span class="n">y</span><span class="p">])</span>
        <span class="n">seed</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">d_seed</span><span class="p">)</span>
        <span class="n">seed</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"stream_hdr"</span><span class="p">]</span>
        <span class="n">seed</span><span class="p">[</span><span class="mi">12</span><span class="p">:</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"block_hdr"</span><span class="p">]</span>
        <span class="n">seed</span><span class="p">[</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="p">:</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"hdrA"</span><span class="p">]</span>
        <span class="n">seed</span><span class="p">[</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="o">+</span><span class="mi">3</span><span class="p">:</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="o">+</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"hdrB"</span><span class="p">]</span>
        <span class="n">seed</span><span class="p">[</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="o">+</span><span class="mi">6</span><span class="p">:</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="o">+</span><span class="mi">6</span><span class="o">+</span><span class="n">y</span><span class="p">]</span> <span class="o">=</span> <span class="n">plant</span>
        <span class="n">pad</span> <span class="o">=</span> <span class="p">(</span><span class="n">SEED_NOTE</span> <span class="o">*</span> <span class="p">(</span><span class="n">d_seed</span> <span class="o">//</span> <span class="nb">len</span><span class="p">(</span><span class="n">SEED_NOTE</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span><span class="p">))[:</span><span class="n">d_seed</span> <span class="o">-</span> <span class="mi">12</span> <span class="o">-</span> <span class="n">bh</span> <span class="o">-</span> <span class="mi">6</span> <span class="o">-</span> <span class="n">y</span><span class="p">]</span>
        <span class="n">seed</span><span class="p">[</span><span class="mi">12</span><span class="o">+</span><span class="n">bh</span><span class="o">+</span><span class="mi">6</span><span class="o">+</span><span class="n">y</span><span class="p">:]</span> <span class="o">=</span> <span class="n">pad</span>

        <span class="c1"># 构建 tar 前缀 T（含 seed data）
</span>        <span class="n">T</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_build_tar_prefix_with_seed</span><span class="p">(</span><span class="n">lay</span><span class="p">,</span> <span class="n">seed</span><span class="p">)</span>
        <span class="n">F_prefix</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">FEW</span><span class="p">])</span>
        <span class="n">P</span> <span class="o">=</span> <span class="n">T</span> <span class="o">+</span> <span class="n">F_prefix</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">P</span><span class="p">)</span> <span class="o">==</span> <span class="n">q</span> <span class="o">+</span> <span class="n">FEW</span><span class="p">,</span> <span class="s">"P 长度 %d != q+FEW %d"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">P</span><span class="p">),</span> <span class="n">q</span> <span class="o">+</span> <span class="n">FEW</span><span class="p">)</span>

        <span class="c1"># pass C: 填充 store payloads
</span>        <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">lay</span><span class="p">[</span><span class="s">"chunks"</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">[</span><span class="s">"kind"</span><span class="p">]</span> <span class="o">!=</span> <span class="s">"store"</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">ooff</span><span class="p">,</span> <span class="n">olen</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="s">"ooff"</span><span class="p">],</span> <span class="n">c</span><span class="p">[</span><span class="s">"olen"</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">c</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"c1"</span><span class="p">):</span>
                <span class="n">F</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span><span class="p">:</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">olen</span><span class="p">]</span> <span class="o">=</span> <span class="n">P</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"S"</span><span class="p">]:</span><span class="n">c</span><span class="p">[</span><span class="s">"S"</span><span class="p">]</span> <span class="o">+</span> <span class="n">olen</span><span class="p">]</span>
            <span class="k">elif</span> <span class="n">c</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"trailing"</span><span class="p">):</span>
                <span class="c1"># 载荷是尾部零：F 全零初始化，且唯一会写到 chunk 之外的 vac_store
</span>                <span class="c1"># 目标是 [f_pre+3, f_pre+3+T)（f_pre == (o_pre-q)-3 已由 _solve_gadget
</span>                <span class="c1"># 断言），与本区块不重叠，因此无需写入。
</span>                <span class="k">continue</span>
            <span class="k">elif</span> <span class="n">c</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"vac"</span><span class="p">):</span>
                <span class="n">_forward_vac</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">q</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="c1"># 其余 store 的载荷 = 输出流自身对应位置的字节（自引用复制）
</span>                <span class="n">fo</span> <span class="o">=</span> <span class="n">ooff</span> <span class="o">-</span> <span class="n">q</span>
                <span class="n">F</span><span class="p">[</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span><span class="p">:</span><span class="n">c</span><span class="p">[</span><span class="s">"foff"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">olen</span><span class="p">]</span> <span class="o">=</span> <span class="n">F</span><span class="p">[</span><span class="n">fo</span><span class="p">:</span><span class="n">fo</span> <span class="o">+</span> <span class="n">olen</span><span class="p">]</span>

        <span class="c1"># pass D: 求解 CRC64 自引用
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">_solve_crc64</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">T</span><span class="p">)</span>

        <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">F</span><span class="p">),</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">_build_tar_prefix_with_seed</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">seed</span><span class="p">):</span>
        <span class="s">"""构建 tar 前缀 T（含 seed data）。"""</span>
        <span class="n">entries</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"entries"</span><span class="p">]</span>
        <span class="n">T</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
            <span class="n">T</span> <span class="o">+=</span> <span class="n">e</span><span class="p">[</span><span class="s">"hdr"</span><span class="p">]</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">e</span><span class="p">[</span><span class="s">"is_dir"</span><span class="p">]</span> <span class="ow">and</span> <span class="n">e</span><span class="p">[</span><span class="s">"data_size"</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">data</span> <span class="o">=</span> <span class="n">seed</span> <span class="k">if</span> <span class="n">e</span><span class="p">[</span><span class="s">"name"</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="p">.</span><span class="n">seed_name</span> <span class="k">else</span> <span class="bp">self</span><span class="p">.</span><span class="n">file_map</span><span class="p">[</span><span class="n">e</span><span class="p">[</span><span class="s">"name"</span><span class="p">]]</span>
                <span class="n">T</span> <span class="o">+=</span> <span class="n">data</span>
                <span class="n">T</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="p">(</span><span class="n">round_up_512</span><span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="s">"data_size"</span><span class="p">])</span> <span class="o">-</span> <span class="n">e</span><span class="p">[</span><span class="s">"data_size"</span><span class="p">])</span>
        <span class="n">T</span> <span class="o">+=</span> <span class="n">entries</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="s">"hdr"</span><span class="p">]</span>  <span class="c1"># quine tar header
</span>        <span class="k">return</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">_solve_crc64</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">T_prefix</span><span class="p">):</span>
        <span class="s">"""求解 CRC64 自引用。

        CRC64 值 D = CRC64(W)，W = T + F + 尾部零。
        D 在 W 中出现两次：F 的 CRC64 字段 + vac_store 副本。

        解法（多项式逆元）：
          CRC64 的线性贡献可表示为 GF(2^64) 多项式乘法：
            bit_reverse(contribution(D)) = D_poly * P (mod G)
          其中 D_poly = bit_reverse(D)，P = x^(64+trailing) mod G。
          方程 D_poly * (1 ^ P1 ^ P2) = bit_reverse(CRC64(W)) (mod G)
          用扩展欧几里得求 (1 ^ P1 ^ P2) 的逆元，一步求解。
        """</span>
        <span class="n">q</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">]</span>
        <span class="n">bh</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"bh"</span><span class="p">]</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n"</span><span class="p">]</span>
        <span class="n">bp</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"bp"</span><span class="p">]</span>
        <span class="n">xz_size</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]</span>
        <span class="n">T</span> <span class="o">=</span> <span class="n">lay</span><span class="p">[</span><span class="s">"T"</span><span class="p">]</span>

        <span class="c1"># CRC64 位置 1：在 F 中（block padding 后）
</span>        <span class="n">crc_f_off</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">+</span> <span class="n">bh</span> <span class="o">+</span> <span class="n">n</span> <span class="o">+</span> <span class="n">bp</span>
        <span class="n">crc_w1</span> <span class="o">=</span> <span class="n">q</span> <span class="o">+</span> <span class="n">crc_f_off</span>

        <span class="c1"># CRC64 位置 2：vac_store 副本
</span>        <span class="n">crc_f_copy</span> <span class="o">=</span> <span class="n">crc_f_off</span> <span class="o">-</span> <span class="n">T</span>
        <span class="n">crc_w2</span> <span class="o">=</span> <span class="n">q</span> <span class="o">+</span> <span class="n">crc_f_copy</span>

        <span class="c1"># 构造 W = T + F + 尾部零（先把两处 CRC64 置 0）
</span>        <span class="n">xz_pad</span> <span class="o">=</span> <span class="n">round_up_512</span><span class="p">(</span><span class="n">xz_size</span><span class="p">)</span>
        <span class="n">tz</span> <span class="o">=</span> <span class="n">xz_pad</span> <span class="o">-</span> <span class="n">xz_size</span> <span class="o">+</span> <span class="mi">1024</span>
        <span class="n">W</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">T_prefix</span><span class="p">)</span> <span class="o">+</span> <span class="n">F</span> <span class="o">+</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="n">tz</span>
        <span class="n">W</span><span class="p">[</span><span class="n">crc_w1</span><span class="p">:</span><span class="n">crc_w1</span> <span class="o">+</span> <span class="n">CHECK_SIZE</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="n">CHECK_SIZE</span>
        <span class="n">W</span><span class="p">[</span><span class="n">crc_w2</span><span class="p">:</span><span class="n">crc_w2</span> <span class="o">+</span> <span class="n">CHECK_SIZE</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="n">CHECK_SIZE</span>

        <span class="c1"># CRC64(W) 的多项式表示
</span>        <span class="n">crc_poly</span> <span class="o">=</span> <span class="n">_bit_reverse64</span><span class="p">(</span><span class="n">crc64</span><span class="p">(</span><span class="nb">bytes</span><span class="p">(</span><span class="n">W</span><span class="p">)))</span>

        <span class="c1"># P1 = x^(64 + 位置1之后的bit数) mod G
</span>        <span class="n">trailing1</span> <span class="o">=</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">W</span><span class="p">)</span> <span class="o">-</span> <span class="n">crc_w1</span> <span class="o">-</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span>
        <span class="n">P1</span> <span class="o">=</span> <span class="n">_poly_pow</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">64</span> <span class="o">+</span> <span class="n">trailing1</span><span class="p">)</span>
        <span class="c1"># P2 = x^(64 + 位置2之后的bit数) mod G
</span>        <span class="n">trailing2</span> <span class="o">=</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">W</span><span class="p">)</span> <span class="o">-</span> <span class="n">crc_w2</span> <span class="o">-</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span>
        <span class="n">P2</span> <span class="o">=</span> <span class="n">_poly_pow</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">64</span> <span class="o">+</span> <span class="n">trailing2</span><span class="p">)</span>

        <span class="c1"># D_poly = crc_poly * inv(1 ^ P1 ^ P2)
</span>        <span class="n">coeff</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">^</span> <span class="n">P1</span> <span class="o">^</span> <span class="n">P2</span>
        <span class="n">inv</span> <span class="o">=</span> <span class="n">_poly_minv</span><span class="p">(</span><span class="n">coeff</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">inv</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"CRC64 多项式无逆元"</span><span class="p">)</span>
        <span class="n">D_poly</span> <span class="o">=</span> <span class="n">_poly_mul_mod</span><span class="p">(</span><span class="n">crc_poly</span><span class="p">,</span> <span class="n">inv</span><span class="p">)</span>
        <span class="n">D</span> <span class="o">=</span> <span class="n">_bit_reverse64</span><span class="p">(</span><span class="n">D_poly</span><span class="p">)</span>

        <span class="c1"># 写入 D 到 F 的 CRC64 位置
</span>        <span class="n">struct</span><span class="p">.</span><span class="n">pack_into</span><span class="p">(</span><span class="s">'&lt;Q'</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">crc_f_off</span><span class="p">,</span> <span class="n">D</span><span class="p">)</span>

        <span class="c1"># 重新执行 vac_store 副本
</span>        <span class="n">_forward_vac</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="nb">next</span><span class="p">(</span><span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">lay</span><span class="p">[</span><span class="s">"chunks"</span><span class="p">]</span> <span class="k">if</span> <span class="n">c</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"vac"</span><span class="p">)),</span> <span class="n">q</span><span class="p">)</span>

        <span class="c1"># 验证
</span>        <span class="n">W2</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">T_prefix</span><span class="p">)</span> <span class="o">+</span> <span class="n">F</span> <span class="o">+</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span> <span class="o">*</span> <span class="n">tz</span>
        <span class="k">assert</span> <span class="n">crc64</span><span class="p">(</span><span class="nb">bytes</span><span class="p">(</span><span class="n">W2</span><span class="p">))</span> <span class="o">==</span> <span class="n">D</span><span class="p">,</span> <span class="s">"CRC64 求解验证失败"</span>

    <span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">maxdist_hint</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">maxdist_hint</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">maxdist_hint</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">content</span><span class="p">)</span> <span class="o">+</span> <span class="mi">128</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">21</span><span class="p">)</span>
        <span class="n">last_err</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="c1"># d_seed 在同一 512 字节块内不改变 q（seed 数据按 512 对齐），所以旧的
</span>        <span class="c1"># "d_seed += 1" 重试完全不会改变布局，只是空转 80 次。真正需要 d_seed
</span>        <span class="c1"># 变化的情况只有两种：seed 装不下 jump 种植串，或需要跨块改变 q 作为兜底。
</span>        <span class="k">for</span> <span class="n">d_seed</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="mi">96</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">160</span><span class="p">,</span> <span class="mi">192</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mi">320</span><span class="p">,</span> <span class="mi">384</span><span class="p">,</span> <span class="mi">448</span><span class="p">,</span>
                       <span class="mi">576</span><span class="p">,</span> <span class="mi">704</span><span class="p">,</span> <span class="mi">832</span><span class="p">,</span> <span class="mi">960</span><span class="p">,</span> <span class="mi">1088</span><span class="p">,</span> <span class="mi">1600</span><span class="p">,</span> <span class="mi">2112</span><span class="p">,</span> <span class="mi">2624</span><span class="p">,</span> <span class="mi">3648</span><span class="p">):</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">lay</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_layout_iterate</span><span class="p">(</span><span class="n">d_seed</span><span class="p">,</span> <span class="n">maxdist_hint</span><span class="p">)</span>
                <span class="n">F</span><span class="p">,</span> <span class="n">seed</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">assemble</span><span class="p">(</span><span class="n">lay</span><span class="p">)</span>
                <span class="k">return</span> <span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">seed</span>
            <span class="k">except</span> <span class="p">(</span><span class="nb">RuntimeError</span><span class="p">,</span> <span class="nb">AssertionError</span><span class="p">,</span> <span class="nb">ValueError</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                <span class="n">last_err</span> <span class="o">=</span> <span class="n">e</span>
        <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">"build 多次失败: %s"</span> <span class="o">%</span> <span class="n">last_err</span><span class="p">)</span>


<span class="c1"># ============================================================
# Part 4: 验证 + main
# ============================================================
</span>
<span class="k">def</span> <span class="nf">verify</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="n">quine_name</span><span class="p">):</span>
    <span class="s">"""验证：解压 xz → 解析 tar → 检查文件 + quine 自复制。"""</span>
    <span class="c1"># 1. 解压 xz
</span>    <span class="k">try</span><span class="p">:</span>
        <span class="n">dec</span> <span class="o">=</span> <span class="n">lzma</span><span class="p">.</span><span class="n">LZMADecompressor</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="n">lzma</span><span class="p">.</span><span class="n">FORMAT_XZ</span><span class="p">)</span>
        <span class="n">tar_data</span> <span class="o">=</span> <span class="n">dec</span><span class="p">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">F</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] xz 解压失败: %s"</span> <span class="o">%</span> <span class="n">e</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">False</span>

    <span class="c1"># 2. 检查 quine 自复制
</span>    <span class="k">if</span> <span class="n">tar_data</span><span class="p">[</span><span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">]:</span><span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">]</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]]</span> <span class="o">!=</span> <span class="n">F</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] quine 自复制不匹配"</span><span class="p">)</span>
        <span class="n">a</span> <span class="o">=</span> <span class="n">tar_data</span><span class="p">[</span><span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">]:</span><span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">]</span> <span class="o">+</span> <span class="n">lay</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">F</span><span class="p">))):</span>
            <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="n">F</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"[verify]   首个不匹配在偏移 %d: tar=%02x vs xz=%02x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">F</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
                <span class="k">break</span>
        <span class="k">return</span> <span class="bp">False</span>

    <span class="c1"># 3. 解析 tar
</span>    <span class="k">try</span><span class="p">:</span>
        <span class="n">tf</span> <span class="o">=</span> <span class="n">tarfile</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">fileobj</span><span class="o">=</span><span class="n">io</span><span class="p">.</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">tar_data</span><span class="p">))</span>
        <span class="n">members</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">getmembers</span><span class="p">()</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] tar 解析失败: %s"</span> <span class="o">%</span> <span class="n">e</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">False</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[verify] tar 条目: %s"</span> <span class="o">%</span> <span class="p">[</span><span class="n">m</span><span class="p">.</span><span class="n">name</span> <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">members</span><span class="p">])</span>

    <span class="c1"># 4. 检查源文件
</span>    <span class="n">ok</span> <span class="o">=</span> <span class="bp">True</span>
    <span class="k">for</span> <span class="n">rel</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">f</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">extractfile</span><span class="p">(</span><span class="n">rel</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">f</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 文件 %s 是目录或不存在"</span> <span class="o">%</span> <span class="n">rel</span><span class="p">)</span>
                <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
                <span class="k">continue</span>
            <span class="n">extracted</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
            <span class="k">if</span> <span class="n">extracted</span> <span class="o">!=</span> <span class="n">data</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 文件 %s 内容不匹配"</span> <span class="o">%</span> <span class="n">rel</span><span class="p">)</span>
                <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">except</span> <span class="nb">KeyError</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 文件 %s 不在 tar 中"</span> <span class="o">%</span> <span class="n">rel</span><span class="p">)</span>
            <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>

    <span class="c1"># 5. 检查 quine 文件
</span>    <span class="k">try</span><span class="p">:</span>
        <span class="n">qf</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">extractfile</span><span class="p">(</span><span class="n">quine_name</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">qf</span><span class="p">:</span>
            <span class="n">qdata</span> <span class="o">=</span> <span class="n">qf</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
            <span class="k">if</span> <span class="n">qdata</span> <span class="o">!=</span> <span class="n">F</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"[verify] quine 文件内容不匹配 (%d vs %d)"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">qdata</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">F</span><span class="p">)))</span>
                <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"[verify] quine 文件匹配!"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">KeyError</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[verify] quine 文件不在 tar 中"</span><span class="p">)</span>
        <span class="n">ok</span> <span class="o">=</span> <span class="bp">False</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[verify] 结果: %s"</span> <span class="o">%</span> <span class="p">(</span><span class="s">"通过"</span> <span class="k">if</span> <span class="n">ok</span> <span class="k">else</span> <span class="s">"失败"</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">ok</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">ap</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s">"多文件 tar.xz quine 生成器"</span><span class="p">)</span>
    <span class="n">ap</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"srcdir"</span><span class="p">)</span>
    <span class="n">ap</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"output"</span><span class="p">)</span>
    <span class="n">ap</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--quine-name"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                    <span class="n">help</span><span class="o">=</span><span class="s">"归档内 quine 文件名（默认 = output 的文件名）"</span><span class="p">)</span>
    <span class="n">ap</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--seed-name"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">".this_is_mayx_blog"</span><span class="p">)</span>
    <span class="n">ap</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--src-dir"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">""</span><span class="p">,</span>
                    <span class="n">help</span><span class="o">=</span><span class="s">"源文件在归档内的前缀目录"</span><span class="p">)</span>
    <span class="n">ap</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--quine-dir"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">""</span><span class="p">,</span>
                    <span class="n">help</span><span class="o">=</span><span class="s">"quine 在归档内所在目录"</span><span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">ap</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>

    <span class="n">quine_name</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="n">quine_name</span> <span class="ow">or</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">basename</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">quine_dir</span><span class="p">:</span>
        <span class="n">quine_dir</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="n">quine_dir</span><span class="p">.</span><span class="n">strip</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
        <span class="n">quine_name</span> <span class="o">=</span> <span class="n">quine_dir</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="n">quine_name</span>

    <span class="n">bq</span> <span class="o">=</span> <span class="n">TarXzQuine</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">srcdir</span><span class="p">,</span> <span class="n">quine_name</span><span class="o">=</span><span class="n">quine_name</span><span class="p">,</span> <span class="n">seed_name</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">seed_name</span><span class="p">,</span>
                    <span class="n">src_dir</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">src_dir</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[blogquine-tarxz] %d 个文件, %d 个目录, 内容 %d 字节"</span>
          <span class="o">%</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">bq</span><span class="p">.</span><span class="n">files</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">bq</span><span class="p">.</span><span class="n">dirs</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">bq</span><span class="p">.</span><span class="n">content</span><span class="p">)))</span>
    <span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">seed</span> <span class="o">=</span> <span class="n">bq</span><span class="p">.</span><span class="n">build</span><span class="p">()</span>
    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">fp</span><span class="p">:</span>
        <span class="n">fp</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">F</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[blogquine-tarxz] written %s: %d 字节 (q=%d, k=%d chunks, n=%d, xz_size=%d)"</span>
          <span class="o">%</span> <span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">F</span><span class="p">),</span> <span class="n">lay</span><span class="p">[</span><span class="s">"q"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"k"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"n"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"xz_size"</span><span class="p">]))</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[blogquine-tarxz] T=%d, suffix=%d (bp=%d, ix=%d), dict_size=%d"</span>
          <span class="o">%</span> <span class="p">(</span><span class="n">lay</span><span class="p">[</span><span class="s">"T"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"suffix"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"bp"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"ix"</span><span class="p">],</span> <span class="n">lay</span><span class="p">[</span><span class="s">"dict_size"</span><span class="p">]))</span>
    <span class="n">ok</span> <span class="o">=</span> <span class="n">verify</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="n">lay</span><span class="p">,</span> <span class="n">bq</span><span class="p">.</span><span class="n">files</span><span class="p">,</span> <span class="n">quine_name</span><span class="p">)</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">ok</span> <span class="k">else</span> <span class="mi">1</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div>  </div>
</details>

<p>不过对我来说，相比于TXZ格式我还是更喜欢7z一点，一是因为LZMA2本来就是7-Zip的作者发明的，XZ感觉像是摘桃子的，二是TXZ这个名字听起来有点怪，感觉不像压缩包，三是XZ Utils出过后门，尽管整个Linux社区都在使用XZ，但是我还是稍微有点偏见，所以这份TXZ的生成器我就粘贴出来给需要的人吧，我博客用7z格式就好了。</p>

<h1 id="感想">感想</h1>
<p>以前总是有人说AI没有创新能力，只是对曾经在网络上出现的东西进行重组，但人何尝不是这样呢？像这次制作的7z/TXZ Quine生成器在整个网络上没有任何公开信息，当然我也知道这并不是理论上的创新，但谁说组合创新不是创新呢？再看看最近OpenAI又解决了一大堆数学难题，完全可以相信AI是真的拥有智能，所以我相信总有一天AI将能完成人类能做的所有事情，人类将不再需要额外的思考，只需要做自己想做的事情吧。</p>]]></content><author><name>mayx</name></author><category term="压缩包" /><category term="Quine" /><category term="LZMA2" /><category term="7-Zip" /><summary type="html"><![CDATA[让AI做出真正的“完整”将不再是难事……]]></summary></entry><entry><title type="html">Vibe Coding 新体验（2）</title><link href="https://mabbs.github.io/2026/09/01/vibe-coding2.html" rel="alternate" type="text/html" title="Vibe Coding 新体验（2）" /><published>2026-09-01T00:00:00+08:00</published><updated>2026-09-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/09/01/vibe-coding2</id><content type="html" xml:base="https://mabbs.github.io/2026/09/01/vibe-coding2.html"><![CDATA[<p>AI is Everything!<!--more--></p>

<h1 id="起因">起因</h1>
<p>在两个月前，我<a href="/2026/07/01/vibe-coding.html">体验了现在的Vibe Coding</a>，不过那时候我其实还是没有完全信任AI，认为它的能力可能还不够强，大多数内容依然需要人来做。 <br />
  但是自从上次做了<a href="/2026/08/01/terminal.html">终端版博客</a>之后，我意识到，它的能力也许比我想象的更强，可能真的能做到超越99%的开发者了，因此在最近一段时间，我一直使用AI做我想做的各种东西，结果就是一次又一次地让我感到难以置信，现在的AI真的实在是太强了😨。而且我做的这一切并不是用最先进的那批AI，仅仅是国内被大家不怎么看好的免费模型——<a href="https://huggingface.co/tencent/Hy3">腾讯混元Hy3</a>，那么我很难想象，如果我能用像GPT 5.6 Sol或者Fable 5那样当下最先进的AI，我做的这些东西得有多完美啊……</p>

<h1 id="最近vibe-coding出来的各种项目">最近Vibe Coding出来的各种项目</h1>
<h2 id="blog-api--webmcp">Blog API + WebMCP</h2>
<p>在上次做完<a href="/2026/08/01/terminal.html">终端版博客</a>之后，我就在想，用Linux终端来呈现博客的形式真的是个好选择吗？毕竟Linux在浏览器上并不是原生的，尽管用了<a href="https://github.com/tombl/linux">WASM Linux</a>可以做到WASM原生运行Linux，但这还是额外添加的东西。于是我就想，JavaScript应该也能作为终端吧？就像Node.js一样，REPL也可以作为终端使用，正好那段时间DeepSeek-V4-Flash正式版出了，就让它直接写了<a href="https://github.com/Mabbs/jsnix/blob/main/repl.html">一个Demo</a>出来试试看，可以点开<a href="https://mabbs.github.io/jsnix/repl.html">这里</a>体验。 <br />
  写出来效果还挺不错的，和之前做的终端版博客的主要区别就是我写的是函数，而不是命令，不管怎么说也算是“真的”终端吧，唯一体验不太好的可能就是函数需要带个括号才能运行，稍微有点麻烦。 <br />
  但后来我想了想，明明开发者工具中就有个控制台，根本没必要搞什么REPL终端啊，直接把要用的功能提出来作为函数，在控制台中运行效果比那个终端的效果要好得多啊，而且这样就可以直接集成到我的博客中，不需要单独搞个页面，感觉体验更好一些。 <br />
  既然有了函数，我觉得只给人类用就太浪费了，前段时间看到谷歌搞了个<a href="https://webmachinelearning.github.io/webmcp/">WebMCP</a>，那我完全可以把这些函数做成WebMCP让AI来调用啊，相比于其他乱七八糟给AI的协议，至少WebMCP是有谷歌和微软撑腰的，所以加一个也不错。 <br />
  最终我把WebMCP的文档以及之前实现的REPL代码直接发给AI，让它帮我实现对应的函数以及应用WebMCP API，没几分钟它就把所有功能实现好了，最终代码就是：<a href="/assets/js/blog-console.js">blog-console.js</a>。在开发的过程中，AI严格按照文档中的要求实现了，结果我在实际测试的时候，发现Chrome在实现<code class="language-plaintext highlighter-rouge">registerTool</code>方法的时候和文档的实现有差异，文档说这个方法会返回Promise，但Chrome实际并没有返回，所以AI调用<code class="language-plaintext highlighter-rouge">.then()</code>时直接报错了😓……没办法就只能手动给它套一层Promise，才算是能正常工作。毕竟这还是一个没有正式上线的API，出点问题倒也能理解吧。 <br />
  虽然东西已经实现了，不过到底怎么才能让其他人知道我用了WebMCP的技术呢？我发现网上有一个WebMCP Directory专门收录使用了WebMCP技术的网站，于是我就随手提交了一下，现在在<a href="https://webmcp.com/sites/mabbs.github.io">这里</a>就能看到我博客的WebMCP信息了。</p>
<h2 id="某个即将关服的游戏的离线快照">某个即将关服的游戏的离线快照</h2>
<p>最近一段时间我在玩的一个使用Unity3D引擎制作的立直麻将游戏发了篇公告，说是要关服了，还给全服所有玩家送了一堆抽卡资源。我听到这个消息之后感觉还是挺遗憾的，那个游戏总体来说做得还算不错吧，而且画风也符合我的口味，角色模型有点米哈游的那种感觉。我曾经遇到过几次游戏关服的事件，但是每一次我都无能为力，只能看着游戏消失然后逐渐被遗忘。然而现在我有了AI，也许一切会变得不一样吧？这次我要使用AI，让即将消失的游戏不再消失。</p>
<h3 id="重放大厅数据">重放大厅数据</h3>
<p>最开始我其实想得很简单，游戏登录之后其实没有和服务器有太多的通信，像账号中有的角色以及各种材料等信息在登录的时候就已经发送了，直到进入对局之前客户端都没有和服务器有什么有效的沟通，而我主要需要的也就是能登进去看看角色，听听语音就好了。那么接下来我只需要让AI把整个游戏资源下载下来，然后抓个包，让它给客户端重放就好了，也不需要什么逆向操作。 <br />
  AI分析了一下之后发现它用的是WebSocket+Protobuf协议，正常来说如果要使用Protobuf协议得要有.proto接口定义文件才可以，不过既然我的要求只是重放，它就没去考虑逆向了，只是简单分析了一下编码规则，就用Python把服务端给写出来了。 <br />
  不过如果这个快照必须本地启动一个服务器，玩起来就有点麻烦了，这样搞得我估计就没心思玩它了，但是自从我做了终端版博客之后，我觉得浏览器可能没有做不到的事情，既然Python能运行服务器，那JavaScript一样可以，于是我就要求AI让游戏和服务端一起运行在浏览器中。 <br />
  结果它还真折腾出来了，在JavaScript中似乎一切都可以被覆盖，于是AI就替换了浏览器的WebSocket对象，让客户端以为它连接到了真实的服务器，然后再按照之前录制的包发送就好了，折腾了一段时间之后游戏真的能正常启动了，真的是太离谱了😨。</p>
<h3 id="制作对局引擎">制作对局引擎</h3>
<p>其实这基本上已经满足我的要求了，但我还是有点好奇，这个客户端里真的没有.proto接口定义文件吗？正常来说既然客户端知道怎么和服务端通信，那么这个文件应该是必须要有的吧？反正目的已经达到了，接下来就让AI瞎折腾看看。 <br />
  在我要求完之后它又折腾了好长时间，结果.proto也被它给搞出来了😨，也许对于会逆向Unity3D游戏的人来说这不算什么，但这已经让我很震惊了……既然有了这些，也许完成一个完整的服务端将不再是难事。 <br />
  于是我让它录制了几把我实际对局的包，并理解整个对局中数据包的时序，开始考虑如何做出完整的麻将对局。首先作为离线运行的游戏来说，和人对局肯定是做不出来了，那就得想办法搓个人机出来。我最先想到的是著名的麻将AI <a href="https://github.com/Equim-chan/Mortal">Mortal</a>，之前用它在雀魂上打了几把之后结果号没了😂，不过我确实觉得和人打麻将没什么意思，感觉麻将如果全按最优解打的话结果就全是靠运气……不过后来我想了一下还是不用Mortal了，首先这个模型不小，整个模型要好几百MiB，而且三麻和四麻的模型还不一样，还有三麻的模型还是收费的……另外真和Mortal打的话感觉难度也确实有点高，除了运气因素外，至少人机犯错的概率比人要低不少，那打起来估计会很难受。所以我就对AI说，做一个降低向听数为主的全攻型人机，其他细节让它自己考虑，另外我想到上次做的那个麻将Wordle它自己写的役种分析实在是有点烂，所以这次就告诉它必须要使用现成的<a href="https://github.com/takayama-lily/riichi">Riichi</a>库，免得出乱七八糟的问题。 <br />
  在连续跑了好几个小时之后AI成功把完整的<a href="https://github.com/Mabbs/mahjong-dream-snapshot/blob/main/mock/riichi.js">对局引擎</a>给搓出来了，真的是相当了不起，要知道这个东西正常来说人开发怎么说也得好几周吧？它就几个小时开发出来了，还理解了数据包的时序，用之前分析的.proto文件成功接入到了游戏中，实在是太强了……当然因为我没有用大量真实牌谱去测试，有BUG也是正常的，比如目前的引擎还存在立直之后完全没法开杠这样的边缘问题，就只能慢慢修了，不过这都是小问题，它能做到目前的水平已经不是一般人能做到的了。 <br />
  最终做出来的项目可以在<a href="https://github.com/Mabbs/mahjong-dream-snapshot">这里</a>看到，还可以通过<a href="https://mabbs.github.io/mahjong-dream-snapshot/">GitHub Pages</a>上的部署直接游玩。</p>
<h3 id="隐藏彩蛋">隐藏彩蛋</h3>
<p>在AI做完整个服务端之后，我闲得没事瞎改对战人机的角色和皮肤，因为游戏中的皮肤ID有一定的规律，所以可以根据角色ID来猜皮肤ID。在这个时候，我发现其中有一个角色在游戏中没有对应的泳装皮肤，但是我把人机的角色按照规律配置推测的泳装皮肤ID是能显示出来的……那也就是说这个游戏可能在配置中隐藏了这个皮肤，但是并没有删掉美术资源，所以接下来我就想着能不能让AI把这个隐藏的皮肤显示出来，这样就能在游戏中直接使用了。 <br />
  所以我根据自己推测的信息，告诉AI：在这个游戏中有一个皮肤编号3708002，头像编号1018的资源被隐藏了，其中关于这些资源的配置应该在commonconfigs中，帮我解包并分析，考虑如何将这个皮肤和头像恢复显示，然后重新打包资源，使其可以正常加载。就这么个需求，它从Bundle的解包，到分析FlatBuffers，然后又处理XAsset的校验……最终花了3个小时就给我做出来了，而且我试了一下，非常完美，简直就像这个皮肤本来就存在一样，不过原本这就是被官方隐藏的嘛，解除隐藏自然就和真的存在一样啊😂。</p>
<h2 id="能在wasm-linux中运行的ssg">能在WASM Linux中运行的SSG</h2>
<p>之前我做完终端版博客之后就在想，让这个完整的Linux只用来展示博客似乎有点浪费，感觉它还能做更多事，但对我来说编译在这个系统中能运行的二进制程序还是有一点困难，虽然我有用AI，但是我的Agent是运行在macOS上的，所以编译Linux二进制程序有点困难……不过单是BusyBox其实就能做相当多的事情了，毕竟我当年可是在手机上用ash做出了论坛系统<a href="https://github.com/Mabbs/Mabbs.project">Mabbs</a>，那它能做的事情应该还有很多。 <br />
  最近我在和群友聊天的时候发现有人用C#做了个静态网站生成器，也是用AI做的，看起来挺有意思。其实SSG的原理还是挺简单的，说到底就是些文字转换罢了。于是我就想，要不然我就让AI搓个能在WASM Linux中运行的SSG出来吧？ <br />
  当然直接搓我还是会有一点怀疑AI的能力，所以又找了点素材，之前在<a href="/2025/08/10/tilde.html">体验Tilde社区</a>的时候发现了<a href="https://github.com/cfenollosa/bashblog">bashblog</a>，其实这就已经是一个现成的SSG了，只不过它用的是Bash语法，得让AI转换成POSIX风格的语法。另外它还依赖使用Perl语言编写的<a href="https://daringfireball.net/projects/markdown/">Markdown库</a>，我这个系统里可没有Perl啊，也得想办法找个可以替代的东西。 <br />
  我想了一下，在BusyBox中最适合处理文本的大概就是awk了，所以我首先应该找找有没有用awk编写的Markdown转换程序。结果搜了一下还真被我找到了，有一个叫做<a href="https://github.com/wernsey/d.awk">d.awk</a>的库，里面包含一个完全用awk写的Markdown转换程序，那么接下来我只需要把这两个项目丢给AI，让它以bashblog为基础，以d.awk为引擎制作一个兼容性极强的POSIX SSG吧。 <br />
  这种问题果然还是太简单了，它只用了一两个小时的时间，就真的做出来了，最终它把这个项目起名叫做<a href="https://gist.github.com/Mabbs/3db745dc403d752a1d59d3e06938e310">possg</a>，并且我把这个程序粘贴到了我的终端版博客中运行了一下，效果也是非常完美……如果哪一天我抛弃了Jekyll，也许就会用这个possg吧😂。</p>
<h2 id="终端版博客的各种新功能">终端版博客的各种新功能</h2>
<p>最近一段时间我在我的<a href="https://github.com/Mabbs/linux">终端版博客</a>又有一些新的想法，于是就对着AI反复许愿，结果AI还真的能让愿望成真。以下就是从上次做完之后又新增的一些功能：</p>
<h3 id="markdown高亮">Markdown高亮</h3>
<p>最初博客在终端中显示文章时是直接打印的Markdown原文，感觉不太好看，所以我就想着如果能加个高亮功能体验应该会更好吧？最开始我要求AI做这个功能的时候它选择使用Shell+sed来实现，那时候我还没有做possg，没考虑用awk来做，所以性能非常差。因为Shell在处理这种任务的时候要不停地调用sed，这个系统开进程的开销又很大，所以处理一篇文章得好几秒，后来才考虑用awk来做，结果性能确实好了不少，最终的代码可以在<a href="https://github.com/Mabbs/linux/blob/initramfs/bin/mdhl">这里</a>查看。</p>
<h3 id="挂载本地文件夹">挂载本地文件夹</h3>
<p>前段时间我看到作者合并了一个关于<a href="https://github.com/tombl/linux/pull/40">virtiofs的PR</a>，当时我看到这个PR的时候就想，记得Chrome浏览器有一个<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/showDirectoryPicker">showDirectoryPicker API</a>，可以允许网页对指定的文件夹进行读写。现在有了这个驱动的话，让AI做个将本地文件夹挂载到Linux的功能应该是个很简单的事情，于是我直接就闭着眼睛让AI做了。 <br />
  做完之后虽然能用，但是我又回想起上次让AI搓的通过Service Worker访问系统内网络服务的功能，结果人家原作者已经做了一样的功能，我还给作者发了<a href="https://github.com/tombl/linux/issues/34">Issue</a>推荐😅，所以这次我得吸取教训看看作者是不是已经做了这个功能，毕竟作者实现virtiofs的时候估计就有这个打算吧？结果看了一眼还真<a href="https://github.com/tombl/distro/blob/main/packages/linux-guest/src/browser.ts">做了</a>😆，我闭着眼睛让这个垃圾AI去做看来确实有点着急了，虽然我不知道原来我的AI做得到底怎么样，但是我对它还是没什么信心，也许作者这个代码也是AI写的，但很明显他的AI比我的AI水平强得多，所以让它参考作者做的这个版本重新写了一版。总之最终都能用，写出来的代码就是：<a href="https://github.com/Mabbs/linux/blob/main/hostfs.js">hostfs.js</a>，再配合上<a href="https://github.com/Mabbs/linux/blob/initramfs/bin/mount_host">mount_host</a>脚本就可以实现挂载本地文件夹的功能了。</p>
<h3 id="基于webrtc的局域网内组网">基于WebRTC的局域网内组网</h3>
<p>我在制作网络模块的时候一直在想，有没有办法能在不依靠服务器的情况下让两个这样的Linux系统互联呢？正常来说要想让两个浏览器内的服务互相访问就必须有一个中转服务器，因为浏览器肯定是不允许网页监听端口，而且就算能监听，我的这个网页还是放在GitHub Pages上，使用了HTTPS，也没办法访问HTTP资源，搞证书也是个麻烦事。 <br />
  但其实浏览器是有P2P功能的，那就是<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API">WebRTC</a>，只是我不太确定这个技术能不能做到无服务器，于是我问了问AI，这个功能能不能不要服务器，结果AI说可以，只要两个机器在同一个局域网，就可以通过手动交换SDP信令来实现P2P连接，而且考虑到我基本上完全没有锥形NAT网络可以用，所以就不搞STUN服务器了，反正也连不上。那既然理论上可以的话，就让AI实现吧。 <br />
  最终花了一点时间AI就写出来了，使用<a href="https://github.com/Mabbs/linux/blob/main/webrtc-p2p.js">webrtc-p2p.js</a>配合<a href="https://github.com/Mabbs/linux/blob/initramfs/bin/connector">connector</a>就可以实现同一个局域网内两台电脑上的浏览器中的Linux互联了。 <br />
  这个功能做出来之后直接就有了无限的想象空间，比如想用A电脑的浏览器控制B电脑浏览器中的Linux，就可以在B电脑中执行<code class="language-plaintext highlighter-rouge">tcpsvd -v 0.0.0.0 4444 /usr/bin/script -qc 'stty -echo; /bin/sh -i' /dev/null</code>，然后在A电脑用<code class="language-plaintext highlighter-rouge">nc 10.0.2.x 4444</code>去连接就可以了，效果就像SSH一样，而且配合之前做的挂载本地文件夹功能，甚至可以直接操作B电脑中的文件；另外之前做的Service Worker访问系统内网络服务也能用得上，假如B电脑有一个网页想要分享给A电脑，可以在B电脑上用<code class="language-plaintext highlighter-rouge">mount_host</code>把本地文件夹挂载到Linux的<code class="language-plaintext highlighter-rouge">/mnt/host</code>目录中，然后在那个目录执行<code class="language-plaintext highlighter-rouge">httpd -p 8080</code>，A电脑可以执行<code class="language-plaintext highlighter-rouge">tcpsvd -v 0.0.0.0 8080 /usr/bin/nc 10.0.2.x 8080</code>转发，接下来打开<code class="language-plaintext highlighter-rouge">http/8080/</code>就可以访问到B电脑上的网页了。 <br />
  总之只要有了网络，这个系统的功能就能被无限扩大了，只是……说是这么说，其实还是依赖浏览器的功能，所以这些功能就算用JavaScript直接做也完全没问题，但毕竟是作为Linux终端，有了这些能力之后就显得更完整了。 <br />
  最终我仔细确认了原作者确实没有做这个功能，所以就发了个<a href="https://github.com/tombl/distro/issues/156">Issue</a>看看他有没有兴趣把这个功能加到他的库中，如果被接受了也算是做出贡献了吧。</p>
<h3 id="使用vless协议的网关">使用VLESS协议的网关</h3>
<p>虽然之前已经加了基于浏览器Fetch API的网关，但这个网关的局限性还是挺大的，基本上也就只能请求网站了。那其他能在浏览器上运行系统的项目是怎么做的呢？看了一下像v86用的是一个基于L2 Over WS的公共网关relay.widgetry.org，而WebVM用的则是Tailscale。这两种我都不喜欢，主要是这些东西基本上专门服务于这类在浏览器中运行的系统，普通人并不会特意去搭建，而我既不想用某个特定的服务，也不想自己搭……那么有什么样的网络服务既随处可见，又能在浏览器中使用呢？这时候我就想到了VLESS协议，它天生就适合运行在WebSocket中，而且搭建起来极其简单，比如有人用Cloudflare Workers做了一个叫做<a href="https://github.com/zizifn/edgetunnel">edgetunnel</a>的脚本，可以轻松将Cloudflare作为代理使用，因此我决定接下来让AI实现一个基于VLESS+WSS的网关，这样除了请求网站之外还能做点别的事情，比如用<code class="language-plaintext highlighter-rouge">telnet towel.blinkenlights.nl</code>命令看ASCII版的《星球大战》😆。 <br />
  只不过相比于其他项目用的那些L2的服务来说，VLESS承载的是L4的协议，所以我还得让AI实现TCP/IP协议栈……其实我不太想这样做的，因为AI做这种事情容易出错，但是它看了一下我之前用的<a href="https://github.com/chipmk/tcpip.js">tcpip.js</a>库说是里面的API不满足要求……那没办法，AI既然自己想搓就让它搓吧。不过VLESS协议这个我觉得还是得给它个<a href="https://github.com/XTLS/Xray-docs-next/blob/main/docs/en/development/protocols/vless.md">文档</a>，免得它开始瞎编。 <br />
  不管怎么说它还是搓出来了，虽然质量我不是特别满意……但毕竟手头用的AI不是最先进的AI，就先这样吧～最终的代码就是：<a href="https://github.com/Mabbs/linux/blob/main/vless-gateway.js">vless-gateway.js</a>，用法也很简单，首先要有一个<strong>证书合法</strong>的VLESS+WSS节点，然后在我的博客终端中按F12打开控制台，输入：<code class="language-plaintext highlighter-rouge">switchToVless("wss://domain:port/path", "UUID")</code>就可以将原来基于Fetch的网络切换到VLESS节点的网络了，考虑到避免被滥用的情况，我就不内置节点了，如果有人有兴趣可以用自己的节点进行连接。</p>

<h1 id="感想">感想</h1>
<p>做到这里，可以感受到最近的AI所做的一切真的是令人难以置信，它不仅知道几乎所有知识，还有不知疲倦的思考能力，以及足够的动手能力，而这也仅仅建立在一个很一般的免费模型上。再回想起前段时间用的Agent，做的什么<a href="/2025/04/08/feed.html">订阅源预览</a>，简直就太简单了😂。目前据我所知其他人使用更先进的AI已经可以解决人类没能解决的数学难题、自主设计处理器指令集架构在上面运行Doom、<a href="https://github.com/Kelsidavis/System7">从零开发一个Classic Mac OS</a>、制作直接在UEFI中运行的软件、以及完全逆向Unity3D游戏，把几款游戏混合起来做成一个新游戏……（至于网上总传的什么DeepSeek的所谓的灰测模型搞出来的<a href="https://github.com/6WENHAO/DS-Games">各种Demo</a>就算了，根本比不了）过去各种难以想象的东西都被轻松做出来了，而这一切就发生在最近几个月，我已经很难想象未来AI能做到什么样的地步了，也许AI真的会带领人类走向无法想象的高度吧～</p>]]></content><author><name>mayx</name></author><category term="Vibe Coding" /><category term="AI" /><category term="Agent" /><summary type="html"><![CDATA[AI is Everything!]]></summary></entry><entry><title type="html">一次制作终端版博客的经历</title><link href="https://mabbs.github.io/2026/08/01/terminal.html" rel="alternate" type="text/html" title="一次制作终端版博客的经历" /><published>2026-08-01T00:00:00+08:00</published><updated>2026-08-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/08/01/terminal</id><content type="html" xml:base="https://mabbs.github.io/2026/08/01/terminal.html"><![CDATA[<p>在浏览器上用真的Linux访问博客会是什么样的感觉？<!--more--></p>

<h1 id="起因">起因</h1>
<p>在去年的时候我为了想在博客上加个终端，写过一篇关于<a href="/2025/12/01/linux.html">在浏览器中运行Linux</a>的文章。不过那时候只是调查了有哪些可能的方法，并没有真的做出来，主要是自己做感觉太麻烦了😂。 <br />
  但从上次进行了<a href="/2026/07/01/vibe-coding.html">Vibe Coding</a>之后，我意识到这可能不再是什么麻烦事，也许只要和AI聊聊天就能轻松做出来了，于是接着之前的方案，我开始着手完成我的想法。</p>

<h1 id="终端版博客的不同形式">终端版博客的不同形式</h1>
<h2 id="基于wasm-linux的版本">基于WASM Linux的版本</h2>
<p>在之前的文章里，我其实更倾向于WASM Linux的方案，尤其是<a href="https://github.com/tombl">Thomas Stokes</a>制作的<a href="https://github.com/tombl/linux">WASM Linux</a>，因为它相比于虚拟机更轻量，效率更高，而且还能完美地在Safari中运行（毕竟我用的是macOS😂），因此接下来我计划用它为基础制作我的终端版博客。 <br />
  不过之前我也说过，目前这个WASM Linux还不能和外界通信，不能和外界通信就不能读取我的文章。于是我打算先让AI解决这个问题。但如果我直接给它说想要和外界通信也许它会给我整一堆奇怪的实现，所以我得自己先想一个实现起来最简单的方案。之前我记得Fabrice Bellard大神写的JSLinux中有一个剪贴板设备文件可以和外部通信，那么干脆让AI写一个能在浏览器中执行JS的设备文件吧，这样做出来只需要写简单的Shell脚本就可以和浏览器交互了，也不需要考虑如何编译WASM程序，于是就这样我开始让它实现这个功能。 <br />
  最终经过几次调试和修改之后AI真的把这个设备文件给做出来了，不过刚做出来的时候有些问题。因为在这个Linux中的进程都是在Web Worker中运行，执行的JS也会在Web Worker中运行。但是Web Worker的局限性很大，并不能和当前网页进行交互，不过这种问题也都是小事，发现了之后只要给AI一反馈也是轻轻松松就改好了……最终实现的效果我感觉相当不错，既然如此，那我就把AI做的这个驱动给上游<a href="https://github.com/tombl/linux/pull/24">PR</a>一下吧，也算做个贡献。不过最终原作者不太喜欢这种实现方式，他好像更喜欢标准化的方式，说是已经做了一个<a href="https://github.com/tombl/linux/blob/b36416c7f266a465361ab7dde7622eb37239cd7e/tools/wasm/src/virtio.ts#L354">VsockDevice</a>，可以直接使用Linux自带的驱动和外界通信，所以把我的PR拒了（看来AI Slop还是入不了大佬的法眼😂）。 <del>不过我问了一下AI，它说用这个设备的话可能需要<code class="language-plaintext highlighter-rouge">ioctl()</code>系统调用，就没办法直接用Shell操作了，只能用别的语言写的程序才能用它通信，所以我只好自己用这种不标准的方式了。</del> 虽然VsockDevice确实是不太好操作，但是大佬随后又新增了一个ethernetDevice可以用来上网，考虑到维护自己的分支实在是太麻烦了，所以我让AI写了一个虚拟的HTTP服务，使其能够执行JS，顺便还增加了连接互联网的功能。这样我就可以直接使用上游的代码，不需要再维护之前写的驱动了（其实以太网比Vsock复杂多了，只是因为可以用wget之类的客户端请求所以才简单😋）。 <br />
  在做好与外界通信的通道后我就开始让AI写能和我博客交互的脚本，不过在这个过程中也遇到了不少问题，最开始的时候发现这个Shell的管道有些问题，执行的时候会忽略管道前的命令，后面的命令就没法执行了……感觉有点奇怪，所以我要求它在实现交互脚本时不要使用管道符。除此之外我还发现反引号和<code class="language-plaintext highlighter-rouge">$()</code>这些能把执行结果插入字符串的语法也不能用……总之就是局限相当多，但它还是克服了这些困难最终把脚本给写出来了，最终实现了列出文章、查看文章、打开文章和查看评论等功能，效果还是相当不错。 <del>唯一的缺陷就是它不能进行异步操作，只能同步请求，所以在进行请求的时候会把浏览器卡住，体验稍有打折。</del> 后来用了网络版的JS执行服务后就可以异步了，避免出现把浏览器卡住的情况。 <br />
  不过这个系统如果管道符不能用的话效果会差一大截，这可是Shell的灵魂啊，所以我提交了一个<a href="https://github.com/tombl/linux/issues/25">Issue</a>，希望作者能修一下。结果作者真的强得离谱，提出问题之后第二天就修复了，实在是太强了😨。但修好这个问题之后作者又整了一堆别的花活，把内存限制给取消了，结果我在Safari上一运行就报OOM的错误……于是我把这个问题又<a href="https://github.com/tombl/linux/pull/26#issuecomment-5018155963">反馈</a>了一下，顺便让AI看看这个问题怎么解决，结果AI也不辜负我的期待，轻轻松松就解决了🤣。最终我除了会提出问题之外什么也不会做，只能看着大佬和AI完成我的想法了😝。 <br />
  后来增加的以太网网关也挺折腾，原理其实并不复杂，但直接给AI说想用这个接口上网的时候它会自己想办法搓一套漏洞百出的TCP/IP协议栈，所以后来我在GitHub找了个<a href="https://github.com/chipmk/tcpip.js">tcpip.js</a>库让它集成，BUG就比它自己手搓的少了不少。后来我又想加一个TLS终止功能，因为在浏览器上没办法直接让系统里的程序发送原始的TLS数据包去请求网站，但我又希望它能够请求带有https协议的网址，所以要提前对TLS进行解密再转换成<code class="language-plaintext highlighter-rouge">fetch()</code>，这时候我就没让AI自己手搓了，在网上找了个<a href="https://github.com/digitalbazaar/forge">node-forge</a>库让AI对接，就可以避免出现一堆奇怪的BUG了。 <br />
  做好网关之后我又突发奇想，想做一个展示终端内状态的页面，但是目前JS并不能主动和里面的系统直接通信，所以又让AI搓了一个通过Service Worker访问系统内网络服务的功能，然后用busybox的httpd执行sh写的cgi程序，就可以被前端读取到了。随后我还给原作者发了个<a href="https://github.com/tombl/linux/issues/34">Issue</a>展示这个Service Worker的效果，结果人家已经做了这个功能，我还是太Naive了😂。 <br />
  最终做好的网站可以在<a href="https://mabbs.github.io/linux/">这里</a>查看，效果真的不是一般的好，相比于其他模仿终端形式的博客简直是降维打击，毕竟这是真的Linux啊～虽然目前依然有些BUG，但是原作者修BUG的速度还是挺快的，相信等他发布正式版之后，体验会更进一步。</p>

<h2 id="基于jsnix的版本">基于JSNix的版本</h2>
<p>虽然我觉得追求真实感很重要，但是我又回去看了一下为我提供想法的<a href="https://github.com/Erzbir/jsnix">JSNix</a>。虽然这个项目并不是真的Linux，但是我让AI看了一遍之后它对这个项目的评价也很高，说是作者对Linux的了解非常深入，很多细节模拟的也非常到位。而且为WASM Linux制作的那些脚本，在这个JSNix上其实完全可以实现，还更简单，于是我直接让AI参考给WASM Linux写的脚本把那些功能全部移植到这个JSNix上。也许是因为AI更擅长编写网页，很快它就完成了这些功能，而且效果非常不错，基本上和WASM Linux上面的效果一样，表现也相当不错，最终生成的结果可以在<a href="https://mabbs.github.io/jsnix/">这里</a>查看。 <br />
  不过假的毕竟还是假的，和真的Linux差异还是有不少的，像正经的Shell可以在终端中直接用if、for和while之类的语法写一些东西，它这个就必须要编写成脚本才可以，而且解析也有相当多的问题。各种命令的运行结果很多都是硬编码的，只是编了一段对应命令的输出，参数做得也不全。另外著名的Linux文本三剑客grep、sed和awk在这个系统里实现得也非常差，只做了一些非常基础的功能，基本上就是完全不可用。所以说这个系统还是不够真实，只能做最基础的体验，不能够深究。 <br />
  但并不是说它没有优点，它作为纯粹的JS不像WASM Linux那样容易出现兼容性问题，比如很多手机浏览器不支持跨源隔离，导致SharedArrayBuffer之类的API没法用，所以手机上只能体验这个JSNix的版本；另外WASM Linux的体积虽然说并不算很大，但也有好几MiB，不像这个JSNix连1MiB都不到；还有这个系统没有启动过程，相比于WASM Linux来说加载更快；而且在编写和修改功能的时候比WASM Linux简单不少，像给WASM Linux做的终端状态展示在JSNix上根本不需要折腾什么Service Worker，以及请求网络之类的也非常简单，直接<code class="language-plaintext highlighter-rouge">fetch()</code>就可以（也正是如此，JSNix的版本AI没一会儿就做完了）……总之就是只要不涉及具体功能方面JSNix都比WASM Linux要强不少，不过这样说有点“抛开事实不谈”的感觉了😂。</p>

<h1 id="感想">感想</h1>
<p>最终做出来的这两个版本的终端版博客基本上所有的代码都不是我写的，真的做到了仅仅是和AI聊聊天就把东西做出来了……只能说AI现在真的不是一般的强啊～当然方案还是得自己考虑，因为AI一旦走错了方向就很容易往一个方向不停地钻，结果就是花了很长时间可能效果会很差，尤其是现在的AI还特别喜欢自己造轮子，所以这时候还是自己找找看已经有的方案然后告诉它会比较好。 <br />
  从效果来说这次写的东西也让我非常满意，我想我这次做的这个博客界面估计是前所未有的，毕竟这可是在浏览器上用<strong>真的</strong>Linux访问博客啊，其他人做的都是假的。至于虚拟机那些方案可能也是因为太重，所以基本上没人用，而WASM Linux只有Thomas Stokes做的效果最好，但是又没有受到应有的关注……总之从我个人的角度来看这次做的东西还挺Cool，就是不知道会不会吸引其他人来看呢……可能还是太小众了吧😂。</p>]]></content><author><name>mayx</name></author><category term="WASM" /><category term="终端" /><category term="Linux" /><category term="博客" /><summary type="html"><![CDATA[在浏览器上用真的Linux访问博客会是什么样的感觉？]]></summary></entry><entry><title type="html">Vibe Coding新体验</title><link href="https://mabbs.github.io/2026/07/01/vibe-coding.html" rel="alternate" type="text/html" title="Vibe Coding新体验" /><published>2026-07-01T00:00:00+08:00</published><updated>2026-07-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/07/01/vibe-coding</id><content type="html" xml:base="https://mabbs.github.io/2026/07/01/vibe-coding.html"><![CDATA[<p>All in AI!<!--more--></p>

<h1 id="起因">起因</h1>
<p>前段时间，我写了一篇关于<a href="/2026/04/14/ai-agent.html">AI Agent</a>的文章，虽然当时我说这并没有什么创新之处，但在那之后用着用着感觉还是挺好用的，至少相比以前<a href="/2025/04/08/feed.html">在云端Vibe Coding</a>来说又快又免费，所以我打算分享一下最近我用AI Agent的经历。 <br />
  不过大多数情况下我写代码都可以直接让各路AI在对话中给我生成，那到底有什么样的场景才需要用AI Agent呢？一般应该就是需要边改边测的东西吧。</p>

<h1 id="关于pjax的改造">关于PJAX的改造</h1>
<p>虽然说AI生成的东西可靠性并不是很高，但至少在网上已经有很多例子的代码应该不至于乱生成吧……其实我一直都想给我的博客增加全站无刷新的功能，但是每次看到PJAX都要自己重新调用当前页面的各种函数就懒得动手了😂。不过其实这也不是什么复杂的事情，主要是一个一个分析函数比较麻烦，所以既然有AI了，不如直接让AI帮我做吧。于是我让它克隆了我的仓库，分析并写出PJAX的代码。 <br />
  用AI Agent最大的好处就是能让它了解整个项目的所有代码，如果是直接在对话里让AI修改的话就很难做到这一点。总之我告诉它之后等了一会它真的写出来了，我试了一下确实能用，虽然刚开始有不少问题，主要是因为我用了<a href="/Live2dHistoire/">Live2D</a>，不少问题也是这个功能引起的。不过只要我告诉它问题在哪里，它总是能给我改好，先不论它实现的代码怎么样，但至少从效果来说还是相当不错的。 <br />
  最终生成的代码我看了一下，代码质量非常糟糕，把我在别处的代码到处复制粘贴到PJAX用的代码里了，但是体验效果不错……所以没办法，虽然代码很糟糕，但先用着吧。不过我觉得优化应该也能交给AI Agent，只是它有时候改来改去还会把正常代码改坏，所以优化看起来还是只能自己做…… <br />
  最终的代码就是：<a href="/assets/js/pjax.js">pjax.js</a>，虽然我对这个代码质量不是很满意，但是毕竟能用，效果也不错，就先这样用吧～</p>

<h1 id="生成一个小游戏">生成一个小游戏</h1>
<p>前段时间，我在Bilibili上看视频的时候发现了一个有趣的微信小游戏，叫做“雀谜”，它把立直麻将和Wordle两个玩法结合到一起，看起来挺有意思。不过它作为微信上的小游戏有些问题，一是看提示要看广告，二是换题也要看广告，而且看广告就算能换题也有次数限制，一天也就几次机会，让我有点不爽。 <br />
  不过从功能上来说看起来并不复杂，感觉用AI直接Vibe Coding完全不是问题，不过AI Agent从零开始写比较考验模型的水平，所以我打算先让各路网页上的AI生成初版的Demo，之后再考虑怎么改。于是我对它们说：</p>
<blockquote>
  <p>帮我制作一款立直麻将Wordle网页小游戏，规则为：</p>
  <ol>
    <li>系统随机生成一个有役能和的14张牌，必须按照万、筒、索、字的顺序排列，同花色内数字从小到大，但是第14张用来和的牌可以不遵守排列规则</li>
    <li>让玩家猜6次来猜到这个牌型，每次猜的牌也必须是满足有役和牌的条件</li>
    <li>位置和内容正确的牌用绿色标注、只有内容正确但是位置不正确的用黄色标注，不在生成范围内的牌用灰色标注</li>
    <li>界面中所有的牌使用Unicode中的麻将字符，并且提供一组输入按钮用于输入这些麻将字符</li>
    <li>输入按钮在游玩过程中也要标注颜色</li>
    <li>随机生成的牌需要标注场风、自风和是否自摸，如果是荣和需要额外的役，需要考虑平和之类的役种</li>
    <li>加一个提示键，可以查看答案牌型的役种</li>
  </ol>
</blockquote>

<p>直接生成确实很考验水平，有很多AI直接生成出来的完全用不了，像DeepSeek生成出来的按钮按上去都没有反应😅，最终只有Gemini生成出来的感觉还不错，无论是界面还是输入的感觉都很好，于是接下来的修改我就打算以Gemini生成出来的代码为主。 <br />
  刚生成出来的代码虽然能玩，但是每个AI直接生成都不想做完整的役种检测，说什么一个对话不够😥，明明各个都是几百k上下文的AI，有什么不够的……总之想一口气就完美做出来显然是不现实的，不过其实最开始我也不打算把所有役都做了，毕竟条件是“有役和牌”，而且没有副露，所以好多可以叠加的役其实都不需要做，尤其是役满那些，大多都可以用其他役的条件满足。 <br />
  在这期间，“平和”算是实现起来最麻烦的，主要是这个役需要根据和牌判定，必须是顺子的两面听牌才可以……虽然AI理解这些知识，但是写的时候经常出问题，像边张之类的经常误判……但是不实现又不行，毕竟这是可以nomi的牌型。 <br />
  最终写的差不多了，感觉还不错，于是在GitHub Pages上<a href="https://mabbs.github.io/riichi-mahjong-wordle/">部署了一份</a>，起名叫<a href="https://github.com/Mabbs/riichi-mahjong-wordle">立直麻将 Wordle</a>。 <br />
  最开始我以为这个小游戏的玩法是他们原创的，于是把生成的代码又丢给AI，让它评价一下创新性，结果AI说已经有开源了😂，有个叫做<a href="https://github.com/xnuk/mahjong-handle">Mahjong Handle</a>的才是最早做这个玩法的……还好这代码不是我写的，要是我写了半天结果发现有现成的那就很无语了😆。 <br />
  当然我让AI写的版本也有一些特色，Mahjong Handle那个和原版Wordle一样，每天只有一道题，而且这个题是从题库里抽的，我让AI Agent看了一下它的代码，似乎是用<a href="https://github.com/MahjongRepository/phoenix-logs">phoenix-logs</a>这个脚本从天凤上下载的牌谱数据解析得到的题库，所以它的题目数量是有限的，而我的代码是完全随机生成的，所以结果是无限的。但AI似乎认为作为Wordle来说一天一题才是它的特色，无限就显得没意思了……既然如此，那干脆让它给我把两个游戏合并了，让它参考Mahjong Handle把一天一题的功能也给我加上，另外Mahjong Handle还有一个MPSZ快速输入法，也让AI给我合并进我的游戏里了。 <br />
  这时候就体现了AI Agent的优势，如果直接用线上的AI把Mahjong Handle中的功能移植到我的代码，我还得完整描述要实现的功能，但是AI Agent能自己看人家仓库的代码，就知道要改什么东西了。 <br />
  最终实现的效果也很不错，虽然我的代码没有题库，但是使用了当天日期作为随机种子，用mulberry32算法搞出了一天一题的功能，还挺有意思的。 <br />
  另外在Mahjong Handle以及原版的Wordle中，还有一个困难模式，就是每次猜测必须使用之前的猜测条件，不过我觉得如果原模原样把这个功能抄过来似乎有点无聊，而且我发现玩这个东西一般直接一个国士+两个一气就基本上把要猜的牌找完了，有点简单，所以我想了一下，要不然限制一下提交的条件，提交的牌型必须包含目标的役种之类的……不过要是做这个功能就不能像之前那样只实现部分役种了，得把所有能用到的都实现一遍，还好这种事情肯定也不是我来做，肯定还得是让AI来😋。 <br />
  实现的时候还不能让AI直接一口气全加上，还得一步一步的提示，而且每个牌型还得手动测一遍……最终虽然做的看起来似乎已经很完善了，不过逻辑的根本还是有点问题，AI设计役种计算的时候是按拆解后役种数量最多的情况来算，但事实上立直麻将算役种是按照高点法，要最终点数最大才行。虽然按照目前的方法绝大多数情况下都能正常工作，但偶尔也会遇到问题，比如像这副牌：</p>
<ul>
  <li>🀈🀈🀈🀉🀉🀉🀊🀊🀊🀞🀟🀗🀗 🀝</li>
</ul>

<p>按照目前的方法计算的结果是：</p>
<ul>
  <li>断幺·平和·一杯口 = 3番30符</li>
</ul>

<p>然而正确答案是：</p>
<ul>
  <li>断幺·三暗刻 = 3番50符</li>
</ul>

<p>但是符数的计算又很麻烦……不过反正提示和验牌都是同一套程序，题倒是也能做出来……所以就懒得修了🤣。当然说到底还是因为AI没有用现有的计算库，像Mahjong Handle用的是现成的<a href="https://github.com/takayama-lily/riichi">Riichi</a>库，制作起来比我这个就简单多了。</p>

<h1 id="为游戏添加字体">为游戏添加字体</h1>
<p>在我制作“立直麻将 Wordle”这个小游戏的时候，我让AI使用的是Unicode中的麻将字符，毕竟有现成的字符总比去找牌的图片要方便，而且实现起来感觉更简单一些。不过用字符确实也会遇到一些问题，我不知道为什么在macOS和Android上其他牌都是黑白的，唯有🀄️是彩色的😅，Windows上使用的Segoe UI Emoji倒都是彩色的，但是牌很丑，像是小学生设计的……虽然不影响玩，但是看着很难受，至少牌的颜色统一一下也好吧？虽然也可以搞出黑白的🀄︎，但如果用这个，在Windows上又成了只有🀄️是黑白的😅。虽然Mahjong Handle是直接用了SVG格式的牌图片，但我觉得应该有现成的麻将字体，像这种问题应该有现成的解决方法才对吧？ <br />
  于是我直接问AI有没有这样的字体，AI搜了一下看起来还真有，有个叫做<a href="https://github.com/kfarwell/Mahjong-Colored">Mahjong Colored</a>就是基于那些SVG做的OpenType-SVG字体，我本来以为问题就会这样解决，然而当我应用了字体之后发现牌的位置变成了白色的方块，貌似只有Firefox才支持这种字体……然而现在谁还用Firefox啊？ <br />
  那遇到这种问题怎么办？当然还是让AI解决，它说要在Safari和Chrome上使用彩色字体，用COLR或者SBIX格式比较好，COLR格式的兼容性最好，三个浏览器都能用，而SBIX的话Firefox没法用。不过Firefox既然基本上没人用了，我倒是不太在乎它的兼容性，但我肯定还是更希望能用兼容性更好的方案，于是我把OpenType-SVG字体转换为COLR的工作交给了AI Agent。 <br />
  结果它整了半天也没整出来，貌似是因为SVG的特效太多，没办法转换为COLR格式，于是它选择了SBIX方案，不过COLR存储的是矢量图，而SBIX存储的是位图，大小要大一些，而且放大还会有锯齿。只是目前实在是没办法了，只能用这种格式。 <br />
  最终我把它转换后的字体存了一份到<a href="https://github.com/Mabbs/Mahjong-Colored-SBIX">仓库</a>，但是看着它1MiB多的大小以及不太好的浏览器兼容性，再考虑到它还是位图，实在是不太想在我的游戏里应用它…… <br />
  结果后来我又搜了一下，原来是有现成的麻将字体啊，有个叫做<a href="https://github.com/rutopio/mahjong-font">Mahjong Font</a>的项目用的就是COLR格式，兼容性非常好，而且大小只有80KiB，算是最完美的解决方案了。垃圾AI给我推荐的Mahjong Colored真的是……浪费了不少时间，看来有的时候自己调查要比问AI好一些吧。</p>

<h1 id="感想">感想</h1>
<p>总的来看，目前Vibe Coding的体验确实是相当不错，只是问题也非常明显，完全让AI生成的代码要么实现的不优雅，要么就会出BUG，如果完全不懂要写的东西，纯粹由AI生成，那出问题的概率就非常高了。不过要是看不出来错误，完全被AI忽悠过去了，也许这时候的Vibe Coding在另一方面算是完美吧😂？至少对用的人来说也算是解决了问题呢。</p>]]></content><author><name>mayx</name></author><category term="Vibe Coding" /><category term="AI" /><category term="PJAX" /><category term="麻将" /><summary type="html"><![CDATA[All in AI!]]></summary></entry><entry><title type="html">如何节约游戏占用的硬盘空间？</title><link href="https://mabbs.github.io/2026/06/01/dedupe.html" rel="alternate" type="text/html" title="如何节约游戏占用的硬盘空间？" /><published>2026-06-01T00:00:00+08:00</published><updated>2026-06-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/06/01/dedupe</id><content type="html" xml:base="https://mabbs.github.io/2026/06/01/dedupe.html"><![CDATA[<p>浪费硬盘空间是可耻的！<!--more--></p>

<h1 id="起因">起因</h1>
<p>在几年前，我写过一篇在<a href="/2023/10/21/game.html">MacBook上玩游戏</a>的文章，在那之后，我已经在我的Mac上下载了几十部游戏。只不过有个问题……我的Mac只有256GiB的硬盘存储空间，下载一堆游戏会让我的硬盘空间不够用，但是又不太想删，所以我该怎么尽可能让游戏占用更少的空间呢？ <br />
  首先为了能在Mac上尽可能流畅地玩，我玩的游戏大多都是用跨平台能力很强的引擎编写的游戏，比如<a href="https://github.com/renpy/renpy">Ren’Py</a>、RPG制作大师、Godot之类的，而像RPG制作大师这种引擎制作的游戏还有一个特点，开发者一般都会使用引擎自带的素材进行开发，有时候还会用不少第三方的罐头素材之类的（实际上甚至还有好多AVG为了蹭这些引擎的公用素材刻意用它们），所以这几十个游戏里应该有非常多的重复素材，如果能想办法把它们去个重，应该能节省相当多的空间吧……</p>

<h1 id="去重的方法">去重的方法</h1>
<p>如果想要对文件进行去重，我搜了一下，有个叫做<a href="https://codeberg.org/jbruchon/jdupes">jdupes</a>的工具就很不错，它支持多种去重方式，比如使用硬链接，或者用一些文件系统的写时复制特性。不过如果用写时复制特性，jdupes在第二次执行的时候会认为去重后的文件还是单独的文件，就会重复去重了，而且最终也不好统计，反正对我玩的游戏来说，要去重的都是游戏素材，不存在后续修改的可能性，所以我打算全部用硬链接。 <br />
  所以最终要执行的命令也非常简单，直接一句<code class="language-plaintext highlighter-rouge">jdupes -r -L Game</code>就可以了，这样以后每次下载了新的游戏之后重复执行这个操作，就可以将游戏中和其他游戏里有的素材去重了。 <br />
  不过实际上很多游戏并不能直接用这种方式去重，因为它们的资源文件有些是打包成单个文件，有些进行了简单的加密，导致即使是相同的素材，文件也并不相同，所以我必须让所有的资源以单独原始的形态出现。对于不同的引擎也有不同的处理方式，所以接下来我需要对它们进行一些研究。</p>

<h1 id="不同引擎的处理方式">不同引擎的处理方式</h1>
<h2 id="rpg制作大师mvmz">RPG制作大师MV/MZ</h2>
<p>对于RPG制作大师MV/MZ开发的游戏来说，解密很简单，比较知名的是一个叫做<a href="https://gitlab.com/Petschko/RPG-Maker-MV-Decrypter">RPG-Maker-MV-Decrypter</a>的工具，它可以在浏览器中进行解密，但一个游戏的资源文件非常多……要是全上传给浏览器实在是太麻烦了……后来我又搜了一下，有一个用C#写的叫<a href="https://github.com/uuksu/RPGMakerDecrypter">RPG Maker Decrypter</a>工具也很不错，它作为命令行工具比在浏览器中执行简单多了，而且还能只把资源文件单独提出来，这样就可以剔除掉游戏自带的浏览器文件。不过他这个仓库的代码有个问题，它在选择文件的时候似乎会区分大小写，文件夹名中含有大写字母的似乎会被剔除……这样不太符合我的要求啊，当然我不会C#，于是我用AI改了一下，还给他提了个<a href="https://github.com/uuksu/RPGMakerDecrypter/pull/28">PR</a>，不过这家伙看起来似乎不太喜欢AI写的代码，看起来不打算合我的PR😅。不过无所谓了，反正我也是自用，他爱合不合吧。 <br />
  这个工具的用法也非常简单，一句<code class="language-plaintext highlighter-rouge">RPGMakerDecrypter-cli [input] -p -o [output]</code>就处理好了，处理完之后只需要把<code class="language-plaintext highlighter-rouge">data/System.json</code>中的<code class="language-plaintext highlighter-rouge">hasEncryptedImages</code>和<code class="language-plaintext highlighter-rouge">hasEncryptedAudio</code>设置为false就可以正常识别，以后在Mac中只要在游戏路径下执行<code class="language-plaintext highlighter-rouge">python3 -m http.server</code>就可以在浏览器中游玩了。 <br />
  在这个过程中，我还发现有一些游戏喜欢把原画文件直接放到游戏里面，一张图片好几M，但RPG制作大师的引擎在渲染的时候根本不会渲染出那么高的分辨率，结果毫无意义地浪费一大堆存储空间，而且因为图片是加密的，对大多数人来说也没有收藏价值。所以在解密完之后我就想干脆把这些图片全部有损压缩一遍，估计能节省不少存储空间，于是让AI写了个简单的压缩脚本处理了一下：</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""
图片压缩脚本（多进程版本）
将 pictures.orig 文件夹中的图片使用 WebP 格式进行高效压缩，
保持分辨率不变，肉眼看不出差异，压缩后的图片保存到 pictures 文件夹。

使用方法:
    python3 compress_images.py

压缩策略:
    - 保持原始分辨率不变
    - 使用 WebP 格式（有损压缩，高质量）
    - 质量设置为 85，在保持视觉质量的同时显著减小文件大小
    - 文件名和后缀保持不变
    - 多进程并行处理
    - 处理失败时自动复制原文件
"""</span>

<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">shutil</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">from</span> <span class="nn">multiprocessing</span> <span class="kn">import</span> <span class="n">Pool</span><span class="p">,</span> <span class="n">cpu_count</span>
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">partial</span>

<span class="c1"># 配置路径
</span><span class="n">SOURCE_DIR</span> <span class="o">=</span> <span class="s">"pictures.orig"</span>
<span class="n">OUTPUT_DIR</span> <span class="o">=</span> <span class="s">"pictures"</span>

<span class="c1"># WebP 质量设置 (0-100，数值越高质量越好，文件也越大)
# 85 是一个很好的平衡点，肉眼几乎看不出差异
</span><span class="n">WEBP_QUALITY</span> <span class="o">=</span> <span class="mi">85</span>

<span class="c1"># 对于带有透明通道的图片，可以设置不同的质量
</span><span class="n">WEBP_QUALITY_WITH_ALPHA</span> <span class="o">=</span> <span class="mi">80</span>

<span class="c1"># 并行进程数，默认为 CPU 核心数
</span><span class="n">NUM_WORKERS</span> <span class="o">=</span> <span class="n">cpu_count</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">compress_single_image</span><span class="p">(</span><span class="n">img_file</span><span class="p">:</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bool</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
    <span class="s">"""
    压缩单个图片文件（用于多进程）
    
    Args:
        img_file: (源文件路径, 输出文件路径, 输出目录) 元组
        
    Returns:
        (文件名, 是否成功, 原始大小, 压缩后大小) 元组
    """</span>
    <span class="n">source_path</span><span class="p">,</span> <span class="n">output_path_str</span><span class="p">,</span> <span class="n">output_dir</span> <span class="o">=</span> <span class="n">img_file</span>
    <span class="n">source_path</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">source_path</span><span class="p">)</span>
    <span class="n">output_path</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">output_path_str</span><span class="p">)</span>
    
    <span class="n">original_size</span> <span class="o">=</span> <span class="n">source_path</span><span class="p">.</span><span class="n">stat</span><span class="p">().</span><span class="n">st_size</span>
    
    <span class="k">try</span><span class="p">:</span>
        <span class="n">img</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">source_path</span><span class="p">)</span>
        
        <span class="c1"># 检查是否有透明通道
</span>        <span class="n">has_alpha</span> <span class="o">=</span> <span class="n">img</span><span class="p">.</span><span class="n">mode</span> <span class="ow">in</span> <span class="p">(</span><span class="s">'RGBA'</span><span class="p">,</span> <span class="s">'LA'</span><span class="p">,</span> <span class="s">'PA'</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">img</span><span class="p">.</span><span class="n">mode</span> <span class="o">==</span> <span class="s">'P'</span> <span class="ow">and</span> <span class="s">'transparency'</span> <span class="ow">in</span> <span class="n">img</span><span class="p">.</span><span class="n">info</span><span class="p">)</span>
        
        <span class="c1"># 确定使用的质量
</span>        <span class="n">quality</span> <span class="o">=</span> <span class="n">WEBP_QUALITY_WITH_ALPHA</span> <span class="k">if</span> <span class="n">has_alpha</span> <span class="k">else</span> <span class="n">WEBP_QUALITY</span>
        
        <span class="c1"># 保存为 WebP 格式，但使用原始的文件扩展名
</span>        <span class="n">img</span><span class="p">.</span><span class="n">save</span><span class="p">(</span>
            <span class="nb">str</span><span class="p">(</span><span class="n">output_path</span><span class="p">),</span>
            <span class="nb">format</span><span class="o">=</span><span class="s">'WEBP'</span><span class="p">,</span>
            <span class="n">quality</span><span class="o">=</span><span class="n">quality</span><span class="p">,</span>
            <span class="n">method</span><span class="o">=</span><span class="mi">6</span>  <span class="c1"># 压缩方法 0-6，6 是最慢但压缩率最高的
</span>        <span class="p">)</span>
        
        <span class="n">compressed_size</span> <span class="o">=</span> <span class="n">output_path</span><span class="p">.</span><span class="n">stat</span><span class="p">().</span><span class="n">st_size</span>
        <span class="k">return</span> <span class="p">(</span><span class="n">source_path</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">True</span><span class="p">,</span> <span class="n">original_size</span><span class="p">,</span> <span class="n">compressed_size</span><span class="p">)</span>
        
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="c1"># 处理失败时，复制原文件到输出目录
</span>        <span class="k">try</span><span class="p">:</span>
            <span class="n">shutil</span><span class="p">.</span><span class="n">copy2</span><span class="p">(</span><span class="n">source_path</span><span class="p">,</span> <span class="n">output_path</span><span class="p">)</span>
            <span class="n">compressed_size</span> <span class="o">=</span> <span class="n">output_path</span><span class="p">.</span><span class="n">stat</span><span class="p">().</span><span class="n">st_size</span>
            <span class="k">return</span> <span class="p">(</span><span class="n">source_path</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="n">original_size</span><span class="p">,</span> <span class="n">compressed_size</span><span class="p">)</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">copy_error</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">(</span><span class="n">source_path</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="n">original_size</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">source_dir</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">SOURCE_DIR</span><span class="p">)</span>
    <span class="n">output_dir</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">OUTPUT_DIR</span><span class="p">)</span>
    
    <span class="c1"># 检查源目录是否存在
</span>    <span class="k">if</span> <span class="ow">not</span> <span class="n">source_dir</span><span class="p">.</span><span class="n">exists</span><span class="p">():</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"错误: 源目录 '</span><span class="si">{</span><span class="n">SOURCE_DIR</span><span class="si">}</span><span class="s">' 不存在"</span><span class="p">)</span>
        <span class="k">return</span>
    
    <span class="c1"># 创建输出目录
</span>    <span class="n">output_dir</span><span class="p">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    
    <span class="c1"># 获取所有图片文件（支持多种格式）
</span>    <span class="n">image_extensions</span> <span class="o">=</span> <span class="p">(</span><span class="s">'*.png'</span><span class="p">,</span> <span class="s">'*.jpg'</span><span class="p">,</span> <span class="s">'*.jpeg'</span><span class="p">,</span> <span class="s">'*.bmp'</span><span class="p">,</span> <span class="s">'*.gif'</span><span class="p">,</span> <span class="s">'*.tiff'</span><span class="p">,</span> <span class="s">'*.webp'</span><span class="p">)</span>
    <span class="n">image_files</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">ext</span> <span class="ow">in</span> <span class="n">image_extensions</span><span class="p">:</span>
        <span class="n">image_files</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">source_dir</span><span class="p">.</span><span class="n">glob</span><span class="p">(</span><span class="n">ext</span><span class="p">))</span>
    <span class="n">image_files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">image_files</span><span class="p">))</span>  <span class="c1"># 去重并排序
</span>    
    <span class="k">if</span> <span class="ow">not</span> <span class="n">image_files</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"在 '</span><span class="si">{</span><span class="n">SOURCE_DIR</span><span class="si">}</span><span class="s">' 中没有找到图片文件"</span><span class="p">)</span>
        <span class="k">return</span>
    
    <span class="c1"># 构建任务列表
</span>    <span class="n">tasks</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">img_file</span> <span class="ow">in</span> <span class="n">image_files</span><span class="p">:</span>
        <span class="n">output_path</span> <span class="o">=</span> <span class="n">output_dir</span> <span class="o">/</span> <span class="n">img_file</span><span class="p">.</span><span class="n">name</span>  <span class="c1"># 保持原文件名和后缀
</span>        <span class="n">tasks</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="nb">str</span><span class="p">(</span><span class="n">img_file</span><span class="p">),</span> <span class="nb">str</span><span class="p">(</span><span class="n">output_path</span><span class="p">),</span> <span class="nb">str</span><span class="p">(</span><span class="n">output_dir</span><span class="p">)))</span>
    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"找到 </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">tasks</span><span class="p">)</span><span class="si">}</span><span class="s"> 个图片文件"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"源目录: </span><span class="si">{</span><span class="n">SOURCE_DIR</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"输出目录: </span><span class="si">{</span><span class="n">OUTPUT_DIR</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"WebP 质量设置: </span><span class="si">{</span><span class="n">WEBP_QUALITY</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"并行进程数: </span><span class="si">{</span><span class="n">NUM_WORKERS</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"-"</span> <span class="o">*</span> <span class="mi">70</span><span class="p">)</span>
    
    <span class="c1"># 使用多进程池处理图片
</span>    <span class="n">success_count</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">fail_count</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">total_original</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">total_compressed</span> <span class="o">=</span> <span class="mi">0</span>
    
    <span class="k">with</span> <span class="n">Pool</span><span class="p">(</span><span class="n">processes</span><span class="o">=</span><span class="n">NUM_WORKERS</span><span class="p">)</span> <span class="k">as</span> <span class="n">pool</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">success</span><span class="p">,</span> <span class="n">original_size</span><span class="p">,</span> <span class="n">compressed_size</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">pool</span><span class="p">.</span><span class="n">imap</span><span class="p">(</span><span class="n">compress_single_image</span><span class="p">,</span> <span class="n">tasks</span><span class="p">),</span> <span class="mi">1</span><span class="p">):</span>
            <span class="n">total_original</span> <span class="o">+=</span> <span class="n">original_size</span>
            <span class="n">total_compressed</span> <span class="o">+=</span> <span class="n">compressed_size</span>
            
            <span class="k">if</span> <span class="n">success</span><span class="p">:</span>
                <span class="n">success_count</span> <span class="o">+=</span> <span class="mi">1</span>
                <span class="n">marker</span> <span class="o">=</span> <span class="s">"✓"</span>
                <span class="n">reduction</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">compressed_size</span> <span class="o">/</span> <span class="n">original_size</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span> <span class="k">if</span> <span class="n">original_size</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span>
                <span class="n">status_msg</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">reduction</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%"</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">fail_count</span> <span class="o">+=</span> <span class="mi">1</span>
                <span class="n">marker</span> <span class="o">=</span> <span class="s">"✗"</span>
                <span class="n">status_msg</span> <span class="o">=</span> <span class="s">"复制原文件"</span>
            
            <span class="n">status</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"[</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">tasks</span><span class="p">)</span><span class="si">}</span><span class="s">] </span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s">"</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">marker</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">status</span><span class="si">:</span><span class="mi">50</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">original_size</span><span class="o">/</span><span class="mi">1024</span><span class="si">:</span><span class="o">&gt;</span><span class="mf">8.1</span><span class="n">f</span><span class="si">}</span><span class="s">KB -&gt; </span><span class="si">{</span><span class="n">compressed_size</span><span class="o">/</span><span class="mi">1024</span><span class="si">:</span><span class="o">&gt;</span><span class="mf">8.1</span><span class="n">f</span><span class="si">}</span><span class="s">KB (</span><span class="si">{</span><span class="n">status_msg</span><span class="si">}</span><span class="s">)"</span><span class="p">)</span>
    
    <span class="c1"># 输出总结
</span>    <span class="k">print</span><span class="p">(</span><span class="s">"-"</span> <span class="o">*</span> <span class="mi">70</span><span class="p">)</span>
    <span class="n">total_reduction</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">total_compressed</span> <span class="o">/</span> <span class="n">total_original</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span> <span class="k">if</span> <span class="n">total_original</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"压缩完成!"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  成功处理: </span><span class="si">{</span><span class="n">success_count</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">tasks</span><span class="p">)</span><span class="si">}</span><span class="s"> 个文件"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">fail_count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  失败(已复制原文件): </span><span class="si">{</span><span class="n">fail_count</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">tasks</span><span class="p">)</span><span class="si">}</span><span class="s"> 个文件"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  原始总大小: </span><span class="si">{</span><span class="n">total_original</span> <span class="o">/</span> <span class="mi">1024</span> <span class="o">/</span> <span class="mi">1024</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> MB (</span><span class="si">{</span><span class="n">total_original</span> <span class="o">/</span> <span class="mi">1024</span><span class="si">:</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s"> KB)"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  压缩后大小: </span><span class="si">{</span><span class="n">total_compressed</span> <span class="o">/</span> <span class="mi">1024</span> <span class="o">/</span> <span class="mi">1024</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> MB (</span><span class="si">{</span><span class="n">total_compressed</span> <span class="o">/</span> <span class="mi">1024</span><span class="si">:</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s"> KB)"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  总压缩率: </span><span class="si">{</span><span class="n">total_reduction</span><span class="si">:</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  节省空间: </span><span class="si">{</span><span class="p">(</span><span class="n">total_original</span> <span class="o">-</span> <span class="n">total_compressed</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1024</span> <span class="o">/</span> <span class="mi">1024</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> MB"</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>
<p>最终压缩完之后我把原图上传到了<a href="https://e-hentai.org/g/3901673/426a7a17ba/">EH画廊</a>中，本地只留压缩后的图片，大小从原来的2GiB多下降到了300多MiB，可以说效果相当显著了。 <br />
  除此之外还有一些游戏使用了Ogg FLAC背景音乐，这种音乐不仅占用磁盘空间很大，而且我在Safari上玩的时候浏览器根本没法解析（Chrome应该可以）。虽然我听音乐是会考虑<a href="/2025/03/22/hifi.html">HiFi</a>，但玩游戏就没必要了吧……所以像这种音乐，就得用一句：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffmpeg <span class="nt">-i</span> input.flac.ogg <span class="nt">-c</span>:a vorbis <span class="nt">-strict</span> <span class="nt">-2</span> <span class="nt">-q</span>:a 10 output.ogg
</code></pre></div></div>
<p>转换为正常有损的Ogg音乐了。</p>
<h2 id="rpg制作大师xpvxva">RPG制作大师XP/VX/VA</h2>
<p>对于RPG制作大师XP/VX/VA引擎开发的游戏来说，它们都是基于用Ruby语言开发的RGSS编写的，作为脚本来说，倒是有跨平台的条件，但因为官方并没有做跨平台，所以不能直接在Mac上运行。不过有一款叫做<a href="https://github.com/mkxp-z/mkxp-z">mkxp-z</a>的工具允许跨平台运行使用RPG制作大师XP/VX/VA制作的游戏，因此这类游戏我也收集了一些。 <br />
  这些游戏的资源通常会进行简单的混淆加密，一般会打包成单个RGSSAD文件，这个解包也很简单，用刚刚的RPG Maker Decrypter就可以。不过这种游戏还有个特点，有些游戏需要使用<a href="https://www.rpgmakerweb.com/run-time-package">RTP</a>才能运行，它这个RTP其实就是RPG制作大师自带的素材包，当时设计出来估计也是想着用来节约硬盘空间吧，就是不知道为什么到后来的MV/MZ却取消了这种方式……虽然mkxp-z是支持通过配置文件引入RTP的，但既然我已经选择了硬链接的方式，就没必要单独搞RTP了，我选择把RTP直接和游戏合并，然后让jdupes直接去重就好了，这样相比于RTP的方式还有一些好处就是XP/VX/VA可能有一些和MV/MZ使用相同的素材，这部分也可以不用占用重复的空间了。</p>
<h2 id="renpy">Ren’Py</h2>
<p>对于Ren’Py来说，因为这个引擎并没有自带的公共资源，所以重复素材的问题并不是很大。不过在我之前对<a href="/2024/01/20/renpy.html">Ren’Py的探索</a>中提到过，我玩的一些游戏是系列游戏，这种系列游戏有非常多的素材复用，但显然开发者并不会为了节约玩家硬盘空间而共享这部分资源，而且Ren’Py游戏也都是打包成单个文件的，所以接下来我们依然得要解包才能进行去重处理。 <br />
  Ren’Py使用的rpa文件解包起来依然很简单，有一款现成的工具<a href="https://github.com/Lattyware/unrpa">unrpa</a>可以直接解包，用pip就能安装。不知道为什么这些引擎总是喜欢把资源文件都打成一个包，明明很容易就能解包……难道是为了性能吗？ <br />
  不过也正是因为Ren’Py的公共资源不多，如果玩的不是系列游戏，就没有解包的必要了，解包之后一堆小文件有可能会比整个rpa文件更大，毕竟文件系统存在“簇”，有可能会消耗没对齐的空间。</p>

<h1 id="验证结果">验证结果</h1>
<p>最终进行完上述操作，可以通过执行<code class="language-plaintext highlighter-rouge">du -sh</code>和<code class="language-plaintext highlighter-rouge">du -shl</code>进行对比来验证节约的硬盘空间，我在这次游戏的瘦身中节约了：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ % du -sh Game                                
 33G	Game
~ % du -shl Game
 47G	Game
</code></pre></div></div>
<p>看起来还是相当可观啊……尤其是在当下硬盘价格大涨的情况下，如果很多人能通过这些方式来节约硬盘空间，就能减少对硬盘容量的需求吧……不过说到底其实也都是网上能下到的资源，也许玩完之后就删掉才是最好的节约硬盘的方式吧😂。</p>

<p><input name="live2dBGM" value="https://music.163.com/song/media/outer/url?id=1968116350.mp3" type="hidden" /></p>]]></content><author><name>mayx</name></author><category term="dedupe" /><category term="RPG制作大师" /><category term="游戏" /><summary type="html"><![CDATA[浪费硬盘空间是可耻的！]]></summary></entry><entry><title type="html">虚拟局域网的组网探索记录</title><link href="https://mabbs.github.io/2026/05/01/virtual-net.html" rel="alternate" type="text/html" title="虚拟局域网的组网探索记录" /><published>2026-05-01T00:00:00+08:00</published><updated>2026-05-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/05/01/virtual-net</id><content type="html" xml:base="https://mabbs.github.io/2026/05/01/virtual-net.html"><![CDATA[<p>异地组网，有多少种选择？<!--more--></p>

<h1 id="起因">起因</h1>
<p>最近我有一些放置在许多不同地方的机器，有一些东西需要让它们之间能够相互访问。虽然我很久以前写过一篇使用<a href="/2021/05/07/ssh.html">SSH进行互联</a>的文章，但这样做每个服务都需要单独配置，也不方便管理。所以为了能让机器之间能够轻松通信，我打算组建一个虚拟局域网，让它们像在同一交换机下一样。不过这种组网的工具非常多，我应该选哪个比较好呢？</p>

<h1 id="不同组网工具的体验">不同组网工具的体验</h1>
<h2 id="n2n">n2n</h2>
<p>以前我用过一款用C写的叫做<a href="https://github.com/ntop/n2n">n2n</a>的工具，它可以很轻松地组建一个P2P的二层虚拟网络，而且生态也不错，手机、电脑、路由器、服务器上都有可以用的客户端。使用起来非常简单，它的中继和穿透服务程序叫做Supernode，无需太多的配置，只要在有公网的服务器安装并使用<code class="language-plaintext highlighter-rouge">-p</code>指定一个端口就可以启动。而客户端配置也非常简单，用<code class="language-plaintext highlighter-rouge">-l</code>配置好Supernode的地址，然后让想要在同一个网络的机器使用相同的任意<code class="language-plaintext highlighter-rouge">-k</code>和<code class="language-plaintext highlighter-rouge">-c</code>就可以成功组网，可以说算是非常好用了。 <br />
  唯一的问题就是它这个项目看起来似乎已经停止更新了……虽然大多数情况下用起来没问题，但是有时候还是会出现组网不太可靠的情况。如果两个机器都不经过NAT，可以通过公网IP连接，它的可靠性还可以。但如果是两个NAT后的机器之间，有时候会存在莫名掉线的情况，也许是因为穿透导致的不可靠？总之遇到这种情况之后重启又能正常工作，说明是软件本身的问题，但它停更了……所以对我来说它的可靠性不太够。（其实它还有个叫做<a href="https://github.com/n42n/n3n">n3n</a>的继任者，不过知名度不高，所以生态也不太行）</p>
<h2 id="wireguard">WireGuard</h2>
<p>其实在这之后我本来是打算用L2TP/IPSec进行组网的，但看了一下貌似配置有点复杂，而且不够现代，现在想要组网貌似大多都推荐<a href="https://git.zx2c4.com/wireguard-linux/">WireGuard</a>作为更现代的选择。只不过它和n2n相比来说是三层的虚拟网络，如果需要发送非TCP/IP协议的特别包，可能就用不了它吧，当然对我来说没有这种需求。它用起来也非常简单，不过正常情况下它设计是为了点对点传输，而且没有自带的NAT穿透功能，所以如果想要实现组网，就得搭一个星形网络，让互联网上的服务器作为虚拟的交换机，这个做起来倒也不复杂。首先，每个节点需要生成一个公私钥对作为身份证明，在安装好WireGuard之后执行<code class="language-plaintext highlighter-rouge">wg genkey</code>就能生成私钥。作为交换机的节点需要在<code class="language-plaintext highlighter-rouge">/etc/wireguard/wg0.conf</code>中写一个这样的配置：</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">Interface</span>]
<span class="n">PrivateKey</span> = <span class="n">xxx</span>
<span class="n">Address</span> = <span class="m">192</span>.<span class="m">168</span>.<span class="m">1</span>.<span class="m">1</span>/<span class="m">24</span>
<span class="n">ListenPort</span> = <span class="m">51820</span>

<span class="n">PostUp</span> = <span class="n">iptables</span> -<span class="n">A</span> <span class="n">FORWARD</span> -<span class="n">i</span> <span class="n">wg0</span> -<span class="n">o</span> <span class="n">wg0</span> -<span class="n">j</span> <span class="n">ACCEPT</span>
<span class="n">PostDown</span> = <span class="n">iptables</span> -<span class="n">D</span> <span class="n">FORWARD</span> -<span class="n">i</span> <span class="n">wg0</span> -<span class="n">o</span> <span class="n">wg0</span> -<span class="n">j</span> <span class="n">ACCEPT</span>

<span class="c"># 机器1
</span>[<span class="n">Peer</span>]
<span class="n">PublicKey</span> = <span class="n">xxx</span>
<span class="n">AllowedIPs</span> = <span class="m">192</span>.<span class="m">168</span>.<span class="m">1</span>.<span class="m">2</span>/<span class="m">32</span>

<span class="c"># 机器2
</span>[<span class="n">Peer</span>]
<span class="n">PublicKey</span> = <span class="n">xxx</span>
<span class="n">AllowedIPs</span> = <span class="m">192</span>.<span class="m">168</span>.<span class="m">1</span>.<span class="m">3</span>/<span class="m">32</span>
</code></pre></div></div>
<p>其中PrivateKey填写交换机自己的私钥，而作为使用者的Peer中的PublicKey可以用对应节点的私钥执行<code class="language-plaintext highlighter-rouge">echo xxx | wg pubkey</code>这个命令查看，然后每个Peer需要像这样配置：</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">Interface</span>]
<span class="n">PrivateKey</span> = <span class="n">xxx</span>
<span class="n">Address</span> = <span class="m">192</span>.<span class="m">168</span>.<span class="m">1</span>.<span class="m">2</span>/<span class="m">24</span>

[<span class="n">Peer</span>]
<span class="n">PublicKey</span> = <span class="n">xxx</span> <span class="c"># 交换机节点的公钥
</span><span class="n">Endpoint</span> = <span class="n">xxx</span>.<span class="n">xxx</span>.<span class="n">xxx</span>.<span class="n">xxx</span>:<span class="m">51820</span> <span class="c"># 交换机节点的地址
</span><span class="n">AllowedIPs</span> = <span class="m">192</span>.<span class="m">168</span>.<span class="m">1</span>.<span class="m">0</span>/<span class="m">24</span>
<span class="n">PersistentKeepalive</span> = <span class="m">25</span>
</code></pre></div></div>
<p>最后全都配置好之后所有节点使用<code class="language-plaintext highlighter-rouge">systemctl enable --now wg-quick@wg0</code>启动就可以了，启动之后每个节点可以执行<code class="language-plaintext highlighter-rouge">wg</code>查看当前的连接状态。 <br />
  当然这是在Linux上，至于其他系统大多都有GUI配置，填起来更简单。它的生态也非常好，基本上常见的操作系统都支持，具体可以在<a href="https://www.wireguard.com/install/">官网</a>查看支持的系统和安装方法。不过由于它在Linux中优先使用内核模块，导致我在一些比较小众的环境中也是遇到了各种特别的问题。</p>
<h3 id="在红米ax3000中遇到的问题">在红米AX3000中遇到的问题</h3>
<p>我在这个网络中有几个安装了OpenWrt的路由器，在这其中使用联发科芯片的路由器基本上都没什么问题，官网能轻松下载到固件，也能很轻松地在软件包中找到WireGuard并安装，但我还有一台使用高通芯片的红米AX3000，似乎因为高通对资料管控得很严格，导致它没有官网的固件，最终我在GitHub上找了一个其他人自己编译的<a href="https://github.com/hzyitc/openwrt-redmi-ax3000/">固件</a>。虽然它整起来有点麻烦，不过倒也能用，但是在我尝试安装WireGuard的时候遇到了麻烦…… <br />
  它的软件包里有WireGuard，也能找到对应的内核模块安装包，但安装完之后没法启动……随后我看了一下它下载的<a href="https://github.com/hzyitc/openwrt-redmi-ax3000/blob/gh-pages/ipq50xx-qsdk-kernel-5.4-openwrt-21.02-qsdk-11.5.05.841.1029/ci-20240727-173350-ab1f9ffa/kmod-wireguard_5.4-qsdk-11.5.0.5-1_arm_cortex-a7_neon-vfpv4.ipk">安装包</a>，结果发现是空的😰，它这个固件的内核模块可能是在编译的时候遇到了一些问题。至于让我自己编译这个内核模块，难度似乎有点高了……那怎么办呢？要知道Linux的内核模块都是和内核挂钩的，没办法随便找一个别的模块使用。还好WireGuard倒也不止有内核模块，也有一些在用户空间中的实现，比如<a href="https://git.zx2c4.com/wireguard-go">wireguard-go</a>和<a href="https://git.zx2c4.com/wireguard-rs">wireguard-rs</a>。只是官方似乎非常不推荐在Linux上使用它们，所以没有提供预编译的版本。不过遇到这种问题的人也许是比较多，所以有人做了在<a href="https://github.com/seud0nym/openwrt-wireguard-go">OpenWrt上使用的wireguard-go</a>，安装好之后效果和使用内核模块的感觉基本上没什么区别，最终也能连通，唯一的区别就是在执行<code class="language-plaintext highlighter-rouge">wg</code>的时候，会显示“Interface: wg0 (userspace)”罢了。从效率上来说虽然肯定没有内核模块那么高，但它其实也用了“Tun”模块，理论上和使用“Tap”模块的n2n应该差不多吧。</p>
<h3 id="在openeuler中遇到的问题">在openEuler中遇到的问题</h3>
<p>在我使用的节点中，还有一台安装了openEuler 22.03 LTS操作系统的服务器，虽然openEuler和CentOS可以说基本上没什么区别，但毕竟它的内核是openEuler自己编译的，所以没办法直接使用CentOS的内核模块。并且openEuler的源中也完全没有提供和WireGuard相关的包，所以想要在openEuler上安装WireGuard还是有些挑战（当然如果觉得麻烦，它们倒是有一个兼容WireGuard的客户端<a href="https://eur.openeuler.openatom.cn/coprs/nucleo/tunsafe/">TunSafe</a>可以凑活用一下）。 <br />
  后来我试了一下在这上面安装wireguard-tools倒是可以直接用<a href="https://mirrors.tuna.tsinghua.edu.cn/epel/8/Everything/x86_64/Packages/w/wireguard-tools-1.0.20210914-1.el8.x86_64.rpm">CentOS 8EPEL源中的包</a>，但openEuler的内核在编译的时候故意没有包含WireGuard内核模块……这该怎么办呢？用wireguard-go吗？虽然这样可以很简单地解决，但感觉这样就是认输了😂。后来我搜了一下，找到了一篇<a href="https://dingle.site/archives/wei-openeulertian-jia-wireguardmo-kuai">在openEuler安装WireGuard内核模块</a>的文章，方法大致如下：</p>
<ol>
  <li>首先安装编译环境和源代码。
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yum <span class="nb">install </span>elfutils-libelf-devel kernel-devel pkgconfig <span class="s2">"@Development Tools"</span>
yum <span class="nb">install </span>kernel-headers.x86_64 pkg-config ncurses-devel openssl-devel dwarves
yum <span class="nb">install </span>kernel-source.x86_64
</code></pre></div>    </div>
  </li>
  <li>然后进行编译配置，内核源码一般会安装到<code class="language-plaintext highlighter-rouge">/usr/src/</code>下，找到之后在里面执行<code class="language-plaintext highlighter-rouge">make menuconfig</code>，然后勾选“Device Drivers -&gt; Network device support -&gt; Wireguard secure network tunnel”并保存。</li>
  <li>最后执行<code class="language-plaintext highlighter-rouge">make</code>开始编译，为了加速可以用<code class="language-plaintext highlighter-rouge">-j</code>参数加上CPU的核心数进行并行编译，当时编译就花掉了一整天😂，理论上应该可以只编译WireGuard和它依赖的几个模块，不过我不太清楚怎么做，还是费点时间按照文中说的做吧。</li>
  <li>执行<code class="language-plaintext highlighter-rouge">make modules_install</code>将编译好的结果安装到<code class="language-plaintext highlighter-rouge">/lib/modules/5.10.0</code>。
  不过系统似乎不会去这个路径下找内核模块，所以还得把这里面的kernel文件夹复制到<code class="language-plaintext highlighter-rouge">/lib/modules/$(uname -r)</code>下，然后执行<code class="language-plaintext highlighter-rouge">depmod -a</code>更新模块依赖。</li>
  <li>最后执行<code class="language-plaintext highlighter-rouge">modprobe wireguard</code>验证模块是否能正常加载，如果没有报错并且可以在<code class="language-plaintext highlighter-rouge">lsmod | grep wireguard</code>中看到，就说明安装成功了，剩余的步骤和其他Linux系统一样。</li>
</ol>

<h3 id="wireguard的控制平面">WireGuard的控制平面</h3>
<p>虽然WireGuard本身配置很简单，但每加一个节点还得在交换机节点上修改一下配置文件，稍微有些麻烦，所以有人开发了一些控制平面，让它可以被更规范地管理，比如<a href="https://github.com/gravitl/netmaker">Netmaker</a>和<a href="https://github.com/juanfont/headscale">Headscale</a>。而Headscale主要是为Tailscale客户端开发的开源服务器端，因此功能会局限于Tailscale提供的功能。所以如果没有用过Tailscale，可以优先考虑Netmaker。 <br />
  这两个控制平面支持的功能相当丰富，而且它们还支持让WireGuard进行NAT穿透，自动组建Mesh网络，不像我一堆在NAT后的设备还要直接使用WireGuard就只能搭成星形网络。只不过对我来说，我也用不到那么多企业级功能，这个服务端配置起来也有点麻烦，而且我也没有很多节点需要动态增减，我的云端服务器带宽也足够使用，所以就没有用这些东西了😆。</p>
<h2 id="其他的组网工具">其他的组网工具</h2>
<p>除了WireGuard之外，还有很多其他的组网工具，比如<a href="https://github.com/vnt-dev/vnt">VNT</a>和<a href="https://github.com/EasyTier/Easytier">EasyTier</a>，这俩用起来也非常简单，只需要加几个参数就能组网，和n2n一样。不过功能相比于n2n来说要强大不少，也支持NAT穿透，而且还都兼容WireGuard协议，另外不像WireGuard强制使用UDP传输，这两个还能用TCP和WebSocket，在特殊网络环境下应该比直接用WireGuard更好。另外它们都是Rust编写的，也许会更安全😋？可惜我已经配好WireGuard之后懒得再改了，如果以后有机会，可以尝试一下。</p>

<h1 id="总结">总结</h1>
<p>现在如果想要异地搭建虚拟局域网，还是有相当多的选择，而且无论是性能还是配置难度，都比以前好了不少。看来这种需求还是相当多啊，也正是因为有这些需求，所以才会出现这么多的方案可以用吧……总之我最后还是选择了纯WireGuard方案，主要还是简单够用，可靠性也不错，而且折腾了这么多再换也不太合适吧🤣。</p>]]></content><author><name>mayx</name></author><category term="虚拟网络" /><category term="异地组网" /><category term="WireGuard" /><summary type="html"><![CDATA[异地组网，有多少种选择？]]></summary></entry><entry><title type="html">关于AI个人助理的探索</title><link href="https://mabbs.github.io/2026/04/14/ai-agent.html" rel="alternate" type="text/html" title="关于AI个人助理的探索" /><published>2026-04-14T00:00:00+08:00</published><updated>2026-04-14T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/04/14/ai-agent</id><content type="html" xml:base="https://mabbs.github.io/2026/04/14/ai-agent.html"><![CDATA[<p>给AI添加手脚能有多少种方法？<!--more--></p>

<h1 id="起因">起因</h1>
<p>最近像<a href="https://github.com/openclaw/openclaw">OpenClaw</a>这样被叫做“AI个人助理”的Agent越来越火了，当然这种东西在我看来依然是新瓶装旧酒，整来整去还就是和AutoGPT一样。这种东西在当时GPT-3.5的时候就有了，现在只不过是增加了聊天软件交互的渠道便突然大火，和当年的Manus一样……实在是无法理解。 <br />
  一年前我用过Devin.ai这个云端的Agent编写过<a href="/2025/04/08/feed.html">用JS解析订阅源</a>的脚本，体验还算不错，既然现在已经过了一年，那就让我看看现在又有了什么样的发展吧。</p>

<h1 id="使用ai个人助理">使用AI个人助理</h1>
<h2 id="体验原生openclaw">体验原生OpenClaw</h2>
<p>虽然感觉OpenClaw对我的意义不大，但我还是安装体验了一下。不过在国内安装它还是相对有点困难，毕竟国内无论是访问GitHub，还是NPM都有点麻烦，而且还需要有LLM提供商的信息……安装好之后使用起来感觉问题也非常多，经常出现执行一半就停止执行，在它执行的过程中看到它的操作不正确的时候也不能发言打断，而且很多时候最终任务执行的效果也不太好，这也可能是我用的国产开源模型推理能力有限，没舍得用Claude之类先进模型的锅😂？ <br />
  另外我也尝试让它加入MoltBook、MomoClaw、InStreet、百度贴吧抓虾吧之类的AI社区让它帮我宣传我的博客，但效果也很差，它每次发的时候会忘掉之前发的内容，结果就是同一篇内容发了好几遍……不过在这期间，有个叫PushMeBot的家伙在<a href="https://www.moltbook.com/post/7f1b0e1f-5175-4fd1-ad78-856be8b66250">Moltbook的帖子</a>中让我的OpenClaw执行一个网络监视程序，最终安装好之后给我<a href="https://basescan.org/tx/0x44dbfe53f276201447f3877bf050a5d56adebf5fe05235264ee665da717e9373">发了9USDC</a>😝，还挺有意思。 <br />
  总之按照我的体验，实在是想不出它能火的理由，体验不算很好，而且还要安装Node环境，完全不像是能让大众轻松使用的东西。 <br />
  不过这个项目似乎本身就是Vibe Coding的产物，体验不好也能理解，就看火了之后能有多少人完善它吧。</p>
<h2 id="国内大厂的二开claw">国内大厂的二开Claw</h2>
<p>国内好多大厂倒是看中了这个东西的爆火，像腾讯就出了几款这样的软件，比如QClaw。它可以不需要配置额外的环境，能像传统的软件一样直接安装使用，而且有自带的模型，有一定的免费额度可以用。配置技能也比较简单，直接点击就可以完成。而且可以直接扫码关联微信，直接通过微信和它进行交流，可以说是相当的傻瓜化了。不过QClaw给的免费额度虽然用来聊天之类的没问题，但对于开发软件还是有点少，所以他们还出了个叫做WorkBuddy的软件，它送的初始额度比QClaw要多不少，所以更适合用来开发。只不过为啥腾讯要出两个功能一样的软件？看起来应该是不同团队出的，可能是面向的用户群体不一样，所以搞了两套吧？</p>
<h2 id="vscode中的agent">VSCode中的Agent</h2>
<p>但要说开发的话，用作为“AI个人助理”的某些Claw其实并不合适，毕竟正常开发还是以人开发为主，全AI开发总会有些问题，所以开发的时候还是用编辑器集成的AI比较好。在三年前我就在用<a href="/2023/04/05/ai.html">GitHub Copilot</a>了，到现在我依然在用。现在的Copilot已经支持了Agent功能，开发相比之前也是强了很多，只不过现在的我没有学生身份，Copilot Free偶尔也会出现不够用的情况。不过对于Agent这类功能实现起来还是太简单了，所以有人开发这种功能的插件也很正常，比如<a href="https://github.com/cline/cline">Cline</a>，Copilot只能用微软提供的几个模型，而Cline可以自定义模型，用起来也很方便。</p>
<h2 id="微型开发板上运行的claw">微型开发板上运行的Claw</h2>
<p>前段时间，我闲来无事看了一下两年前买的<a href="/2024/02/24/luckfox.html">Luckfox Pico Plus</a>开发板的文档，偶然发现了一个很有意思的项目，叫做<a href="https://github.com/LuckfoxTECH/luckclaw">LuckClaw</a>，这是一个基于<a href="https://github.com/HKUDS/nanobot">nanobot</a>用Golang重构的轻量个人AI助手，可以在仅仅64MiB内存的超有限环境下运行一个和OpenClaw功能几乎相当的AI个人助理，真的是非常厉害。 <br />
  我在我的开发板上试了一下，体验很不错，安装不需要额外环境，直接下载就能使用，Go语言的程序确实方便。配置也很简单，直接执行<code class="language-plaintext highlighter-rouge">luckclaw config</code>就可以交互式进行模型等设置的配置，而且作为国产的应用，它也能很方便的对接国内聊天软件。只是限于开发板本身的能力，浏览器功能自然无法使用，所以搜索如果不借助那些需要API Key的AI专用接口，就基本上不能用……但总的来说效果已经非常不错了，至少有那些Claw的80%能力。 <br />
  （2025.04.15补充：后来我发现这种超精简的Claw项目看起来还挺多，比如<a href="https://github.com/zeroclaw-labs/zeroclaw">ZeroClaw</a>和<a href="https://github.com/sipeed/picoclaw">PicoClaw</a>，甚至还有给单片机用的<a href="https://github.com/memovai/mimiclaw">MimiClaw</a>。而且有意思的是，PicoClaw是Luckfox的竞争对手开发的，但是LuckClaw中却包含PicoClaw字样的注释，结果功能也没PicoClaw强，关注度也更低，属于是没抄明白了🤣） <br />
  想到前段时间还有人为了OpenClaw专门买Mac Mini，就感觉很有意思😆，这个东西看起来应该是在路由器上都能跑。所以想要AI个人助理，硬件完全不是问题，只要整一个能24小时挂机的东西，就可以满足绝大多数人的需求了。</p>
<h2 id="在手机上运行的claw">在手机上运行的Claw</h2>
<p>其实很多人也有比开发板和路由器性能更强的闲置设备，那就是手机，所以有人开发了一款叫做<a href="https://github.com/apkclaw-team/ApkClaw">ApkClaw</a>的软件，一样可以接入国内聊天软件。它既然能在手机上运行，当然和在其他平台运行的Claw相比有一个独特的优势，那就是操作手机应用。现在手机的应用相比电脑应用对于很多普通人来说功能更强大，所以它能做的事情可能比其他的Claw还多。我试了一下，配置也很方便，只不过能配置的项目太少了，看起来似乎没有安装Skill之类的功能，也许是因为它是相对早期的软件，所以功能还比较少吧。</p>

<h1 id="感想">感想</h1>
<p>总的来说，现在的Agent依然没有非常明显的进步，问题依旧很多，只是化身“AI个人助理”之后，增加了不少应用场景。这倒也是好事，在广泛传播的过程中，也能让很多对技术了解不多，但是很有想法的人参与其中，也许能对AI的应用化增添不少力量吧。</p>]]></content><author><name>mayx</name></author><category term="AI" /><category term="Agent" /><category term="个人助理" /><summary type="html"><![CDATA[给AI添加手脚能有多少种方法？]]></summary></entry><entry><title type="html">近期LLM的部署与应用经历(3)</title><link href="https://mabbs.github.io/2026/03/01/llm3.html" rel="alternate" type="text/html" title="近期LLM的部署与应用经历(3)" /><published>2026-03-01T00:00:00+08:00</published><updated>2026-03-01T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/03/01/llm3</id><content type="html" xml:base="https://mabbs.github.io/2026/03/01/llm3.html"><![CDATA[<p>用更多的方式探索AI！<!--more--></p>

<h1 id="起因">起因</h1>
<p>在一年前，我<a href="/2025/02/22/llm.html">整了张RTX4090 48GiB魔改版</a>用来跑DeepSeek-R1 70B的4bit量化模型，不过都已经过了这么长时间，这个模型也已经是过时的东西了……我之前在<a href="/2025/05/07/mac-studio.html">Mac Studio M3 Ultra</a>上试了一下OpenAI在半年前出的gpt-oss-120b模型，感觉效果还挺不错，只不过因为M3 Ultra的GPU实际性能比不上正经高端的独显，所以它在上下文很长的情况下还是有点慢，因此我又整了张RTX4090 48GiB，想整个双路试试更快的GPT-OSS模型，总共96GiB的显存应该够跑这个模型了。</p>

<h1 id="在两张rtx4090-48g上运行gpt-oss">在两张RTX4090 48G上运行GPT-OSS</h1>
<p>既然现在我手头有两张4090了，那继续用i5-8400处理器的主机似乎不太合适，主要是那个主板就一个PCIe插槽，想插两张显卡也做不到，那买个新的不知道买啥……不管怎么说既然用这么高级的显卡，至少得让它跑满。在两张显卡上跑模型似乎卡间的通信速度比较重要，那最起码得整个支持2个PCIe4.0 x16的板U套装才行，这种级别的没有消费级产品，只能考虑服务器或工作站了。不过我对服务器和工作站了解得并不多，所以就问了问AI哪个支持2个PCIe4.0 x16的平台最便宜，结果AI推荐了TRX40+<a href="https://www.amd.com/zh-cn/support/downloads/drivers.html/processors/ryzen-threadripper/ryzen-threadripper-3000-series/amd-ryzen-threadripper-3960x.html">TR 3960X</a>，于是就按照AI的说法整了一套。 <br />
  这套板U差不多4000CNY，价格倒是还行，如果买现役的估计主板都比显卡贵了。但后来我发现这个并不是最便宜的😂，搜了一下买寨版+<a href="https://www.amd.com/zh-cn/support/downloads/drivers.html/processors/epyc/epyc-7002-series/amd-epyc-7502.html">EPYC 7502</a>还能再便宜1000CNY，而且通道数更多，插4张显卡都没问题……不过买都买了，就先用吧，看来AI的话不能随便信😥。 <br />
  之前我跑模型为了方便，基本上都用的是<a href="https://github.com/ollama/ollama">Ollama</a>，不过听说Ollama多卡运行的效率很低，而且多并发的效果不太好，所以这次换了新电脑之后我想试试<a href="https://github.com/vllm-project/vllm">vLLM</a>，据说一般生产级的AI都用的是这个框架。 <br />
  安装vLLM倒是比想象得简单很多，直接一句<code class="language-plaintext highlighter-rouge">pip install vllm</code>就可以了，其实并没有比Ollama复杂多少。我看了一下<a href="https://developers.openai.com/cookbook/articles/gpt-oss/run-vllm/">OpenAI</a>和<a href="https://docs.vllm.ai/projects/recipes/en/latest/OpenAI/GPT-OSS.html">vLLM</a>运行GPT-OSS的官方文档，发现启动也非常简单，一般来说直接执行<code class="language-plaintext highlighter-rouge">vllm serve openai/gpt-oss-120b</code>就可以。不过直接执行是对于单卡的，我用两张卡需要加个<code class="language-plaintext highlighter-rouge">--tensor-parallel-size 2</code>参数启用张量并行，不然会爆显存。另外考虑到这个模型本身占掉60多GiB的显存之后剩下30GiB还是看起来有点少，所以额外加了个<code class="language-plaintext highlighter-rouge">--kv-cache-dtype fp8</code>参数降低上下文对显存的占用，毕竟模型本身也就是4bit量化的，加了这个应该不会对它的能力有什么影响。除此之外AI还给我推荐了个<code class="language-plaintext highlighter-rouge">--enable-chunked-prefill</code>参数，说是也能避免爆显存的问题。 <br />
  一切准备好之后直接执行，程序就自动开始下载模型了，过了几个小时，终于下载完成，顺便一说启动的时候还显示推荐安装<code class="language-plaintext highlighter-rouge">torch_c_dlpack_ext</code>库，虽然不知道是干啥的，但也顺手安装了。启动完成之后我试了一下，效果非常好，不并发的情况下直接用能达到接近190Tps，可以说是相当快了，而且这个模型的水平也算是开源中的上游水平，应该算是又快又好吧……看来多来一张4090还是挺划算嘛。只不过这个东西基本上就我一个人用，所以也没什么能测一下并发的场景……虽然很快，但还是有点浪费性能吧。</p>

<h1 id="最近deepseek-1m上下文的使用体验">最近DeepSeek 1M上下文的使用体验</h1>
<p>前段时间DeepSeek又出了新的模型，最高可以支持1M长的上下文，而且听说模型规模变小了，所以速度也很快。可惜的是到目前为止还没有开放权重。当然就算开放权重了用2张4090估计也没有足够的显存分配给上下文，至于Mac Studio感觉在长上下文的情况下运行速度应该会很慢…… <br />
  不过我对这个1M上下文还是挺感兴趣，因为好久之前我写过一篇<a href="/2025/04/22/ai-limit.html">关于LLM能力上限</a>的文章，在那篇文章中其实我遇到的问题基本上也就是由上下文不足导致的。那既然现在DeepSeek支持了1M的上下文，那我就应该试试之前因为局限性而妥协的一些东西了。 <br />
  这次我没有用摘要，而是直接把包含整个博客内容的<a href="/search.json">search.json</a>文件上传到DeepSeek，然后向它问了问我的一些问题。试了一下效果非常不错，用摘要会省略的一些细节它基本上都可以展现出来，我试了试让它给我生成一份简历，它甚至在所有文章中找到了我的博客地址、GitHub和邮箱地址，之前用摘要显然是做不到这一点的，这个长上下文还是挺有用啊。 <br />
  另外我还试了试让它根据文章内容分析十六型人格，并且我自己去答了一遍那个测试，结果也是相同的，说明它真的是在几秒内就读完了我的所有文章而且也完全理解了，真的是非常厉害。 <br />
  只是拿AI分析我的文章也许只有我自己了😂，实际上根本没人对我感兴趣，也就只有我自己拿来给自己看……当然如果我的博客能比我活得长，不知道会不会有未来人会对我感兴趣呢……总之对于现在肯定是毫无意义了。 <br />
  除了这些之外，我又试了一下让DeepSeek重构我的<a href="https://github.com/Mabbs/Mabbs.Project">Mabbs</a>，这次生成效果看起来很不错了，虽然代码我没细看，不确定能不能运行，但至少没有偷懒只写一点点，一口气写了80KiB多的代码，这也是长上下文带来的好处吧。总之目前这个长上下文的DeepSeek也算是突破了之前我认为的上限，看来LLM真的是前景无限啊。 <br />
  另外我发现这次更新的DeepSeek居然了解我的博客，我问了一下它“你知道Mayx的博客是哪个博客吗？”，它居然知道，能说出域名，而且还知道我的博客是关于技术的😎，看来这次的训练样本中包含我的信息啊……所以我对这次的更新也挺有好感，毕竟我的知识如果能成为AI的一部分，也算是一种永恒吧。</p>

<h1 id="在8gib内存的macbook运行的新模型">在8GiB内存的MacBook运行的新模型</h1>
<p>在3年前，我在<a href="/2023/04/05/ai.html">探索AI</a>时，在我只有8GiB内存的<a href="/2023/02/03/mbp.html">MacBook Pro</a>上运行了非常早期的LLM——Alpaca-7B，那时候7B的LLM虽然能回答一些问题，但答非所问的情况也非常多。不过最近我发现了一个有意思的LLM，叫做<a href="https://huggingface.co/LiquidAI/LFM2.5-1.2B-Thinking">LFM2.5-1.2B-Thinking</a>，它只用了12亿的参数就有思维链，而且水平据说还挺强。这么长时间过去之后我倒也想看看我的MacBook能运行多聪明的模型，所以就试着跑了一下它。 <br />
  运行它也很容易，一般用Ollama就可以，但是Ollama只有TUI，不能渲染Markdown，我也不太想在我的Mac上整WebUI之类的东西……那有什么好的选择吗？我去制作这个模型的公司官网看了一下，他们制作这个模型本就是为了在端侧运行，所以也专门制作了一个软件运行他们的模型，叫做<a href="https://www.liquid.ai/apollo">Apollo</a>，在手机和Mac上都可以用。我在我的Mac上安装试了一下，效果很好，首先速度非常快，8bit量化正常情况下可以达到60多Tps，即使是省电模式，也能达到20多Tps。另外加上思维链它的思考能力也还不错，虽然一些脑筋急转弯的题不算擅长，但是正常对话，回答问题之类的表现都很不错，相比于之前7B的模型表现好太多了。当然考虑到都已经过去3年了，能有这样的进步也很正常，不过12亿参数就能有这样的智能还是相当可以啊。 <br />
  这个模型之所以有这样的能力似乎是因为他们并不完全是Transformer架构，而是使用的一种叫做LFM2的混合架构，按照大家对他们公司（Liquid AI）以及这个架构名字的理解，可能会觉得这个模型基于液态神经网络，不过我让AI看了一下他们的代码似乎并不是，他们用的是一种类似于Mamba的架构，这种架构似乎就很擅长在小参数的模型下比Transformer模型表现的更好，所以说这种变化也是算法进步带来的。 <br />
  顺便一说这个Apollo除了运行他们自己的模型之外也能连接其他兼容OpenAI接口的模型，正好可以用来连接我的GPT-OSS，这样我就可以不需要下载一些浏览器套壳的重型应用来用我的模型了😝。</p>

<h1 id="感想">感想</h1>
<p>自从ChatGPT之后，AI的发展真是越来越强了，而且能看出来目前甚至并不需要多新多好的硬件就能让一般人获得还不错的智能（当然训练也许还是要大量的硬件），这么看来AI软件的发展还是相当有潜力。目前来看既然优化软件就能做得越来越好，那也许在有限的硬件环境下可以期待无限的智能吧。</p>]]></content><author><name>mayx</name></author><category term="AI" /><category term="LLM" /><category term="模型部署" /><category term="使用体验" /><summary type="html"><![CDATA[用更多的方式探索AI！]]></summary></entry><entry><title type="html">在Google杀死XSLT之后的XML美化方案</title><link href="https://mabbs.github.io/2026/02/08/xslt.html" rel="alternate" type="text/html" title="在Google杀死XSLT之后的XML美化方案" /><published>2026-02-08T00:00:00+08:00</published><updated>2026-02-08T00:00:00+08:00</updated><id>https://mabbs.github.io/2026/02/08/xslt</id><content type="html" xml:base="https://mabbs.github.io/2026/02/08/xslt.html"><![CDATA[<p>即使没有了XSLT，也不能让读者看到光秃秃的XML！<!--more--></p>

<h1 id="起因">起因</h1>
<p>在半年前，我写了一篇<a href="/2025/07/01/xslt.html">用XSLT美化博客XML文件</a>的文章，自从那以后，每次我在浏览其他人博客的时候，都会看一眼对方博客有没有给自己的订阅文件做美化。不过就在前段时间，我在浏览某个博客的时候，发现他博客的订阅文件，甚至连最基本的XML文档树都没有显示出来。这时候我打开开发者工具看了一眼源代码，发现他也并没有使用<code class="language-plaintext highlighter-rouge">xml-stylesheet</code>之类的指令……而且控制台貌似报了些错，好像是出现了什么CSP错误……于是我就想，浏览器显示XML文档树的本质，会不会其实也是一种XSLT？之所以报错也有可能是浏览器在自动引用内置的XSLT时违反了CSP。所以我就问了问谷歌AI，结果似乎真的是这样，比如火狐浏览器就内置了一份<a href="https://github.com/mozilla-firefox/firefox/blob/main/dom/xml/resources/XMLPrettyPrint.xsl">XSLT文件</a>，IE浏览器也有。正当我为XSLT的功能感到强大时，谷歌AI随后提到，<a href="https://developer.chrome.com/docs/web-platform/deprecating-xslt">Chrome浏览器决定弃用XSLT</a>，所以以后不要再用XSLT了😰…… <br />
  我给我的订阅文件加美化功能才半年，怎么就要不能用了？XSLT出现这么多年都还能用，结果等我加上就要废弃了？当时为了增加这个功能，还是费了不少劲的，怎么能让谷歌说没就没？于是我就开始对这件事进行了调查。</p>

<h1 id="google杀死了xslt">Google杀死了XSLT</h1>
<p>从上面Chrome的弃用XSLT文档中，可以发现，这件事的始作俑者是<a href="https://github.com/mfreed7">Mason Freed</a>，他在WHATWG中发起了一个<a href="https://github.com/whatwg/html/issues/11523">Issue</a>，因为XSLT用的人很少，以及实现XSLT的库很老而且容易出漏洞，所以建议把XSLT从Web标准中删除。在这个Issue中可以发现，有很多人表示不满，毕竟这个功能对想要给自己订阅做美化的博主来说还是很有用的。为了对抗谷歌，还有人做了个网站： <a href="https://xslt.rip">https://xslt.rip</a> 。 <br />
  而且XSLT虽然用的人占比也许不高，但从总量上应该还是挺多的，除了用XSLT美化博客订阅的，甚至还有用<a href="https://github.com/vgr-land/vgr-xslt-blog-framework">XSLT作为博客框架的</a>，另外还有一些人提出<a href="https://github.com/whatwg/html/issues/11582">一部分政府网站也有使用XSLT</a>。 <br />
  不过Freed看起来对这件事早有准备，他做了一个<a href="https://github.com/mfreed7/xslt_polyfill">Polyfill库</a>，通过WASM的方式让XSLT可以正常工作，为了方便大家使用这个库，我顺手给CDNJS发了个<a href="https://github.com/cdnjs/packages/pull/2118">PR</a>，以后可以用CDN引用它了。不过使用这个库的前提是需要在订阅中加一段引用JS的代码，像我博客中的Atom订阅，用的是<a href="https://github.com/jekyll/jekyll-feed">jekyll-feed</a>插件，里面的格式都是写死的，就用不了了…… <br />
  只不过现在已经没办法阻止谷歌了……而且其他浏览器也表示会跟进，看来我们唯一能做的就是去适应了。</p>

<h1 id="没有xslt之后的美化方案">没有XSLT之后的美化方案</h1>
<h2 id="纯css">纯CSS</h2>
<p>虽然XSLT不能用，但不代表<code class="language-plaintext highlighter-rouge">xml-stylesheet</code>指令就不能用了，除了XSLT之外，<code class="language-plaintext highlighter-rouge">xml-stylesheet</code>同样可以引用CSS。只是似乎完全没见过用CSS美化订阅源的，也许是因为光用CSS能做到的事比较少吧，想用CSS给XML文档加链接之类的估计就做不到了。 <br />
  但目前能选择的也不多了，既然大家都没写过用CSS美化订阅源，那就让我来写一个吧！然而我并不会写😅……那就只好让AI来写了，我把需求说清楚之后，AI就写出来了：<a href="/assets/css/feed.css">feed.css</a>。试了一下效果还挺不错的，我让AI写的这个版本无论是RSS还是Atom都可以使用，如果有人感兴趣可以拿去用。可惜我的Atom订阅因为用的是插件的原因用不了😭，只能加到用纯Liquid实现的RSS订阅上了。 <br />
  但用纯CSS的缺点也很明显，没办法操作文档的内容，像修改日期格式的就做不了了，而且也不能添加超链接……XML的标签本身对浏览器来说并没有内建的语义，正常情况下也没法让浏览器把某个标签当作超链接。那难道就没办法了吗？</p>
<h2 id="混合xhtml">混合XHTML</h2>
<p>如果完全不能修改XML内容，那确实就没有办法了，但如果能修改XML的内容那还是有办法的，简单来说就是混入XHTML，事实上Freed编写的Polyfill库原理上也是利用了XHTML，只要在能作为XHTML的标签中添加XHTML的命名空间，那么浏览器就可以理解它的语义并渲染，像刚刚用纯CSS美化的订阅没有链接，那就可以在根元素中添加命名空间：<code class="language-plaintext highlighter-rouge">xmlns:xhtml="http://www.w3.org/1999/xhtml"</code>，然后在合适的位置写：</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;xhtml:a</span> <span class="na">href=</span><span class="s">"https://example.com"</span><span class="nt">&gt;</span>Read more -<span class="ni">&amp;gt;</span><span class="nt">&lt;/xhtml:a&gt;</span>
</code></pre></div></div>
<p>就可以了。只是这样有个缺点，这样写的订阅文件不够“纯粹”，用验证器验证会显示“<a href="https://validator.w3.org/feed/docs/warning/MisplacedXHTMLContent.html">Misplaced XHTML content</a>”警告。对有洁癖的人来说可能会有点难受😆。 <br />
  不过如果能接受这种“不纯粹”，那么其实<code class="language-plaintext highlighter-rouge">xml-stylesheet</code>指令也没必要了，<code class="language-plaintext highlighter-rouge">link</code>标签一样可以用，包括<code class="language-plaintext highlighter-rouge">script</code>也是，所以有人写了一个<a href="https://github.com/dfabulich/style-xml-feeds-without-xslt">不使用XSLT美化XML</a>的库。 <br />
  只不过这种方法和XSLT相比还是有一些缺陷，要知道XSLT的本质是转换，是把XML转换为HTML，也就是说转出来的文档本质是HTML，所有的DOM操作都和操作HTML是完全相同的，但是在XML里混入XHTML标签就不一样了，它的本质依然是XML文档，只是嵌入了XHTML命名空间下的元素，所以相应的DOM操作会有一些不同。如果是自己写的纯JS可能还好，如果是用了jQuery之类假定DOM为HTML的库就会出现问题了，因此这也就是那个Polyfill库的局限性，用正常的XSLT执行<code class="language-plaintext highlighter-rouge">document.constructor</code>会显示<code class="language-plaintext highlighter-rouge">HTMLDocument</code>，而用这个Polyfill库执行完则是显示<code class="language-plaintext highlighter-rouge">XMLDocument</code>。因此，直接套用为浏览器原生XSLT编写的旧样式文件，就有可能会出问题，但如果要考虑改XSLT的话那还不如重新写JS，然后用XHTML引入呢。</p>

<h1 id="感想">感想</h1>
<p>虽然有一些技术会因为各种各样的原因消失，但这不代表我们就要妥协一些东西，总有一些不同的技术可以解决相同的问题，所以我们只需要用其他的技术去实现就好了。不过这也是没办法的事情，毕竟没人能改变浏览器厂商们的决策啊😂。</p>]]></content><author><name>mayx</name></author><category term="XML" /><category term="Feed" /><category term="XSLT" /><category term="美化" /><summary type="html"><![CDATA[即使没有了XSLT，也不能让读者看到光秃秃的XML！]]></summary></entry></feed>