mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-25 15:50:20 +00:00
295 lines
7.6 KiB
Go
295 lines
7.6 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 (
|
||
|
"encoding/base64"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
// NewValueReflect creates a Value backed by an "interface{}" type,
|
||
|
// typically an structured object in Kubernetes world that is uses reflection to expose.
|
||
|
// The provided "interface{}" value must be a pointer so that the value can be modified via reflection.
|
||
|
// The provided "interface{}" may contain structs and types that are converted to Values
|
||
|
// by the jsonMarshaler interface.
|
||
|
func NewValueReflect(value interface{}) (Value, error) {
|
||
|
if value == nil {
|
||
|
return NewValueInterface(nil), nil
|
||
|
}
|
||
|
v := reflect.ValueOf(value)
|
||
|
if v.Kind() != reflect.Ptr {
|
||
|
// The root value to reflect on must be a pointer so that map.Set() and map.Delete() operations are possible.
|
||
|
return nil, fmt.Errorf("value provided to NewValueReflect must be a pointer")
|
||
|
}
|
||
|
return wrapValueReflect(v, nil, nil)
|
||
|
}
|
||
|
|
||
|
// wrapValueReflect wraps the provide reflect.Value as a value. If parent in the data tree is a map, parentMap
|
||
|
// and parentMapKey must be provided so that the returned value may be set and deleted.
|
||
|
func wrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) (Value, error) {
|
||
|
val := HeapAllocator.allocValueReflect()
|
||
|
return val.reuse(value, nil, parentMap, parentMapKey)
|
||
|
}
|
||
|
|
||
|
// wrapValueReflect wraps the provide reflect.Value as a value, and panics if there is an error. If parent in the data
|
||
|
// tree is a map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
|
||
|
func mustWrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) Value {
|
||
|
v, err := wrapValueReflect(value, parentMap, parentMapKey)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return v
|
||
|
}
|
||
|
|
||
|
// the value interface doesn't care about the type for value.IsNull, so we can use a constant
|
||
|
var nilType = reflect.TypeOf(&struct{}{})
|
||
|
|
||
|
// reuse replaces the value of the valueReflect. If parent in the data tree is a map, parentMap and parentMapKey
|
||
|
// must be provided so that the returned value may be set and deleted.
|
||
|
func (r *valueReflect) reuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) (Value, error) {
|
||
|
if cacheEntry == nil {
|
||
|
cacheEntry = TypeReflectEntryOf(value.Type())
|
||
|
}
|
||
|
if cacheEntry.CanConvertToUnstructured() {
|
||
|
u, err := cacheEntry.ToUnstructured(value)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if u == nil {
|
||
|
value = reflect.Zero(nilType)
|
||
|
} else {
|
||
|
value = reflect.ValueOf(u)
|
||
|
}
|
||
|
}
|
||
|
r.Value = dereference(value)
|
||
|
r.ParentMap = parentMap
|
||
|
r.ParentMapKey = parentMapKey
|
||
|
r.kind = kind(r.Value)
|
||
|
return r, nil
|
||
|
}
|
||
|
|
||
|
// mustReuse replaces the value of the valueReflect and panics if there is an error. If parent in the data tree is a
|
||
|
// map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
|
||
|
func (r *valueReflect) mustReuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) Value {
|
||
|
v, err := r.reuse(value, cacheEntry, parentMap, parentMapKey)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return v
|
||
|
}
|
||
|
|
||
|
func dereference(val reflect.Value) reflect.Value {
|
||
|
kind := val.Kind()
|
||
|
if (kind == reflect.Interface || kind == reflect.Ptr) && !safeIsNil(val) {
|
||
|
return val.Elem()
|
||
|
}
|
||
|
return val
|
||
|
}
|
||
|
|
||
|
type valueReflect struct {
|
||
|
ParentMap *reflect.Value
|
||
|
ParentMapKey *reflect.Value
|
||
|
Value reflect.Value
|
||
|
kind reflectType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsMap() bool {
|
||
|
return r.kind == mapType || r.kind == structMapType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsList() bool {
|
||
|
return r.kind == listType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsBool() bool {
|
||
|
return r.kind == boolType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsInt() bool {
|
||
|
return r.kind == intType || r.kind == uintType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsFloat() bool {
|
||
|
return r.kind == floatType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsString() bool {
|
||
|
return r.kind == stringType || r.kind == byteStringType
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) IsNull() bool {
|
||
|
return r.kind == nullType
|
||
|
}
|
||
|
|
||
|
type reflectType = int
|
||
|
|
||
|
const (
|
||
|
mapType = iota
|
||
|
structMapType
|
||
|
listType
|
||
|
intType
|
||
|
uintType
|
||
|
floatType
|
||
|
stringType
|
||
|
byteStringType
|
||
|
boolType
|
||
|
nullType
|
||
|
)
|
||
|
|
||
|
func kind(v reflect.Value) reflectType {
|
||
|
typ := v.Type()
|
||
|
rk := typ.Kind()
|
||
|
switch rk {
|
||
|
case reflect.Map:
|
||
|
if v.IsNil() {
|
||
|
return nullType
|
||
|
}
|
||
|
return mapType
|
||
|
case reflect.Struct:
|
||
|
return structMapType
|
||
|
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
|
||
|
return intType
|
||
|
case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8:
|
||
|
// Uint64 deliberately excluded, see valueUnstructured.Int.
|
||
|
return uintType
|
||
|
case reflect.Float64, reflect.Float32:
|
||
|
return floatType
|
||
|
case reflect.String:
|
||
|
return stringType
|
||
|
case reflect.Bool:
|
||
|
return boolType
|
||
|
case reflect.Slice:
|
||
|
if v.IsNil() {
|
||
|
return nullType
|
||
|
}
|
||
|
elemKind := typ.Elem().Kind()
|
||
|
if elemKind == reflect.Uint8 {
|
||
|
return byteStringType
|
||
|
}
|
||
|
return listType
|
||
|
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.UnsafePointer, reflect.Interface:
|
||
|
if v.IsNil() {
|
||
|
return nullType
|
||
|
}
|
||
|
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||
|
default:
|
||
|
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO find a cleaner way to avoid panics from reflect.IsNil()
|
||
|
func safeIsNil(v reflect.Value) bool {
|
||
|
k := v.Kind()
|
||
|
switch k {
|
||
|
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
||
|
return v.IsNil()
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsMap() Map {
|
||
|
return r.AsMapUsing(HeapAllocator)
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsMapUsing(a Allocator) Map {
|
||
|
switch r.kind {
|
||
|
case structMapType:
|
||
|
v := a.allocStructReflect()
|
||
|
v.valueReflect = r
|
||
|
return v
|
||
|
case mapType:
|
||
|
v := a.allocMapReflect()
|
||
|
v.valueReflect = r
|
||
|
return v
|
||
|
default:
|
||
|
panic("value is not a map or struct")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsList() List {
|
||
|
return r.AsListUsing(HeapAllocator)
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsListUsing(a Allocator) List {
|
||
|
if r.IsList() {
|
||
|
v := a.allocListReflect()
|
||
|
v.Value = r.Value
|
||
|
return v
|
||
|
}
|
||
|
panic("value is not a list")
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsBool() bool {
|
||
|
if r.IsBool() {
|
||
|
return r.Value.Bool()
|
||
|
}
|
||
|
panic("value is not a bool")
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsInt() int64 {
|
||
|
if r.kind == intType {
|
||
|
return r.Value.Int()
|
||
|
}
|
||
|
if r.kind == uintType {
|
||
|
return int64(r.Value.Uint())
|
||
|
}
|
||
|
|
||
|
panic("value is not an int")
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsFloat() float64 {
|
||
|
if r.IsFloat() {
|
||
|
return r.Value.Float()
|
||
|
}
|
||
|
panic("value is not a float")
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) AsString() string {
|
||
|
switch r.kind {
|
||
|
case stringType:
|
||
|
return r.Value.String()
|
||
|
case byteStringType:
|
||
|
return base64.StdEncoding.EncodeToString(r.Value.Bytes())
|
||
|
}
|
||
|
panic("value is not a string")
|
||
|
}
|
||
|
|
||
|
func (r valueReflect) Unstructured() interface{} {
|
||
|
val := r.Value
|
||
|
switch {
|
||
|
case r.IsNull():
|
||
|
return nil
|
||
|
case val.Kind() == reflect.Struct:
|
||
|
return structReflect{r}.Unstructured()
|
||
|
case val.Kind() == reflect.Map:
|
||
|
return mapReflect{valueReflect: r}.Unstructured()
|
||
|
case r.IsList():
|
||
|
return listReflect{r.Value}.Unstructured()
|
||
|
case r.IsString():
|
||
|
return r.AsString()
|
||
|
case r.IsInt():
|
||
|
return r.AsInt()
|
||
|
case r.IsBool():
|
||
|
return r.AsBool()
|
||
|
case r.IsFloat():
|
||
|
return r.AsFloat()
|
||
|
default:
|
||
|
panic(fmt.Sprintf("value of type %s is not a supported by value reflector", val.Type()))
|
||
|
}
|
||
|
}
|