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 }