mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-23 12:07:17 +00:00
dd2c047cdf
* Initial * Read freelist pages * Fix * Fix lint * Fix lint * Fix lint
219 lines
6.6 KiB
Go
219 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
)
|
|
|
|
const PageSize = 4096
|
|
const MdbMagic uint32 = 0xBEEFC0DE
|
|
const MdbDataVersion uint32 = 1
|
|
const BranchPageFlag uint16 = 1
|
|
const BigDataFlag uint16 = 1
|
|
const HeaderSize int = 16
|
|
|
|
func defrag(chaindata string) error {
|
|
fmt.Printf("Defrag %s\n", chaindata)
|
|
datafile := path.Join(chaindata, "data.mdb")
|
|
f, err := os.Open(datafile)
|
|
if err != nil {
|
|
return fmt.Errorf("opening data.mdb: %v", err)
|
|
}
|
|
defer f.Close()
|
|
var meta [PageSize]byte
|
|
// Read meta page 0
|
|
if _, err = f.ReadAt(meta[:], 0*PageSize); err != nil {
|
|
return fmt.Errorf("reading meta page 0: %v", err)
|
|
}
|
|
pos, pageID, _, _ := readPageHeader(meta[:], 0)
|
|
if pageID != 0 {
|
|
return fmt.Errorf("meta page 0 has wrong page ID: %d != %d", pageID, 0)
|
|
}
|
|
var freeRoot0, mainRoot0, txnID0 uint64
|
|
var freeDepth0, mainDepth0 uint16
|
|
freeRoot0, freeDepth0, mainRoot0, mainDepth0, txnID0, err = readMetaPage(meta[:], pos)
|
|
if err != nil {
|
|
return fmt.Errorf("reading meta page 0: %v", err)
|
|
}
|
|
|
|
// Read meta page 0
|
|
if _, err = f.ReadAt(meta[:], 1*PageSize); err != nil {
|
|
return fmt.Errorf("reading meta page 1: %v", err)
|
|
}
|
|
pos, pageID, _, _ = readPageHeader(meta[:], 0)
|
|
if pageID != 1 {
|
|
return fmt.Errorf("meta page 1 has wrong page ID: %d != %d", pageID, 1)
|
|
}
|
|
var freeRoot1, mainRoot1, txnID1 uint64
|
|
var freeDepth1, mainDepth1 uint16
|
|
freeRoot1, freeDepth1, mainRoot1, mainDepth1, txnID1, err = readMetaPage(meta[:], pos)
|
|
if err != nil {
|
|
return fmt.Errorf("reading meta page 1: %v", err)
|
|
}
|
|
|
|
var freeRoot, mainRoot uint64
|
|
var freeDepth, mainDepth uint16
|
|
if txnID0 > txnID1 {
|
|
freeRoot = freeRoot0
|
|
freeDepth = freeDepth0
|
|
mainRoot = mainRoot0
|
|
mainDepth = mainDepth0
|
|
} else {
|
|
freeRoot = freeRoot1
|
|
freeDepth = freeDepth1
|
|
mainRoot = mainRoot1
|
|
mainDepth = mainDepth1
|
|
}
|
|
fmt.Printf("FREE_DBI root page ID: %d, depth: %d\n", freeRoot, freeDepth)
|
|
fmt.Printf("MAIN_DBI root page ID: %d, depth: %d\n", mainRoot, mainDepth)
|
|
|
|
var freelist = make(map[uint64]struct{})
|
|
var freeEntries int
|
|
var overflows int
|
|
var pages [8][PageSize]byte // Stack of pages
|
|
var pageIDs [8]uint64
|
|
var numKeys [8]int
|
|
var indices [8]int
|
|
var top int
|
|
pageIDs[0] = freeRoot
|
|
for top >= 0 {
|
|
branch := top < int(freeDepth)-1
|
|
i := indices[top]
|
|
num := numKeys[top]
|
|
page := &pages[top]
|
|
if num == 0 {
|
|
pageID = pageIDs[top]
|
|
if _, err = f.ReadAt(page[:], int64(pageID*PageSize)); err != nil {
|
|
return fmt.Errorf("reading FREE_DBI page: %v", err)
|
|
}
|
|
var flags uint16
|
|
var lowerFree int
|
|
pos, _, flags, lowerFree = readPageHeader(page[:], 0)
|
|
branchFlag := flags&BranchPageFlag > 0
|
|
if branchFlag && !branch {
|
|
return fmt.Errorf("unexpected branch page on level %d of FREE_DBI", top)
|
|
}
|
|
if !branchFlag && branch {
|
|
return fmt.Errorf("expected branch page on level %d of FREE_DBI", top)
|
|
}
|
|
num = (lowerFree - pos) / 2
|
|
i = 0
|
|
numKeys[top] = num
|
|
indices[top] = i
|
|
} else if i < num {
|
|
nodePtr := int(binary.LittleEndian.Uint16(page[HeaderSize+i*2:]))
|
|
i++
|
|
indices[top] = i
|
|
if branch {
|
|
top++
|
|
indices[top] = 0
|
|
numKeys[top] = 0
|
|
pageIDs[top] = binary.LittleEndian.Uint64(page[nodePtr:]) & 0xFFFFFFFFFFFF
|
|
} else {
|
|
freeEntries++
|
|
dataSize := int(binary.LittleEndian.Uint32(page[nodePtr:]))
|
|
flags := binary.LittleEndian.Uint16(page[nodePtr+4:])
|
|
keySize := int(binary.LittleEndian.Uint16(page[nodePtr+6:]))
|
|
if flags&BigDataFlag > 0 {
|
|
overflowPageID := binary.LittleEndian.Uint64(page[nodePtr+8+keySize:])
|
|
if _, err = f.ReadAt(meta[:], int64(overflowPageID*PageSize)); err != nil {
|
|
return fmt.Errorf("reading FREE_DBI overflow page: %v", err)
|
|
}
|
|
var overflowNum int
|
|
_, _, overflowNum = readOverflowPageHeader(meta[:], 0)
|
|
overflows += overflowNum
|
|
left := dataSize - 8
|
|
// Start with pos + 8 because first 8 bytes is the size of the list
|
|
for j := HeaderSize + 8; j < PageSize && left > 0; j += 8 {
|
|
pn := binary.LittleEndian.Uint64(meta[j:])
|
|
freelist[pn] = struct{}{}
|
|
left -= 8
|
|
}
|
|
for i := 1; i < overflowNum; i++ {
|
|
if _, err = f.ReadAt(meta[:], int64((overflowPageID+uint64(i))*PageSize)); err != nil {
|
|
return fmt.Errorf("reading FREE_DBI overflow page: %v", err)
|
|
}
|
|
for j := 0; j < PageSize && left > 0; j += 8 {
|
|
pn := binary.LittleEndian.Uint64(meta[j:])
|
|
freelist[pn] = struct{}{}
|
|
left -= 8
|
|
}
|
|
}
|
|
} else {
|
|
// First 8 bytes is the size of the list
|
|
for j := nodePtr + 8 + keySize + 8; j < nodePtr+8+keySize+dataSize; j += 8 {
|
|
pn := binary.LittleEndian.Uint64(page[j:])
|
|
freelist[pn] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
top--
|
|
}
|
|
}
|
|
fmt.Printf("Size of freelist: %d, entries: %d, overflows: %d\n", len(freelist), freeEntries, overflows)
|
|
return nil
|
|
}
|
|
|
|
func readPageHeader(page []byte, pos int) (newpos int, pageID uint64, flags uint16, lowerFree int) {
|
|
pageID = binary.LittleEndian.Uint64(page[pos:])
|
|
pos += 8
|
|
pos += 2 // Padding
|
|
flags = binary.LittleEndian.Uint16(page[pos:])
|
|
pos += 2
|
|
lowerFree = int(binary.LittleEndian.Uint16(page[pos:]))
|
|
pos += 4 // Overflow page number / lower upper bound of free space
|
|
newpos = pos
|
|
return
|
|
}
|
|
|
|
func readMetaPage(page []byte, pos int) (freeRoot uint64, freeDepth uint16, mainRoot uint64, mainDepth uint16, txnID uint64, err error) {
|
|
magic := binary.LittleEndian.Uint32(page[pos:])
|
|
if magic != MdbMagic {
|
|
err = fmt.Errorf("meta page has wrong magic: %X != %X", magic, MdbMagic)
|
|
return
|
|
}
|
|
pos += 4
|
|
version := binary.LittleEndian.Uint32(page[pos:])
|
|
if version != MdbDataVersion {
|
|
err = fmt.Errorf("meta page has wrong version: %d != %d", version, MdbDataVersion)
|
|
return
|
|
}
|
|
pos += 4
|
|
pos += 8 // Fixed address
|
|
pos += 8 // Map size
|
|
pos, freeRoot, freeDepth = readDbRecord(page[:], pos)
|
|
pos, mainRoot, mainDepth = readDbRecord(page[:], pos)
|
|
pos += 8 // Last page
|
|
txnID = binary.LittleEndian.Uint64(page[pos:])
|
|
return
|
|
}
|
|
|
|
func readDbRecord(page []byte, pos int) (newpos int, rootPageID uint64, depth uint16) {
|
|
pos += 4 // Padding (key size for fixed key databases)
|
|
pos += 2 // Flags
|
|
depth = binary.LittleEndian.Uint16(page[pos:])
|
|
pos += 2 // Depth
|
|
pos += 8 // Number of branch pages
|
|
pos += 8 // Number of leaf pages
|
|
pos += 8 // Number of overflow pages
|
|
pos += 8 // Number of entries
|
|
rootPageID = binary.LittleEndian.Uint64(page[pos:])
|
|
pos += 8
|
|
newpos = pos
|
|
return
|
|
}
|
|
|
|
func readOverflowPageHeader(page []byte, pos int) (newpos int, flags uint16, overflowNum int) {
|
|
pos += 8 // Page ID
|
|
pos += 2 // Padding
|
|
flags = binary.LittleEndian.Uint16(page[pos:])
|
|
pos += 2
|
|
overflowNum = int(binary.LittleEndian.Uint32(page[pos:]))
|
|
pos += 4 // Overflow page number / lower upper bound of free space
|
|
newpos = pos
|
|
return
|
|
}
|