diff --git a/internal/hash/zipmap.go b/internal/hash/zipmap.go index c94c225..4d25edc 100644 --- a/internal/hash/zipmap.go +++ b/internal/hash/zipmap.go @@ -1,6 +1,7 @@ package hash import ( + "encoding/binary" "unsafe" "github.com/xgzlucario/rotom/internal/list" @@ -8,7 +9,7 @@ import ( var _ MapI = (*ZipMap)(nil) -// ZipMap store datas as [key1, val1, key2, val2...] in listpack. +// ZipMap store datas as [entry1, entry2, entry3...] in listpack. type ZipMap struct { m *list.ListPack } @@ -17,29 +18,57 @@ func NewZipMap() *ZipMap { return &ZipMap{list.NewListPack()} } +// encodeEntry encode data to [vlen, val, key]. +func encodeEntry(key string, val []byte) []byte { + buf := make([]byte, 0, len(key)+len(val)+1) + buf = binary.AppendUvarint(buf, uint64(len(val))) + buf = append(buf, val...) + return append(buf, key...) +} + +func decodeEntry(buf []byte) (key string, val []byte) { + vlen, n := binary.Uvarint(buf) + val = buf[n : n+int(vlen)] + key = b2s(buf[n+int(vlen):]) + return +} + func (zm *ZipMap) Set(key string, val []byte) (newField bool) { it := zm.m.Iterator().SeekLast() + b := key[len(key)-1] + newEntry := b2s(encodeEntry(key, val)) + for !it.IsFirst() { - it.Prev() - keyBytes := it.Prev() - if key == b2s(keyBytes) { - // update val - it.Next() - it.ReplaceNext(b2s(val)) + entry := it.Prev() + // fast equal + if entry[len(entry)-1] != b { + continue + } + + kb, _ := decodeEntry(entry) + if key == kb { + it.ReplaceNext(newEntry) return false } } - zm.m.RPush(key, b2s(val)) + zm.m.RPush(newEntry) return true } func (zm *ZipMap) Get(key string) ([]byte, bool) { it := zm.m.Iterator().SeekLast() + b := key[len(key)-1] + for !it.IsFirst() { - valBytes := it.Prev() - keyBytes := it.Prev() - if key == b2s(keyBytes) { - return valBytes, true + entry := it.Prev() + // fast equal + if entry[len(entry)-1] != b { + continue + } + + kb, vb := decodeEntry(entry) + if key == kb { + return vb, true } } return nil, false @@ -47,27 +76,29 @@ func (zm *ZipMap) Get(key string) ([]byte, bool) { func (zm *ZipMap) Remove(key string) bool { it := zm.m.Iterator().SeekLast() + b := key[len(key)-1] + for !it.IsFirst() { - it.Prev() - keyBytes := it.Prev() - if key == b2s(keyBytes) { - it.RemoveNexts(2) + entry := it.Prev() + // fast equal + if entry[len(entry)-1] != b { + continue + } + + kb, _ := decodeEntry(entry) + if key == kb { + it.RemoveNext() return true } } return false } -func (zm *ZipMap) Len() int { - return zm.m.Size() / 2 -} - func (zm *ZipMap) Scan(fn func(string, []byte)) { it := zm.m.Iterator().SeekLast() for !it.IsFirst() { - valBytes := it.Prev() - keyBytes := it.Prev() - fn(b2s(keyBytes), valBytes) + kb, vb := decodeEntry(it.Prev()) + fn(kb, vb) } } @@ -79,6 +110,8 @@ func (zm *ZipMap) ToMap() *Map { return m } +func (zm *ZipMap) Len() int { return zm.m.Size() } + func (zm *ZipMap) Compress() { zm.m.Compress() } func (zm *ZipMap) Decompress() { zm.m.Decompress() }