github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/double.go (about) 1 // Copyright 2020 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 "encoding/binary" 19 "math" 20 "math/big" 21 22 "github.com/datastax/go-cassandra-native-protocol/datatype" 23 "github.com/datastax/go-cassandra-native-protocol/primitive" 24 ) 25 26 // Double is a codec for the CQL double type. Its preferred Go type is float64, but it can encode from and 27 // decode to most floating-point types, including big.Float. 28 var Double Codec = &doubleCodec{} 29 30 type doubleCodec struct{} 31 32 func (c *doubleCodec) DataType() datatype.DataType { 33 return datatype.Double 34 } 35 36 func (c *doubleCodec) Encode(source interface{}, version primitive.ProtocolVersion) (dest []byte, err error) { 37 var val float64 38 var wasNil bool 39 if val, wasNil, err = convertToFloat64(source); err == nil && !wasNil { 40 dest = writeFloat64(val) 41 } 42 if err != nil { 43 err = errCannotEncode(source, c.DataType(), version, err) 44 } 45 return 46 } 47 48 func (c *doubleCodec) Decode(source []byte, dest interface{}, version primitive.ProtocolVersion) (wasNull bool, err error) { 49 var val float64 50 if val, wasNull, err = readFloat64(source); err == nil { 51 err = convertFromFloat64(val, wasNull, dest) 52 } 53 if err != nil { 54 err = errCannotDecode(dest, c.DataType(), version, err) 55 } 56 return 57 } 58 59 func convertToFloat64(source interface{}) (val float64, wasNil bool, err error) { 60 switch s := source.(type) { 61 case float64: 62 val = s 63 case float32: 64 val = float64(s) 65 case *float64: 66 if wasNil = s == nil; !wasNil { 67 val = *s 68 } 69 case *float32: 70 if wasNil = s == nil; !wasNil { 71 val = float64(*s) 72 } 73 case *big.Float: 74 // Note: non-pointer big.Float is not supported as per its docs, it should always be a pointer. 75 if wasNil = s == nil; !wasNil { 76 val, err = bigFloatToFloat64(s) 77 } 78 case nil: 79 wasNil = true 80 default: 81 err = ErrConversionNotSupported 82 } 83 if err != nil { 84 err = errSourceConversionFailed(source, val, err) 85 } 86 return 87 } 88 89 func convertFromFloat64(val float64, wasNull bool, dest interface{}) (err error) { 90 switch d := dest.(type) { 91 case *interface{}: 92 if d == nil { 93 err = ErrNilDestination 94 } else if wasNull { 95 *d = nil 96 } else { 97 *d = val 98 } 99 case *float64: 100 if d == nil { 101 err = ErrNilDestination 102 } else if wasNull { 103 *d = 0 104 } else { 105 *d = val 106 } 107 case *float32: 108 if d == nil { 109 err = ErrNilDestination 110 } else if wasNull { 111 *d = 0 112 } else { 113 *d, err = float64ToFloat32(val) 114 } 115 case *big.Float: 116 if d == nil { 117 err = ErrNilDestination 118 } else if wasNull { 119 *d = big.Float{} 120 } else { 121 err = float64ToBigFloat(val, d) 122 } 123 default: 124 err = errDestinationInvalid(dest) 125 } 126 if err != nil { 127 err = errDestinationConversionFailed(val, dest, err) 128 } 129 return 130 } 131 132 const lengthOfDouble = primitive.LengthOfLong 133 134 func writeFloat64(val float64) (dest []byte) { 135 dest = make([]byte, lengthOfDouble) 136 binary.BigEndian.PutUint64(dest, math.Float64bits(val)) 137 return 138 } 139 140 func readFloat64(source []byte) (val float64, wasNull bool, err error) { 141 length := len(source) 142 if length == 0 { 143 wasNull = true 144 } else if length != lengthOfDouble { 145 err = errWrongFixedLength(lengthOfDouble, length) 146 } else { 147 val = math.Float64frombits(binary.BigEndian.Uint64(source)) 148 } 149 if err != nil { 150 err = errCannotRead(val, err) 151 } 152 return 153 }