1/* 2Copyright 2019 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package value 18 19import ( 20 "fmt" 21 "reflect" 22) 23 24type structReflect struct { 25 valueReflect 26} 27 28func (r structReflect) Length() int { 29 i := 0 30 eachStructField(r.Value, func(_ *TypeReflectCacheEntry, s string, value reflect.Value) bool { 31 i++ 32 return true 33 }) 34 return i 35} 36 37func (r structReflect) Empty() bool { 38 return eachStructField(r.Value, func(_ *TypeReflectCacheEntry, s string, value reflect.Value) bool { 39 return false // exit early if the struct is non-empty 40 }) 41} 42 43func (r structReflect) Get(key string) (Value, bool) { 44 return r.GetUsing(HeapAllocator, key) 45} 46 47func (r structReflect) GetUsing(a Allocator, key string) (Value, bool) { 48 if val, ok := r.findJsonNameField(key); ok { 49 return a.allocValueReflect().mustReuse(val, nil, nil, nil), true 50 } 51 return nil, false 52} 53 54func (r structReflect) Has(key string) bool { 55 _, ok := r.findJsonNameField(key) 56 return ok 57} 58 59func (r structReflect) Set(key string, val Value) { 60 fieldEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[key] 61 if !ok { 62 panic(fmt.Sprintf("key %s may not be set on struct %T: field does not exist", key, r.Value.Interface())) 63 } 64 oldVal := fieldEntry.GetFrom(r.Value) 65 newVal := reflect.ValueOf(val.Unstructured()) 66 r.update(fieldEntry, key, oldVal, newVal) 67} 68 69func (r structReflect) Delete(key string) { 70 fieldEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[key] 71 if !ok { 72 panic(fmt.Sprintf("key %s may not be deleted on struct %T: field does not exist", key, r.Value.Interface())) 73 } 74 oldVal := fieldEntry.GetFrom(r.Value) 75 if oldVal.Kind() != reflect.Ptr && !fieldEntry.isOmitEmpty { 76 panic(fmt.Sprintf("key %s may not be deleted on struct: %T: value is neither a pointer nor an omitempty field", key, r.Value.Interface())) 77 } 78 r.update(fieldEntry, key, oldVal, reflect.Zero(oldVal.Type())) 79} 80 81func (r structReflect) update(fieldEntry *FieldCacheEntry, key string, oldVal, newVal reflect.Value) { 82 if oldVal.CanSet() { 83 oldVal.Set(newVal) 84 return 85 } 86 87 // map items are not addressable, so if a struct is contained in a map, the only way to modify it is 88 // to write a replacement fieldEntry into the map. 89 if r.ParentMap != nil { 90 if r.ParentMapKey == nil { 91 panic("ParentMapKey must not be nil if ParentMap is not nil") 92 } 93 replacement := reflect.New(r.Value.Type()).Elem() 94 fieldEntry.GetFrom(replacement).Set(newVal) 95 r.ParentMap.SetMapIndex(*r.ParentMapKey, replacement) 96 return 97 } 98 99 // This should never happen since NewValueReflect ensures that the root object reflected on is a pointer and map 100 // item replacement is handled above. 101 panic(fmt.Sprintf("key %s may not be modified on struct: %T: struct is not settable", key, r.Value.Interface())) 102} 103 104func (r structReflect) Iterate(fn func(string, Value) bool) bool { 105 return r.IterateUsing(HeapAllocator, fn) 106} 107 108func (r structReflect) IterateUsing(a Allocator, fn func(string, Value) bool) bool { 109 vr := a.allocValueReflect() 110 defer a.Free(vr) 111 return eachStructField(r.Value, func(e *TypeReflectCacheEntry, s string, value reflect.Value) bool { 112 return fn(s, vr.mustReuse(value, e, nil, nil)) 113 }) 114} 115 116func eachStructField(structVal reflect.Value, fn func(*TypeReflectCacheEntry, string, reflect.Value) bool) bool { 117 for _, fieldCacheEntry := range TypeReflectEntryOf(structVal.Type()).OrderedFields() { 118 fieldVal := fieldCacheEntry.GetFrom(structVal) 119 if fieldCacheEntry.CanOmit(fieldVal) { 120 // omit it 121 continue 122 } 123 ok := fn(fieldCacheEntry.TypeEntry, fieldCacheEntry.JsonName, fieldVal) 124 if !ok { 125 return false 126 } 127 } 128 return true 129} 130 131func (r structReflect) Unstructured() interface{} { 132 // Use number of struct fields as a cheap way to rough estimate map size 133 result := make(map[string]interface{}, r.Value.NumField()) 134 r.Iterate(func(s string, value Value) bool { 135 result[s] = value.Unstructured() 136 return true 137 }) 138 return result 139} 140 141func (r structReflect) Equals(m Map) bool { 142 return r.EqualsUsing(HeapAllocator, m) 143} 144 145func (r structReflect) EqualsUsing(a Allocator, m Map) bool { 146 // MapEquals uses zip and is fairly efficient for structReflect 147 return MapEqualsUsing(a, &r, m) 148} 149 150func (r structReflect) findJsonNameFieldAndNotEmpty(jsonName string) (reflect.Value, bool) { 151 structCacheEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[jsonName] 152 if !ok { 153 return reflect.Value{}, false 154 } 155 fieldVal := structCacheEntry.GetFrom(r.Value) 156 return fieldVal, !structCacheEntry.CanOmit(fieldVal) 157} 158 159func (r structReflect) findJsonNameField(jsonName string) (val reflect.Value, ok bool) { 160 structCacheEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[jsonName] 161 if !ok { 162 return reflect.Value{}, false 163 } 164 fieldVal := structCacheEntry.GetFrom(r.Value) 165 return fieldVal, !structCacheEntry.CanOmit(fieldVal) 166} 167 168func (r structReflect) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool { 169 return r.ZipUsing(HeapAllocator, other, order, fn) 170} 171 172func (r structReflect) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool { 173 if otherStruct, ok := other.(*structReflect); ok && r.Value.Type() == otherStruct.Value.Type() { 174 lhsvr, rhsvr := a.allocValueReflect(), a.allocValueReflect() 175 defer a.Free(lhsvr) 176 defer a.Free(rhsvr) 177 return r.structZip(otherStruct, lhsvr, rhsvr, fn) 178 } 179 return defaultMapZip(a, &r, other, order, fn) 180} 181 182// structZip provides an optimized zip for structReflect types. The zip is always lexical key ordered since there is 183// no additional cost to ordering the zip for structured types. 184func (r structReflect) structZip(other *structReflect, lhsvr, rhsvr *valueReflect, fn func(key string, lhs, rhs Value) bool) bool { 185 lhsVal := r.Value 186 rhsVal := other.Value 187 188 for _, fieldCacheEntry := range TypeReflectEntryOf(lhsVal.Type()).OrderedFields() { 189 lhsFieldVal := fieldCacheEntry.GetFrom(lhsVal) 190 rhsFieldVal := fieldCacheEntry.GetFrom(rhsVal) 191 lhsOmit := fieldCacheEntry.CanOmit(lhsFieldVal) 192 rhsOmit := fieldCacheEntry.CanOmit(rhsFieldVal) 193 if lhsOmit && rhsOmit { 194 continue 195 } 196 var lhsVal, rhsVal Value 197 if !lhsOmit { 198 lhsVal = lhsvr.mustReuse(lhsFieldVal, fieldCacheEntry.TypeEntry, nil, nil) 199 } 200 if !rhsOmit { 201 rhsVal = rhsvr.mustReuse(rhsFieldVal, fieldCacheEntry.TypeEntry, nil, nil) 202 } 203 if !fn(fieldCacheEntry.JsonName, lhsVal, rhsVal) { 204 return false 205 } 206 } 207 return true 208} 209