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  }