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  }