Skip to content

Commit

Permalink
feat: load from slice and map
Browse files Browse the repository at this point in the history
  • Loading branch information
ppzqh committed Sep 17, 2024
1 parent e329ece commit fc5c5c5
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 20 deletions.
36 changes: 27 additions & 9 deletions container/strmap/strmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,39 +228,57 @@ type Str2Str struct {
}

func NewStr2Str() *Str2Str {
return &Str2Str{}
return &Str2Str{
strMap: New[int](),
strStore: strstore.New(),
}
}

// NewStr2StrFromSlice creates StrMapStr2Str from key, value slices.
func NewStr2StrFromSlice(kk, vv []string) *Str2Str {
m := NewStr2Str()
m.LoadFromSlice(kk, vv)
if err := m.LoadFromSlice(kk, vv); err != nil {
panic(err)
}
return m
}

// NewStr2StrFromMap creates StrMapStr2Str from map.
func NewStr2StrFromMap(m map[string]string) *Str2Str {
sm := NewStr2Str()
sm.LoadFromMap(m)
if err := sm.LoadFromMap(m); err != nil {
panic(err)
}
return sm
}

// LoadFromSlice resets Str2Str and loads from slices.
func (sm *Str2Str) LoadFromSlice(kk, vv []string) {
ss, ids := strstore.New(vv)
sm.strStore = ss
sm.strMap = NewFromSlice(kk, ids)
func (sm *Str2Str) LoadFromSlice(kk, vv []string) error {
if len(kk) != len(vv) {
return errors.New("kv len not match")
}
if sm.strStore == nil {
sm.strStore = strstore.New()
}
ids, err := sm.strStore.Load(vv)
if err != nil {
return err
}
if sm.strMap == nil {
sm.strMap = New[int]()
}
return sm.strMap.LoadFromSlice(kk, ids)
}

// LoadFromMap resets Str2Str and loads from map.
func (sm *Str2Str) LoadFromMap(m map[string]string) {
func (sm *Str2Str) LoadFromMap(m map[string]string) error {
kk := make([]string, 0, len(m))
vv := make([]string, 0, len(m))
for k, v := range m {
kk = append(kk, k)
vv = append(vv, v)
}
sm.LoadFromSlice(kk, vv)
return sm.LoadFromSlice(kk, vv)
}

// Get ...
Expand Down
47 changes: 46 additions & 1 deletion container/strmap/strmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func TestStrMapString(t *testing.T) {
t.Log(sm.debugString())
}

func TestStrMapStr(t *testing.T) {
func TestStr2Str(t *testing.T) {
kk := randStrings(20, 100000)
vv := randStrings(20, 100000)
m := newStdStr2StrMap(kk, vv)
Expand All @@ -120,6 +120,51 @@ func TestStrMapStr(t *testing.T) {
}
}

func TestStr2StrLoad(t *testing.T) {
round := 10
str2str := NewStr2Str()
// from slice
for r := 0; r < round; r++ {
kk := randStrings(20, 100000)
vv := randStrings(20, 100000)

err := str2str.LoadFromSlice(kk, vv)
require.NoError(t, err)
for i, k := range kk {
v0 := vv[i]
v1, _ := str2str.Get(k)
require.Equal(t, v0, v1, i)
}
}

// from map
for r := 0; r < round; r++ {
kk := randStrings(20, 100000)
vv := randStrings(20, 100000)
m := newStdStr2StrMap(kk, vv)

err := str2str.LoadFromMap(m)
require.NoError(t, err)
for i, k := range kk {
v0 := vv[i]
v1, _ := str2str.Get(k)
require.Equal(t, v0, v1, i)
}
}

// not initialized
str2str = &Str2Str{}
kk := randStrings(20, 100000)
vv := randStrings(20, 100000)
err := str2str.LoadFromSlice(kk, vv)
require.NoError(t, err)
for i, k := range kk {
v0 := vv[i]
v1, _ := str2str.Get(k)
require.Equal(t, v0, v1, i)
}
}

func BenchmarkLoadFromMap(b *testing.B) {
sz := 50
n := 100000
Expand Down
33 changes: 26 additions & 7 deletions internal/strstore/strstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,24 @@ type StrStore struct {
buf []byte
}

// New constructs a StrStore with the input string slice and returns the StrStore and indexes for the following reads.
// New creates a StrStore instance.
func New() *StrStore {
return &StrStore{}
}

// NewFromSlice constructs a StrStore with the input string slice and returns the StrStore and indexes for the following reads.
// It panics if any string in the slice is longer than math.MaxUint32.
func New(ss []string) (*StrStore, []int) {
func NewFromSlice(ss []string) (*StrStore, []int) {
st := &StrStore{}
idxes, err := st.Load(ss)
if err != nil {
panic(err)
}
return st, idxes
}

// Load resets the StrStore and set from input string slices.
func (s *StrStore) Load(ss []string) ([]int, error) {
n := len(ss)
totalLen := strlenSize * n
for i := 0; i < n; i++ {
Expand All @@ -41,16 +56,20 @@ func New(ss []string) (*StrStore, []int) {
totalLen += len(ss[i])
}
idxes := make([]int, n)
buf := make([]byte, totalLen)
if cap(s.buf) < totalLen {
s.buf = make([]byte, totalLen)
} else {
s.buf = s.buf[:totalLen]
}

offset := 0
for i := 0; i < n; i++ {
idxes[i] = offset
*(*uint32)(unsafe.Pointer(&buf[offset])) = uint32(len(ss[i]))
copy(buf[offset+strlenSize:offset+strlenSize+len(ss[i])], ss[i])
*(*uint32)(unsafe.Pointer(&s.buf[offset])) = uint32(len(ss[i]))
copy(s.buf[offset+strlenSize:offset+strlenSize+len(ss[i])], ss[i])
offset += strlenSize + len(ss[i])
}
st := &StrStore{buf: buf}
return st, idxes
return idxes, nil
}

// Get gets the string with the idx.
Expand Down
28 changes: 25 additions & 3 deletions internal/strstore/strstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
func TestStrStore(t *testing.T) {
// test when the pages grow
ss := randStrings(50, 1000000)
strStore, idxes := New(ss)
strStore, idxes := NewFromSlice(ss)
totalLen := 0
for i := 0; i < len(ss); i++ {
assert.Equal(t, ss[i], strStore.Get(idxes[i]))
Expand All @@ -39,9 +39,22 @@ func TestStrStore(t *testing.T) {
assert.Equal(t, "", s)
}

func TestStrStoreLoad(t *testing.T) {
rounds := 10
strStore := New()
for r := 0; r < rounds; r++ {
ss := randStrings(50, 100000)
idxes, err := strStore.Load(ss)
assert.Nil(t, err)
for i, s := range ss {
assert.Equal(t, s, strStore.Get(idxes[i]))
}
}
}

func BenchmarkStrStoreGetSet(b *testing.B) {
ss := randStrings(50, 1000000)
strStore, idxes := New(ss)
strStore, idxes := NewFromSlice(ss)
strSlice := make([]string, 0, len(ss))
for i := 0; i < len(ss); i++ {
strSlice = append(strSlice, ss[i])
Expand All @@ -64,9 +77,18 @@ func BenchmarkStrStoreGetSet(b *testing.B) {
})
}

func BenchmarkStrStoreLoad(b *testing.B) {
ss := randStrings(50, 1000000)
strStore := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
strStore.Load(ss)
}
}

func BenchmarkStrStoreGC(b *testing.B) {
ss := randStrings(50, 1000000)
strStore, idxes := New(ss)
strStore, idxes := NewFromSlice(ss)
_ = ss
runtime.GC()
b.ResetTimer()
Expand Down

0 comments on commit fc5c5c5

Please sign in to comment.