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:
110
internal/codec_index.go
Normal file
110
internal/codec_index.go
Normal 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
7
internal/item.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package internal
|
||||
|
||||
type Item struct {
|
||||
FileID int `json:"fileid"`
|
||||
Offset int64 `json:"offset"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user