walpage.go 3.4 KB

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