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  }