package page import ( "encoding/binary" "unsafe" ) // ============================================================================= // DataPageHdr —— 数据页头: 公共页头 + 行计数 + pkID 极值 + 存活/失效统计。 // // 磁盘布局 (48B): // // off 0 PageHdr 20B 公共页头 // off 20 Count 4B 行数 (本机字节序, 含墓碑) // off 24 Min 8B 页内最小 pkID ★BigEndian 编码★ // off 32 Max 8B 页内最大 pkID ★BigEndian 编码★ // off 40 LiveCnt 4B 未删除行数 (本机字节序) // off 44 Dead 4B 失效字节数 = Σ(4B前缀 + 墓碑行) (本机字节序) // // 行数据区紧接在偏移 48 之后, 每行 [len(4B)][rowData...], len 的最高位是软删除标志。 // // ⚠️ Min/Max 刻意按 BigEndian 存放 (pkID 本身也按 BE 比较), 所以这里声明成 // [8]byte 而不是 uint64 —— 若声明成 uint64, unsafe 映射在小端机上会读到字节反转 // 的值。读写必须走 GetMin/SetMin/GetMax/SetMax。 // // LiveCnt/Dead 由所有写路径增量维护 (追加/软删/整理页/页内重建/WAL 行级恢复), // Load 直接读走 —— 无条件 COUNT(*) 从"逐行走链"降到 O(1) 读页头, MaybeCompact // 的失效占比判断也不再要求 Load 时全页重算。 // // 这份布局原先在 wal.go(dpHdrSize/dpCountOffset/dpUseSizeOffset)、transaction.go、 // index/datapage 三处各写一遍, 现在只有这里一处定义。 // ============================================================================= type DataPageHdr struct { PageHdr Count uint32 // off 20 行数 (含墓碑) Min [8]byte // off 24 最小 pkID (BigEndian) Max [8]byte // off 32 最大 pkID (BigEndian) LiveCnt uint32 // off 40 未删除行数 Dead uint32 // off 44 失效字节数 } // DataPageHdrSize 数据页头长度; 行数据区从该偏移开始 const DataPageHdrSize = uint32(unsafe.Sizeof(DataPageHdr{})) // 48 // 编译期布局断言 var ( _ [48]byte = [unsafe.Sizeof(DataPageHdr{})]byte{} // 头必须 48 字节 _ [20]byte = [unsafe.Offsetof(DataPageHdr{}.Count)]byte{} // Count 必须偏移 20 _ [24]byte = [unsafe.Offsetof(DataPageHdr{}.Min)]byte{} // Min 必须偏移 24 _ [32]byte = [unsafe.Offsetof(DataPageHdr{}.Max)]byte{} // Max 必须偏移 32 _ [40]byte = [unsafe.Offsetof(DataPageHdr{}.LiveCnt)]byte{} // LiveCnt 必须偏移 40 _ [44]byte = [unsafe.Offsetof(DataPageHdr{}.Dead)]byte{} // Dead 必须偏移 44 ) // DataHdrOf 把页缓冲区按 DataPageHdr 解析 (页头固定在偏移 0)。 func DataHdrOf(buf []byte) *DataPageHdr { return (*DataPageHdr)(unsafe.Pointer(&buf[0])) } // GetMin 读页内最小 pkID。 func (h *DataPageHdr) GetMin() uint64 { return binary.BigEndian.Uint64(h.Min[:]) } // SetMin 写页内最小 pkID。 func (h *DataPageHdr) SetMin(v uint64) { binary.BigEndian.PutUint64(h.Min[:], v) } // GetMax 读页内最大 pkID。 func (h *DataPageHdr) GetMax() uint64 { return binary.BigEndian.Uint64(h.Max[:]) } // SetMax 写页内最大 pkID。 func (h *DataPageHdr) SetMax(v uint64) { binary.BigEndian.PutUint64(h.Max[:], v) }