HeadPage.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package core
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "time"
  6. )
  7. // 数据库全局头页(PageID=0 固定)
  8. type HeaderPage struct {
  9. Magic [4]byte // 4 魔术字,识别文件类型
  10. Version uint32 // 4 版本
  11. PageSize uint32 // 4 每页大小(比如 4096)
  12. NextFreePage uint32 // 4 下一个可用空闲页
  13. TablePage uint32 // 4 【唯一重要】表链起始页ID
  14. TableCount uint32 // 4 表数量
  15. CreateTime int64 // 8
  16. ModifyTime int64 // 8
  17. FreePageStart uint32 // 4 空闲页链表头
  18. FreePageCount uint32 // 4 空闲页数量
  19. GlobalFlags uint64 // 8 全局状态
  20. FirstWALPage uint32 // 4 未恢复的 WAL 链首页 ID (0=无待恢复WAL)
  21. LastWALPage uint32 // 4 WAL 链尾页 ID (追加写入用)
  22. NextTxnID uint64 // 8 下一个事务 ID
  23. ExtConfig [256]byte // 256 扩展配置
  24. Comment [512]byte // 512 备注
  25. // 填充对齐到整页(PageSize - 已用字节)
  26. _ [PageSize4K - 840]byte // 自动补齐
  27. }
  28. // newPage 分配新页:优先从空闲链表取,没有则递增分配
  29. func (header *HeaderPage) newPage() uint32 {
  30. if header.FreePageStart != 0 {
  31. // 从空闲链表头取一个页
  32. pid := header.FreePageStart
  33. // 该页的前 4 字节存了下一个空闲页 ID(BigEndian)
  34. // 这里只是返回 pid,实际读取 next 需要外部传入 ReadPage
  35. // 所以 newPageWithReader 才是正确入口
  36. return pid
  37. }
  38. pid := header.NextFreePage
  39. header.NextFreePage++
  40. return pid
  41. }
  42. // newPageWithReader 带读页能力的分配(用于读空闲链表)
  43. func (header *HeaderPage) newPageWithReader(readPage func(uint32, []byte) error) uint32 {
  44. if header.FreePageStart != 0 {
  45. pid := header.FreePageStart
  46. buf := make([]byte, PageSize)
  47. if err := readPage(pid, buf); err == nil {
  48. // 前 4 字节是下一个空闲页 ID
  49. next := binary.BigEndian.Uint32(buf[:4])
  50. header.FreePageStart = next
  51. header.FreePageCount--
  52. } else {
  53. // 读失败,回退到递增分配
  54. pid = header.NextFreePage
  55. header.NextFreePage++
  56. }
  57. return pid
  58. }
  59. pid := header.NextFreePage
  60. header.NextFreePage++
  61. return pid
  62. }
  63. // recyclePage 回收一个页到空闲链表
  64. func (header *HeaderPage) recyclePage(pageID uint32, savePage func(uint32, []byte) error) {
  65. // 在该页前 4 字节写入当前链表头
  66. buf := make([]byte, PageSize)
  67. binary.BigEndian.PutUint32(buf[:4], header.FreePageStart)
  68. header.FreePageStart = pageID
  69. header.FreePageCount++
  70. _ = savePage(pageID, buf)
  71. }
  72. func newHeader(desc string) (*HeaderPage, error) {
  73. var comment [512]byte
  74. // 1. 安全判断:防止描述超长
  75. if len(desc) > len(comment) {
  76. return nil, fmt.Errorf("描述字符串长度 %d 超过最大限制 %d", len(desc), len(comment))
  77. }
  78. copy(comment[:], desc)
  79. // 2. 初始化头部(全部正确赋值,无遗漏、无坑)
  80. return &HeaderPage{
  81. Magic: Magic, // 魔数
  82. Version: Version, // 版本
  83. PageSize: PageSize, // 页大小
  84. NextFreePage: 2, // 下一个分配页从 2 开始(0=header, 1=table page 已预留)
  85. TablePage: 0, // 初始无表
  86. TableCount: 0, // 表数量=0
  87. CreateTime: time.Now().UnixNano(), // 纳秒时间戳
  88. ModifyTime: time.Now().UnixNano(),
  89. FreePageStart: 0, // 初始无空闲页
  90. FreePageCount: 0, // 空闲页数量=0
  91. GlobalFlags: 0, // 无标志
  92. ExtConfig: [256]byte{}, // 清空扩展配置
  93. Comment: comment, // 数据库描述
  94. }, nil
  95. }