github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/memstore/common/data_value.go (about)

     1  //  Copyright (c) 2017-2018 Uber Technologies, Inc.
     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 common
    16  
    17  import (
    18  	"encoding/hex"
    19  	"fmt"
    20  	"github.com/uber/aresdb/utils"
    21  	"strconv"
    22  	"strings"
    23  	"unsafe"
    24  )
    25  
    26  // NullDataValue is a global data value that stands a null value where the newly added
    27  // columns haven't received any data.
    28  var NullDataValue = DataValue{}
    29  
    30  // SizeOfGeoPoint is the size of GeoPointGo in memory
    31  const SizeOfGeoPoint = unsafe.Sizeof(GeoPointGo{})
    32  
    33  // CompareFunc represents compare function
    34  type CompareFunc func(a, b unsafe.Pointer) int
    35  
    36  // CompareBool compares boolean value
    37  func CompareBool(a, b bool) int {
    38  	if a != b {
    39  		if a {
    40  			return 1
    41  		}
    42  		return -1
    43  	}
    44  	return 0
    45  }
    46  
    47  // CompareInt8 compares int8 value
    48  func CompareInt8(a, b unsafe.Pointer) int {
    49  	return int(*(*int8)(a)) - int(*(*int8)(b))
    50  }
    51  
    52  // CompareUint8 compares uint8 value
    53  func CompareUint8(a, b unsafe.Pointer) int {
    54  	return int(*(*uint8)(a)) - int(*(*uint8)(b))
    55  }
    56  
    57  // CompareInt16 compares int16 value
    58  func CompareInt16(a, b unsafe.Pointer) int {
    59  	return int(*(*int16)(a)) - int(*(*int16)(b))
    60  }
    61  
    62  // CompareUint16 compares uint16 value
    63  func CompareUint16(a, b unsafe.Pointer) int {
    64  	return int(*(*uint16)(a)) - int(*(*uint16)(b))
    65  }
    66  
    67  // CompareInt32 compares int32 value
    68  func CompareInt32(a, b unsafe.Pointer) int {
    69  	return int(*(*int32)(a)) - int(*(*int32)(b))
    70  }
    71  
    72  // CompareUint32 compares uint32 value
    73  func CompareUint32(a, b unsafe.Pointer) int {
    74  	return int(*(*uint32)(a)) - int(*(*uint32)(b))
    75  }
    76  
    77  // CompareInt64 compares int64 value
    78  func CompareInt64(a, b unsafe.Pointer) int {
    79  	return int(*(*int64)(a)) - int(*(*int64)(b))
    80  }
    81  
    82  // CompareFloat32 compares float32 value
    83  func CompareFloat32(a, b unsafe.Pointer) int {
    84  	fa := *(*float32)(a)
    85  	fb := *(*float32)(b)
    86  	if fa < fb {
    87  		return -1
    88  	} else if fa == fb {
    89  		return 0
    90  	} else {
    91  		return 1
    92  	}
    93  }
    94  
    95  // GetCompareFunc get the compare function for specific data type
    96  func GetCompareFunc(dataType DataType) CompareFunc {
    97  	switch dataType {
    98  	case Int8:
    99  		return CompareInt8
   100  	case Uint8, SmallEnum:
   101  		return CompareUint8
   102  	case Int16:
   103  		return CompareInt16
   104  	case Uint16, BigEnum:
   105  		return CompareUint16
   106  	case Int32:
   107  		return CompareInt32
   108  	case Uint32:
   109  		return CompareUint32
   110  	case Int64:
   111  		return CompareInt64
   112  	case Float32:
   113  		return CompareFloat32
   114  	}
   115  	return nil
   116  }
   117  
   118  // GoDataValue represents a value backed in golang memory
   119  type GoDataValue interface {
   120  	// GetBytes returns number of bytes copied in golang memory for this value
   121  	GetBytes() int
   122  	// GetSerBytes return the number of bytes required for serialize this value
   123  	GetSerBytes() int
   124  	Write(writer *utils.StreamDataWriter) error
   125  	Read(reader *utils.StreamDataReader) error
   126  }
   127  
   128  // DataValue is the wrapper to encapsulate validity, bool value and other value type
   129  // into a single struct to make it easier for value comparison.
   130  type DataValue struct {
   131  	// Used for golang vector party
   132  	GoVal    GoDataValue
   133  	OtherVal unsafe.Pointer
   134  	DataType DataType
   135  	CmpFunc  CompareFunc
   136  	Valid    bool
   137  
   138  	IsBool  bool
   139  	BoolVal bool
   140  }
   141  
   142  // GeoPointGo represents GeoPoint Golang Type
   143  type GeoPointGo [2]float32
   144  
   145  // GeoShapeGo represents GeoShape Golang Type
   146  type GeoShapeGo struct {
   147  	Polygons [][]GeoPointGo
   148  }
   149  
   150  // Compare compares two value wrapper.
   151  func (v1 DataValue) Compare(v2 DataValue) int {
   152  	if !v1.Valid || !v2.Valid {
   153  		return CompareBool(v1.Valid, v2.Valid)
   154  	}
   155  	if v1.IsBool {
   156  		return CompareBool(v1.BoolVal, v2.BoolVal)
   157  	}
   158  	if v1.CmpFunc != nil {
   159  		return v1.CmpFunc(v1.OtherVal, v2.OtherVal)
   160  	}
   161  	return 0
   162  }
   163  
   164  // ConvertToHumanReadable convert DataValue to meaningful golang data types
   165  func (v1 DataValue) ConvertToHumanReadable(dataType DataType) interface{} {
   166  	if !v1.Valid {
   167  		return nil
   168  	}
   169  
   170  	if v1.IsBool {
   171  		return v1.BoolVal
   172  	}
   173  
   174  	switch dataType {
   175  	case Int8:
   176  		return *(*int8)(v1.OtherVal)
   177  	case Uint8, SmallEnum:
   178  		return *(*uint8)(v1.OtherVal)
   179  	case Int16:
   180  		return *(*int16)(v1.OtherVal)
   181  	case Uint16, BigEnum:
   182  		return *(*uint16)(v1.OtherVal)
   183  	case Int32:
   184  		return *(*int32)(v1.OtherVal)
   185  	case Uint32:
   186  		return *(*uint32)(v1.OtherVal)
   187  	case Int64:
   188  		return *(*int64)(v1.OtherVal)
   189  	case Float32:
   190  		return *(*float32)(v1.OtherVal)
   191  	case UUID:
   192  		bys := *(*[16]byte)(v1.OtherVal)
   193  		uuidStr := hex.EncodeToString(bys[:])
   194  		if len(uuidStr) == 32 {
   195  			return fmt.Sprintf("%s-%s-%s-%s-%s",
   196  				uuidStr[:8],
   197  				uuidStr[8:12],
   198  				uuidStr[12:16],
   199  				uuidStr[16:20],
   200  				uuidStr[20:])
   201  		}
   202  	case GeoPoint:
   203  		latLngs := *(*[2]float32)(v1.OtherVal)
   204  		// in string format, lng goes first and lat second
   205  		return fmt.Sprintf("Point(%.4f,%.4f)", latLngs[1], latLngs[0])
   206  	case GeoShape:
   207  		shape, ok := (v1.GoVal).(*GeoShapeGo)
   208  		if ok {
   209  			polygons := make([]string, len(shape.Polygons))
   210  			for i, points := range shape.Polygons {
   211  				pointsStrs := make([]string, len(points))
   212  				for j, point := range points {
   213  					// in string format, lng goes first and lat second
   214  					pointsStrs[j] = fmt.Sprintf("%.4f+%.4f", point[1], point[0])
   215  				}
   216  				polygons[i] = fmt.Sprintf("(%s)", strings.Join(pointsStrs, ","))
   217  			}
   218  			return fmt.Sprintf("Polygon(%s)", strings.Join(polygons, ","))
   219  		}
   220  	}
   221  	return nil
   222  }
   223  
   224  // ValueFromString converts raw string value to actual value given input data type.
   225  func ValueFromString(str string, dataType DataType) (val DataValue, err error) {
   226  	val.DataType = dataType
   227  
   228  	if len(str) == 0 || str == "null" {
   229  		return
   230  	}
   231  
   232  	var b bool
   233  	var i int64
   234  	var f float64
   235  	var ui uint64
   236  
   237  	switch dataType {
   238  	case Bool:
   239  		val.IsBool = true
   240  		b, err = strconv.ParseBool(str)
   241  		if err != nil {
   242  			err = utils.StackError(err, "")
   243  			return
   244  		}
   245  		val.Valid = true
   246  		val.BoolVal = b
   247  		return
   248  	case Int8:
   249  		i, err = strconv.ParseInt(str, 10, 8)
   250  		if err != nil {
   251  			err = utils.StackError(err, "")
   252  			return
   253  		}
   254  
   255  		// We need to convert it from i64 to i8 since strconv.ParseXXX
   256  		// always returns the largest bit size value.
   257  		i8 := int8(i)
   258  		val.Valid = true
   259  		val.OtherVal = unsafe.Pointer(&i8)
   260  		return
   261  	case Uint8, SmallEnum:
   262  		ui, err = strconv.ParseUint(str, 10, 8)
   263  		if err != nil {
   264  			err = utils.StackError(err, "")
   265  			return
   266  		}
   267  		ui8 := uint8(ui)
   268  		val.Valid = true
   269  		val.OtherVal = unsafe.Pointer(&ui8)
   270  		return
   271  	case Int16:
   272  		i, err = strconv.ParseInt(str, 10, 16)
   273  		if err != nil {
   274  			err = utils.StackError(err, "")
   275  			return
   276  		}
   277  		i16 := int16(i)
   278  		val.Valid = true
   279  		val.OtherVal = unsafe.Pointer(&i16)
   280  		return
   281  	case Uint16, BigEnum:
   282  		ui, err = strconv.ParseUint(str, 10, 16)
   283  		if err != nil {
   284  			err = utils.StackError(err, "")
   285  			return
   286  		}
   287  		ui16 := uint16(ui)
   288  		val.Valid = true
   289  		val.OtherVal = unsafe.Pointer(&ui16)
   290  		return
   291  	case Int32:
   292  		i, err = strconv.ParseInt(str, 10, 32)
   293  		if err != nil {
   294  			err = utils.StackError(err, "")
   295  			return
   296  		}
   297  		i32 := int32(i)
   298  		val.Valid = true
   299  		val.OtherVal = unsafe.Pointer(&i32)
   300  		return
   301  	case Uint32:
   302  		ui, err = strconv.ParseUint(str, 10, 32)
   303  		if err != nil {
   304  			err = utils.StackError(err, "")
   305  			return
   306  		}
   307  		ui32 := uint32(ui)
   308  		val.Valid = true
   309  		val.OtherVal = unsafe.Pointer(&ui32)
   310  		return
   311  	case Int64:
   312  		i, err = strconv.ParseInt(str, 10, 64)
   313  		if err != nil {
   314  			err = utils.StackError(err, "")
   315  			return
   316  		}
   317  		val.Valid = true
   318  		val.OtherVal = unsafe.Pointer(&i)
   319  		return
   320  	case Float32:
   321  		f, err = strconv.ParseFloat(str, 32)
   322  		if err != nil {
   323  			err = utils.StackError(err, "")
   324  			return
   325  		}
   326  		f32 := float32(f)
   327  		val.Valid = true
   328  		val.OtherVal = unsafe.Pointer(&f32)
   329  		return
   330  	case UUID:
   331  		var uuidBytes []byte
   332  		if strings.HasPrefix(str, "0x") {
   333  			str = str[2:]
   334  		}
   335  		uuidBytes, err = hex.DecodeString(strings.Replace(str, "-", "", -1))
   336  		if err != nil || len(uuidBytes) != 16 {
   337  			err = utils.StackError(err, "Failed to decode uuid string: %s", str)
   338  			return
   339  		}
   340  		val.Valid = true
   341  		val.OtherVal = unsafe.Pointer(&uuidBytes[0])
   342  		return
   343  	case GeoPoint:
   344  		var point [2]float32
   345  		point, err = GeoPointFromString(str)
   346  		if err != nil {
   347  			err = utils.StackError(err, "Failed to read geopoint string: %s", str)
   348  			return
   349  		}
   350  		val.Valid = true
   351  		val.OtherVal = unsafe.Pointer(&point[0])
   352  		return
   353  	default:
   354  		err = utils.StackError(nil, "Unsupported data type value %#x", dataType)
   355  		return
   356  	}
   357  }
   358  
   359  // GetBytes implements GoDataValue interface
   360  func (gs *GeoShapeGo) GetBytes() int {
   361  	numBytes := 0
   362  	for _, polygon := range gs.Polygons {
   363  		numPoints := len(polygon)
   364  		numBytes += numPoints * int(SizeOfGeoPoint)
   365  	}
   366  	return numBytes
   367  }
   368  
   369  // GetSerBytes implements GoDataValue interface
   370  func (gs *GeoShapeGo) GetSerBytes() int {
   371  	totalBytes := 0
   372  	// 1. numPolygons (uint32)
   373  	totalBytes += 4
   374  	for _, polygon := range gs.Polygons {
   375  		numPoints := len(polygon)
   376  		// numPoints (uint32)
   377  		totalBytes += 4
   378  		// 8 bytes per point [2]float32
   379  		totalBytes += numPoints * 8
   380  	}
   381  	return totalBytes
   382  }
   383  
   384  // Read implements Read interface for GoDataValue
   385  func (gs *GeoShapeGo) Read(dataReader *utils.StreamDataReader) error {
   386  	numPolygons, err := dataReader.ReadUint32()
   387  	if err != nil {
   388  		return err
   389  	}
   390  	gs.Polygons = make([][]GeoPointGo, numPolygons)
   391  	for i := 0; i < int(numPolygons); i++ {
   392  		numPoints, err := dataReader.ReadUint32()
   393  		if err != nil {
   394  			return err
   395  		}
   396  		polygon := make([]GeoPointGo, numPoints)
   397  		allBytes := make([]byte, numPoints*8)
   398  		err = dataReader.Read(allBytes)
   399  		if err != nil {
   400  			return err
   401  		}
   402  		offset := 0
   403  		for j := 0; j < int(numPoints); j++ {
   404  			lat := *(*float32)(unsafe.Pointer(&allBytes[offset]))
   405  			lng := *(*float32)(unsafe.Pointer(&allBytes[offset+4]))
   406  			point := GeoPointGo{lat, lng}
   407  			polygon[j] = point
   408  			offset += 8
   409  		}
   410  		gs.Polygons[i] = polygon
   411  	}
   412  	return dataReader.ReadPadding(int(dataReader.GetBytesRead()), 4)
   413  }
   414  
   415  // Write implements Read interface for GoDataValue
   416  func (gs *GeoShapeGo) Write(dataWriter *utils.StreamDataWriter) error {
   417  	numPolygons := len(gs.Polygons)
   418  	err := dataWriter.WriteUint32(uint32(numPolygons))
   419  	if err != nil {
   420  		return err
   421  	}
   422  	for _, polygon := range gs.Polygons {
   423  		numPoints := len(polygon)
   424  		err = dataWriter.WriteUint32(uint32(numPoints))
   425  		if err != nil {
   426  			return err
   427  		}
   428  		for _, point := range polygon {
   429  			err = dataWriter.WriteFloat32(point[0])
   430  			if err != nil {
   431  				return err
   432  			}
   433  			err = dataWriter.WriteFloat32(point[1])
   434  			if err != nil {
   435  				return err
   436  			}
   437  		}
   438  	}
   439  	return dataWriter.WritePadding(int(dataWriter.GetBytesWritten()), 4)
   440  }