把 VP8 畫面預測模式接成邏輯閘:Video2NAND 的組合邏輯實驗
sharedobject.blog · 2026-07-11
部落格 sharedobject.blog 的一篇文章示範了如何把 VP8 影片編碼器裡的畫面內預測(intra-frame prediction)模式接成真正的邏輯閘。作者不是在講怎麼把電路訊號編碼進影片畫面裡儲存,而是讓解碼器在還原畫面時「順便」算出 NAND 閘該有的輸出。整個實驗只利用 VP8 關鍵畫面(keyframe)的三種區塊預測模式:H_PRED、V_PRED 與 TM_PRED。
原本的問題
VP8 對每個區塊會用鄰近已解碼區塊的像素去預測目前區塊的值,再疊上殘差修正。H_PRED 直接複製左側像素、V_PRED 複製上方像素,這兩種都只是單純搬移,適合拿來當「訊號線」傳遞 0(黑)或 255(白)。真正關鍵是 TM_PRED(True Motion Prediction),公式是用左方像素加上方像素再減去左上角像素,結果會被 clamp 在 0 到 255 之間。
predicted = clamp(left + top - top_left, 0, 255)這條公式在輸入被限制成純黑(0)或純白(255)時,行為會退化成一組固定的布林運算,這正是整篇文章的槓桿點。只要控制好區塊周邊的黑白排列,TM_PRED 就能被逼成邏輯閘。
採用的方法
NOT 閘的做法是把上方一整排設成白(255)、左方一整排設成黑(0),此時 TM_PRED 退化成 255 - input,正好是反閘。AND 閘則反過來安排周邊像素,讓公式收斂成 A + B - 255:兩個輸入都是 255 時輸出才會是 255,其餘情況因 clamp 到 0 而輸出黑。NAND 閘不需要新公式,把 AND 的輸出區塊接上一個 NOT 區塊就完成了。
| A | B | AND (A+B-255, clamp) | NAND |
|---|---|---|---|
| 0 | 0 | 0 | 255 |
| 0 | 255 | 0 | 255 |
| 255 | 0 | 0 | 255 |
| 255 | 255 | 255 | 0 |
- H_PRED / V_PRED:搬移訊號,充當「線路」
- TM_PRED(周邊全白全黑):NOT 閘
- TM_PRED(周邊排列成 A+B-255):AND 閘
- AND 串接 NOT:NAND 閘
實際效果
目前公開的成果停在 NAND 這一層,還沒組成加法器或更完整的電路,作者預告後續會有進一步文章。NAND 本身已是通用邏輯閘,理論上足以拼出任意組合邏輯,只是巨塊能承載的像素狀態有限,規模化很快會碰上區塊相依限制。這手法呼應了早年「用影片格式塞資料」的把戲,但這次算的不是儲存,而是解碼器內建的像素運算。
原始來源:sharedobject.blog
Nix 的 sandbox 路徑其實是隱藏輸入:從 OpenJDK bootstrap 挖出的可重現性漏洞
fzakaria.com · 2026-07-30
Farid Zakaria 在部落格 fzakaria.com 記錄了他用 Guix 重現 OpenJDK bootstrap 建置時踩到的坑:同一份 .drv 衍生檔案,在不同機器上因為 sandbox 掛載內容不同,算出了不一樣的結果。問題根源是 Nix 的沙箱路徑(sandbox-paths)並不算在建置的輸入裡,卻實際上會影響建置輸出。
原本的問題
Nix 的可重現性假設建立在:同一份 .drv 在任何機器上建置,只要輸入雜湊相同就該得到相同輸出。但 Nix 預設會把 /bin/sh 掛進 sandbox,這個路徑是在編譯 Nix 本身時寫進二進位檔的,寫在 src/libstore/globals.cc 裡:
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SANDBOX_SHELL)
sandboxPaths = {{"/bin/sh", {.source = SANDBOX_SHELL}}};
#endifGuix 分支出去的版本沒有預設掛 /bin/sh。作者原本寫了一段預期會因為找不到 shebang 解譯器而失敗的建置腳本,結果在 Nix 上卻悄悄執行成功,輸出的雜湊和「理應失敗」的版本一樣正常,只是內容早就不對了。
採用的方法
作者用一個最小化例子重現這個現象:一段檢查 /truth 檔案是否存在的 shell 腳本。
derivation { builder = "/bin/sh"; args = [ "-c"
"if [ -f /truth ]; then read -r x < /truth; else x=4; fi" ]; }正常情況下 /truth 不存在,x 會是預設值 4。但只要加上 --option extra-sandbox-paths "/truth=/tmp/truth",把外部檔案掛進 sandbox,同一份 .drv、同一個輸入雜湊,就能讓 x 變成 /tmp/truth 裡寫的任何值。sandbox-paths 從頭到尾沒有出現在 derivation 的雜湊計算裡。
實際效果
作者指出這不是單純的 bug,而是設計上的兩難:如果把 sandbox 路徑(例如掛進去的 busybox)也算進輸出雜湊,不同機器上 busybox 的 store 路徑不同,會直接打斷 binary cache 的可攜性,同一份建置在別台機器上永遠對不上快取。文章引用 Ken Thompson 的《Reflections on Trusting Trust》,把這個現象類比成信任鏈中看不見的那一環——讓分散式快取得以成立的機制,恰好也是讓沙箱內容能被悄悄替換的那個機制。
原始來源:fzakaria.com
99 行 C 寫一個 Lisp:tinylisp 如何靠 NaN boxing 塞進 8K 記憶體
GitHub · Robert van Engelen · 討論見於 2026-08-04 Lobsters
Robert van Engelen 的專案 tinylisp 附上一篇論文,詳細說明怎麼用剛好 99 行 C 程式碼寫出一個能跑 REPL 的完整 Lisp 解譯器,內含 21 個內建原語(primitive)。整個直譯器連同資料全部塞進約 8K 記憶體,靠的是把所有 Lisp 值都編碼進 IEEE 754 雙精度浮點數裡的 NaN boxing 技巧。
核心機制
NaN boxing 利用一個冷知識:IEEE 754 的 double 只要指數欄位全部是 1、尾數不是全 0,這個值就是 NaN,而 NaN 的尾數位(payload)在規格上是「不理會」的,可以自由塞資料。tinylisp 把型別標籤和陣列索引塞進這段 payload,讓一個 double 同時能表示數字本身、cons 對、原語函式或閉包之一,不需要另外的 tagged union 結構。整個環境是一個大小為 N=1024 的 cell 陣列,cons 對就是兩個指向這個陣列的索引配對,car/cdr 只是陣列存取。
採用的方法
解譯器沒有另外實作追殺垂死物件的回收器,而是用一個簡化版垃圾回收搭配線性配置的 cell 陣列,把管理成本壓到最低;文中提到的優化版本改用參考計數(reference counting),執行速度可以快上十倍以上。21 個核心原語涵蓋求值與控制流(eval、quote、cond、if、lambda、define、let*)、列表操作(cons、car、cdr、pair?)、算術與邏輯運算,足以撐起遞迴、閉包與柯里化函式。
- 求值/控制:eval、quote、cond、if、lambda、define、let*
- 列表:cons、car、cdr、pair?
- 算術:+、-、*、/、int
- 邏輯:<、eq?、or、and、not
實際效果
基礎的 99 行版本採用靜態範疇(static scoping)與詞法閉包,支援巢狀 lambda 與柯里化,但不含尾呼叫優化,遞迴太深會吃光 call stack。作者另外提供擴充版「extras」,多加 16 個原語(read、print、load、catch、throw、trace 等)與巨集支援;千行等級的完整版本則換上 mark-sweep 或 Cheney's copying garbage collector。這篇論文更像是把 McCarthy 最初 Lisp 論文的精神,用能實際編譯執行的 99 行 C 重新示範一次。
原始來源:tinylisp.pdf、GitHub repo