Skip to content

Commit

Permalink
feat: add str2str map
Browse files Browse the repository at this point in the history
  • Loading branch information
ppzqh committed Sep 17, 2024
1 parent 9832438 commit e669acd
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 0 deletions.
58 changes: 58 additions & 0 deletions container/strmap/strmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/cloudwego/gopkg/internal/hack"
"github.com/cloudwego/gopkg/internal/hash/maphash"
"github.com/cloudwego/gopkg/internal/strstore"
)

// StrMap represents GC friendly readonly string map implementation.
Expand Down Expand Up @@ -219,3 +220,60 @@ func (m *StrMap[V]) debugString() string {
fmt.Fprintf(b, "}(slots=%d, items=%d)", len(m.hashtable), len(m.items))
return b.String()
}

// Str2Str uses StrMap and strstore.StrStore to store map[string]string
type Str2Str struct {
strMap *StrMap[int]
strStore *strstore.StrStore
}

func NewStr2Str() *Str2Str {
return &Str2Str{}
}

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

// NewStr2StrFromMap creates StrMapStr2Str from map.
func NewStr2StrFromMap(m map[string]string) *Str2Str {
sm := NewStr2Str()
sm.LoadFromMap(m)
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)
}

// LoadFromMap resets Str2Str and loads from map.
func (sm *Str2Str) LoadFromMap(m map[string]string) {
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)
}

// Get ...
func (sm *Str2Str) Get(k string) (string, bool) {
if idx, ok := sm.strMap.Get(k); ok {
v := sm.strStore.Get(idx)
// TODO: any check?
return v, true
}
return "", false
}

// Len returns the size of map
func (sm *Str2Str) Len() int {
return sm.strMap.Len()
}
69 changes: 69 additions & 0 deletions container/strmap/strmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ func newStdStrMap(ss []string) map[string]uint {
return m
}

// newStdStr2StrMap generates a map with uniq values
func newStdStr2StrMap(kk, vv []string) map[string]string {
if len(kk) != len(vv) {
panic("len(kk) != len(vv)")
}
m := make(map[string]string, len(kk))
for i := 0; i < len(kk); i++ {
m[kk[i]] = vv[i]
}
return m
}

func TestStrMap(t *testing.T) {
ss := randStrings(20, 100000)
m := newStdStrMap(ss)
Expand Down Expand Up @@ -84,6 +96,30 @@ func TestStrMapString(t *testing.T) {
t.Log(sm.debugString())
}

func TestStrMapStr(t *testing.T) {
kk := randStrings(20, 100000)
vv := randStrings(20, 100000)
m := newStdStr2StrMap(kk, vv)

// from slice
ms := NewStr2StrFromSlice(kk, vv)
require.Equal(t, len(m), ms.Len())
for i, k := range kk {
v0 := vv[i]
v1, _ := ms.Get(k)
require.Equal(t, v0, v1, i)
}

// from map
mm := NewStr2StrFromMap(m)
require.Equal(t, len(m), mm.Len())
for i, k := range kk {
v0 := vv[i]
v1, _ := mm.Get(k)
require.Equal(t, v0, v1, i)
}
}

func BenchmarkLoadFromMap(b *testing.B) {
sz := 50
n := 100000
Expand Down Expand Up @@ -165,3 +201,36 @@ func BenchmarkGC(b *testing.B) {
}
}
}

func BenchmarkStr2StrMapGC(b *testing.B) {
sizes := []int{20, 100}
nn := []int{100000, 400000}

for _, n := range nn {
for _, sz := range sizes {
kk := randStrings(sz, n)
vv := randStrings(sz, n)
m := newStdStr2StrMap(kk, vv)

b.Run(fmt.Sprintf("std-keysize_%d_n_%d", sz, n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
runtime.GC()
}
})

sm := NewStr2StrFromMap(m)
m = nil
runtime.GC()

b.Run(fmt.Sprintf("new-keysize_%d_n_%d", sz, n), func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
runtime.GC()
}
})

_ = m // fix lint ineffassign of m = nil
runtime.KeepAlive(sm)
}
}
}
File renamed without changes.
File renamed without changes.

0 comments on commit e669acd

Please sign in to comment.