go-ethereum/nomt/bitbox/probe.go
weiihann fef1ed4c4f nomt/bitbox: add Phase 3 Bitbox on-disk hash table storage
Implement the on-disk open-addressing hash table for storing trie pages:
- htfile.go: HT file layout with header, meta pages, and data pages
- metamap.go: in-memory meta byte map with dirty page tracking
- probe.go: triangular probing with xxhash64 page ID hashing
- db.go: Bitbox DB with StorePage, LoadPage, DeletePage, FlushMeta, Sync

The hash table uses 1-byte meta tags (top 7 bits of hash) for fast
filtering before reading full 4096-byte data pages. Triangular probing
with power-of-2 capacity guarantees all buckets are visited.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:14:20 +08:00

81 lines
2 KiB
Go

package bitbox
import (
"encoding/binary"
"github.com/cespare/xxhash/v2"
"github.com/ethereum/go-ethereum/nomt/core"
)
// HashPageID computes the xxhash64 of seed||encodedPageID.
func HashPageID(seed [16]byte, pageID core.PageID) uint64 {
encoded := pageID.Encode()
var buf [48]byte
copy(buf[:16], seed[:])
copy(buf[16:], encoded[:])
return xxhash.Sum64(buf[:])
}
// HashPageIDBytes computes the xxhash64 from seed and raw encoded page ID.
func HashPageIDBytes(seed [16]byte, encodedPageID [32]byte) uint64 {
var buf [48]byte
copy(buf[:16], seed[:])
copy(buf[16:], encodedPageID[:])
return xxhash.Sum64(buf[:])
}
// HashSeedFromBytes creates a [16]byte seed from a byte slice.
func HashSeedFromBytes(b []byte) [16]byte {
var seed [16]byte
copy(seed[:], b)
return seed
}
// HashSeedFromUint64 creates a deterministic seed from two uint64 values.
func HashSeedFromUint64(a, b uint64) [16]byte {
var seed [16]byte
binary.LittleEndian.PutUint64(seed[:8], a)
binary.LittleEndian.PutUint64(seed[8:], b)
return seed
}
// ProbeSequence implements triangular probing over the hash table.
//
// Bucket(step) = (initial + step*(step+1)/2) mod capacity
//
// With a power-of-2 capacity, triangular probing visits every bucket before
// repeating, guaranteeing termination.
type ProbeSequence struct {
hash uint64
bucket uint64
step uint64
capacity uint64
}
// NewProbeSequence creates a new probe sequence for the given hash and
// capacity. The capacity MUST be a power of 2.
func NewProbeSequence(hash, capacity uint64) ProbeSequence {
initial := hash % capacity
return ProbeSequence{
hash: hash,
bucket: initial,
step: 0,
capacity: capacity,
}
}
// Bucket returns the current bucket index.
func (p *ProbeSequence) Bucket() uint64 {
return p.bucket
}
// Hash returns the hash used to seed this probe.
func (p *ProbeSequence) Hash() uint64 {
return p.hash
}
// Next advances to the next bucket in the triangular probe sequence.
func (p *ProbeSequence) Next() {
p.step++
p.bucket = (p.bucket + p.step) % p.capacity
}