github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/reflection.go (about) 1 // Copyright 2021 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package datacodec 16 17 import ( 18 "math/big" 19 "net" 20 "reflect" 21 "strings" 22 "time" 23 24 "github.com/datastax/go-cassandra-native-protocol/primitive" 25 ) 26 27 var ( 28 typeOfInt = reflect.TypeOf(0) 29 typeOfInt64 = reflect.TypeOf(int64(0)) 30 typeOfInt32 = reflect.TypeOf(int32(0)) 31 typeOfInt16 = reflect.TypeOf(int16(0)) 32 typeOfInt8 = reflect.TypeOf(int8(0)) 33 typeOfFloat64 = reflect.TypeOf(float64(0)) 34 typeOfFloat32 = reflect.TypeOf(float32(0)) 35 typeOfString = reflect.TypeOf("") 36 typeOfBoolean = reflect.TypeOf(false) 37 typeOfCqlDecimal = reflect.TypeOf(CqlDecimal{}) 38 typeOfCqlDuration = reflect.TypeOf(CqlDuration{}) 39 typeOfTime = reflect.TypeOf(time.Time{}) 40 typeOfDuration = reflect.TypeOf(time.Duration(0)) 41 typeOfNetIP = reflect.TypeOf((*net.IP)(nil)).Elem() 42 typeOfUUID = reflect.TypeOf(primitive.UUID{}) 43 typeOfByteSlice = reflect.TypeOf([]byte{}) 44 typeOfInterfaceSlice = reflect.TypeOf([]interface{}{}) 45 typeOfStringToInterfaceMap = reflect.TypeOf(map[string]interface{}{}) 46 typeOfBigIntPointer = reflect.TypeOf(big.NewInt(0)) 47 ) 48 49 // reflectSource is used for collections, maps, tuples and udts. It analyzes the source and returns reflection data. 50 // If the source is a pointer, the pointer is de-referenced and the reflection data will refer to the pointer's target 51 // value. 52 func reflectSource(source interface{}) (sourceValue reflect.Value, sourceType reflect.Type, wasNil bool) { 53 sourceType = reflect.TypeOf(source) 54 if source == nil { 55 wasNil = true 56 } else { 57 sourceValue = reflect.ValueOf(source) 58 if sourceValue.Kind() == reflect.Ptr { 59 sourceType = sourceType.Elem() 60 wasNil = sourceValue.IsNil() 61 sourceValue = reflect.Indirect(sourceValue) 62 } 63 if sourceValue.Kind() == reflect.Slice || sourceValue.Kind() == reflect.Map { 64 wasNil = sourceValue.IsNil() 65 } 66 } 67 return 68 } 69 70 // reflectDest is used for collections, maps, tuples and udts. It analyzes the destination and returns reflection data. 71 // If the destination is nil or is not a pointer, an error is returned; if the source was null, the destination is set 72 // to its zero value; otherwise, the pointer is de-referenced and the reflection data will refer to the pointer's target 73 // value. 74 func reflectDest(dest interface{}, wasNull bool) (destValue reflect.Value, err error) { 75 if dest == nil { 76 err = ErrNilDestination 77 } else { 78 destValue = reflect.ValueOf(dest) 79 if destValue.Kind() != reflect.Ptr { 80 err = ErrPointerTypeExpected 81 } else if destValue.IsNil() { 82 err = ErrNilDestination 83 } else { 84 if wasNull { 85 zero := reflect.Zero(destValue.Elem().Type()) 86 destValue.Elem().Set(zero) 87 } 88 destValue = reflect.Indirect(destValue) 89 } 90 } 91 return 92 } 93 94 // Adjusts the given slice so that its length is >= targetSize, if the slice is addressable. If the capacity is enough, 95 // this is done simply by extending the slice's length; if the capacity is not enough though, or if the slice is nil, 96 // this is done by allocating a new slice. 97 func adjustSliceLength(sliceValue reflect.Value, targetSize int) { 98 if sliceValue.CanSet() { 99 if sliceValue.IsNil() || sliceValue.Cap() < targetSize { 100 sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), targetSize, targetSize)) 101 } else if sliceValue.Len() != targetSize { 102 sliceValue.SetLen(targetSize) 103 } 104 } 105 } 106 107 // Sets the given map to a new map with the target size, if it is nil and addressable. 108 func adjustMapSize(mapValue reflect.Value, targetSize int) { 109 if mapValue.CanSet() && mapValue.IsNil() { 110 mapValue.Set(reflect.MakeMapWithSize(mapValue.Type(), targetSize)) 111 } 112 } 113 114 // if the value is pointer, returns the value as is; otherwise, returns a pointer to it. 115 // This is done prior to decoding a value, since Codec.Decode requires a pointer value when decoding. 116 func ensurePointer(value reflect.Value) reflect.Value { 117 if value.Kind() != reflect.Ptr { 118 return pointerTo(value) 119 } 120 return value 121 } 122 123 // if the target type is not pointer but the value is pointer, returns the pointer's target, otherwise returns the 124 // value unchanged. This is done after decoding a value, since Codec.Decode requires a pointer value when decoding. 125 func maybeIndirect(targetType reflect.Type, value reflect.Value) reflect.Value { 126 if value.Kind() == reflect.Ptr && targetType.Kind() != reflect.Ptr { 127 return reflect.Indirect(value) 128 } 129 return value 130 } 131 132 // equivalent to reflect.Zero, but with one exception: for pointers, instead of returning a nil pointer, returns a 133 // pointer to a zero value, effectively achieving the same effect as using reflect.New. 134 // Used in injectors when creating zero values for decoding purposes, since Codec.Decode does not accept nil pointers. 135 func nilSafeZero(targetType reflect.Type) reflect.Value { 136 switch targetType.Kind() { 137 case reflect.Ptr: 138 return pointerTo(nilSafeZero(targetType.Elem())) 139 default: 140 return reflect.Zero(targetType) 141 } 142 } 143 144 // functionally equivalent to Value.Addr() but doesn't panic if target is unaddressable. 145 func pointerTo(target reflect.Value) reflect.Value { 146 ptr := reflect.New(target.Type()) 147 ptr.Elem().Set(target) 148 return ptr 149 } 150 151 // if the given type is nillable (that is, it's an interface, pointer, map or slice), returns the type as is; 152 // otherwise, returns a pointer type having the original type as its target. 153 func ensureNillable(targetType reflect.Type) reflect.Type { 154 kind := targetType.Kind() 155 if kind != reflect.Interface && 156 kind != reflect.Ptr && 157 kind != reflect.Slice && 158 kind != reflect.Map { 159 targetType = reflect.PtrTo(targetType) 160 } 161 return targetType 162 } 163 164 // Locates a struct field given a UDT field name. If no field can be located, this function returns a zero 165 // reflect.Value. If the struct field has a "cassandra" tag, then the tag must match the UDT field name exactly; 166 // otherwise, the UDT field name must match the struct field name, but case insensitively. 167 // The case-insensitive lookup is required because only exported struct fields can be located, and such fields are 168 // required to have a first upper-case letter. 169 func locateFieldByName(structValue reflect.Value, name string) (value reflect.Value) { 170 structType := structValue.Type() 171 for i := 0; i < structType.NumField(); i++ { 172 field := structType.Field(i) 173 if strings.EqualFold(name, field.Name) || field.Tag.Get("cassandra") == name { 174 value = structValue.Field(i) 175 break 176 } 177 } 178 return 179 } 180 181 // Locates a struct field given its index. If the index is out of range, this function returns a zero reflect.Value. 182 func locateFieldByIndex(structValue reflect.Value, index int) (value reflect.Value) { 183 structType := structValue.Type() 184 if index >= 0 && index < structType.NumField() { 185 value = structValue.Field(index) 186 } 187 return 188 }