gobplustree/page.go

151 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gobplustree
import (
"encoding/binary"
"errors"
)
type PageType int8
type Offset uint32
const (
PageTypeLeaf PageType = iota
PageTypeInternal
)
const (
CellHeaderSize = 17
)
type PageHead struct {
PageId int64
Type PageType
CellCount int
FirstFreeOffset Offset
}
type Cell struct {
IsFree bool
NextFreeOffset Offset
ActualSize int
Start Offset
}
type Page struct {
*PageHead
Cells []*Cell
data []byte
}
func DeSerializePageHead(data []byte) (*PageHead, error) {
PageId := binary.LittleEndian.Uint64(data[0:8])
Type := PageType(data[8])
CellCount := int(data[9])
FirstFreeOffset := Offset(data[10])
return &PageHead{
PageId: int64(PageId),
Type: Type,
CellCount: CellCount,
FirstFreeOffset: FirstFreeOffset,
}, nil
}
// DeSerializePage 从字节数据中反序列化Page结构
// data: 包含页面数据的字节切片
// 返回值: 解析出的Page指针和可能的错误
func DeSerializePage(data []byte) (*Page, error) {
head, err := DeSerializePageHead(data[0:BlockFileHeaderSize])
if err != nil {
return nil, err
}
// 如果没有cell直接返回空的cells数组
if head.CellCount == 0 {
return &Page{
PageHead: head,
Cells: []*Cell{},
data: data,
}, nil
}
cells := make([]*Cell, head.CellCount)
// 查找cell偏移数组的起始位置
cellPOffset := 0
for i := 17; i < BlockFilePageSize; i++ {
t := binary.LittleEndian.Uint32(data[i : i+4])
if t != 0 {
cellPOffset = i
break
}
}
if cellPOffset == 0 {
return nil, errors.New("invalid page")
}
previousCellOffset := 0
currentFreeOffset := head.FirstFreeOffset
// 遍历所有cell解析每个cell的信息
for i := 0; i < head.CellCount; i++ {
cellOffset := binary.LittleEndian.Uint32(data[cellPOffset+i*4 : cellPOffset+i*4+4])
cellSize := 0
// cell if from end to start
if i == 0 {
cellSize = BlockFilePageSize - int(cellOffset)
} else {
cellSize = int(cellOffset) - int(previousCellOffset)
}
cells[i] = &Cell{
IsFree: false,
NextFreeOffset: 0,
ActualSize: int(cellSize),
Start: Offset(cellOffset),
}
// 检查当前cell是否为空闲cell
if cellOffset == uint32(currentFreeOffset) {
cells[i].IsFree = true
cells[i].NextFreeOffset = Offset(binary.LittleEndian.Uint32(data[cellOffset+4 : cellOffset+8]))
currentFreeOffset = cells[i].NextFreeOffset
}
previousCellOffset = int(cellOffset)
}
return &Page{
PageHead: head,
Cells: cells,
data: data,
}, nil
}
func (p *Page) Serialize() []byte {
// 写入PageHead
binary.LittleEndian.PutUint64(p.data[0:8], uint64(p.PageId))
p.data[8] = byte(p.Type)
p.data[9] = byte(p.CellCount)
p.data[10] = byte(p.FirstFreeOffset)
// 写入Cell偏移数组
lastCellOffset := p.Cells[p.CellCount-1].Start
cellOffsets := make([]byte, p.CellCount*4)
for i, cell := range p.Cells {
binary.LittleEndian.PutUint32(cellOffsets[i*4:i*4+4], uint32(cell.Start))
}
copy(p.data[int(lastCellOffset)-len(cellOffsets):lastCellOffset], cellOffsets)
return p.data
}
func (p *Page) GetCell(index int) *Cell {
return p.Cells[index]
}
func (p *Page) GetCellData(cell *Cell) []byte {
return p.data[cell.Start : cell.Start+Offset(cell.ActualSize)]
}
func (p *Page) WriteNewCell(data []byte) (*Cell, error) {
}