Skip to content

Commit 036e4bf

Browse files
authored
Merge pull request #79 from aeneasr/fix-memory-keyparts
Reduce memory consumption for populateKeyParts
2 parents 6692d05 + d174d00 commit 036e4bf

2 files changed

Lines changed: 31 additions & 8 deletions

File tree

koanf.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/mitchellh/copystructure"
77
"sort"
88
"strconv"
9-
"strings"
109

1110
"github.com/knadh/koanf/maps"
1211
"github.com/knadh/koanf/providers/confmap"
@@ -69,7 +68,7 @@ type UnmarshalConf struct {
6968
// or a / for `parent/child/key`.
7069
func New(delim string) *Koanf {
7170
return NewWithConf(Conf{
72-
Delim: delim,
71+
Delim: delim,
7372
StrictMerge: false,
7473
})
7574
}
@@ -80,7 +79,7 @@ func NewWithConf(conf Conf) *Koanf {
8079
confMap: make(map[string]interface{}),
8180
confMapFlat: make(map[string]interface{}),
8281
keyMap: make(KeyMap),
83-
conf: conf,
82+
conf: conf,
8483
}
8584
}
8685

@@ -385,7 +384,7 @@ func (ko *Koanf) MapKeys(path string) []string {
385384
return out
386385
}
387386

388-
func (ko *Koanf) merge(c map[string]interface{}) error{
387+
func (ko *Koanf) merge(c map[string]interface{}) error {
389388
maps.IntfaceKeysToStrings(c)
390389
if ko.conf.StrictMerge {
391390
if err := maps.MergeStrict(c, ko.confMap); err != nil {
@@ -469,10 +468,19 @@ func toBool(v interface{}) (bool, error) {
469468
// traversal paths. For instance, `parent.child.key` generates
470469
// `parent`, and `parent.child`.
471470
func populateKeyParts(m KeyMap, delim string) KeyMap {
472-
out := make(KeyMap)
471+
out := make(KeyMap, len(m)) // The size of the result is at very least same to KeyMap
473472
for _, parts := range m {
473+
// parts is a slice of [parent, child, key]
474+
var nk string
475+
474476
for i := range parts {
475-
nk := strings.Join(parts[0:i+1], delim)
477+
if i == 0 {
478+
// On first iteration only use first part
479+
nk = parts[i]
480+
} else {
481+
// If nk already contains a part (e.g. `parent`) append delim + `child`
482+
nk += delim + parts[i]
483+
}
476484
if _, ok := out[nk]; ok {
477485
continue
478486
}

koanf_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,21 @@ func init() {
279279
}
280280
}
281281

282+
func BenchmarkLoadFile(b *testing.B) {
283+
k := koanf.New(delim)
284+
285+
// Don't use TOML here because it distorts memory benchmarks due to high memory use
286+
providers := []*file.File{file.Provider(mockJSON), file.Provider(mockYAML)}
287+
parsers := []koanf.Parser{json.Parser(), yaml.Parser()}
288+
289+
b.ResetTimer()
290+
for n := 0; n < b.N; n++ {
291+
if err := k.Load(providers[n%2], parsers[n%2]); err != nil {
292+
b.Fatalf("Unexpected error: %+v", k)
293+
}
294+
}
295+
}
296+
282297
func TestLoadFile(t *testing.T) {
283298
// Load a non-existent file.
284299
_, err := file.Provider("does-not-exist").ReadBytes()
@@ -996,7 +1011,7 @@ func TestGetTypes(t *testing.T) {
9961011
assert.Equal([]string{"red", "blue", "orange"}, c.koanf.Strings("orphan"))
9971012

9981013
assert.Equal(map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"}, c.koanf.StringMap("parent1.strmap"))
999-
assert.Equal(map[string][]string{"key1": []string{"val1", "val2", "val3"}, "key2": []string{"val4", "val5"}}, c.koanf.StringsMap("parent1.strsmap"))
1014+
assert.Equal(map[string][]string{"key1": {"val1", "val2", "val3"}, "key2": {"val4", "val5"}}, c.koanf.StringsMap("parent1.strsmap"))
10001015
assert.Equal(map[string]string{}, c.koanf.StringMap("xxxx"))
10011016
assert.Equal(map[string]string{}, c.koanf.StringMap("parent1.intmap"))
10021017

@@ -1084,7 +1099,7 @@ func TestMustGetTypes(t *testing.T) {
10841099
assert.Panics(func() { c.koanf.MustStringMap("xxxx") })
10851100
assert.Panics(func() { c.koanf.MustStringMap("parent1.intmap") })
10861101
assert.Equal(map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"}, c.koanf.MustStringMap("parent1.strmap"))
1087-
assert.Equal(map[string][]string{"key1": []string{"val1", "val2", "val3"}, "key2": []string{"val4", "val5"}}, c.koanf.MustStringsMap("parent1.strsmap"))
1102+
assert.Equal(map[string][]string{"key1": {"val1", "val2", "val3"}, "key2": {"val4", "val5"}}, c.koanf.MustStringsMap("parent1.strsmap"))
10881103

10891104
// // Bools.
10901105
assert.Panics(func() { c.koanf.MustBools("xxxx") })

0 commit comments

Comments
 (0)