github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/common/dimval.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  	memCom "github.com/uber/aresdb/memstore/common"
    19  	"github.com/uber/aresdb/utils"
    20  	"strconv"
    21  	"time"
    22  	"unsafe"
    23  )
    24  
    25  // DimCountsPerDimWidth defines dimension counts per dimension width
    26  // 16-byte 8-byte 4-byte 2-byte 1-byte
    27  type DimCountsPerDimWidth [5]uint8
    28  
    29  // ReadDimension reads a dimension value given the index and corresponding data type of node.
    30  // tzRemedy is used to remedy the timezone offset
    31  func ReadDimension(valueStart, nullStart unsafe.Pointer,
    32  	index int, dataType memCom.DataType, enumReverseDict []string, meta *TimeDimensionMeta, cache map[TimeDimensionMeta]map[int64]string) *string {
    33  	isTimeDimension := meta != nil
    34  	// check for nulls
    35  	if *(*uint8)(memAccess(nullStart, index)) == 0 {
    36  		return nil
    37  	}
    38  
    39  	// determine value width in bytes
    40  	valueBytes := memCom.DataTypeBytes(dataType)
    41  	valuePtr := memAccess(valueStart, valueBytes*index)
    42  
    43  	// read intValue; handle float and signed types
    44  	var intValue int64
    45  	var result string
    46  	switch dataType {
    47  	case memCom.Float32:
    48  		// in case time dimension value was converted to float for division
    49  		if isTimeDimension {
    50  			intValue = int64(*(*float32)(valuePtr))
    51  		} else {
    52  			result = strconv.FormatFloat(float64(*(*float32)(valuePtr)), 'g', -1, 32)
    53  			return &result
    54  		}
    55  	case memCom.Int64, memCom.Int32, memCom.Int16, memCom.Int8, memCom.Bool:
    56  		switch valueBytes {
    57  		case 8:
    58  			intValue = int64(*(*int64)(valuePtr))
    59  		case 4:
    60  			intValue = int64(*(*int32)(valuePtr))
    61  		case 2:
    62  			intValue = int64(*(*int16)(valuePtr))
    63  		case 1:
    64  			intValue = int64(*(*int8)(valuePtr))
    65  		}
    66  		result = strconv.FormatInt(intValue, 10)
    67  		return &result
    68  	case memCom.Uint32, memCom.Uint16, memCom.BigEnum, memCom.Uint8, memCom.SmallEnum:
    69  		switch valueBytes {
    70  		case 4:
    71  			intValue = int64(*(*uint32)(valuePtr))
    72  		case 2:
    73  			intValue = int64(*(*uint16)(valuePtr))
    74  		case 1:
    75  			intValue = int64(*(*uint8)(valuePtr))
    76  		}
    77  	case memCom.UUID:
    78  		return formatWithDataValue(valuePtr, memCom.UUID)
    79  	case memCom.GeoPoint:
    80  		return formatWithDataValue(valuePtr, memCom.GeoPoint)
    81  	default:
    82  		// Should never happen.
    83  		return nil
    84  	}
    85  
    86  	// translate enum case back to string for unsigned types
    87  	if intValue >= 0 && intValue < int64(len(enumReverseDict)) {
    88  		result = enumReverseDict[int(intValue)]
    89  	} else if isTimeDimension {
    90  		result = formatTimeDimension(intValue, *meta, cache)
    91  	} else {
    92  		result = strconv.FormatInt(intValue, 10)
    93  	}
    94  
    95  	return &result
    96  }
    97  
    98  // formatWithDataValue formats value with given type
    99  func formatWithDataValue(valuePtr unsafe.Pointer, dataType memCom.DataType) *string {
   100  	formatted := memCom.DataValue{
   101  		Valid:    true,
   102  		DataType: dataType,
   103  		OtherVal: valuePtr,
   104  	}.ConvertToHumanReadable(dataType)
   105  	if formatted == nil {
   106  		return nil
   107  	}
   108  	if result, ok := formatted.(string); !ok {
   109  		return nil
   110  	} else {
   111  		return &result
   112  	}
   113  }
   114  
   115  // GetDimensionStartOffsets calculates the value and null starting position for given dimension inside dimension vector
   116  // dimIndex is the ordered index of given dimension inside the dimension vector
   117  func GetDimensionStartOffsets(numDimsPerDimWidth DimCountsPerDimWidth, dimIndex int, length int) (valueOffset, nullOffset int) {
   118  	startDim := 0
   119  	dimBytes := 1 << uint(len(numDimsPerDimWidth)-1)
   120  	for _, numDim := range numDimsPerDimWidth {
   121  		// found which range this dimension vector belongs to
   122  		if startDim+int(numDim) > dimIndex {
   123  			valueOffset += (dimIndex - startDim) * length * dimBytes
   124  			break
   125  		}
   126  		startDim += int(numDim)
   127  		valueOffset += int(numDim) * length * dimBytes
   128  		// dimBytes /= 2
   129  		dimBytes >>= 1
   130  	}
   131  
   132  	valueBytes := 0
   133  	for index, numDim := range numDimsPerDimWidth {
   134  		valueBytes += (1 << uint(len(numDimsPerDimWidth)-index-1)) * int(numDim)
   135  	}
   136  
   137  	nullOffset = (valueBytes + dimIndex) * length
   138  	return valueOffset, nullOffset
   139  }
   140  
   141  func formatTimeDimension(val int64, meta TimeDimensionMeta, cache map[TimeDimensionMeta]map[int64]string) (result string) {
   142  	// We will not process timeUnit for application/hll because if application/hll holds the raw uint32
   143  	// value. If we convert it to milliseconds, it will overflow.
   144  	if meta.TimeUnit != "" {
   145  		val = utils.AdjustOffset(meta.FromOffset, meta.ToOffset,
   146  			meta.DSTSwitchTs, val)
   147  		switch meta.TimeUnit {
   148  		case "day":
   149  			val /= SecondsPerDay
   150  		case "hour":
   151  			val /= SecondsPerHour
   152  		case "minute":
   153  			val /= SecondsPerMinute
   154  		case "millisecond":
   155  			val *= 1000
   156  		}
   157  		return strconv.FormatInt(val, 10)
   158  	}
   159  	// skip timezone table dims
   160  	// TODO(shz): support timezone table dims
   161  	if !meta.IsTimezoneTable {
   162  		if cacheMap, ok := cache[meta]; ok {
   163  			if result, exists := cacheMap[val]; exists {
   164  				return result
   165  			}
   166  		} else if cache != nil {
   167  			cache[meta] = make(map[int64]string)
   168  		}
   169  	}
   170  
   171  	switch meta.TimeBucketizer {
   172  	case "time of day":
   173  		t := time.Unix(val, 0)
   174  		return t.UTC().Format("15:04")
   175  	case "hour of day":
   176  		t := time.Unix(val-val%3600, 0)
   177  		return t.UTC().Format("15:04")
   178  	case "hour of week":
   179  		t := time.Unix(val+SecondsPer4Day, 0)
   180  		return t.UTC().Format("Monday 15:04")
   181  	case "day of week":
   182  		// 1970-01-01 was a Thursday
   183  		t := time.Unix(((val+4)%7)*SecondsPerDay, 0)
   184  		return t.UTC().Format("Monday")
   185  	default:
   186  		bucket, err := ParseRegularTimeBucketizer(meta.TimeBucketizer)
   187  		if err != nil {
   188  			return strconv.FormatInt(val, 10)
   189  		}
   190  		switch bucket.Unit {
   191  		case "m":
   192  			t := time.Unix(val, 0)
   193  			return t.UTC().Format("2006-01-02 15:04")
   194  		case "h":
   195  			t := time.Unix(val-val%3600, 0)
   196  			return t.UTC().Format("2006-01-02 15:00")
   197  		case "d":
   198  			t := time.Unix(val-val%(24*60*60), 0)
   199  			return t.UTC().Format("2006-01-02")
   200  		}
   201  
   202  	}
   203  
   204  	if !meta.IsTimezoneTable {
   205  		cache[meta][val] = result
   206  	}
   207  	return
   208  }