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  }