mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-27 22:28:21 +00:00
184 lines
5.8 KiB
Go
184 lines
5.8 KiB
Go
/*
|
|
Copyright 2021 Erigon contributors
|
|
|
|
Licensed 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 recsplit
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io"
|
|
"math/bits"
|
|
"unsafe"
|
|
|
|
"github.com/ledgerwatch/erigon-lib/common/bitutil"
|
|
)
|
|
|
|
// Optimal Golomb-Rice parameters for leaves
|
|
var bijMemo []uint32 = []uint32{0, 0, 0, 1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 21, 22, 23, 25, 26, 28, 29, 30}
|
|
|
|
// GolombRice can build up the golomb-rice encoding of the sequeuce of numbers, as well as read the numbers back from it.
|
|
type GolombRice struct {
|
|
data []uint64 // Present in the builder and in the reader
|
|
bitCount int // Speficic to the builder - number of bits added to the encoding so far
|
|
}
|
|
|
|
// appendUnaryAll adds the unary encoding of specified sequence of numbers to the end of the
|
|
// current encoding
|
|
func (g *GolombRice) appendUnaryAll(unary []uint64) {
|
|
bitInc := 0
|
|
for _, u := range unary {
|
|
// Each number u uses u+1 bits for its unary representation
|
|
bitInc += int(u) + 1
|
|
}
|
|
targetSize := (g.bitCount + bitInc + 63) / 64
|
|
for len(g.data) < targetSize {
|
|
g.data = append(g.data, 0)
|
|
}
|
|
|
|
for _, u := range unary {
|
|
g.bitCount += int(u)
|
|
appendPtr := g.bitCount / 64
|
|
g.data[appendPtr] |= uint64(1) << (g.bitCount & 63)
|
|
g.bitCount++
|
|
}
|
|
}
|
|
|
|
// appendFixed encodes the next value using specified Golomb parameter. Since we are using Golomb-Rice encoding,
|
|
// all Golomb parameters are powers of two. Therefore we input log2 of golomb parameter, rather than golomn paramter itself,
|
|
// for convinience
|
|
func (g *GolombRice) appendFixed(v uint64, log2golomb int) {
|
|
if log2golomb == 0 {
|
|
return
|
|
}
|
|
lowerBits := v & ((uint64(1) << log2golomb) - 1) // Extract the part of the number that will be encoded using truncated binary encoding
|
|
usedBits := g.bitCount & 63 // How many bits of the last element of b.data is used by previous value
|
|
targetSize := (g.bitCount + log2golomb + 63) / 64
|
|
//fmt.Printf("g.bitCount = %d, log2golomb = %d, targetSize = %d\n", g.bitCount, log2golomb, targetSize)
|
|
for len(g.data) < targetSize {
|
|
g.data = append(g.data, 0)
|
|
}
|
|
appendPtr := g.bitCount / 64 // The index in b.data corresponding to the last element used by previous value, or if previous values fits perfectly, the index of the next free element
|
|
curWord := g.data[appendPtr]
|
|
curWord |= lowerBits << usedBits // curWord now contains the new value potentially combined with the part of the previous value
|
|
if usedBits+log2golomb > 64 {
|
|
// New value overflows to the next element
|
|
g.data[appendPtr] = curWord
|
|
appendPtr++
|
|
curWord = lowerBits >> (64 - usedBits) // curWord now contains the part of the new value that overflows
|
|
}
|
|
g.data[appendPtr] = curWord
|
|
g.bitCount += log2golomb
|
|
}
|
|
|
|
// Bits returns currrent number of bits in the compact encoding of the hash function representation
|
|
func (g *GolombRice) Bits() int {
|
|
return g.bitCount
|
|
}
|
|
|
|
func (g *GolombRiceReader) ReadReset(bitPos, unaryOffset int) {
|
|
g.currFixedOffset = bitPos
|
|
unaryPos := bitPos + unaryOffset
|
|
g.currPtrUnary = unaryPos / 64
|
|
g.currWindowUnary = g.data[g.currPtrUnary] >> (unaryPos & 63)
|
|
g.currPtrUnary++
|
|
g.validLowerBitsUnary = 64 - (unaryPos & 63)
|
|
}
|
|
|
|
func (g *GolombRiceReader) SkipSubtree(nodes, fixedLen int) {
|
|
if nodes <= 0 {
|
|
panic("nodes <= 0")
|
|
}
|
|
missing := nodes
|
|
var cnt int
|
|
for cnt = bits.OnesCount64(g.currWindowUnary); cnt < missing; cnt = bits.OnesCount64(g.currWindowUnary) {
|
|
g.currWindowUnary = g.data[g.currPtrUnary]
|
|
g.currPtrUnary++
|
|
missing -= cnt
|
|
g.validLowerBitsUnary = 64
|
|
}
|
|
cnt = bitutil.Select64(g.currWindowUnary, missing-1)
|
|
g.currWindowUnary >>= cnt
|
|
g.currWindowUnary >>= 1
|
|
g.validLowerBitsUnary -= cnt + 1
|
|
|
|
g.currFixedOffset += fixedLen
|
|
}
|
|
|
|
func (g *GolombRiceReader) ReadNext(log2golomb int) uint64 {
|
|
var result uint64
|
|
|
|
if g.currWindowUnary == 0 {
|
|
result += uint64(g.validLowerBitsUnary)
|
|
g.currWindowUnary = g.data[g.currPtrUnary]
|
|
g.currPtrUnary++
|
|
g.validLowerBitsUnary = 64
|
|
for g.currWindowUnary == 0 {
|
|
result += 64
|
|
g.currWindowUnary = g.data[g.currPtrUnary]
|
|
g.currPtrUnary++
|
|
}
|
|
}
|
|
|
|
pos := bits.TrailingZeros64(g.currWindowUnary)
|
|
|
|
g.currWindowUnary >>= pos
|
|
g.currWindowUnary >>= 1
|
|
g.validLowerBitsUnary -= pos + 1
|
|
|
|
result += uint64(pos)
|
|
result <<= log2golomb
|
|
|
|
idx64 := g.currFixedOffset >> 6
|
|
var fixed uint64
|
|
shift := g.currFixedOffset & 63
|
|
fixed = g.data[idx64] >> shift
|
|
if shift+log2golomb > 64 {
|
|
fixed |= g.data[idx64+1] << (64 - shift)
|
|
}
|
|
result |= fixed & ((uint64(1) << log2golomb) - 1)
|
|
g.currFixedOffset += log2golomb
|
|
return result
|
|
}
|
|
|
|
// Data returns the binary representation of the Golomb-Rice code that is built
|
|
func (g *GolombRice) Data() []uint64 {
|
|
return g.data
|
|
}
|
|
|
|
const maxDataSize = 0xFFFFFFFFFFFF
|
|
|
|
// Write outputs the state of golomb rice encoding into a writer, which can be recovered later by Read
|
|
func (g *GolombRice) Write(w io.Writer) error {
|
|
var numBuf [8]byte
|
|
binary.BigEndian.PutUint64(numBuf[:], uint64(len(g.data)))
|
|
if _, e := w.Write(numBuf[:]); e != nil {
|
|
return e
|
|
}
|
|
p := (*[maxDataSize]byte)(unsafe.Pointer(&g.data[0]))
|
|
b := (*p)[:]
|
|
if _, e := w.Write(b[:len(g.data)*8]); e != nil {
|
|
return e
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type GolombRiceReader struct {
|
|
data []uint64 // Present in the builder and in the reader
|
|
currFixedOffset int // Specific to the reader
|
|
currWindowUnary uint64
|
|
currPtrUnary int
|
|
validLowerBitsUnary int
|
|
}
|