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