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 }