...

Source file src/sigs.k8s.io/structured-merge-diff/v4/value/structreflect.go

Documentation: sigs.k8s.io/structured-merge-diff/v4/value

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package value
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  )
    23  
    24  type structReflect struct {
    25  	valueReflect
    26  }
    27  
    28  func (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  
    37  func (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  
    43  func (r structReflect) Get(key string) (Value, bool) {
    44  	return r.GetUsing(HeapAllocator, key)
    45  }
    46  
    47  func (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  
    54  func (r structReflect) Has(key string) bool {
    55  	_, ok := r.findJsonNameField(key)
    56  	return ok
    57  }
    58  
    59  func (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  
    69  func (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  
    81  func (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  
   104  func (r structReflect) Iterate(fn func(string, Value) bool) bool {
   105  	return r.IterateUsing(HeapAllocator, fn)
   106  }
   107  
   108  func (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  
   116  func 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  
   131  func (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  
   141  func (r structReflect) Equals(m Map) bool {
   142  	return r.EqualsUsing(HeapAllocator, m)
   143  }
   144  
   145  func (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  
   150  func (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  
   159  func (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  
   168  func (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  
   172  func (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.
   184  func (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  

View as plain text