| // Licensed to Apache Software Foundation (ASF) under one or more contributor |
| // license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright |
| // ownership. Apache Software Foundation (ASF) licenses this file to you under |
| // the Apache License, Version 2.0 (the "License"); you may |
| // not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package encoding |
| |
| import ( |
| "encoding/binary" |
| "fmt" |
| |
| "github.com/apache/skywalking-banyandb/pkg/pool" |
| ) |
| |
| // Uint16ToBytes appends the bytes of the given uint16 to the given byte slice. |
| func Uint16ToBytes(dst []byte, u uint16) []byte { |
| return append(dst, byte(u>>8), byte(u)) |
| } |
| |
| // BytesToUint16 converts the first two bytes of the given byte slice to a uint16. |
| func BytesToUint16(src []byte) uint16 { |
| return binary.BigEndian.Uint16(src[:2]) |
| } |
| |
| // Uint32ToBytes appends the bytes of the given uint32 to the given byte slice. |
| func Uint32ToBytes(dst []byte, u uint32) []byte { |
| return append(dst, byte(u>>24), byte(u>>16), byte(u>>8), byte(u)) |
| } |
| |
| // BytesToUint32 converts the first four bytes of the given byte slice to a uint32. |
| func BytesToUint32(src []byte) uint32 { |
| return binary.BigEndian.Uint32(src[:4]) |
| } |
| |
| // Uint64ToBytes appends the bytes of the given uint64 to the given byte slice. |
| func Uint64ToBytes(dst []byte, u uint64) []byte { |
| return append(dst, byte(u>>56), byte(u>>48), byte(u>>40), byte(u>>32), byte(u>>24), byte(u>>16), byte(u>>8), byte(u)) |
| } |
| |
| // BytesToUint64 converts the first eight bytes of the given byte slice to a uint64. |
| func BytesToUint64(src []byte) uint64 { |
| return binary.BigEndian.Uint64(src[:8]) |
| } |
| |
| // Int64ToBytes appends the bytes of the given int64 to the given byte slice. |
| func Int64ToBytes(dst []byte, v int64) []byte { |
| v = (v << 1) ^ (v >> 63) |
| u := uint64(v) |
| return append(dst, byte(u>>56), byte(u>>48), byte(u>>40), byte(u>>32), byte(u>>24), byte(u>>16), byte(u>>8), byte(u)) |
| } |
| |
| // BytesToInt64 converts the first eight bytes of the given byte slice to an int64. |
| func BytesToInt64(src []byte) int64 { |
| u := binary.BigEndian.Uint64(src[:8]) |
| v := int64(u>>1) ^ (int64(u<<63) >> 63) |
| return v |
| } |
| |
| // VarInt64ToBytes appends the bytes of the given int64 to the given byte slice. |
| // It uses variable-length encoding. |
| func VarInt64ToBytes(dst []byte, v int64) []byte { |
| var tmp [1]int64 |
| tmp[0] = v |
| return VarInt64ListToBytes(dst, tmp[:]) |
| } |
| |
| // VarInt64ListToBytes appends the bytes of the given int64s to the given byte slice. |
| // It uses variable-length encoding. |
| func VarInt64ListToBytes(dst []byte, vs []int64) []byte { |
| for _, v := range vs { |
| if v < 0x40 && v > -0x40 { |
| c := int8(v) |
| v := (c << 1) ^ (c >> 7) |
| dst = append(dst, byte(v)) |
| continue |
| } |
| |
| v = (v << 1) ^ (v >> 63) |
| u := uint64(v) |
| for u > 0x7f { |
| dst = append(dst, 0x80|byte(u)) |
| u >>= 7 |
| } |
| dst = append(dst, byte(u)) |
| } |
| return dst |
| } |
| |
| // BytesToVarInt64 converts the first bytes of the given byte slice to an int64. |
| // It uses variable-length encoding. |
| func BytesToVarInt64(src []byte) ([]byte, int64, error) { |
| var tmp [1]int64 |
| tail, err := BytesToVarInt64List(tmp[:], src) |
| return tail, tmp[0], err |
| } |
| |
| // BytesToVarInt64List converts the first bytes of the given byte slice to an int64s. |
| // It uses variable-length encoding. |
| func BytesToVarInt64List(dst []int64, src []byte) ([]byte, error) { |
| idx := uint(0) |
| for i := range dst { |
| // Check if there's enough data to decode |
| if idx >= uint(len(src)) { |
| return nil, fmt.Errorf("cannot decode varint from empty data") |
| } |
| c := src[idx] |
| idx++ |
| // If c < 128, the integer is represented by a single byte |
| if c < 0x80 { |
| v := int8(c>>1) ^ (int8(c<<7) >> 7) |
| dst[i] = int64(v) |
| continue |
| } |
| |
| // If c >= 128, the integer is represented by multiple bytes |
| u := uint64(c & 0x7f) |
| startIdx := idx - 1 |
| shift := uint8(0) |
| for c >= 0x80 { |
| if idx >= uint(len(src)) { |
| return nil, fmt.Errorf("unexpected end of encoded varint at byte %d; src=%x", idx-startIdx, src[startIdx:]) |
| } |
| if idx-startIdx > 9 { |
| return src[idx:], fmt.Errorf("too long encoded varint; the maximum allowed length is 10 bytes; got %d bytes; src=%x", |
| (idx-startIdx)+1, src[startIdx:]) |
| } |
| c = src[idx] |
| idx++ |
| shift += 7 |
| u |= uint64(c&0x7f) << shift |
| } |
| v := int64(u>>1) ^ (int64(u<<63) >> 63) |
| dst[i] = v |
| } |
| return src[idx:], nil |
| } |
| |
| // VarUint64ToBytes appends the bytes of the given uint64 to the given byte slice. |
| // It uses variable-length encoding. |
| func VarUint64ToBytes(dst []byte, u uint64) []byte { |
| if u < (1 << 7) { |
| return append(dst, byte(u)) |
| } |
| if u < (1 << (2 * 7)) { |
| return append(dst, byte(u|0x80), byte(u>>7)) |
| } |
| if u < (1 << (3 * 7)) { |
| return append(dst, byte(u|0x80), byte((u>>7)|0x80), byte(u>>(2*7))) |
| } |
| |
| var tmp [1]uint64 |
| tmp[0] = u |
| return VarUint64sToBytes(dst, tmp[:]) |
| } |
| |
| // VarUint64sToBytes appends the bytes of the given uint64s to the given byte slice. |
| // It uses variable-length encoding. |
| func VarUint64sToBytes(dst []byte, us []uint64) []byte { |
| for _, u := range us { |
| if u < 0x80 { |
| dst = append(dst, byte(u)) |
| continue |
| } |
| for u > 0x7f { |
| dst = append(dst, 0x80|byte(u)) |
| u >>= 7 |
| } |
| dst = append(dst, byte(u)) |
| } |
| return dst |
| } |
| |
| // BytesToVarUint64 converts the first bytes of the given byte slice to a uint64. |
| // It uses variable-length encoding. |
| func BytesToVarUint64(src []byte) ([]byte, uint64) { |
| if len(src) == 0 { |
| return src, 0 |
| } |
| if src[0] < 0x80 { |
| // Fast path for a single byte |
| return src[1:], uint64(src[0]) |
| } |
| if len(src) == 1 { |
| return src, 0 |
| } |
| if src[1] < 0x80 { |
| // Fast path for two bytes |
| return src[2:], uint64(src[0]&0x7f) | uint64(src[1])<<7 |
| } |
| |
| // Slow path for other number of bytes |
| x, o := binary.Uvarint(src) |
| if o <= 0 { |
| return src, 0 |
| } |
| return src[o:], x |
| } |
| |
| // BytesToVarUint64s converts the first bytes of the given byte slice to a uint64s. |
| // It uses variable-length encoding. |
| func BytesToVarUint64s(dst []uint64, src []byte) ([]byte, error) { |
| idx := uint(0) |
| for i := range dst { |
| if idx >= uint(len(src)) { |
| return nil, fmt.Errorf("cannot decode varuint from empty data") |
| } |
| c := src[idx] |
| idx++ |
| if c < 0x80 { |
| dst[i] = uint64(c) |
| continue |
| } |
| |
| u := uint64(c & 0x7f) |
| startIdx := idx - 1 |
| shift := uint8(0) |
| for c >= 0x80 { |
| if idx >= uint(len(src)) { |
| return nil, fmt.Errorf("unexpected end of encoded varint at byte %d; src=%x", idx-startIdx, src[startIdx:]) |
| } |
| if idx-startIdx > 9 { |
| return src[idx:], fmt.Errorf("too long encoded varint; the maximum allowed length is 10 bytes; got %d bytes; src=%x", |
| (idx-startIdx)+1, src[startIdx:]) |
| } |
| c = src[idx] |
| idx++ |
| shift += 7 |
| u |= uint64(c&0x7f) << shift |
| } |
| dst[i] = u |
| } |
| return src[idx:], nil |
| } |
| |
| // GenerateInt64List generates a list of int64 with the given size. |
| // The returned list may be from a pool and should be released after use. |
| func GenerateInt64List(size int) *Int64List { |
| v := int64ListPool.Get() |
| if v == nil { |
| return &Int64List{ |
| L: make([]int64, size), |
| } |
| } |
| is := v |
| if n := size - cap(is.L); n > 0 { |
| is.L = append(is.L[:cap(is.L)], make([]int64, n)...) |
| } |
| is.L = is.L[:size] |
| return is |
| } |
| |
| // ReleaseInt64List releases the given list of int64. |
| // The list may be put into a pool for reuse. |
| func ReleaseInt64List(is *Int64List) { |
| int64ListPool.Put(is) |
| } |
| |
| // Int64List is a list of int64. |
| type Int64List struct { |
| L []int64 |
| } |
| |
| var int64ListPool = pool.Register[*Int64List]("encoding-int64List") |
| |
| // GenerateUint64List generates a list of uint64 with the given size. |
| // The returned list may be from a pool and should be released after use. |
| func GenerateUint64List(size int) *Uint64List { |
| v := uint64ListPool.Get() |
| if v == nil { |
| return &Uint64List{ |
| L: make([]uint64, size), |
| } |
| } |
| is := v |
| if n := size - cap(is.L); n > 0 { |
| is.L = append(is.L[:cap(is.L)], make([]uint64, n)...) |
| } |
| is.L = is.L[:size] |
| return is |
| } |
| |
| // ReleaseUint64List releases the given list of uint64. |
| // The list may be put into a pool for reuse. |
| func ReleaseUint64List(is *Uint64List) { |
| uint64ListPool.Put(is) |
| } |
| |
| // Uint64List is a list of uint64. |
| type Uint64List struct { |
| L []uint64 |
| } |
| |
| var uint64ListPool = pool.Register[*Uint64List]("encoding-uin64List") |