mirror of
https://github.com/spf13/viper
synced 2025-05-06 12:17:18 +00:00
Changed: simplified find() fast path and increase performance
Removed searchMapForKey(), fast path directly integrated into searchMap() and searchMapWithPathPrefixes() => more generic (searchMapForKey() wasn't called everywhere it should have) At the same time, significantly speed up searchMap() and searchMapWithPathPrefixes(), which are still used for nested keys: the assumption that map keys are all lower-cased allows to perform val = m[key] instead of for k, v := range m { if strings.ToLower(k) == strings.ToLower(key) { val = v } } => i.e., directly access the map instead of enumerate the keys
This commit is contained in:
parent
93f1be46b4
commit
88e3ba5452
1 changed files with 28 additions and 64 deletions
92
viper.go
92
viper.go
|
@ -399,49 +399,22 @@ func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchMapForKey may end up traversing the map if the key references a nested
|
|
||||||
// item (foo.bar), but will use a fast path for the common case.
|
|
||||||
// Note: This assumes that the key given is already lowercase.
|
|
||||||
func (v *Viper) searchMapForKey(source map[string]interface{}, lcaseKey string) interface{} {
|
|
||||||
if !strings.Contains(lcaseKey, v.keyDelim) {
|
|
||||||
v, ok := source[lcaseKey]
|
|
||||||
if ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
path := strings.Split(lcaseKey, v.keyDelim)
|
|
||||||
return v.searchMap(source, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// searchMap recursively searches for a value for path in source map.
|
// searchMap recursively searches for a value for path in source map.
|
||||||
// Returns nil if not found.
|
// Returns nil if not found.
|
||||||
// Note: This assumes that the path entries are lower cased.
|
// Note: This assumes that the path entries and map keys are lower cased.
|
||||||
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
|
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast path
|
next, ok := source[path[0]]
|
||||||
if len(path) == 1 {
|
|
||||||
if v, ok := source[path[0]]; ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
var next interface{}
|
|
||||||
for k, v := range source {
|
|
||||||
if k == path[0] {
|
|
||||||
ok = true
|
|
||||||
next = v
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
|
// Fast path
|
||||||
|
if len(path) == 1 {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested case
|
||||||
switch next.(type) {
|
switch next.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
return v.searchMap(cast.ToStringMap(next), path[1:])
|
return v.searchMap(cast.ToStringMap(next), path[1:])
|
||||||
|
@ -450,9 +423,6 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
|
||||||
// if the type of `next` is the same as the type being asserted
|
// if the type of `next` is the same as the type being asserted
|
||||||
return v.searchMap(next.(map[string]interface{}), path[1:])
|
return v.searchMap(next.(map[string]interface{}), path[1:])
|
||||||
default:
|
default:
|
||||||
if len(path) == 1 {
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
// got a value but nested key expected, return "nil" for not found
|
// got a value but nested key expected, return "nil" for not found
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -469,6 +439,8 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
|
||||||
//
|
//
|
||||||
// This should be useful only at config level (other maps may not contain dots
|
// This should be useful only at config level (other maps may not contain dots
|
||||||
// in their keys).
|
// in their keys).
|
||||||
|
//
|
||||||
|
// Note: This assumes that the path entries and map keys are lower cased.
|
||||||
func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
|
func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return source
|
return source
|
||||||
|
@ -478,17 +450,14 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
|
||||||
for i := len(path); i > 0; i-- {
|
for i := len(path); i > 0; i-- {
|
||||||
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
|
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
|
||||||
|
|
||||||
var ok bool
|
next, ok := source[prefixKey]
|
||||||
var next interface{}
|
|
||||||
for k, v := range source {
|
|
||||||
if strings.ToLower(k) == prefixKey {
|
|
||||||
ok = true
|
|
||||||
next = v
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
|
// Fast path
|
||||||
|
if i == len(path) {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested case
|
||||||
var val interface{}
|
var val interface{}
|
||||||
switch next.(type) {
|
switch next.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
|
@ -498,9 +467,6 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
|
||||||
// if the type of `next` is the same as the type being asserted
|
// if the type of `next` is the same as the type being asserted
|
||||||
val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:])
|
val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:])
|
||||||
default:
|
default:
|
||||||
if len(path) == i {
|
|
||||||
val = next
|
|
||||||
}
|
|
||||||
// got a value but nested key expected, do nothing and look for next prefix
|
// got a value but nested key expected, do nothing and look for next prefix
|
||||||
}
|
}
|
||||||
if val != nil {
|
if val != nil {
|
||||||
|
@ -620,7 +586,8 @@ func (v *Viper) Get(key string) interface{} {
|
||||||
valType := val
|
valType := val
|
||||||
if v.typeByDefValue {
|
if v.typeByDefValue {
|
||||||
// TODO(bep) this branch isn't covered by a single test.
|
// TODO(bep) this branch isn't covered by a single test.
|
||||||
defVal := v.searchMapForKey(v.defaults, lcaseKey)
|
path := strings.Split(lcaseKey, v.keyDelim)
|
||||||
|
defVal := v.searchMap(v.defaults, path)
|
||||||
if defVal != nil {
|
if defVal != nil {
|
||||||
valType = defVal
|
valType = defVal
|
||||||
}
|
}
|
||||||
|
@ -883,16 +850,14 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
|
|
||||||
// if the requested key is an alias, then return the proper key
|
// if the requested key is an alias, then return the proper key
|
||||||
lcaseKey = v.realKey(lcaseKey)
|
lcaseKey = v.realKey(lcaseKey)
|
||||||
|
|
||||||
// Set() override first
|
|
||||||
val = v.searchMapForKey(v.override, lcaseKey)
|
|
||||||
if val != nil {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
path = strings.Split(lcaseKey, v.keyDelim)
|
path = strings.Split(lcaseKey, v.keyDelim)
|
||||||
nested = len(path) > 1
|
nested = len(path) > 1
|
||||||
|
|
||||||
|
// Set() override first
|
||||||
|
val = v.searchMap(v.override, path)
|
||||||
|
if val != nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
|
if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -912,7 +877,6 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
return flag.ValueString()
|
return flag.ValueString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
|
if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -934,7 +898,7 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if shadow := v.isPathShadowedInFlatMap(path, v.env); shadow != "" {
|
if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,7 +907,7 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
if val != nil {
|
if val != nil {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
if shadow := v.isPathShadowedInDeepMap(path, v.config); shadow != "" {
|
if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,7 +916,7 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
if val != nil {
|
if val != nil {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
if shadow := v.isPathShadowedInDeepMap(path, v.kvstore); shadow != "" {
|
if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -961,7 +925,7 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
if val != nil {
|
if val != nil {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
if shadow := v.isPathShadowedInDeepMap(path, v.defaults); shadow != "" {
|
if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue