Lucene 8.5.0 的 `.pos` 文件**邏輯結構**(按真實實現重新整理)
```
.pos 文件
├─ Header (CodecHeader)
├─ TermPositions × TermCount ? ? ? ?← 每個 term 一段,順序由詞典隱式決定
│ ? ├─ PackedPosDeltaBlock × N ? ? ?← 僅當 **無 payload 且 無 offset** 時存在
│ ? │ ? └─ 64 個 position-delta(PackedInts 壓縮)
│ ? ├─ VIntBlock × PosVIntCount ? ? ← 必寫;剩余所有位置
│ ? │ ? ├─ PositionDelta ? VInt
│ ? │ ? ├─ PayloadLength? ?VInt ? ? ← 最低位標記法,payload 啟用時出現
│ ? │ ? ├─ PayloadData? ? ?byte[len] ← **尾巴**階段內聯;整塊階段在 .pay
│ ? │ ? ├─ OffsetDelta? ? ?VInt ? ? ← offsets 啟用時出現
│ ? │ ? └─ OffsetLength? ? VInt ? ? ← 同上
└─ Footer (CodecFooter)
```
關鍵結論 ?
`TermPositions` 就是一段連續的二進制數據,其內部按順序包含:
- 0 個或多個 `PackedPosDeltaBlock`(≥64 個 delta 時才有) ?
- 1 個 `VIntBlock`(剩余尾巴,長度 ≥0)
因此:
TermPositions = [PackedPosDeltaBlock × N] + VIntBlock
1. **PackedPosDeltaBlock** 只存 **純 position delta**; ?
2. **只要啟用 payload 或 offset,則 PackedPosDeltaBlock 不出現**,全部走 VIntBlock; ?
3. **payload bytes** 在“尾巴”階段 **內聯在 `.pos`**,整塊階段 **在 `.pay`**; ?
4. **offset 元數據** 始終寫在 `.pos` 的 VIntBlock。