HeapPage.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package core
  2. import (
  3. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  4. "sync"
  5. "unsafe"
  6. )
  7. // heapBufPool 堆页临时缓冲池。
  8. //
  9. // LoadHeapPage / Save 每次都要一块 PageSize 的脚手架缓冲。旧实现每次 make:
  10. // 逐行插入路径里每个动态列 (字符串/变长) 都要 Load+Alloc+Save 一趟堆页
  11. // = 每列 ~32KB 的纯垃圾, 双动态列的表灌数实测 ~300KB/行分配的大头之一。
  12. // 缓冲在构造/序列化后即还池: 数组转换 ([N]byte)(buf[...]) 与 Save 的
  13. // copy 都是把内容搬进/搬出 HeapPage.Info, 不与还池后的 buf 共享内存。
  14. var heapBufPool = sync.Pool{New: func() any { b := make([]byte, PageSize); return &b }}
  15. type HeapInfo struct {
  16. Hdr page.PageHdr
  17. Data [PageSize - page.PageHdrSize]byte
  18. }
  19. type HeapPage struct {
  20. Info HeapInfo
  21. handle page.HandleDb // 回调函数
  22. }
  23. // =============================================================================
  24. // HeapPage 核心自治方法(所有操作全封装在这里,无管理器)
  25. // =============================================================================
  26. // NewHeapPage 创建一个全新的堆页(开辟函数)
  27. func NewHeapPage(handle page.HandleDb, pageType page.PageType) *HeapPage {
  28. hp := &HeapPage{handle: handle}
  29. hp.Info.Hdr.Type = pageType // 固定=堆页
  30. hp.Info.Hdr.PageID = handle.NewPage() // 自动分配新页ID
  31. hp.Info.Hdr.UseSize = page.PageHdrSize // 初始已用 = 页头大小
  32. return hp
  33. }
  34. // LoadHeapPage 从磁盘加载已存在的堆页
  35. func LoadHeapPage(handle page.HandleDb, pageID uint32) (*HeapPage, error) {
  36. bpp := heapBufPool.Get().(*[]byte)
  37. buf := *bpp
  38. defer heapBufPool.Put(bpp)
  39. if err := handle.ReadPage(pageID, buf); err != nil {
  40. return nil, err
  41. }
  42. return &HeapPage{
  43. Info: HeapInfo{
  44. Hdr: *page.HdrOf(buf),
  45. Data: ([PageSize - page.PageHdrSize]byte)(buf[page.PageHdrSize:]),
  46. },
  47. handle: handle,
  48. }, nil
  49. }
  50. // -----------------------
  51. // 修复完成!Alloc 返回 【数据区内部偏移】(去掉页头)
  52. // -----------------------
  53. //func (hp *HeapPage) Alloc(need uint32) (allocPageID uint32, allocOff uint32, err error) {
  54. // if need > PageSize-page.PageHdrSize {
  55. // return 0, 0, ErrHeapPageTooLarge
  56. // }
  57. //
  58. // // 1. 计算剩余空间(只算数据区)
  59. // usedData := hp.Info.Hdr.UseSize - page.PageHdrSize // 已用数据区大小
  60. // freeData := (PageSize - page.PageHdrSize) - usedData // 剩余数据区大小
  61. //
  62. // // 空间足够 → 分配【数据区偏移】
  63. // if freeData >= need {
  64. // // 🔥 核心修复:返回的是 Data 数组的偏移(去掉页头)
  65. // off := usedData
  66. // hp.Info.Hdr.UseSize += need
  67. // _ = hp.Save()
  68. // return hp.Info.Hdr.PageID, off, nil
  69. // }
  70. //
  71. // // 2. 空间不足 → 创建新页
  72. // newHp := NewHeapPage(hp.handle, hp.Info.Hdr.Type)
  73. //
  74. // // 维护页链
  75. // hp.Info.Hdr.NextPage = newHp.Info.Hdr.PageID
  76. // newHp.Info.Hdr.PrePage = hp.Info.Hdr.PageID
  77. // _ = hp.Save()
  78. //
  79. // // 3. 新页分配:返回【数据区偏移 0】
  80. // newOff := uint32(0)
  81. // newHp.Info.Hdr.UseSize += need
  82. // _ = newHp.Save()
  83. //
  84. // return newHp.Info.Hdr.PageID, newOff, nil
  85. //}
  86. func (hp *HeapPage) Alloc(need uint32) (allocPageID uint32, allocOff uint32, err error) {
  87. // 安全检查:申请空间不能超过单页最大容量
  88. if need > PageSize-page.PageHdrSize {
  89. return 0, 0, ErrHeapPageTooLarge
  90. }
  91. // 1. 当前页剩余空间足够 → 直接分配【页内物理偏移】
  92. //
  93. // 本方法不 Save: 唯一调用方 (Table.heapAllocBlock → heapWrite) 随后会把块数据
  94. // 写进页里再统一 Save, 这里提前落盘纯属重复 (一次 16KB 往返拷贝)。
  95. // 页头变更 (UseSize/RefCount) 留在内存态 hp.Info.Hdr, 随那次 Save 一并持久。
  96. free := PageSize - hp.Info.Hdr.UseSize
  97. if free >= need {
  98. // ✅ 直接返回当前页的物理偏移(包含页头,真实地址)
  99. off := hp.Info.Hdr.UseSize
  100. hp.Info.Hdr.UseSize += need
  101. hp.Info.Hdr.AddRef() // 本页新增一个引用
  102. return hp.Info.Hdr.PageID, off, nil
  103. }
  104. // 2. 空间不足 → 创建新堆页
  105. newHp := NewHeapPage(hp.handle, hp.Info.Hdr.Type)
  106. // 维护页双向链
  107. hp.Info.Hdr.NextPage = newHp.Info.Hdr.PageID
  108. newHp.Info.Hdr.PrePage = hp.Info.Hdr.PageID
  109. _ = hp.Save()
  110. // 3. 新页分配:返回新页的物理偏移(PageHdrSize,真实起始地址)
  111. newOff := newHp.Info.Hdr.UseSize // = page.PageHdrSize
  112. newHp.Info.Hdr.UseSize += need
  113. newHp.Info.Hdr.AddRef() // 新页的第一个引用
  114. _ = newHp.Save()
  115. return newHp.Info.Hdr.PageID, newOff, nil
  116. }
  117. // Save 自动保存当前堆页到磁盘
  118. func (hp *HeapPage) Save() error {
  119. bpp := heapBufPool.Get().(*[]byte)
  120. buf := *bpp
  121. defer heapBufPool.Put(bpp)
  122. // 写入页头
  123. *page.HdrOf(buf) = hp.Info.Hdr
  124. // 写入数据区
  125. copy(buf[page.PageHdrSize:], hp.Info.Data[:])
  126. return hp.handle.SavePage(hp.Info.Hdr.PageID, buf)
  127. }
  128. // Get 返回 HeapInfo 的零拷贝字节切片(直接指向内部内存)
  129. func (hp *HeapPage) Get() []byte {
  130. size := unsafe.Sizeof(hp.Info)
  131. ptr := (*byte)(unsafe.Pointer(&hp.Info))
  132. return unsafe.Slice(ptr, size)
  133. }