diff --git a/page.go b/page.go new file mode 100644 index 0000000..80ce356 --- /dev/null +++ b/page.go @@ -0,0 +1,99 @@ +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 +} +func DeSerializePage(data []byte) (*Page, error) { + head, err := DeSerializePageHead(data[0:BlockFileHeaderSize]) + if err != nil { + return nil, err + } + if head.CellCount == 0 { + return &Page{ + PageHead: head, + Cells: []*Cell{}, + data: data, + }, nil + } + cells := make([]*Cell, head.CellCount) + // find cell offset array start + 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") + } + currentFreeOffset := head.FirstFreeOffset + for i := 0; i < head.CellCount; i++ { + cellOffset := binary.LittleEndian.Uint32(data[cellPOffset+i*4 : cellPOffset+i*4+4]) + nextOffSet := binary.LittleEndian.Uint32(data[cellOffset : cellOffset+4]) + cellSize := nextOffSet - cellOffset + cells[i] = &Cell{ + IsFree: false, + NextFreeOffset: 0, + ActualSize: int(cellSize), + Start: Offset(cellOffset), + } + if cellOffset == uint32(currentFreeOffset) { + cells[i].IsFree = true + cells[i].NextFreeOffset = Offset(binary.LittleEndian.Uint32(data[cellOffset+4 : cellOffset+8])) + currentFreeOffset = cells[i].NextFreeOffset + } + } + return &Page{ + PageHead: head, + Cells: cells, + data: data, + }, nil +}