blob: 39de269cea5d0c2a7a3026af9d346bdde0e83ff6 [file]
// 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 (
"bytes"
"fmt"
"math/bits"
)
const maxUniqueValues = 256
// Dictionary is used for dictionary encoding.
type Dictionary struct {
values [][]byte
indices []uint32
tmp []uint32
}
// NewDictionary creates a dictionary.
func NewDictionary() *Dictionary {
return &Dictionary{
values: make([][]byte, 0),
indices: make([]uint32, 0),
tmp: make([]uint32, 0),
}
}
// Reset resets the dictionary.
func (d *Dictionary) Reset() {
d.values = d.values[:0]
d.indices = d.indices[:0]
d.tmp = d.tmp[:0]
}
// Add adds a value to the dictionary.
func (d *Dictionary) Add(value []byte) bool {
for i, v := range d.values {
if valuesEqual(v, value) {
d.indices = append(d.indices, uint32(i))
return true
}
}
if len(d.values) == maxUniqueValues {
return false
}
d.values = append(d.values, value)
index := uint32(len(d.values) - 1)
d.indices = append(d.indices, index)
return true
}
// valuesEqual compares two byte slices, distinguishing nil from empty slice.
func valuesEqual(a, b []byte) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
return bytes.Equal(a, b)
}
// Encode encodes the dictionary.
func (d *Dictionary) Encode(dst []byte) []byte {
dst = VarUint64ToBytes(dst, uint64(len(d.values)))
dst = EncodeBytesBlock(dst, d.values)
re := encodeRLE(d.tmp, d.indices)
be := encodeBitPacking(re)
dst = append(dst, be...)
return dst
}
// Decode decodes the dictionary.
func (d *Dictionary) Decode(dst [][]byte, src []byte, itemsCount uint64) ([][]byte, error) {
src, count := BytesToVarUint64(src)
if count == 0 {
return dst, nil
}
values, src, err := d.decodeBytesBlockWithTail(src, count)
if err != nil {
return nil, err
}
d.values = values
d.tmp, err = decodeBitPacking(d.tmp, src)
if err != nil {
return nil, err
}
d.indices = decodeRLE(d.indices, d.tmp)
if uint64(len(d.indices)) != itemsCount {
return nil, fmt.Errorf("unexpected item counts; got %d; want %d", len(d.indices), itemsCount)
}
for _, index := range d.indices {
dst = append(dst, d.values[index])
}
return dst, nil
}
func (d *Dictionary) decodeBytesBlockWithTail(src []byte, itemsCount uint64) ([][]byte, []byte, error) {
u64List := GenerateUint64List(0)
defer ReleaseUint64List(u64List)
var tail []byte
var err error
u64List.L, tail, err = DecodeUint64Block(u64List.L[:0], src, itemsCount)
if err != nil {
return nil, nil, fmt.Errorf("cannot decode string lengths: %w", err)
}
aLens := u64List.L
src = tail
var decompressedData []byte
decompressedData, tail, err = decompressBlock(nil, src)
if err != nil {
return nil, nil, fmt.Errorf("cannot decode bytes block with strings: %w", err)
}
dst := d.values[:0]
data := decompressedData
for _, sLen := range aLens {
if sLen == 0 {
dst = append(dst, nil)
continue
}
// Reverse the offset
actualLen := sLen - 1
if uint64(len(data)) < actualLen {
return nil, nil, fmt.Errorf("cannot decode a string with the length %d bytes from %d bytes", actualLen, len(data))
}
if actualLen == 0 {
// Explicitly create non-nil empty slice for empty strings
dst = append(dst, []byte{})
} else {
dst = append(dst, data[:actualLen])
}
data = data[actualLen:]
}
return dst, tail, nil
}
func encodeRLE(dst []uint32, src []uint32) []uint32 {
if len(src) == 0 {
return nil
}
currentValue := src[0]
count := uint32(1)
for i := 1; i < len(src); i++ {
if src[i] == currentValue {
count++
} else {
dst = append(dst, currentValue, count)
currentValue = src[i]
count = 1
}
}
dst = append(dst, currentValue, count)
return dst
}
func decodeRLE(dst []uint32, src []uint32) []uint32 {
if len(src) == 0 {
return nil
}
for i := 0; i < len(src); i += 2 {
value := src[i]
count := src[i+1]
for j := uint32(1); j <= count; j++ {
dst = append(dst, value)
}
}
return dst
}
type bitPackingEncoder struct {
bw *Writer
}
func newBitPackingEncoder(bw *Writer) *bitPackingEncoder {
return &bitPackingEncoder{
bw: bw,
}
}
func (bpe *bitPackingEncoder) encode(src []uint32) {
if len(src) == 0 {
bpe.bw.WriteBits(0, 32)
return
}
bpe.bw.WriteBits(uint64(len(src)), 32)
maxValue := uint32(0)
for _, v := range src {
if v > maxValue {
maxValue = v
}
}
bitsWidth := 1
if maxValue > 0 {
bitsWidth = bits.Len32(maxValue)
}
bpe.bw.WriteBits(uint64(bitsWidth), 8)
for _, v := range src {
bpe.bw.WriteBits(uint64(v), bitsWidth)
}
}
type bitPackingDecoder struct {
br *Reader
}
func newBitPackingDecoder(br *Reader) *bitPackingDecoder {
return &bitPackingDecoder{
br: br,
}
}
func (bpd *bitPackingDecoder) decode(dst []uint32) ([]uint32, error) {
length, err := bpd.br.ReadBits(32)
if err != nil {
return nil, err
}
if length == 0 {
return []uint32{}, nil
}
bitsWidth, err := bpd.br.ReadBits(8)
if err != nil {
return nil, err
}
for i := uint64(0); i < length; i++ {
value, err := bpd.br.ReadBits(int(bitsWidth))
if err != nil {
return nil, err
}
dst = append(dst, uint32(value))
}
return dst, nil
}
func encodeBitPacking(src []uint32) []byte {
bw := NewWriter()
var buf bytes.Buffer
bw.Reset(&buf)
encoder := newBitPackingEncoder(bw)
encoder.encode(src)
bw.Flush()
return buf.Bytes()
}
func decodeBitPacking(dst []uint32, src []byte) ([]uint32, error) {
reader := bytes.NewReader(src)
br := NewReader(reader)
decoder := newBitPackingDecoder(br)
return decoder.decode(dst)
}
// DecodeDictionaryValues extracts only the dictionary values without decoding indices.
func DecodeDictionaryValues(src []byte) ([][]byte, error) {
if len(src) == 0 {
return nil, nil
}
src, count := BytesToVarUint64(src)
if count == 0 {
return nil, nil
}
// Decode only the values part, skip the indices
u64List := GenerateUint64List(0)
defer ReleaseUint64List(u64List)
var tail []byte
var err error
u64List.L, tail, err = DecodeUint64Block(u64List.L[:0], src, count)
if err != nil {
return nil, fmt.Errorf("cannot decode string lengths: %w", err)
}
aLens := u64List.L
src = tail
var decompressedData []byte
decompressedData, _, err = decompressBlock(nil, src)
if err != nil {
return nil, fmt.Errorf("cannot decode bytes block with strings: %w", err)
}
var values [][]byte
data := decompressedData
for _, sLen := range aLens {
if sLen == 0 {
values = append(values, nil)
continue
}
// Reverse the offset
actualLen := sLen - 1
if uint64(len(data)) < actualLen {
return nil, fmt.Errorf("cannot decode a string with the length %d bytes from %d bytes", actualLen, len(data))
}
if actualLen == 0 {
// Explicitly create non-nil empty slice for empty strings
values = append(values, []byte{})
} else {
values = append(values, data[:actualLen])
}
data = data[actualLen:]
}
return values, nil
}