github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/syntax/deepcopy.go (about)

     1  /*
     2  Copyright 2022 The Katalyst 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 syntax
    18  
    19  import (
    20  	"reflect"
    21  	"time"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	"k8s.io/apimachinery/pkg/api/resource"
    25  	"k8s.io/apimachinery/pkg/labels"
    26  )
    27  
    28  // DeepCopiable is an interface that can be implemented by types that need to
    29  // customize their deep copy behavior.
    30  type DeepCopiable interface {
    31  	DeepCopy() interface{}
    32  }
    33  
    34  // DeepCopy returns a deep copy of the given object.
    35  func DeepCopy(src interface{}) interface{} {
    36  	if src == nil {
    37  		return nil
    38  	}
    39  
    40  	old := reflect.ValueOf(src)
    41  	cpy := reflect.New(old.Type()).Elem()
    42  	copyRecursive(old, cpy)
    43  	return cpy.Interface()
    44  }
    45  
    46  func copyRecursive(oldItem, newItem reflect.Value) {
    47  	if oldItem.CanInterface() {
    48  		if copyItem, ok := oldItem.Interface().(DeepCopiable); ok {
    49  			newItem.Set(reflect.ValueOf(copyItem.DeepCopy()))
    50  			return
    51  		}
    52  	}
    53  
    54  	switch oldItem.Kind() {
    55  	case reflect.Ptr:
    56  		oldValue := oldItem.Elem()
    57  
    58  		if !oldValue.IsValid() {
    59  			return
    60  		}
    61  
    62  		newItem.Set(reflect.New(oldValue.Type()))
    63  		copyRecursive(oldValue, newItem.Elem())
    64  	case reflect.Interface:
    65  		if oldItem.IsNil() {
    66  			return
    67  		}
    68  		oldValue := oldItem.Elem()
    69  
    70  		copyValue := reflect.New(oldValue.Type()).Elem()
    71  		copyRecursive(oldValue, copyValue)
    72  		newItem.Set(copyValue)
    73  	case reflect.Struct:
    74  		// check for some native struct copy
    75  		if nativeStructCopy(oldItem, newItem) {
    76  			return
    77  		}
    78  
    79  		for i := 0; i < oldItem.NumField(); i++ {
    80  			if oldItem.Type().Field(i).PkgPath != "" {
    81  				continue
    82  			}
    83  			copyRecursive(oldItem.Field(i), newItem.Field(i))
    84  		}
    85  	case reflect.Slice:
    86  		if oldItem.IsNil() {
    87  			return
    88  		}
    89  
    90  		newItem.Set(reflect.MakeSlice(oldItem.Type(), oldItem.Len(), oldItem.Cap()))
    91  		for i := 0; i < oldItem.Len(); i++ {
    92  			copyRecursive(oldItem.Index(i), newItem.Index(i))
    93  		}
    94  	case reflect.Map:
    95  		if oldItem.IsNil() {
    96  			return
    97  		}
    98  
    99  		newItem.Set(reflect.MakeMap(oldItem.Type()))
   100  		for _, key := range oldItem.MapKeys() {
   101  			oldValue := oldItem.MapIndex(key)
   102  			newValue := reflect.New(oldValue.Type()).Elem()
   103  			copyRecursive(oldValue, newValue)
   104  			copyKey := DeepCopy(key.Interface())
   105  			newItem.SetMapIndex(reflect.ValueOf(copyKey), newValue)
   106  		}
   107  	default:
   108  		newItem.Set(oldItem)
   109  	}
   110  }
   111  
   112  // nativeStructCopy support copy function for some native struct
   113  func nativeStructCopy(oldItem, newItem reflect.Value) bool {
   114  	if !oldItem.CanInterface() {
   115  		return false
   116  	}
   117  
   118  	switch copier := oldItem.Interface().(type) {
   119  	case time.Time:
   120  		newItem.Set(reflect.ValueOf(copier))
   121  		return true
   122  	case v1.ResourceList:
   123  		newItem.Set(reflect.ValueOf(copier.DeepCopy()))
   124  		return true
   125  	case resource.Quantity:
   126  		newItem.Set(reflect.ValueOf(copier.DeepCopy()))
   127  		return true
   128  	case labels.Selector:
   129  		newItem.Set(reflect.ValueOf(copier.DeepCopySelector()))
   130  		return true
   131  	}
   132  
   133  	return false
   134  }