210 lines
5.3 KiB
Go
210 lines
5.3 KiB
Go
|
/*
|
||
|
Copyright 2019 The Kubernetes Authors.
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package value
|
||
|
|
||
|
import (
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
type mapReflect struct {
|
||
|
valueReflect
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Length() int {
|
||
|
val := r.Value
|
||
|
return val.Len()
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Empty() bool {
|
||
|
val := r.Value
|
||
|
return val.Len() == 0
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Get(key string) (Value, bool) {
|
||
|
return r.GetUsing(HeapAllocator, key)
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) GetUsing(a Allocator, key string) (Value, bool) {
|
||
|
k, v, ok := r.get(key)
|
||
|
if !ok {
|
||
|
return nil, false
|
||
|
}
|
||
|
return a.allocValueReflect().mustReuse(v, nil, &r.Value, &k), true
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) get(k string) (key, value reflect.Value, ok bool) {
|
||
|
mapKey := r.toMapKey(k)
|
||
|
val := r.Value.MapIndex(mapKey)
|
||
|
return mapKey, val, val.IsValid() && val != reflect.Value{}
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Has(key string) bool {
|
||
|
var val reflect.Value
|
||
|
val = r.Value.MapIndex(r.toMapKey(key))
|
||
|
if !val.IsValid() {
|
||
|
return false
|
||
|
}
|
||
|
return val != reflect.Value{}
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Set(key string, val Value) {
|
||
|
r.Value.SetMapIndex(r.toMapKey(key), reflect.ValueOf(val.Unstructured()))
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Delete(key string) {
|
||
|
val := r.Value
|
||
|
val.SetMapIndex(r.toMapKey(key), reflect.Value{})
|
||
|
}
|
||
|
|
||
|
// TODO: Do we need to support types that implement json.Marshaler and are used as string keys?
|
||
|
func (r mapReflect) toMapKey(key string) reflect.Value {
|
||
|
val := r.Value
|
||
|
return reflect.ValueOf(key).Convert(val.Type().Key())
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Iterate(fn func(string, Value) bool) bool {
|
||
|
return r.IterateUsing(HeapAllocator, fn)
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) IterateUsing(a Allocator, fn func(string, Value) bool) bool {
|
||
|
if r.Value.Len() == 0 {
|
||
|
return true
|
||
|
}
|
||
|
v := a.allocValueReflect()
|
||
|
defer a.Free(v)
|
||
|
return eachMapEntry(r.Value, func(e *TypeReflectCacheEntry, key reflect.Value, value reflect.Value) bool {
|
||
|
return fn(key.String(), v.mustReuse(value, e, &r.Value, &key))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func eachMapEntry(val reflect.Value, fn func(*TypeReflectCacheEntry, reflect.Value, reflect.Value) bool) bool {
|
||
|
iter := val.MapRange()
|
||
|
entry := TypeReflectEntryOf(val.Type().Elem())
|
||
|
for iter.Next() {
|
||
|
next := iter.Value()
|
||
|
if !next.IsValid() {
|
||
|
continue
|
||
|
}
|
||
|
if !fn(entry, iter.Key(), next) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Unstructured() interface{} {
|
||
|
result := make(map[string]interface{}, r.Length())
|
||
|
r.Iterate(func(s string, value Value) bool {
|
||
|
result[s] = value.Unstructured()
|
||
|
return true
|
||
|
})
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Equals(m Map) bool {
|
||
|
return r.EqualsUsing(HeapAllocator, m)
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) EqualsUsing(a Allocator, m Map) bool {
|
||
|
lhsLength := r.Length()
|
||
|
rhsLength := m.Length()
|
||
|
if lhsLength != rhsLength {
|
||
|
return false
|
||
|
}
|
||
|
if lhsLength == 0 {
|
||
|
return true
|
||
|
}
|
||
|
vr := a.allocValueReflect()
|
||
|
defer a.Free(vr)
|
||
|
entry := TypeReflectEntryOf(r.Value.Type().Elem())
|
||
|
return m.Iterate(func(key string, value Value) bool {
|
||
|
_, lhsVal, ok := r.get(key)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
return Equals(vr.mustReuse(lhsVal, entry, nil, nil), value)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||
|
return r.ZipUsing(HeapAllocator, other, order, fn)
|
||
|
}
|
||
|
|
||
|
func (r mapReflect) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||
|
if otherMapReflect, ok := other.(*mapReflect); ok && order == Unordered {
|
||
|
return r.unorderedReflectZip(a, otherMapReflect, fn)
|
||
|
}
|
||
|
return defaultMapZip(a, &r, other, order, fn)
|
||
|
}
|
||
|
|
||
|
// unorderedReflectZip provides an optimized unordered zip for mapReflect types.
|
||
|
func (r mapReflect) unorderedReflectZip(a Allocator, other *mapReflect, fn func(key string, lhs, rhs Value) bool) bool {
|
||
|
if r.Empty() && (other == nil || other.Empty()) {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
lhs := r.Value
|
||
|
lhsEntry := TypeReflectEntryOf(lhs.Type().Elem())
|
||
|
|
||
|
// map lookup via reflection is expensive enough that it is better to keep track of visited keys
|
||
|
visited := map[string]struct{}{}
|
||
|
|
||
|
vlhs, vrhs := a.allocValueReflect(), a.allocValueReflect()
|
||
|
defer a.Free(vlhs)
|
||
|
defer a.Free(vrhs)
|
||
|
|
||
|
if other != nil {
|
||
|
rhs := other.Value
|
||
|
rhsEntry := TypeReflectEntryOf(rhs.Type().Elem())
|
||
|
iter := rhs.MapRange()
|
||
|
|
||
|
for iter.Next() {
|
||
|
key := iter.Key()
|
||
|
keyString := key.String()
|
||
|
next := iter.Value()
|
||
|
if !next.IsValid() {
|
||
|
continue
|
||
|
}
|
||
|
rhsVal := vrhs.mustReuse(next, rhsEntry, &rhs, &key)
|
||
|
visited[keyString] = struct{}{}
|
||
|
var lhsVal Value
|
||
|
if _, v, ok := r.get(keyString); ok {
|
||
|
lhsVal = vlhs.mustReuse(v, lhsEntry, &lhs, &key)
|
||
|
}
|
||
|
if !fn(keyString, lhsVal, rhsVal) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter := lhs.MapRange()
|
||
|
for iter.Next() {
|
||
|
key := iter.Key()
|
||
|
if _, ok := visited[key.String()]; ok {
|
||
|
continue
|
||
|
}
|
||
|
next := iter.Value()
|
||
|
if !next.IsValid() {
|
||
|
continue
|
||
|
}
|
||
|
if !fn(key.String(), vlhs.mustReuse(next, lhsEntry, &lhs, &key), nil) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|