pkg/encoding/int_list.go (144 lines of code) (raw):

// 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 ( "fmt" "github.com/apache/skywalking-banyandb/pkg/logger" ) // Int64ListToBytes encodes a list of int64 into bytes. func Int64ListToBytes(dst []byte, a []int64) (result []byte, mt EncodeType, firstValue int64) { if len(a) == 0 { logger.Panicf("a must contain at least one item") } if isConst(a) { firstValue = a[0] return dst, EncodeTypeConst, firstValue } isDelta, isDeltaConst := isDelta(a) if isDeltaConst { firstValue = a[0] dst = VarInt64ToBytes(dst, a[1]-a[0]) return dst, EncodeTypeDeltaConst, firstValue } if isDelta { dst, firstValue = int64sDeltaOfDeltaToBytes(dst, a) return dst, EncodeTypeDeltaOfDelta, firstValue } if isIncremental(a) { mt = EncodeTypeDeltaOfDelta dst, firstValue = int64sDeltaOfDeltaToBytes(dst, a) return dst, mt, firstValue } mt = EncodeTypeDelta dst, firstValue = int64ListDeltaToBytes(dst, a) return dst, mt, firstValue } // BytesToInt64List decodes bytes into a list of int64. func BytesToInt64List(dst []int64, src []byte, mt EncodeType, firstValue int64, itemsCount int) ([]int64, error) { dst = ExtendListCapacity(dst, itemsCount) var err error switch mt { case EncodeTypeDelta: dst, err = bytesDeltaToInt64List(dst, src, firstValue, itemsCount) if err != nil { return nil, fmt.Errorf("cannot decode nearest delta data: %w", err) } return dst, nil case EncodeTypeDeltaOfDelta: dst, err = bytesDeltaOfDeltaToInt64s(dst, src, firstValue, itemsCount) if err != nil { return nil, fmt.Errorf("cannot decode nearest delta2 data: %w", err) } return dst, nil case EncodeTypeConst: if len(src) > 0 { return nil, fmt.Errorf("unexpected data left in const encoding: %d bytes", len(src)) } for itemsCount > 0 { dst = append(dst, firstValue) itemsCount-- } return dst, nil case EncodeTypeDeltaConst: v := firstValue tail, d, err := BytesToVarInt64(src) if err != nil { return nil, fmt.Errorf("cannot decode delta value for delta const: %w", err) } if len(tail) > 0 { return nil, fmt.Errorf("unexpected trailing data after delta const (d=%d): %d bytes", d, len(tail)) } for itemsCount > 0 { dst = append(dst, v) itemsCount-- v += d } return dst, nil default: return nil, fmt.Errorf("unknown EncodeType=%d", mt) } } // ExtendListCapacity extends the capacity of the given list. func ExtendListCapacity[T any](dst []T, additionalItems int) []T { dstLen := len(dst) if n := dstLen + additionalItems - cap(dst); n > 0 { dst = append(dst[:cap(dst)], make([]T, n)...) } return dst[:dstLen] } func isConst(a []int64) bool { if len(a) == 0 { return false } v1 := a[0] for _, v := range a { if v != v1 { return false } } return true } func isDelta(a []int64) (bool, bool) { if len(a) < 2 { return false, false } ct := true d1 := a[1] - a[0] asc := getSignBit(d1) prev := a[1] for _, next := range a[2:] { d := next - prev if (getSignBit(d) ^ asc) == 1 { return false, false } if ct && d != d1 { ct = false } prev = next } return true, ct } func getSignBit(n int64) int64 { return n >> 63 & 1 } func isIncremental(a []int64) bool { if len(a) < 2 { return false } resets := 0 vPrev := a[0] if vPrev < 0 { return true } for _, v := range a[1:] { if v < vPrev { if v < 0 { return false } if v > (vPrev >> 3) { // Decremental data is found. return false } resets++ } vPrev = v } if resets <= 2 { return true } // Assume an incremental list has less than len(a)/8 resets . return resets < (len(a) >> 3) }