github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/utils/bitutils.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package utils 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "math" 25 "strconv" 26 "time" 27 "unsafe" 28 29 jp "github.com/buger/jsonparser" 30 log "github.com/sirupsen/logrus" 31 ) 32 33 const UINT8_MAX = 255 34 const UINT16_MAX = 65_535 35 const UINT32_MAX = 4_294_967_295 36 37 func BoolToBytesLittleEndian(b bool) []byte { 38 if b { 39 return []byte{1} 40 } else { 41 return []byte{0} 42 } 43 } 44 45 func BytesToBoolLittleEndian(bytes []byte) bool { 46 return bytes[0] == []byte{1}[0] 47 } 48 49 func Float64ToBytesLittleEndian(val float64) []byte { 50 buf := new(bytes.Buffer) 51 err := binary.Write(buf, binary.LittleEndian, val) 52 if err != nil { 53 log.Error("binary.Write failed:", err) 54 } 55 return buf.Bytes() 56 } 57 58 func BytesToFloat64LittleEndian(bytes []byte) float64 { 59 bits := binary.LittleEndian.Uint64(bytes) 60 return math.Float64frombits(bits) 61 } 62 63 func Uint64ToBytesLittleEndian(val uint64) []byte { 64 b := make([]byte, 8) 65 binary.LittleEndian.PutUint64(b, val) 66 return b 67 } 68 69 func Uint32ToBytesLittleEndian(val uint32) []byte { 70 b := make([]byte, 4) 71 binary.LittleEndian.PutUint32(b, val) 72 return b 73 } 74 75 func Uint16ToBytesLittleEndian(val uint16) []byte { 76 b := make([]byte, 2) 77 binary.LittleEndian.PutUint16(b, val) 78 return b 79 } 80 81 func BytesToUint64LittleEndian(bytes []byte) uint64 { 82 return binary.LittleEndian.Uint64(bytes) 83 } 84 func BytesToUint32LittleEndian(bytes []byte) uint32 { 85 return binary.LittleEndian.Uint32(bytes) 86 } 87 func BytesToUint16LittleEndian(bytes []byte) uint16 { 88 return binary.LittleEndian.Uint16(bytes) 89 } 90 91 func Int16ToBytesLittleEndian(signedval int16) []byte { 92 buf := new(bytes.Buffer) 93 err := binary.Write(buf, binary.LittleEndian, signedval) 94 if err != nil { 95 log.Errorf("binary.Write failed:%v\n", err) 96 } 97 return buf.Bytes() 98 } 99 100 func Int32ToBytesLittleEndian(signedval int32) []byte { 101 buf := new(bytes.Buffer) 102 err := binary.Write(buf, binary.LittleEndian, signedval) 103 if err != nil { 104 log.Errorf("binary.Write failed:%v\n", err) 105 } 106 return buf.Bytes() 107 } 108 109 func Int64ToBytesLittleEndian(signedval int64) []byte { 110 buf := new(bytes.Buffer) 111 err := binary.Write(buf, binary.LittleEndian, signedval) 112 if err != nil { 113 log.Errorf("binary.Write failed:%v\n", err) 114 } 115 return buf.Bytes() 116 } 117 func BytesToInt64LittleEndian(bytes []byte) int64 { 118 return int64(binary.LittleEndian.Uint64(bytes)) 119 } 120 func BytesToInt32LittleEndian(bytes []byte) int32 { 121 return int32(binary.LittleEndian.Uint32(bytes)) 122 } 123 func BytesToInt16LittleEndian(bytes []byte) int16 { 124 return int16(binary.LittleEndian.Uint16(bytes)) 125 } 126 127 func IsTimeInMilli(tval uint64) bool { 128 if tval >= 99999999999 { 129 return true 130 } else { 131 return false 132 } 133 } 134 135 func UInt64ToStringBytes(val uint64) string { 136 const unit = 1000 137 if val < unit { 138 return fmt.Sprintf("%db", val) 139 } 140 div, exp := int64(unit), 0 141 for n := val / unit; n >= unit; n /= unit { 142 div *= unit 143 exp++ 144 } 145 return fmt.Sprintf("%.1f%cb", float64(val)/float64(div), "kmgtpe"[exp]) 146 } 147 func GetCurrentTimeInMs() uint64 { 148 return uint64(time.Now().UTC().UnixNano()) / uint64(time.Millisecond) 149 } 150 151 // This function will extract the timestamp from the raw body. This will assume the timestamp key exists at the root level 152 func ExtractTimeStamp(raw []byte, timestampKey *string) uint64 { 153 rawVal, dType, _, err := jp.Get(raw, *timestampKey) 154 if err != nil { 155 // timestamp key does not exist in doc 156 return 0 157 } 158 switch dType { 159 case jp.String: 160 tsStr, err := jp.ParseString(rawVal) 161 if err != nil { 162 log.Errorf("Failed to parse timestamp of raw string val: %v. Error: %v", rawVal, err) 163 return 0 164 } 165 ts_millis, err := convertTimestampToMillis(tsStr) 166 if err != nil { 167 ts_millis = GetCurrentTimeInMs() 168 log.Errorf("ExtractTimeStamp: Setting timestamp to current time in milli sec as parsing timestamp failed, err = %v", err) 169 } 170 return ts_millis 171 case jp.Number: 172 var ts_millis uint64 173 val, err := jp.ParseInt(rawVal) 174 if err != nil { 175 val, err := jp.ParseFloat(rawVal) 176 if err != nil { 177 log.Errorf("Failed to parse timestamp of float val: %v. Error: %v", rawVal, err) 178 return 0 179 } 180 ts_millis = uint64(val) 181 } else { 182 ts_millis = uint64(val) 183 } 184 185 if !IsTimeInMilli(ts_millis) { 186 ts_millis *= 1000 187 } 188 return ts_millis 189 default: 190 return 0 191 } 192 } 193 194 func convertTimestampToMillis(value string) (uint64, error) { 195 parsed_value, err := strconv.ParseUint(string(value), 10, 64) 196 if err == nil { 197 if !IsTimeInMilli(parsed_value) { 198 parsed_value *= 1000 199 } 200 return parsed_value, nil 201 } 202 203 timeFormats := []string{"2006-01-02T15:04:05Z", 204 "2006-01-02T15:04:05.999Z", 205 "2006-01-02T15:04:05.999-07:00"} 206 207 for _, timeFormat := range timeFormats { 208 parsed_value, err := time.Parse(timeFormat, value) 209 if err != nil { 210 continue 211 } 212 return uint64(parsed_value.UTC().UnixNano() / 1000000), nil 213 } 214 return 0, errors.New("couldn't find matching time format") 215 } 216 217 func BinarySearchUint16(needle uint16, haystack []uint16) bool { 218 219 low := 0 220 high := len(haystack) - 1 221 222 for low <= high { 223 median := (low + high) / 2 224 225 if haystack[median] < needle { 226 low = median + 1 227 } else { 228 high = median - 1 229 } 230 } 231 232 if low == len(haystack) || haystack[low] != needle { 233 return false 234 } 235 236 return true 237 } 238 239 // todo write a optimized version of this and replace all invocations of this func 240 func SearchStr(needle string, haystack []string) bool { 241 242 for _, h := range haystack { 243 if needle == h { 244 return true 245 } 246 } 247 return false 248 } 249 250 // returns string using unsafe. This is zero-copy and uses unsafe 251 func UnsafeByteSliceToString(haystack []byte) string { 252 return *(*string)(unsafe.Pointer(&haystack)) 253 }