mirror of
https://github.com/PeernetOfficial/Abstraction.git
synced 2026-07-20 12:27:51 +01:00
127 lines
3.0 KiB
Go
127 lines
3.0 KiB
Go
package pogreb
|
|
|
|
import (
|
|
"encoding/binary"
|
|
)
|
|
|
|
const (
|
|
bucketSize = 512
|
|
slotsPerBucket = 31 // Maximum number of slots possible to fit in a 512-byte bucket.
|
|
)
|
|
|
|
// slot corresponds to a single item in the hash table.
|
|
type slot struct {
|
|
hash uint32
|
|
segmentID uint16
|
|
keySize uint16
|
|
valueSize uint32
|
|
offset uint32 // Offset of the record in a segment.
|
|
}
|
|
|
|
func (sl slot) kvSize() uint32 {
|
|
return uint32(sl.keySize) + sl.valueSize
|
|
}
|
|
|
|
// bucket is an array of slots.
|
|
type bucket struct {
|
|
slots [slotsPerBucket]slot
|
|
next int64 // Offset of overflow bucket.
|
|
}
|
|
|
|
// bucketHandle is a bucket, plus its offset and the file it's written to.
|
|
type bucketHandle struct {
|
|
bucket
|
|
file *file
|
|
offset int64
|
|
}
|
|
|
|
func (b bucket) MarshalBinary() ([]byte, error) {
|
|
buf := make([]byte, bucketSize)
|
|
data := buf
|
|
for i := 0; i < slotsPerBucket; i++ {
|
|
sl := b.slots[i]
|
|
binary.LittleEndian.PutUint32(buf[:4], sl.hash)
|
|
binary.LittleEndian.PutUint16(buf[4:6], sl.segmentID)
|
|
binary.LittleEndian.PutUint16(buf[6:8], sl.keySize)
|
|
binary.LittleEndian.PutUint32(buf[8:12], sl.valueSize)
|
|
binary.LittleEndian.PutUint32(buf[12:16], sl.offset)
|
|
buf = buf[16:]
|
|
}
|
|
binary.LittleEndian.PutUint64(buf[:8], uint64(b.next))
|
|
return data, nil
|
|
}
|
|
|
|
func (b *bucket) UnmarshalBinary(data []byte) error {
|
|
for i := 0; i < slotsPerBucket; i++ {
|
|
_ = data[16] // bounds check hint to compiler; see golang.org/issue/14808
|
|
b.slots[i].hash = binary.LittleEndian.Uint32(data[:4])
|
|
b.slots[i].segmentID = binary.LittleEndian.Uint16(data[4:6])
|
|
b.slots[i].keySize = binary.LittleEndian.Uint16(data[6:8])
|
|
b.slots[i].valueSize = binary.LittleEndian.Uint32(data[8:12])
|
|
b.slots[i].offset = binary.LittleEndian.Uint32(data[12:16])
|
|
data = data[16:]
|
|
}
|
|
b.next = int64(binary.LittleEndian.Uint64(data[:8]))
|
|
return nil
|
|
}
|
|
|
|
func (b *bucket) del(slotIdx int) {
|
|
i := slotIdx
|
|
// Shift slots.
|
|
for ; i < slotsPerBucket-1; i++ {
|
|
b.slots[i] = b.slots[i+1]
|
|
}
|
|
b.slots[i] = slot{}
|
|
}
|
|
|
|
func (b *bucketHandle) read() error {
|
|
buf, err := b.file.Slice(b.offset, b.offset+int64(bucketSize))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return b.UnmarshalBinary(buf)
|
|
}
|
|
|
|
func (b *bucketHandle) write() error {
|
|
buf, err := b.MarshalBinary()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = b.file.WriteAt(buf, b.offset)
|
|
return err
|
|
}
|
|
|
|
// slotWriter inserts and writes slots into a bucket.
|
|
type slotWriter struct {
|
|
bucket *bucketHandle
|
|
slotIdx int
|
|
prevBuckets []*bucketHandle
|
|
}
|
|
|
|
func (sw *slotWriter) insert(sl slot, idx *index) error {
|
|
if sw.slotIdx == slotsPerBucket {
|
|
// Bucket is full, create a new overflow bucket.
|
|
nextBucket, err := idx.createOverflowBucket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sw.bucket.next = nextBucket.offset
|
|
sw.prevBuckets = append(sw.prevBuckets, sw.bucket)
|
|
sw.bucket = nextBucket
|
|
sw.slotIdx = 0
|
|
}
|
|
sw.bucket.slots[sw.slotIdx] = sl
|
|
sw.slotIdx++
|
|
return nil
|
|
}
|
|
|
|
func (sw *slotWriter) write() error {
|
|
// Write previous buckets first.
|
|
for i := len(sw.prevBuckets) - 1; i >= 0; i-- {
|
|
if err := sw.prevBuckets[i].write(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return sw.bucket.write()
|
|
}
|