package page import "unsafe" // ============================================================================= // WALPageHdr —— WAL 页头, 首页与续页共用同一布局。 // // 磁盘布局 (40B): // // off 0 PageHdr 20B 公共页头 // off 20 _ 4B 对齐填充: TxnID 需 8 字节对齐 // off 24 TxnID 8B 事务 ID (仅首页有效, 续页为 0) // off 32 TotalEntries 4B 本事务条目总数 (仅首页有效) // off 36 State 1B WAL 状态 (仅首页有效) // off 37 _ 1B 对齐填充: EntryCount 需 2 字节对齐 // off 38 EntryCount 2B 本页条目数 // // 合并前首页 35B / 续页 22B, 二者 EntryCount 落在不同偏移 (33/20), 于是读写到处 // 都要带 isFirst 分支, 并派生出一整组手工偏移常量。这里让续页也保留 TxnID/ // TotalEntries/State 三个字段 (填 0), 头部等长 40B —— 换来整条链可以用同一个 // 结构体解析。代价是续页每页多花 18B, 占 16KB 的 0.11%。 // // WAL 条目 (变长, 紧跟在页头之后) 不在此结构体内, 因为它以 13B 为步长紧密排列, // 而任何包含它的 struct 都会被编译器对齐到 16B (见 WALEntryMeta 注释)。 // ============================================================================= type WALPageHdr struct { PageHdr _ [4]byte // off 20 对齐填充 (勿删: TxnID 需 8 字节对齐) TxnID uint64 // off 24 事务 ID (仅首页) TotalEntries uint32 // off 32 条目总数 (仅首页) State byte // off 36 WAL 状态 (仅首页) _ [1]byte // off 37 对齐填充 (勿删: EntryCount 需 2 字节对齐) EntryCount uint16 // off 38 本页条目数 } // WALPageHdrSize WAL 页头长度; 条目区从该偏移开始 const WALPageHdrSize = uint32(unsafe.Sizeof(WALPageHdr{})) // 40 // WALEntryMeta WAL 条目元数据长度: TargetPageID(4) + DataLen(4) + Checksum(4) + OpType(1)。 // // ⚠️ 条目在页内以 13B 为步长紧密排列, 所以不能用 struct 的 unsafe.Sizeof 来定位 // 下一条 —— 任何含 uint32 的 struct 都会被对齐到 16B, 每条的步长会凭空多出 3B。 // 条目字段的读写请继续用 binary.LittleEndian 手工编解码。 const WALEntryMeta = 4 + 4 + 4 + 1 // 13 // 编译期布局断言 var ( _ [40]byte = [unsafe.Sizeof(WALPageHdr{})]byte{} // 页头必须 40 字节 _ [24]byte = [unsafe.Offsetof(WALPageHdr{}.TxnID)]byte{} // TxnID 必须偏移 24 _ [32]byte = [unsafe.Offsetof(WALPageHdr{}.TotalEntries)]byte{} // TotalEntries 必须偏移 32 _ [36]byte = [unsafe.Offsetof(WALPageHdr{}.State)]byte{} // State 必须偏移 36 _ [38]byte = [unsafe.Offsetof(WALPageHdr{}.EntryCount)]byte{} // EntryCount 必须偏移 38 _ [13]byte = [WALEntryMeta]byte{} // 条目元数据必须 13 字节 ) // WalHdrOf 把页缓冲区按 WALPageHdr 解析 (页头固定在偏移 0)。 func WalHdrOf(buf []byte) *WALPageHdr { return (*WALPageHdr)(unsafe.Pointer(&buf[0])) } // Init 把一个 WAL 页初始化为空页: 类型 WALIndex, 清空首页元数据, UseSize 指向页头之后。 func (h *WALPageHdr) Init(pageID, prevPageID uint32) { h.Type = WALIndex h.Flags = 0 h.RefCount = 0 h.PageID = pageID h.PrePage = prevPageID h.NextPage = 0 h.UseSize = WALPageHdrSize h.TxnID = 0 h.TotalEntries = 0 h.State = 0 h.EntryCount = 0 }