| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- // Unless explicitly stated otherwise all files in this repository are licensed
- // under the Apache License 2.0.
- // This product includes software developed at Datadog (https://www.datadoghq.com/).
- // Copyright 2021 Datadog, Inc.
- package encoding
- import (
- "encoding/binary"
- "errors"
- "io"
- "math"
- "math/bits"
- )
- // Encoding functions append bytes to the provided *[]byte, allowing avoiding
- // allocations if the slice initially has a large enough capacity.
- // Decoding functions also take *[]byte as input, and when they do not return an
- // error, advance the slice so that it starts at the immediate byte after the
- // decoded part (or so that it is empty if there is no such byte).
- const (
- MaxVarLen64 = 9
- varfloat64Rotate = 6
- )
- var uvarint64Sizes = initUvarint64Sizes()
- var varfloat64Sizes = initVarfloat64Sizes()
- // EncodeUvarint64 serializes 64-bit unsigned integers 7 bits at a time,
- // starting with the least significant bits. The most significant bit in each
- // output byte is the continuation bit and indicates whether there are
- // additional non-zero bits encoded in following bytes. There are at most 9
- // output bytes and the last one does not have a continuation bit, allowing for
- // it to encode 8 bits (8*7+8 = 64).
- func EncodeUvarint64(b *[]byte, v uint64) {
- for i := 0; i < MaxVarLen64-1; i++ {
- if v < 0x80 {
- break
- }
- *b = append(*b, byte(v)|byte(0x80))
- v >>= 7
- }
- *b = append(*b, byte(v))
- }
- // DecodeUvarint64 deserializes 64-bit unsigned integers that have been encoded
- // using EncodeUvarint64.
- func DecodeUvarint64(b *[]byte) (uint64, error) {
- x := uint64(0)
- s := uint(0)
- for i := 0; ; i++ {
- if len(*b) <= i {
- return 0, io.EOF
- }
- n := (*b)[i]
- if n < 0x80 || i == MaxVarLen64-1 {
- *b = (*b)[i+1:]
- return x | uint64(n)<<s, nil
- }
- x |= uint64(n&0x7F) << s
- s += 7
- }
- }
- // Uvarint64Size returns the number of bytes that EncodeUvarint64 encodes a
- // 64-bit unsigned integer into.
- func Uvarint64Size(v uint64) int {
- return uvarint64Sizes[bits.LeadingZeros64(v)]
- }
- func initUvarint64Sizes() [65]int {
- var sizes [65]int
- b := []byte{}
- for i := 0; i <= 64; i++ {
- b = b[:0]
- EncodeUvarint64(&b, ^uint64(0)>>i)
- sizes[i] = len(b)
- }
- return sizes
- }
- // EncodeVarint64 serializes 64-bit signed integers using zig-zag encoding,
- // which ensures small-scale integers are turned into unsigned integers that
- // have leading zeros, whether they are positive or negative, hence allows for
- // space-efficient varuint encoding of those values.
- func EncodeVarint64(b *[]byte, v int64) {
- EncodeUvarint64(b, uint64(v>>(64-1)^(v<<1)))
- }
- // DecodeVarint64 deserializes 64-bit signed integers that have been encoded
- // using EncodeVarint32.
- func DecodeVarint64(b *[]byte) (int64, error) {
- v, err := DecodeUvarint64(b)
- return int64((v >> 1) ^ -(v & 1)), err
- }
- // Varint64Size returns the number of bytes that EncodeVarint64 encodes a 64-bit
- // signed integer into.
- func Varint64Size(v int64) int {
- return Uvarint64Size(uint64(v>>(64-1) ^ (v << 1)))
- }
- var errVarint32Overflow = errors.New("varint overflows a 32-bit integer")
- // DecodeVarint32 deserializes 32-bit signed integers that have been encoded
- // using EncodeVarint64.
- func DecodeVarint32(b *[]byte) (int32, error) {
- v, err := DecodeVarint64(b)
- if err != nil {
- return 0, err
- }
- if v > math.MaxInt32 || v < math.MinInt32 {
- return 0, errVarint32Overflow
- }
- return int32(v), nil
- }
- // EncodeFloat64LE serializes 64-bit floating-point values, starting with the
- // least significant bytes.
- func EncodeFloat64LE(b *[]byte, v float64) {
- *b = append(*b, make([]byte, 8)...)
- binary.LittleEndian.PutUint64((*b)[len(*b)-8:], math.Float64bits(v))
- }
- // DecodeFloat64LE deserializes 64-bit floating-point values that have been
- // encoded with EncodeFloat64LE.
- func DecodeFloat64LE(b *[]byte) (float64, error) {
- if len(*b) < 8 {
- return 0, io.EOF
- }
- v := math.Float64frombits(binary.LittleEndian.Uint64(*b))
- *b = (*b)[8:]
- return v, nil
- }
- // EncodeVarfloat64 serializes 64-bit floating-point values using a method that
- // is similar to the varuint encoding and that is space-efficient for
- // non-negative integer values. The output takes at most 9 bytes.
- // Input values are first shifted as floating-point values (+1), then transmuted
- // to integer values, then shifted again as integer values (-Float64bits(1)).
- // That is in order to minimize the number of non-zero bits when dealing with
- // non-negative integer values.
- // After that transformation, any input integer value no greater than 2^53 (the
- // largest integer value that can be encoded exactly as a 64-bit floating-point
- // value) will have at least 6 leading zero bits. By rotating bits to the left,
- // those bits end up at the right of the binary representation.
- // The resulting bits are then encoded similarly to the varuint method, but
- // starting with the most significant bits.
- func EncodeVarfloat64(b *[]byte, v float64) {
- x := bits.RotateLeft64(math.Float64bits(v+1)-math.Float64bits(1), varfloat64Rotate)
- for i := 0; i < MaxVarLen64-1; i++ {
- n := byte(x >> (8*8 - 7))
- x <<= 7
- if x == 0 {
- *b = append(*b, n)
- return
- }
- *b = append(*b, n|byte(0x80))
- }
- n := byte(x >> (8 * 7))
- *b = append(*b, n)
- }
- // DecodeVarfloat64 deserializes 64-bit floating-point values that have been
- // encoded with EncodeVarfloat64.
- func DecodeVarfloat64(b *[]byte) (float64, error) {
- x := uint64(0)
- i := int(0)
- s := uint(8*8 - 7)
- for {
- if len(*b) <= i {
- return 0, io.EOF
- }
- n := (*b)[i]
- if i == MaxVarLen64-1 {
- x |= uint64(n)
- break
- }
- if n < 0x80 {
- x |= uint64(n) << s
- break
- }
- x |= uint64(n&0x7F) << s
- i++
- s -= 7
- }
- *b = (*b)[i+1:]
- return math.Float64frombits(bits.RotateLeft64(x, -varfloat64Rotate)+math.Float64bits(1)) - 1, nil
- }
- // Varfloat64Size returns the number of bytes that EncodeVarfloat64 encodes a
- // 64-bit floating-point value into.
- func Varfloat64Size(v float64) int {
- x := bits.RotateLeft64(math.Float64bits(v+1)-math.Float64bits(1), varfloat64Rotate)
- return varfloat64Sizes[bits.TrailingZeros64(x)]
- }
- func initVarfloat64Sizes() [65]int {
- var sizes [65]int
- b := []byte{}
- for i := 0; i <= 64; i++ {
- b = b[:0]
- EncodeVarfloat64(&b, math.Float64frombits(bits.RotateLeft64(^uint64(0)<<i, -varfloat64Rotate)+math.Float64bits(1))-1)
- sizes[i] = len(b)
- }
- return sizes
- }
|