151 lines
3.3 KiB
Go
151 lines
3.3 KiB
Go
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) {
|
||
|
||
}
|