layout_assert.go 4.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package main
  2. import (
  3. "git.ycl.pub/yl32940/TsDB2/DB/core"
  4. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  5. "git.ycl.pub/yl32940/TsDB2/DB/core/util"
  6. "unsafe"
  7. )
  8. // 编译期断言: 把 analyzer 里硬编码的盘上布局 (META_SZ / TINFO_SZ / TI_* / IM_*)
  9. // 与 DB/core 中的真实结构体逐个钉在一起。
  10. //
  11. // 为什么需要这个文件:
  12. //
  13. // analyzer 在自己的模块里手写偏移直接读盘。过去这件事只能靠 DB/core 的注释约束
  14. // (「改布局时记得同步 analyzer/main.go 的 TI_*」) —— 而漏改的后果不是报错, 而是
  15. // **静默读错**: 把索引名数组的高位字节当成 IndexType、把别的字段当成 EntryPage,
  16. // 页面照常渲染但数据全错, 极难发现。
  17. //
  18. // analyzer 的 go.mod 已有 `replace TsDB2 => ../`, 可以直接引用真实结构体, 于是把这层
  19. // 依赖交给编译器: 任何一边改了布局, 这里当场编译失败。
  20. //
  21. // 断言写法:
  22. // - [a - b]struct{} 与 [b - a]struct{} 同时成立 ⇔ a == b (差值为负时数组长度非法);
  23. // - _ [N]byte = [unsafe.Offsetof(...)]byte{} ⇔ 偏移恰好等于 N (长度不等则类型不符)。
  24. var (
  25. // 结构体整体尺寸
  26. _ [int(unsafe.Sizeof(core.IndexMeta{})) - META_SZ]struct{}
  27. _ [META_SZ - int(unsafe.Sizeof(core.IndexMeta{}))]struct{}
  28. _ [int(unsafe.Sizeof(core.TableInfo{})) - TINFO_SZ]struct{}
  29. _ [TINFO_SZ - int(unsafe.Sizeof(core.TableInfo{}))]struct{}
  30. // TableInfo 字段偏移
  31. _ [TI_FLAG]byte = [unsafe.Offsetof(core.TableInfo{}.Flag)]byte{}
  32. _ [TI_HPCNT]byte = [unsafe.Offsetof(core.TableInfo{}.HeapPageCount)]byte{}
  33. _ [TI_ROWS]byte = [unsafe.Offsetof(core.TableInfo{}.TotalRows)]byte{}
  34. _ [TI_FHEAP]byte = [unsafe.Offsetof(core.TableInfo{}.FirstHeap)]byte{}
  35. _ [TI_FDATA]byte = [unsafe.Offsetof(core.TableInfo{}.FirstData)]byte{}
  36. _ [TI_ROWSZ]byte = [unsafe.Offsetof(core.TableInfo{}.RowSize)]byte{}
  37. _ [TI_ALLOC]byte = [unsafe.Offsetof(core.TableInfo{}.FieldAllocateSize)]byte{}
  38. _ [TI_FIDX]byte = [unsafe.Offsetof(core.TableInfo{}.FirstIndexID)]byte{}
  39. _ [TI_EXTPG]byte = [unsafe.Offsetof(core.TableInfo{}.ExtPage)]byte{}
  40. _ [TI_EXTOFF]byte = [unsafe.Offsetof(core.TableInfo{}.ExtOff)]byte{}
  41. // IndexMeta 字段偏移 (这些会随 IndexNameSize 整体平移)
  42. _ [IM_NAMSZ]byte = [unsafe.Sizeof(core.IndexMeta{}.IndexName)]byte{}
  43. _ [IM_TYPE]byte = [unsafe.Offsetof(core.IndexMeta{}.IndexType)]byte{}
  44. _ [IM_FCNT]byte = [unsafe.Offsetof(core.IndexMeta{}.FieldCnt)]byte{}
  45. _ [IM_FLDS]byte = [unsafe.Offsetof(core.IndexMeta{}.FieldIDs)]byte{}
  46. _ [IM_EP]byte = [unsafe.Offsetof(core.IndexMeta{}.EntryPage)]byte{}
  47. _ [IM_FLAG]byte = [unsafe.Offsetof(core.IndexMeta{}.Flag)]byte{}
  48. // HeaderPage 字段偏移 (v11 重排): audit.go / main.go 里所有 hdr[...] 手写的
  49. // 读盘偏移。漏改的后果与其它结构体一样 —— 页面照常渲染, 读出来的全是错值。
  50. _ [HDR_FREE_END]byte = [unsafe.Offsetof(core.HeaderPage{}.FreePageEnd)]byte{}
  51. _ [HDR_CRC]byte = [unsafe.Offsetof(core.HeaderPage{}.HeaderCrc)]byte{}
  52. _ [12]byte = [unsafe.Offsetof(core.HeaderPage{}.NextFreePage)]byte{}
  53. _ [48]byte = [unsafe.Offsetof(core.HeaderPage{}.TablePage)]byte{}
  54. _ [56]byte = [unsafe.Offsetof(core.HeaderPage{}.FreePageStart)]byte{}
  55. _ [64]byte = [unsafe.Offsetof(core.HeaderPage{}.FreePageCount)]byte{}
  56. _ [68]byte = [unsafe.Offsetof(core.HeaderPage{}.FirstWALPage)]byte{}
  57. _ [332]byte = [unsafe.Offsetof(core.HeaderPage{}.Comment)]byte{}
  58. // 位掩码同样是"读盘约定": analyzer 里手写的位必须与 core 的常量一致。
  59. // 读错位的后果和读错偏移一样安静 —— 列 / 索引照样渲染出来, 只是"可空"
  60. // 读成了"非空"、"唯一"读成了"普通"。
  61. _ [FLAG_DELETED - int(types.FlagDeleted)]struct{}
  62. _ [int(types.FlagDeleted) - FLAG_DELETED]struct{}
  63. _ [FLAG_NULLABLE - int(types.FlagNullable)]struct{}
  64. _ [int(types.FlagNullable) - FLAG_NULLABLE]struct{}
  65. _ [FLAG_UNIQUE - int(types.FlagUnique)]struct{}
  66. _ [int(types.FlagUnique) - FLAG_UNIQUE]struct{}
  67. _ [FLAG_SCALE - int(types.FlagScaleMask)]struct{}
  68. _ [int(types.FlagScaleMask) - FLAG_SCALE]struct{}
  69. _ [IM_FLAG_UNIQUE - int(core.IndexFlagUnique)]struct{}
  70. _ [int(core.IndexFlagUnique) - IM_FLAG_UNIQUE]struct{}
  71. )
  72. // init 兜底: core 没有给"这一位是删除位"单列常量 (那一位的语义由 util.IsHighBitSet
  73. // 表达), 所以它只能用运行时断言。只跑一次, 不在热路径上。
  74. func init() {
  75. if !util.IsHighBitSet(IM_FLAG_DELETED) {
  76. panic("IM_FLAG_DELETED 与 core/util.IsHighBitSet 的判定不一致: 删除位读错会让已删的索引照常渲染")
  77. }
  78. }