Use []byte byte slices as keys directly avoiding serialing string(s) (#46) (#51)

This commit is contained in:
James Mills 2019-08-08 08:14:48 +10:00 committed by GitHub
parent d0c913ccee
commit 755b1879b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 147 additions and 99 deletions

@ -112,7 +112,7 @@ func (b *Bitcask) Sync() error {
// Get retrieves the value of the given key. If the key is not found or an/I/O // Get retrieves the value of the given key. If the key is not found or an/I/O
// error occurs a null byte slice is returned along with the error. // error occurs a null byte slice is returned along with the error.
func (b *Bitcask) Get(key string) ([]byte, error) { func (b *Bitcask) Get(key []byte) ([]byte, error) {
var df *internal.Datafile var df *internal.Datafile
item, ok := b.keydir.Get(key) item, ok := b.keydir.Get(key)
@ -140,13 +140,13 @@ func (b *Bitcask) Get(key string) ([]byte, error) {
} }
// Has returns true if the key exists in the database, false otherwise. // Has returns true if the key exists in the database, false otherwise.
func (b *Bitcask) Has(key string) bool { func (b *Bitcask) Has(key []byte) bool {
_, ok := b.keydir.Get(key) _, ok := b.keydir.Get(key)
return ok return ok
} }
// Put stores the key and value in the database. // Put stores the key and value in the database.
func (b *Bitcask) Put(key string, value []byte) error { func (b *Bitcask) Put(key, value []byte) error {
if len(key) > b.config.maxKeySize { if len(key) > b.config.maxKeySize {
return ErrKeyTooLarge return ErrKeyTooLarge
} }
@ -160,21 +160,21 @@ func (b *Bitcask) Put(key string, value []byte) error {
} }
item := b.keydir.Add(key, b.curr.FileID(), offset, n) item := b.keydir.Add(key, b.curr.FileID(), offset, n)
b.trie.Add(key, item) b.trie.Add(string(key), item)
return nil return nil
} }
// Delete deletes the named key. If the key doesn't exist or an I/O error // Delete deletes the named key. If the key doesn't exist or an I/O error
// occurs the error is returned. // occurs the error is returned.
func (b *Bitcask) Delete(key string) error { func (b *Bitcask) Delete(key []byte) error {
_, _, err := b.put(key, []byte{}) _, _, err := b.put(key, []byte{})
if err != nil { if err != nil {
return err return err
} }
b.keydir.Delete(key) b.keydir.Delete(key)
b.trie.Remove(key) b.trie.Remove(string(key))
return nil return nil
} }
@ -182,10 +182,10 @@ func (b *Bitcask) Delete(key string) error {
// Scan performs a prefix scan of keys matching the given prefix and calling // Scan performs a prefix scan of keys matching the given prefix and calling
// the function `f` with the keys found. If the function returns an error // the function `f` with the keys found. If the function returns an error
// no further keys are processed and the first error returned. // no further keys are processed and the first error returned.
func (b *Bitcask) Scan(prefix string, f func(key string) error) error { func (b *Bitcask) Scan(prefix []byte, f func(key []byte) error) error {
keys := b.trie.PrefixSearch(prefix) keys := b.trie.PrefixSearch(string(prefix))
for _, key := range keys { for _, key := range keys {
if err := f(key); err != nil { if err := f([]byte(key)); err != nil {
return err return err
} }
} }
@ -198,14 +198,14 @@ func (b *Bitcask) Len() int {
} }
// Keys returns all keys in the database as a channel of string(s) // Keys returns all keys in the database as a channel of string(s)
func (b *Bitcask) Keys() chan string { func (b *Bitcask) Keys() chan []byte {
return b.keydir.Keys() return b.keydir.Keys()
} }
// Fold iterates over all keys in the database calling the function `f` for // Fold iterates over all keys in the database calling the function `f` for
// each key. If the function returns an error, no further keys are processed // each key. If the function returns an error, no further keys are processed
// and the error returned. // and the error returned.
func (b *Bitcask) Fold(f func(key string) error) error { func (b *Bitcask) Fold(f func(key []byte) error) error {
for key := range b.keydir.Keys() { for key := range b.keydir.Keys() {
if err := f(key); err != nil { if err := f(key); err != nil {
return err return err
@ -214,7 +214,7 @@ func (b *Bitcask) Fold(f func(key string) error) error {
return nil return nil
} }
func (b *Bitcask) put(key string, value []byte) (int64, int64, error) { func (b *Bitcask) put(key, value []byte) (int64, int64, error) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
@ -301,7 +301,7 @@ func (b *Bitcask) reopen() error {
} }
for key := range keydir.Keys() { for key := range keydir.Keys() {
item, _ := keydir.Get(key) item, _ := keydir.Get(key)
trie.Add(key, item) trie.Add(string(key), item)
} }
} else { } else {
for i, df := range datafiles { for i, df := range datafiles {
@ -321,7 +321,7 @@ func (b *Bitcask) reopen() error {
} }
item := keydir.Add(e.Key, ids[i], e.Offset, n) item := keydir.Add(e.Key, ids[i], e.Offset, n)
trie.Add(e.Key, item) trie.Add(string(e.Key), item)
} }
} }
} }
@ -366,7 +366,7 @@ func (b *Bitcask) Merge() error {
// Rewrite all key/value pairs into merged database // Rewrite all key/value pairs into merged database
// Doing this automatically strips deleted keys and // Doing this automatically strips deleted keys and
// old key/value pairs // old key/value pairs
err = b.Fold(func(key string) error { err = b.Fold(func(key []byte) error {
value, err := b.Get(key) value, err := b.Get(key)
if err != nil { if err != nil {
return err return err

@ -1,6 +1,7 @@
package bitcask package bitcask
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -13,6 +14,32 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
type sortByteArrays [][]byte
func (b sortByteArrays) Len() int {
return len(b)
}
func (b sortByteArrays) Less(i, j int) bool {
switch bytes.Compare(b[i], b[j]) {
case -1:
return true
case 0, 1:
return false
}
return false
}
func (b sortByteArrays) Swap(i, j int) {
b[j], b[i] = b[i], b[j]
}
func SortByteArrays(src [][]byte) [][]byte {
sorted := sortByteArrays(src)
sort.Sort(sorted)
return sorted
}
func TestAll(t *testing.T) { func TestAll(t *testing.T) {
var ( var (
db *Bitcask db *Bitcask
@ -31,12 +58,12 @@ func TestAll(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
err = db.Put("foo", []byte("bar")) err = db.Put([]byte([]byte("foo")), []byte("bar"))
assert.NoError(err) assert.NoError(err)
}) })
t.Run("Get", func(t *testing.T) { t.Run("Get", func(t *testing.T) {
val, err := db.Get("foo") val, err := db.Get([]byte("foo"))
assert.NoError(err) assert.NoError(err)
assert.Equal([]byte("bar"), val) assert.Equal([]byte("bar"), val)
}) })
@ -46,24 +73,24 @@ func TestAll(t *testing.T) {
}) })
t.Run("Has", func(t *testing.T) { t.Run("Has", func(t *testing.T) {
assert.True(db.Has("foo")) assert.True(db.Has([]byte("foo")))
}) })
t.Run("Keys", func(t *testing.T) { t.Run("Keys", func(t *testing.T) {
keys := make([]string, 0) keys := make([][]byte, 0)
for key := range db.Keys() { for key := range db.Keys() {
keys = append(keys, key) keys = append(keys, key)
} }
assert.Equal([]string{"foo"}, keys) assert.Equal([][]byte{[]byte("foo")}, keys)
}) })
t.Run("Fold", func(t *testing.T) { t.Run("Fold", func(t *testing.T) {
var ( var (
keys []string keys [][]byte
values [][]byte values [][]byte
) )
err := db.Fold(func(key string) error { err := db.Fold(func(key []byte) error {
value, err := db.Get(key) value, err := db.Get(key)
if err != nil { if err != nil {
return err return err
@ -73,14 +100,14 @@ func TestAll(t *testing.T) {
return nil return nil
}) })
assert.NoError(err) assert.NoError(err)
assert.Equal([]string{"foo"}, keys) assert.Equal([][]byte{[]byte("foo")}, keys)
assert.Equal([][]byte{[]byte("bar")}, values) assert.Equal([][]byte{[]byte("bar")}, values)
}) })
t.Run("Delete", func(t *testing.T) { t.Run("Delete", func(t *testing.T) {
err := db.Delete("foo") err := db.Delete([]byte("foo"))
assert.NoError(err) assert.NoError(err)
_, err = db.Get("foo") _, err = db.Get([]byte("foo"))
assert.Error(err) assert.Error(err)
assert.Equal(ErrKeyNotFound, err) assert.Equal(ErrKeyNotFound, err)
}) })
@ -114,20 +141,20 @@ func TestDeletedKeys(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
err = db.Put("foo", []byte("bar")) err = db.Put([]byte("foo"), []byte("bar"))
assert.NoError(err) assert.NoError(err)
}) })
t.Run("Get", func(t *testing.T) { t.Run("Get", func(t *testing.T) {
val, err := db.Get("foo") val, err := db.Get([]byte("foo"))
assert.NoError(err) assert.NoError(err)
assert.Equal([]byte("bar"), val) assert.Equal([]byte("bar"), val)
}) })
t.Run("Delete", func(t *testing.T) { t.Run("Delete", func(t *testing.T) {
err := db.Delete("foo") err := db.Delete([]byte("foo"))
assert.NoError(err) assert.NoError(err)
_, err = db.Get("foo") _, err = db.Get([]byte("foo"))
assert.Error(err) assert.Error(err)
assert.Equal(ErrKeyNotFound, err) assert.Equal(ErrKeyNotFound, err)
}) })
@ -155,7 +182,7 @@ func TestDeletedKeys(t *testing.T) {
}) })
t.Run("Get", func(t *testing.T) { t.Run("Get", func(t *testing.T) {
_, err = db.Get("foo") _, err = db.Get([]byte("foo"))
assert.Error(err) assert.Error(err)
assert.Equal(ErrKeyNotFound, err) assert.Equal(ErrKeyNotFound, err)
}) })
@ -181,7 +208,7 @@ func TestMaxKeySize(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
key := strings.Repeat(" ", 17) key := []byte(strings.Repeat(" ", 17))
value := []byte("foobar") value := []byte("foobar")
err = db.Put(key, value) err = db.Put(key, value)
assert.Error(err) assert.Error(err)
@ -203,7 +230,7 @@ func TestMaxValueSize(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
key := "foo" key := []byte("foo")
value := []byte(strings.Repeat(" ", 17)) value := []byte(strings.Repeat(" ", 17))
err = db.Put(key, value) err = db.Put(key, value)
assert.Error(err) assert.Error(err)
@ -229,12 +256,12 @@ func TestStats(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
err := db.Put("foo", []byte("bar")) err := db.Put([]byte("foo"), []byte("bar"))
assert.NoError(err) assert.NoError(err)
}) })
t.Run("Get", func(t *testing.T) { t.Run("Get", func(t *testing.T) {
val, err := db.Get("foo") val, err := db.Get([]byte("foo"))
assert.NoError(err) assert.NoError(err)
assert.Equal([]byte("bar"), val) assert.Equal([]byte("bar"), val)
}) })
@ -276,7 +303,7 @@ func TestMerge(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
err := db.Put("foo", []byte("bar")) err := db.Put([]byte("foo"), []byte("bar"))
assert.NoError(err) assert.NoError(err)
}) })
@ -287,7 +314,7 @@ func TestMerge(t *testing.T) {
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
err := db.Put("foo", []byte("bar")) err := db.Put([]byte("foo"), []byte("bar"))
assert.NoError(err) assert.NoError(err)
} }
}) })
@ -340,7 +367,7 @@ func TestConcurrent(t *testing.T) {
}) })
t.Run("Put", func(t *testing.T) { t.Run("Put", func(t *testing.T) {
err = db.Put("foo", []byte("bar")) err = db.Put([]byte("foo"), []byte("bar"))
assert.NoError(err) assert.NoError(err)
}) })
}) })
@ -353,7 +380,7 @@ func TestConcurrent(t *testing.T) {
}() }()
for i := 0; i <= 100; i++ { for i := 0; i <= 100; i++ {
if i%x == 0 { if i%x == 0 {
key := fmt.Sprintf("k%d", i) key := []byte(fmt.Sprintf("k%d", i))
value := []byte(fmt.Sprintf("v%d", i)) value := []byte(fmt.Sprintf("v%d", i))
err := db.Put(key, value) err := db.Put(key, value)
assert.NoError(err) assert.NoError(err)
@ -377,7 +404,7 @@ func TestConcurrent(t *testing.T) {
wg.Done() wg.Done()
}() }()
for i := 0; i <= N; i++ { for i := 0; i <= N; i++ {
value, err := db.Get("foo") value, err := db.Get([]byte("foo"))
assert.NoError(err) assert.NoError(err)
assert.Equal([]byte("bar"), value) assert.Equal([]byte("bar"), value)
} }
@ -420,12 +447,12 @@ func TestScan(t *testing.T) {
"2": []byte("2"), "2": []byte("2"),
"3": []byte("3"), "3": []byte("3"),
"food": []byte("pizza"), "food": []byte("pizza"),
"foo": []byte("foo"), "foo": []byte([]byte("foo")),
"fooz": []byte("fooz ball"), "fooz": []byte("fooz ball"),
"hello": []byte("world"), "hello": []byte("world"),
} }
for k, v := range items { for k, v := range items {
err = db.Put(k, v) err = db.Put([]byte(k), v)
assert.NoError(err) assert.NoError(err)
} }
}) })
@ -433,21 +460,21 @@ func TestScan(t *testing.T) {
t.Run("Scan", func(t *testing.T) { t.Run("Scan", func(t *testing.T) {
var ( var (
vals []string vals [][]byte
expected = []string{ expected = [][]byte{
"foo", []byte("foo"),
"fooz ball", []byte("fooz ball"),
"pizza", []byte("pizza"),
} }
) )
err = db.Scan("fo", func(key string) error { err = db.Scan([]byte("fo"), func(key []byte) error {
val, err := db.Get(key) val, err := db.Get(key)
assert.NoError(err) assert.NoError(err)
vals = append(vals, string(val)) vals = append(vals, val)
return nil return nil
}) })
sort.Strings(vals) vals = SortByteArrays(vals)
assert.Equal(expected, vals) assert.Equal(expected, vals)
}) })
} }
@ -510,7 +537,7 @@ func BenchmarkGet(b *testing.B) {
b.Run(tt.name, func(b *testing.B) { b.Run(tt.name, func(b *testing.B) {
b.SetBytes(int64(tt.size)) b.SetBytes(int64(tt.size))
key := "foo" key := []byte("foo")
value := []byte(strings.Repeat(" ", tt.size)) value := []byte(strings.Repeat(" ", tt.size))
options := []Option{ options := []Option{
@ -536,7 +563,7 @@ func BenchmarkGet(b *testing.B) {
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
if string(val) != string(value) { if !bytes.Equal(val, value) {
b.Errorf("unexpected value") b.Errorf("unexpected value")
} }
} }
@ -580,7 +607,7 @@ func BenchmarkPut(b *testing.B) {
b.Run(tt.name, func(b *testing.B) { b.Run(tt.name, func(b *testing.B) {
b.SetBytes(int64(tt.size)) b.SetBytes(int64(tt.size))
key := "foo" key := []byte("foo")
value := []byte(strings.Repeat(" ", tt.size)) value := []byte(strings.Repeat(" ", tt.size))
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
@ -616,30 +643,30 @@ func BenchmarkScan(b *testing.B) {
"2": []byte("2"), "2": []byte("2"),
"3": []byte("3"), "3": []byte("3"),
"food": []byte("pizza"), "food": []byte("pizza"),
"foo": []byte("foo"), "foo": []byte([]byte("foo")),
"fooz": []byte("fooz ball"), "fooz": []byte("fooz ball"),
"hello": []byte("world"), "hello": []byte("world"),
} }
for k, v := range items { for k, v := range items {
err := db.Put(k, v) err := db.Put([]byte(k), v)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
} }
var expected = []string{"foo", "food", "fooz"} var expected = [][]byte{[]byte("foo"), []byte("food"), []byte("fooz")}
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
var keys []string var keys [][]byte
err = db.Scan("fo", func(key string) error { err = db.Scan([]byte("fo"), func(key []byte) error {
keys = append(keys, key) keys = append(keys, key)
return nil return nil
}) })
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
sort.Strings(keys) keys = SortByteArrays(keys)
if !reflect.DeepEqual(expected, keys) { if !reflect.DeepEqual(expected, keys) {
b.Fatal(fmt.Errorf("expected keys=#%v got=%#v", expected, keys)) b.Fatal(fmt.Errorf("expected keys=#%v got=%#v", expected, keys))
} }

@ -37,7 +37,7 @@ func del(path, key string) int {
} }
defer db.Close() defer db.Close()
err = db.Delete(key) err = db.Delete([]byte(key))
if err != nil { if err != nil {
log.WithError(err).Error("error deleting key") log.WithError(err).Error("error deleting key")
return 1 return 1

@ -90,7 +90,7 @@ func export(path, output string) int {
} }
} }
err = db.Fold(func(key string) error { err = db.Fold(func(key []byte) error {
value, err := db.Get(key) value, err := db.Get(key)
if err != nil { if err != nil {
log.WithError(err). log.WithError(err).

@ -38,7 +38,7 @@ func get(path, key string) int {
} }
defer db.Close() defer db.Close()
value, err := db.Get(key) value, err := db.Get([]byte(key))
if err != nil { if err != nil {
log.WithError(err).Error("error reading key") log.WithError(err).Error("error reading key")
return 1 return 1

@ -89,7 +89,7 @@ func _import(path, input string) int {
return 2 return 2
} }
if err := db.Put(string(key), value); err != nil { if err := db.Put(key, value); err != nil {
log.WithError(err).Error("error writing key/value") log.WithError(err).Error("error writing key/value")
return 2 return 2
} }

@ -36,8 +36,8 @@ func keys(path string) int {
} }
defer db.Close() defer db.Close()
err = db.Fold(func(key string) error { err = db.Fold(func(key []byte) error {
fmt.Printf("%s\n", key) fmt.Printf("%s\n", string(key))
return nil return nil
}) })
if err != nil { if err != nil {

@ -55,7 +55,7 @@ func put(path, key string, value io.Reader) int {
return 1 return 1
} }
err = db.Put(key, data) err = db.Put([]byte(key), data)
if err != nil { if err != nil {
log.WithError(err).Error("error writing key") log.WithError(err).Error("error writing key")
return 1 return 1

@ -40,7 +40,7 @@ func scan(path, prefix string) int {
} }
defer db.Close() defer db.Close()
err = db.Scan(prefix, func(key string) error { err = db.Scan([]byte(prefix), func(key []byte) error {
value, err := db.Get(key) value, err := db.Get(key)
if err != nil { if err != nil {
log.WithError(err).Error("error reading key") log.WithError(err).Error("error reading key")

@ -76,7 +76,7 @@ func main() {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return return
} }
key := string(cmd.Args[1]) key := cmd.Args[1]
value := cmd.Args[2] value := cmd.Args[2]
err = db.Put(key, value) err = db.Put(key, value)
if err != nil { if err != nil {
@ -89,7 +89,7 @@ func main() {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return return
} }
key := string(cmd.Args[1]) key := cmd.Args[1]
value, err := db.Get(key) value, err := db.Get(key)
if err != nil { if err != nil {
conn.WriteNull() conn.WriteNull()
@ -106,7 +106,7 @@ func main() {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return return
} }
key := string(cmd.Args[1]) key := cmd.Args[1]
if db.Has(key) { if db.Has(key) {
conn.WriteInt(1) conn.WriteInt(1)
} else { } else {
@ -117,7 +117,7 @@ func main() {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return return
} }
key := string(cmd.Args[1]) key := cmd.Args[1]
err := db.Delete(key) err := db.Delete(key)
if err != nil { if err != nil {
conn.WriteInt(0) conn.WriteInt(0)

@ -6,7 +6,7 @@ import (
pb "github.com/prologic/bitcask/internal/proto" pb "github.com/prologic/bitcask/internal/proto"
) )
func NewEntry(key string, value []byte) pb.Entry { func NewEntry(key, value []byte) pb.Entry {
checksum := crc32.ChecksumIEEE(value) checksum := crc32.ChecksumIEEE(value)
return pb.Entry{ return pb.Entry{

@ -17,51 +17,58 @@ type Item struct {
type Keydir struct { type Keydir struct {
sync.RWMutex sync.RWMutex
kv map[string]Item keys map[uint64][]byte
items map[uint64]Item
} }
func NewKeydir() *Keydir { func NewKeydir() *Keydir {
return &Keydir{ return &Keydir{
kv: make(map[string]Item), keys: make(map[uint64][]byte),
items: make(map[uint64]Item),
} }
} }
func (k *Keydir) Add(key string, fileid int, offset, size int64) Item { func (k *Keydir) Add(key []byte, fileid int, offset, size int64) Item {
item := Item{ item := Item{
FileID: fileid, FileID: fileid,
Offset: offset, Offset: offset,
Size: size, Size: size,
} }
hash := Hash(key)
k.Lock() k.Lock()
k.kv[key] = item k.keys[hash] = key
k.items[hash] = item
k.Unlock() k.Unlock()
return item return item
} }
func (k *Keydir) Get(key string) (Item, bool) { func (k *Keydir) Get(key []byte) (Item, bool) {
k.RLock() k.RLock()
item, ok := k.kv[key] item, ok := k.items[Hash(key)]
k.RUnlock() k.RUnlock()
return item, ok return item, ok
} }
func (k *Keydir) Delete(key string) { func (k *Keydir) Delete(key []byte) {
hash := Hash(key)
k.Lock() k.Lock()
delete(k.kv, key) delete(k.keys, hash)
delete(k.items, hash)
k.Unlock() k.Unlock()
} }
func (k *Keydir) Len() int { func (k *Keydir) Len() int {
return len(k.kv) return len(k.keys)
} }
func (k *Keydir) Keys() chan string { func (k *Keydir) Keys() chan []byte {
ch := make(chan string) ch := make(chan []byte)
go func() { go func() {
k.RLock() k.RLock()
for key := range k.kv { for _, key := range k.keys {
ch <- key ch <- key
} }
close(ch) close(ch)
@ -73,8 +80,10 @@ func (k *Keydir) Keys() chan string {
func (k *Keydir) Bytes() ([]byte, error) { func (k *Keydir) Bytes() ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
enc := gob.NewEncoder(&buf) enc := gob.NewEncoder(&buf)
err := enc.Encode(k.kv) if err := enc.Encode(k.keys); err != nil {
if err != nil { return nil, err
}
if err := enc.Encode(k.items); err != nil {
return nil, err return nil, err
} }
return buf.Bytes(), nil return buf.Bytes(), nil
@ -88,7 +97,10 @@ func (k *Keydir) Load(fn string) error {
defer f.Close() defer f.Close()
dec := gob.NewDecoder(f) dec := gob.NewDecoder(f)
if err := dec.Decode(&k.kv); err != nil { if err := dec.Decode(&k.keys); err != nil {
return err
}
if err := dec.Decode(&k.items); err != nil {
return err return err
} }
@ -107,8 +119,10 @@ func (k *Keydir) Save(fn string) error {
func NewKeydirFromBytes(r io.Reader) (*Keydir, error) { func NewKeydirFromBytes(r io.Reader) (*Keydir, error) {
k := NewKeydir() k := NewKeydir()
dec := gob.NewDecoder(r) dec := gob.NewDecoder(r)
err := dec.Decode(&k.kv) if err := dec.Decode(&k.keys); err != nil {
if err != nil { return nil, err
}
if err := dec.Decode(&k.items); err != nil {
return nil, err return nil, err
} }
return k, nil return k, nil

@ -20,7 +20,7 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Entry struct { type Entry struct {
Checksum uint32 `protobuf:"varint,1,opt,name=Checksum,proto3" json:"Checksum,omitempty"` Checksum uint32 `protobuf:"varint,1,opt,name=Checksum,proto3" json:"Checksum,omitempty"`
Key string `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"` Key []byte `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"`
Offset int64 `protobuf:"varint,3,opt,name=Offset,proto3" json:"Offset,omitempty"` Offset int64 `protobuf:"varint,3,opt,name=Offset,proto3" json:"Offset,omitempty"`
Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"` Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -32,7 +32,7 @@ func (m *Entry) Reset() { *m = Entry{} }
func (m *Entry) String() string { return proto.CompactTextString(m) } func (m *Entry) String() string { return proto.CompactTextString(m) }
func (*Entry) ProtoMessage() {} func (*Entry) ProtoMessage() {}
func (*Entry) Descriptor() ([]byte, []int) { func (*Entry) Descriptor() ([]byte, []int) {
return fileDescriptor_entry_db5b99f271e6b4b6, []int{0} return fileDescriptor_entry_085f82c8520d7cd0, []int{0}
} }
func (m *Entry) XXX_Unmarshal(b []byte) error { func (m *Entry) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Entry.Unmarshal(m, b) return xxx_messageInfo_Entry.Unmarshal(m, b)
@ -59,11 +59,11 @@ func (m *Entry) GetChecksum() uint32 {
return 0 return 0
} }
func (m *Entry) GetKey() string { func (m *Entry) GetKey() []byte {
if m != nil { if m != nil {
return m.Key return m.Key
} }
return "" return nil
} }
func (m *Entry) GetOffset() int64 { func (m *Entry) GetOffset() int64 {
@ -84,16 +84,16 @@ func init() {
proto.RegisterType((*Entry)(nil), "proto.Entry") proto.RegisterType((*Entry)(nil), "proto.Entry")
} }
func init() { proto.RegisterFile("entry.proto", fileDescriptor_entry_db5b99f271e6b4b6) } func init() { proto.RegisterFile("entry.proto", fileDescriptor_entry_085f82c8520d7cd0) }
var fileDescriptor_entry_db5b99f271e6b4b6 = []byte{ var fileDescriptor_entry_085f82c8520d7cd0 = []byte{
// 126 bytes of a gzipped FileDescriptorProto // 123 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xcd, 0x2b, 0x29, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xcd, 0x2b, 0x29,
0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0xc9, 0x5c, 0xac, 0xae, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0xc9, 0x5c, 0xac, 0xae,
0x20, 0x51, 0x21, 0x29, 0x2e, 0x0e, 0xe7, 0x8c, 0xd4, 0xe4, 0xec, 0xe2, 0xd2, 0x5c, 0x09, 0x46, 0x20, 0x51, 0x21, 0x29, 0x2e, 0x0e, 0xe7, 0x8c, 0xd4, 0xe4, 0xec, 0xe2, 0xd2, 0x5c, 0x09, 0x46,
0x05, 0x46, 0x0d, 0xde, 0x20, 0x38, 0x5f, 0x48, 0x80, 0x8b, 0xd9, 0x3b, 0xb5, 0x52, 0x82, 0x49, 0x05, 0x46, 0x0d, 0xde, 0x20, 0x38, 0x5f, 0x48, 0x80, 0x8b, 0xd9, 0x3b, 0xb5, 0x52, 0x82, 0x49,
0x81, 0x51, 0x83, 0x33, 0x08, 0xc4, 0x14, 0x12, 0xe3, 0x62, 0xf3, 0x4f, 0x4b, 0x2b, 0x4e, 0x2d, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc4, 0x14, 0x12, 0xe3, 0x62, 0xf3, 0x4f, 0x4b, 0x2b, 0x4e, 0x2d,
0x91, 0x60, 0x56, 0x60, 0xd4, 0x60, 0x0e, 0x82, 0xf2, 0x84, 0x44, 0xb8, 0x58, 0xc3, 0x12, 0x73, 0x91, 0x60, 0x56, 0x60, 0xd4, 0x60, 0x0e, 0x82, 0xf2, 0x84, 0x44, 0xb8, 0x58, 0xc3, 0x12, 0x73,
0x4a, 0x53, 0x25, 0x58, 0x14, 0x18, 0x35, 0x78, 0x82, 0x20, 0x9c, 0x24, 0x36, 0xb0, 0x5d, 0xc6, 0x4a, 0x53, 0x25, 0x58, 0xc0, 0x6a, 0x21, 0x9c, 0x24, 0x36, 0xb0, 0x5d, 0xc6, 0x80, 0x00, 0x00,
0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0xd2, 0x3e, 0x83, 0x81, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, 0x6a, 0x41, 0xd4, 0x81, 0x00, 0x00, 0x00,
} }

@ -4,7 +4,7 @@ package proto;
message Entry { message Entry {
uint32 Checksum = 1; uint32 Checksum = 1;
string Key = 2; bytes Key = 2;
int64 Offset = 3; int64 Offset = 3;
bytes Value = 4; bytes Value = 4;
} }

@ -2,6 +2,7 @@ package internal
import ( import (
"fmt" "fmt"
"hash/fnv"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -9,6 +10,12 @@ import (
"strings" "strings"
) )
func Hash(key []byte) uint64 {
h := fnv.New64a()
h.Write(key)
return h.Sum64()
}
func Exists(path string) bool { func Exists(path string) bool {
_, err := os.Stat(path) _, err := os.Stat(path)
return err == nil return err == nil