mirror of
https://github.com/prometheus-community/postgres_exporter
synced 2025-05-01 07:28:12 +00:00
This commit implements a massive refactor of the repository, and moves the build system over to use Mage (magefile.org) which should allow seamless building across multiple platforms.
295 lines
6.0 KiB
Go
295 lines
6.0 KiB
Go
package rardecode
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
mainSize5 = 306
|
|
offsetSize5 = 64
|
|
lowoffsetSize5 = 16
|
|
lengthSize5 = 44
|
|
tableSize5 = mainSize5 + offsetSize5 + lowoffsetSize5 + lengthSize5
|
|
)
|
|
|
|
var (
|
|
errUnknownFilter = errors.New("rardecode: unknown V5 filter")
|
|
errCorruptDecodeHeader = errors.New("rardecode: corrupt decode header")
|
|
)
|
|
|
|
// decoder50 implements the decoder interface for RAR 5 compression.
|
|
// Decode input it broken up into 1 or more blocks. Each block starts with
|
|
// a header containing block length and optional code length tables to initialize
|
|
// the huffman decoders with.
|
|
type decoder50 struct {
|
|
r io.ByteReader
|
|
br bitReader // bit reader for current data block
|
|
codeLength [tableSize5]byte
|
|
|
|
lastBlock bool // current block is last block in compressed file
|
|
|
|
mainDecoder huffmanDecoder
|
|
offsetDecoder huffmanDecoder
|
|
lowoffsetDecoder huffmanDecoder
|
|
lengthDecoder huffmanDecoder
|
|
|
|
offset [4]int
|
|
length int
|
|
}
|
|
|
|
func (d *decoder50) init(r io.ByteReader, reset bool) error {
|
|
d.r = r
|
|
d.lastBlock = false
|
|
|
|
if reset {
|
|
for i := range d.offset {
|
|
d.offset[i] = 0
|
|
}
|
|
d.length = 0
|
|
for i := range d.codeLength {
|
|
d.codeLength[i] = 0
|
|
}
|
|
}
|
|
err := d.readBlockHeader()
|
|
if err == io.EOF {
|
|
return errDecoderOutOfData
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *decoder50) readBlockHeader() error {
|
|
flags, err := d.r.ReadByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bytecount := (flags>>3)&3 + 1
|
|
if bytecount == 4 {
|
|
return errCorruptDecodeHeader
|
|
}
|
|
|
|
hsum, err := d.r.ReadByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
blockBits := int(flags)&0x07 + 1
|
|
blockBytes := 0
|
|
sum := 0x5a ^ flags
|
|
for i := byte(0); i < bytecount; i++ {
|
|
n, err := d.r.ReadByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sum ^= n
|
|
blockBytes |= int(n) << (i * 8)
|
|
}
|
|
if sum != hsum { // bad header checksum
|
|
return errCorruptDecodeHeader
|
|
}
|
|
blockBits += (blockBytes - 1) * 8
|
|
|
|
// create bit reader for block
|
|
d.br = limitBitReader(newRarBitReader(d.r), blockBits, errDecoderOutOfData)
|
|
d.lastBlock = flags&0x40 > 0
|
|
|
|
if flags&0x80 > 0 {
|
|
// read new code length tables and reinitialize huffman decoders
|
|
cl := d.codeLength[:]
|
|
err = readCodeLengthTable(d.br, cl, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.mainDecoder.init(cl[:mainSize5])
|
|
cl = cl[mainSize5:]
|
|
d.offsetDecoder.init(cl[:offsetSize5])
|
|
cl = cl[offsetSize5:]
|
|
d.lowoffsetDecoder.init(cl[:lowoffsetSize5])
|
|
cl = cl[lowoffsetSize5:]
|
|
d.lengthDecoder.init(cl)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func slotToLength(br bitReader, n int) (int, error) {
|
|
if n >= 8 {
|
|
bits := uint(n/4 - 1)
|
|
n = (4 | (n & 3)) << bits
|
|
if bits > 0 {
|
|
b, err := br.readBits(bits)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
n |= b
|
|
}
|
|
}
|
|
n += 2
|
|
return n, nil
|
|
}
|
|
|
|
// readFilter5Data reads an encoded integer used in V5 filters.
|
|
func readFilter5Data(br bitReader) (int, error) {
|
|
// TODO: should data really be uint? (for 32bit ints).
|
|
// It will be masked later anyway by decode window mask.
|
|
bytes, err := br.readBits(2)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
bytes++
|
|
|
|
var data int
|
|
for i := 0; i < bytes; i++ {
|
|
n, err := br.readBits(8)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
data |= n << (uint(i) * 8)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func readFilter(br bitReader) (*filterBlock, error) {
|
|
fb := new(filterBlock)
|
|
var err error
|
|
|
|
fb.offset, err = readFilter5Data(br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fb.length, err = readFilter5Data(br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ftype, err := br.readBits(3)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch ftype {
|
|
case 0:
|
|
n, err := br.readBits(5)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fb.filter = func(buf []byte, offset int64) ([]byte, error) { return filterDelta(n+1, buf) }
|
|
case 1:
|
|
fb.filter = func(buf []byte, offset int64) ([]byte, error) { return filterE8(0xe8, true, buf, offset) }
|
|
case 2:
|
|
fb.filter = func(buf []byte, offset int64) ([]byte, error) { return filterE8(0xe9, true, buf, offset) }
|
|
case 3:
|
|
fb.filter = filterArm
|
|
default:
|
|
return nil, errUnknownFilter
|
|
}
|
|
return fb, nil
|
|
}
|
|
|
|
func (d *decoder50) decodeSym(win *window, sym int) (*filterBlock, error) {
|
|
switch {
|
|
case sym < 256:
|
|
// literal
|
|
win.writeByte(byte(sym))
|
|
return nil, nil
|
|
case sym == 256:
|
|
f, err := readFilter(d.br)
|
|
f.offset += win.buffered()
|
|
return f, err
|
|
case sym == 257:
|
|
// use previous offset and length
|
|
case sym < 262:
|
|
i := sym - 258
|
|
offset := d.offset[i]
|
|
copy(d.offset[1:i+1], d.offset[:i])
|
|
d.offset[0] = offset
|
|
|
|
sl, err := d.lengthDecoder.readSym(d.br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d.length, err = slotToLength(d.br, sl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
length, err := slotToLength(d.br, sym-262)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
offset := 1
|
|
slot, err := d.offsetDecoder.readSym(d.br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if slot < 4 {
|
|
offset += slot
|
|
} else {
|
|
bits := uint(slot/2 - 1)
|
|
offset += (2 | (slot & 1)) << bits
|
|
|
|
if bits >= 4 {
|
|
if bits > 4 {
|
|
n, err := d.br.readBits(bits - 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
offset += n << 4
|
|
}
|
|
n, err := d.lowoffsetDecoder.readSym(d.br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
offset += n
|
|
} else {
|
|
n, err := d.br.readBits(bits)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
offset += n
|
|
}
|
|
}
|
|
if offset > 0x100 {
|
|
length++
|
|
if offset > 0x2000 {
|
|
length++
|
|
if offset > 0x40000 {
|
|
length++
|
|
}
|
|
}
|
|
}
|
|
copy(d.offset[1:], d.offset[:])
|
|
d.offset[0] = offset
|
|
d.length = length
|
|
}
|
|
win.copyBytes(d.length, d.offset[0])
|
|
return nil, nil
|
|
}
|
|
|
|
func (d *decoder50) fill(w *window) ([]*filterBlock, error) {
|
|
var fl []*filterBlock
|
|
|
|
for w.available() > 0 {
|
|
sym, err := d.mainDecoder.readSym(d.br)
|
|
if err == nil {
|
|
var f *filterBlock
|
|
f, err = d.decodeSym(w, sym)
|
|
if f != nil {
|
|
fl = append(fl, f)
|
|
}
|
|
} else if err == io.EOF {
|
|
// reached end of the block
|
|
if d.lastBlock {
|
|
return fl, io.EOF
|
|
}
|
|
err = d.readBlockHeader()
|
|
}
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return fl, errDecoderOutOfData
|
|
}
|
|
return fl, err
|
|
}
|
|
}
|
|
return fl, nil
|
|
}
|