diff --git a/bitcask.go b/bitcask.go index 625193b..0612f17 100644 --- a/bitcask.go +++ b/bitcask.go @@ -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 // 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 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. -func (b *Bitcask) Has(key string) bool { +func (b *Bitcask) Has(key []byte) bool { _, ok := b.keydir.Get(key) return ok } // 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 { 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) - b.trie.Add(key, item) + b.trie.Add(string(key), item) return nil } // Delete deletes the named key. If the key doesn't exist or an I/O error // occurs the error is returned. -func (b *Bitcask) Delete(key string) error { +func (b *Bitcask) Delete(key []byte) error { _, _, err := b.put(key, []byte{}) if err != nil { return err } b.keydir.Delete(key) - b.trie.Remove(key) + b.trie.Remove(string(key)) 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 // the function `f` with the keys found. If the function returns an error // no further keys are processed and the first error returned. -func (b *Bitcask) Scan(prefix string, f func(key string) error) error { - keys := b.trie.PrefixSearch(prefix) +func (b *Bitcask) Scan(prefix []byte, f func(key []byte) error) error { + keys := b.trie.PrefixSearch(string(prefix)) for _, key := range keys { - if err := f(key); err != nil { + if err := f([]byte(key)); err != nil { return err } } @@ -198,14 +198,14 @@ func (b *Bitcask) Len() int { } // 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() } // 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 // 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() { if err := f(key); err != nil { return err @@ -214,7 +214,7 @@ func (b *Bitcask) Fold(f func(key string) error) error { 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() defer b.mu.Unlock() @@ -301,7 +301,7 @@ func (b *Bitcask) reopen() error { } for key := range keydir.Keys() { item, _ := keydir.Get(key) - trie.Add(key, item) + trie.Add(string(key), item) } } else { for i, df := range datafiles { @@ -321,7 +321,7 @@ func (b *Bitcask) reopen() error { } 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 // Doing this automatically strips deleted keys and // old key/value pairs - err = b.Fold(func(key string) error { + err = b.Fold(func(key []byte) error { value, err := b.Get(key) if err != nil { return err diff --git a/bitcask_test.go b/bitcask_test.go index 8f6a3a8..5bb5c2a 100644 --- a/bitcask_test.go +++ b/bitcask_test.go @@ -1,6 +1,7 @@ package bitcask import ( + "bytes" "fmt" "io/ioutil" "os" @@ -13,6 +14,32 @@ import ( "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) { var ( db *Bitcask @@ -31,12 +58,12 @@ func TestAll(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) }) t.Run("Get", func(t *testing.T) { - val, err := db.Get("foo") + val, err := db.Get([]byte("foo")) assert.NoError(err) assert.Equal([]byte("bar"), val) }) @@ -46,24 +73,24 @@ func TestAll(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) { - keys := make([]string, 0) + keys := make([][]byte, 0) for key := range db.Keys() { keys = append(keys, key) } - assert.Equal([]string{"foo"}, keys) + assert.Equal([][]byte{[]byte("foo")}, keys) }) t.Run("Fold", func(t *testing.T) { var ( - keys []string + keys [][]byte values [][]byte ) - err := db.Fold(func(key string) error { + err := db.Fold(func(key []byte) error { value, err := db.Get(key) if err != nil { return err @@ -73,14 +100,14 @@ func TestAll(t *testing.T) { return nil }) assert.NoError(err) - assert.Equal([]string{"foo"}, keys) + assert.Equal([][]byte{[]byte("foo")}, keys) assert.Equal([][]byte{[]byte("bar")}, values) }) t.Run("Delete", func(t *testing.T) { - err := db.Delete("foo") + err := db.Delete([]byte("foo")) assert.NoError(err) - _, err = db.Get("foo") + _, err = db.Get([]byte("foo")) assert.Error(err) assert.Equal(ErrKeyNotFound, err) }) @@ -114,20 +141,20 @@ func TestDeletedKeys(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) }) t.Run("Get", func(t *testing.T) { - val, err := db.Get("foo") + val, err := db.Get([]byte("foo")) assert.NoError(err) assert.Equal([]byte("bar"), val) }) t.Run("Delete", func(t *testing.T) { - err := db.Delete("foo") + err := db.Delete([]byte("foo")) assert.NoError(err) - _, err = db.Get("foo") + _, err = db.Get([]byte("foo")) assert.Error(err) assert.Equal(ErrKeyNotFound, err) }) @@ -155,7 +182,7 @@ func TestDeletedKeys(t *testing.T) { }) t.Run("Get", func(t *testing.T) { - _, err = db.Get("foo") + _, err = db.Get([]byte("foo")) assert.Error(err) assert.Equal(ErrKeyNotFound, err) }) @@ -181,7 +208,7 @@ func TestMaxKeySize(t *testing.T) { }) t.Run("Put", func(t *testing.T) { - key := strings.Repeat(" ", 17) + key := []byte(strings.Repeat(" ", 17)) value := []byte("foobar") err = db.Put(key, value) assert.Error(err) @@ -203,7 +230,7 @@ func TestMaxValueSize(t *testing.T) { }) t.Run("Put", func(t *testing.T) { - key := "foo" + key := []byte("foo") value := []byte(strings.Repeat(" ", 17)) err = db.Put(key, value) assert.Error(err) @@ -229,12 +256,12 @@ func TestStats(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) }) t.Run("Get", func(t *testing.T) { - val, err := db.Get("foo") + val, err := db.Get([]byte("foo")) assert.NoError(err) assert.Equal([]byte("bar"), val) }) @@ -276,7 +303,7 @@ func TestMerge(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) }) @@ -287,7 +314,7 @@ func TestMerge(t *testing.T) { t.Run("Put", func(t *testing.T) { for i := 0; i < 10; i++ { - err := db.Put("foo", []byte("bar")) + err := db.Put([]byte("foo"), []byte("bar")) assert.NoError(err) } }) @@ -340,7 +367,7 @@ func TestConcurrent(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) }) }) @@ -353,7 +380,7 @@ func TestConcurrent(t *testing.T) { }() for i := 0; i <= 100; i++ { if i%x == 0 { - key := fmt.Sprintf("k%d", i) + key := []byte(fmt.Sprintf("k%d", i)) value := []byte(fmt.Sprintf("v%d", i)) err := db.Put(key, value) assert.NoError(err) @@ -377,7 +404,7 @@ func TestConcurrent(t *testing.T) { wg.Done() }() for i := 0; i <= N; i++ { - value, err := db.Get("foo") + value, err := db.Get([]byte("foo")) assert.NoError(err) assert.Equal([]byte("bar"), value) } @@ -420,12 +447,12 @@ func TestScan(t *testing.T) { "2": []byte("2"), "3": []byte("3"), "food": []byte("pizza"), - "foo": []byte("foo"), + "foo": []byte([]byte("foo")), "fooz": []byte("fooz ball"), "hello": []byte("world"), } for k, v := range items { - err = db.Put(k, v) + err = db.Put([]byte(k), v) assert.NoError(err) } }) @@ -433,21 +460,21 @@ func TestScan(t *testing.T) { t.Run("Scan", func(t *testing.T) { var ( - vals []string - expected = []string{ - "foo", - "fooz ball", - "pizza", + vals [][]byte + expected = [][]byte{ + []byte("foo"), + []byte("fooz ball"), + []byte("pizza"), } ) - err = db.Scan("fo", func(key string) error { + err = db.Scan([]byte("fo"), func(key []byte) error { val, err := db.Get(key) assert.NoError(err) - vals = append(vals, string(val)) + vals = append(vals, val) return nil }) - sort.Strings(vals) + vals = SortByteArrays(vals) assert.Equal(expected, vals) }) } @@ -510,7 +537,7 @@ func BenchmarkGet(b *testing.B) { b.Run(tt.name, func(b *testing.B) { b.SetBytes(int64(tt.size)) - key := "foo" + key := []byte("foo") value := []byte(strings.Repeat(" ", tt.size)) options := []Option{ @@ -536,7 +563,7 @@ func BenchmarkGet(b *testing.B) { if err != nil { b.Fatal(err) } - if string(val) != string(value) { + if !bytes.Equal(val, value) { b.Errorf("unexpected value") } } @@ -580,7 +607,7 @@ func BenchmarkPut(b *testing.B) { b.Run(tt.name, func(b *testing.B) { b.SetBytes(int64(tt.size)) - key := "foo" + key := []byte("foo") value := []byte(strings.Repeat(" ", tt.size)) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -616,30 +643,30 @@ func BenchmarkScan(b *testing.B) { "2": []byte("2"), "3": []byte("3"), "food": []byte("pizza"), - "foo": []byte("foo"), + "foo": []byte([]byte("foo")), "fooz": []byte("fooz ball"), "hello": []byte("world"), } for k, v := range items { - err := db.Put(k, v) + err := db.Put([]byte(k), v) if err != nil { b.Fatal(err) } } - var expected = []string{"foo", "food", "fooz"} + var expected = [][]byte{[]byte("foo"), []byte("food"), []byte("fooz")} b.ResetTimer() for i := 0; i < b.N; i++ { - var keys []string - err = db.Scan("fo", func(key string) error { + var keys [][]byte + err = db.Scan([]byte("fo"), func(key []byte) error { keys = append(keys, key) return nil }) if err != nil { b.Fatal(err) } - sort.Strings(keys) + keys = SortByteArrays(keys) if !reflect.DeepEqual(expected, keys) { b.Fatal(fmt.Errorf("expected keys=#%v got=%#v", expected, keys)) } diff --git a/cmd/bitcask/del.go b/cmd/bitcask/del.go index b101631..cbd55d4 100644 --- a/cmd/bitcask/del.go +++ b/cmd/bitcask/del.go @@ -37,7 +37,7 @@ func del(path, key string) int { } defer db.Close() - err = db.Delete(key) + err = db.Delete([]byte(key)) if err != nil { log.WithError(err).Error("error deleting key") return 1 diff --git a/cmd/bitcask/export.go b/cmd/bitcask/export.go index 1adbacd..290ca03 100644 --- a/cmd/bitcask/export.go +++ b/cmd/bitcask/export.go @@ -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) if err != nil { log.WithError(err). diff --git a/cmd/bitcask/get.go b/cmd/bitcask/get.go index 800ac70..3cf4cf7 100644 --- a/cmd/bitcask/get.go +++ b/cmd/bitcask/get.go @@ -38,7 +38,7 @@ func get(path, key string) int { } defer db.Close() - value, err := db.Get(key) + value, err := db.Get([]byte(key)) if err != nil { log.WithError(err).Error("error reading key") return 1 diff --git a/cmd/bitcask/import.go b/cmd/bitcask/import.go index aa526be..ba95c5c 100644 --- a/cmd/bitcask/import.go +++ b/cmd/bitcask/import.go @@ -89,7 +89,7 @@ func _import(path, input string) int { 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") return 2 } diff --git a/cmd/bitcask/keys.go b/cmd/bitcask/keys.go index f00d3ed..d629d9f 100644 --- a/cmd/bitcask/keys.go +++ b/cmd/bitcask/keys.go @@ -36,8 +36,8 @@ func keys(path string) int { } defer db.Close() - err = db.Fold(func(key string) error { - fmt.Printf("%s\n", key) + err = db.Fold(func(key []byte) error { + fmt.Printf("%s\n", string(key)) return nil }) if err != nil { diff --git a/cmd/bitcask/put.go b/cmd/bitcask/put.go index f8622e7..45969f2 100644 --- a/cmd/bitcask/put.go +++ b/cmd/bitcask/put.go @@ -55,7 +55,7 @@ func put(path, key string, value io.Reader) int { return 1 } - err = db.Put(key, data) + err = db.Put([]byte(key), data) if err != nil { log.WithError(err).Error("error writing key") return 1 diff --git a/cmd/bitcask/scan.go b/cmd/bitcask/scan.go index 4218f80..19b3fd2 100644 --- a/cmd/bitcask/scan.go +++ b/cmd/bitcask/scan.go @@ -40,7 +40,7 @@ func scan(path, prefix string) int { } 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) if err != nil { log.WithError(err).Error("error reading key") diff --git a/cmd/bitcaskd/main.go b/cmd/bitcaskd/main.go index 2650c58..ba40096 100644 --- a/cmd/bitcaskd/main.go +++ b/cmd/bitcaskd/main.go @@ -76,7 +76,7 @@ func main() { conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") return } - key := string(cmd.Args[1]) + key := cmd.Args[1] value := cmd.Args[2] err = db.Put(key, value) if err != nil { @@ -89,7 +89,7 @@ func main() { conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") return } - key := string(cmd.Args[1]) + key := cmd.Args[1] value, err := db.Get(key) if err != nil { conn.WriteNull() @@ -106,7 +106,7 @@ func main() { conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") return } - key := string(cmd.Args[1]) + key := cmd.Args[1] if db.Has(key) { conn.WriteInt(1) } else { @@ -117,7 +117,7 @@ func main() { conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") return } - key := string(cmd.Args[1]) + key := cmd.Args[1] err := db.Delete(key) if err != nil { conn.WriteInt(0) diff --git a/internal/entry.go b/internal/entry.go index 8fa2292..4d0de13 100644 --- a/internal/entry.go +++ b/internal/entry.go @@ -6,7 +6,7 @@ import ( 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) return pb.Entry{ diff --git a/internal/keydir.go b/internal/keydir.go index df7147b..ae9d070 100644 --- a/internal/keydir.go +++ b/internal/keydir.go @@ -17,51 +17,58 @@ type Item struct { type Keydir struct { sync.RWMutex - kv map[string]Item + keys map[uint64][]byte + items map[uint64]Item } func NewKeydir() *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{ FileID: fileid, Offset: offset, Size: size, } + hash := Hash(key) + k.Lock() - k.kv[key] = item + k.keys[hash] = key + k.items[hash] = item k.Unlock() return item } -func (k *Keydir) Get(key string) (Item, bool) { +func (k *Keydir) Get(key []byte) (Item, bool) { k.RLock() - item, ok := k.kv[key] + item, ok := k.items[Hash(key)] k.RUnlock() return item, ok } -func (k *Keydir) Delete(key string) { +func (k *Keydir) Delete(key []byte) { + hash := Hash(key) k.Lock() - delete(k.kv, key) + delete(k.keys, hash) + delete(k.items, hash) k.Unlock() } func (k *Keydir) Len() int { - return len(k.kv) + return len(k.keys) } -func (k *Keydir) Keys() chan string { - ch := make(chan string) +func (k *Keydir) Keys() chan []byte { + ch := make(chan []byte) go func() { k.RLock() - for key := range k.kv { + for _, key := range k.keys { ch <- key } close(ch) @@ -73,8 +80,10 @@ func (k *Keydir) Keys() chan string { func (k *Keydir) Bytes() ([]byte, error) { var buf bytes.Buffer enc := gob.NewEncoder(&buf) - err := enc.Encode(k.kv) - if err != nil { + 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 @@ -88,7 +97,10 @@ func (k *Keydir) Load(fn string) error { defer f.Close() 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 } @@ -107,8 +119,10 @@ func (k *Keydir) Save(fn string) error { func NewKeydirFromBytes(r io.Reader) (*Keydir, error) { k := NewKeydir() dec := gob.NewDecoder(r) - err := dec.Decode(&k.kv) - if err != nil { + 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 diff --git a/internal/proto/entry.pb.go b/internal/proto/entry.pb.go index d0ecf59..e39b620 100644 --- a/internal/proto/entry.pb.go +++ b/internal/proto/entry.pb.go @@ -20,7 +20,7 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Entry struct { 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"` Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -32,7 +32,7 @@ func (m *Entry) Reset() { *m = Entry{} } func (m *Entry) String() string { return proto.CompactTextString(m) } func (*Entry) ProtoMessage() {} 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 { return xxx_messageInfo_Entry.Unmarshal(m, b) @@ -59,11 +59,11 @@ func (m *Entry) GetChecksum() uint32 { return 0 } -func (m *Entry) GetKey() string { +func (m *Entry) GetKey() []byte { if m != nil { return m.Key } - return "" + return nil } func (m *Entry) GetOffset() int64 { @@ -84,16 +84,16 @@ func init() { 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{ - // 126 bytes of a gzipped FileDescriptorProto +var fileDescriptor_entry_085f82c8520d7cd0 = []byte{ + // 123 bytes of a gzipped FileDescriptorProto 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, 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, - 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, - 0x4a, 0x53, 0x25, 0x58, 0x14, 0x18, 0x35, 0x78, 0x82, 0x20, 0x9c, 0x24, 0x36, 0xb0, 0x5d, 0xc6, - 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0xd2, 0x3e, 0x83, 0x81, 0x00, 0x00, 0x00, + 0x4a, 0x53, 0x25, 0x58, 0xc0, 0x6a, 0x21, 0x9c, 0x24, 0x36, 0xb0, 0x5d, 0xc6, 0x80, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x47, 0x6a, 0x41, 0xd4, 0x81, 0x00, 0x00, 0x00, } diff --git a/internal/proto/entry.proto b/internal/proto/entry.proto index 7c927d0..ac0a79a 100644 --- a/internal/proto/entry.proto +++ b/internal/proto/entry.proto @@ -4,7 +4,7 @@ package proto; message Entry { uint32 Checksum = 1; - string Key = 2; + bytes Key = 2; int64 Offset = 3; bytes Value = 4; } diff --git a/internal/utils.go b/internal/utils.go index 158c0d1..ca365fd 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -2,6 +2,7 @@ package internal import ( "fmt" + "hash/fnv" "os" "path/filepath" "sort" @@ -9,6 +10,12 @@ import ( "strings" ) +func Hash(key []byte) uint64 { + h := fnv.New64a() + h.Write(key) + return h.Sum64() +} + func Exists(path string) bool { _, err := os.Stat(path) return err == nil