github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/float.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 21 "github.com/datastax/go-cassandra-native-protocol/datatype" 22 "github.com/datastax/go-cassandra-native-protocol/primitive" 23 ) 24 25 // Float is a codec for the CQL float type. Its preferred Go type is float32, but it can encode from and decode 26 // to float64 as well. 27 var Float Codec = &floatCodec{} 28 29 type floatCodec struct{} 30 31 func (c *floatCodec) DataType() datatype.DataType { 32 return datatype.Float 33 } 34 35 func (c *floatCodec) Encode(source interface{}, version primitive.ProtocolVersion) (dest []byte, err error) { 36 var val float32 37 var wasNil bool 38 if val, wasNil, err = convertToFloat32(source); err == nil && !wasNil { 39 dest = writeFloat32(val) 40 } 41 if err != nil { 42 err = errCannotEncode(source, c.DataType(), version, err) 43 } 44 return 45 } 46 47 func (c *floatCodec) Decode(source []byte, dest interface{}, version primitive.ProtocolVersion) (wasNull bool, err error) { 48 var val float32 49 if val, wasNull, err = readFloat32(source); err == nil { 50 err = convertFromFloat32(val, wasNull, dest) 51 } 52 if err != nil { 53 err = errCannotDecode(dest, c.DataType(), version, err) 54 } 55 return 56 } 57 58 func convertToFloat32(source interface{}) (val float32, wasNil bool, err error) { 59 switch s := source.(type) { 60 case float64: 61 val, err = float64ToFloat32(s) 62 case float32: 63 val = s 64 case *float64: 65 if wasNil = s == nil; !wasNil { 66 val, err = float64ToFloat32(*s) 67 } 68 case *float32: 69 if wasNil = s == nil; !wasNil { 70 val = *s 71 } 72 case nil: 73 wasNil = true 74 default: 75 err = ErrConversionNotSupported 76 } 77 if err != nil { 78 err = errSourceConversionFailed(source, val, err) 79 } 80 return 81 } 82 83 func convertFromFloat32(val float32, wasNull bool, dest interface{}) (err error) { 84 switch d := dest.(type) { 85 case *interface{}: 86 if d == nil { 87 err = ErrNilDestination 88 } else if wasNull { 89 *d = nil 90 } else { 91 *d = val 92 } 93 case *float64: 94 if d == nil { 95 err = ErrNilDestination 96 } else if wasNull { 97 *d = 0 98 } else { 99 *d = float64(val) 100 } 101 case *float32: 102 if d == nil { 103 err = ErrNilDestination 104 } else if wasNull { 105 *d = 0 106 } else { 107 *d = val 108 } 109 default: 110 err = errDestinationInvalid(dest) 111 } 112 if err != nil { 113 err = errDestinationConversionFailed(val, dest, err) 114 } 115 return 116 } 117 118 const lengthOfFloat = primitive.LengthOfInt 119 120 func writeFloat32(val float32) (dest []byte) { 121 dest = make([]byte, lengthOfFloat) 122 binary.BigEndian.PutUint32(dest, math.Float32bits(val)) 123 return 124 } 125 126 func readFloat32(source []byte) (val float32, wasNull bool, err error) { 127 length := len(source) 128 if length == 0 { 129 wasNull = true 130 } else if length != lengthOfFloat { 131 err = errWrongFixedLength(lengthOfFloat, length) 132 } else { 133 val = math.Float32frombits(binary.BigEndian.Uint32(source)) 134 } 135 if err != nil { 136 err = errCannotRead(val, err) 137 } 138 return 139 }