1
0
mirror of https://github.com/taigrr/bitcask synced 2025-01-18 04:03:17 -08:00

Replace keydir with ART trie (#75)

* Replace keydir with ART trie

* Address some review feedback

* Address review feedback (consts)
This commit is contained in:
James Mills
2019-09-02 08:38:56 +10:00
committed by GitHub
parent 36bc134b22
commit abbbeb8e1d
8 changed files with 201 additions and 228 deletions

110
internal/codec_index.go Normal file
View File

@@ -0,0 +1,110 @@
package internal
import (
"encoding/binary"
"io"
art "github.com/plar/go-adaptive-radix-tree"
)
const (
Int32Size = 4
Int64Size = 8
FileIDSize = Int32Size
OffsetSize = Int64Size
SizeSize = Int64Size
)
func ReadBytes(r io.Reader) ([]byte, error) {
s := make([]byte, Int32Size)
_, err := io.ReadFull(r, s)
if err != nil {
return nil, err
}
size := binary.BigEndian.Uint32(s)
b := make([]byte, size)
_, err = io.ReadFull(r, b)
if err != nil {
return nil, err
}
return b, nil
}
func WriteBytes(b []byte, w io.Writer) (int, error) {
s := make([]byte, Int32Size)
binary.BigEndian.PutUint32(s, uint32(len(b)))
n, err := w.Write(s)
if err != nil {
return n, err
}
m, err := w.Write(b)
if err != nil {
return (n + m), err
}
return (n + m), nil
}
func ReadItem(r io.Reader) (Item, error) {
buf := make([]byte, (FileIDSize + OffsetSize + SizeSize))
_, err := io.ReadFull(r, buf)
if err != nil {
return Item{}, err
}
return Item{
FileID: int(binary.BigEndian.Uint32(buf[:FileIDSize])),
Offset: int64(binary.BigEndian.Uint64(buf[FileIDSize:(FileIDSize + OffsetSize)])),
Size: int64(binary.BigEndian.Uint64(buf[(FileIDSize + OffsetSize):])),
}, nil
}
func WriteItem(item Item, w io.Writer) (int, error) {
buf := make([]byte, (FileIDSize + OffsetSize + SizeSize))
binary.BigEndian.PutUint32(buf[:FileIDSize], uint32(item.FileID))
binary.BigEndian.PutUint64(buf[FileIDSize:(FileIDSize+OffsetSize)], uint64(item.Offset))
binary.BigEndian.PutUint64(buf[(FileIDSize+OffsetSize):], uint64(item.Size))
n, err := w.Write(buf)
if err != nil {
return 0, err
}
return n, nil
}
func ReadIndex(r io.Reader, t art.Tree) error {
for {
key, err := ReadBytes(r)
if err != nil {
if err == io.EOF {
break
}
return err
}
item, err := ReadItem(r)
if err != nil {
return err
}
t.Insert(key, item)
}
return nil
}
func WriteIndex(t art.Tree, w io.Writer) (err error) {
t.ForEach(func(node art.Node) bool {
_, err = WriteBytes(node.Key(), w)
if err != nil {
return false
}
item := node.Value().(Item)
_, err := WriteItem(item, w)
if err != nil {
return false
}
return true
})
return
}

7
internal/item.go Normal file
View File

@@ -0,0 +1,7 @@
package internal
type Item struct {
FileID int `json:"fileid"`
Offset int64 `json:"offset"`
Size int64 `json:"size"`
}

View File

@@ -1,129 +0,0 @@
package internal
import (
"bytes"
"encoding/gob"
"io"
"io/ioutil"
"os"
"sync"
)
type Item struct {
FileID int
Offset int64
Size int64
}
type Keydir struct {
sync.RWMutex
keys map[uint64][]byte
items map[uint64]Item
}
func NewKeydir() *Keydir {
return &Keydir{
keys: make(map[uint64][]byte),
items: make(map[uint64]Item),
}
}
func (k *Keydir) Add(key []byte, fileid int, offset, size int64) Item {
item := Item{
FileID: fileid,
Offset: offset,
Size: size,
}
hash := Hash(key)
k.Lock()
k.keys[hash] = key
k.items[hash] = item
k.Unlock()
return item
}
func (k *Keydir) Get(key []byte) (Item, bool) {
k.RLock()
item, ok := k.items[Hash(key)]
k.RUnlock()
return item, ok
}
func (k *Keydir) Delete(key []byte) {
hash := Hash(key)
k.Lock()
delete(k.keys, hash)
delete(k.items, hash)
k.Unlock()
}
func (k *Keydir) Len() int {
return len(k.keys)
}
func (k *Keydir) Keys() chan []byte {
ch := make(chan []byte)
go func() {
k.RLock()
for _, key := range k.keys {
ch <- key
}
close(ch)
k.RUnlock()
}()
return ch
}
func (k *Keydir) Bytes() ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(k.keys); err != nil {
return nil, err
}
if err := enc.Encode(k.items); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (k *Keydir) Load(fn string) error {
f, err := os.Open(fn)
if err != nil {
return err
}
defer f.Close()
dec := gob.NewDecoder(f)
if err := dec.Decode(&k.keys); err != nil {
return err
}
if err := dec.Decode(&k.items); err != nil {
return err
}
return nil
}
func (k *Keydir) Save(fn string) error {
data, err := k.Bytes()
if err != nil {
return err
}
return ioutil.WriteFile(fn, data, 0644)
}
func NewKeydirFromBytes(r io.Reader) (*Keydir, error) {
k := NewKeydir()
dec := gob.NewDecoder(r)
if err := dec.Decode(&k.keys); err != nil {
return nil, err
}
if err := dec.Decode(&k.items); err != nil {
return nil, err
}
return k, nil
}

View File

@@ -9,20 +9,6 @@ import (
"strings"
)
const (
offset64 = 14695981039346656037
prime64 = 1099511628211
)
func Hash(key []byte) uint64 {
var s uint64 = offset64
for _, c := range key {
s ^= uint64(c)
s *= prime64
}
return s
}
func Exists(path string) bool {
_, err := os.Stat(path)
return err == nil