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