You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-08-09 15:42:47 +03: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.
291 lines
6.9 KiB
Go
291 lines
6.9 KiB
Go
package rardecode
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
minWindowSize = 0x40000
|
|
maxQueuedFilters = 8192
|
|
)
|
|
|
|
var (
|
|
errTooManyFilters = errors.New("rardecode: too many filters")
|
|
errInvalidFilter = errors.New("rardecode: invalid filter")
|
|
)
|
|
|
|
// filter functions take a byte slice, the current output offset and
|
|
// returns transformed data.
|
|
type filter func(b []byte, offset int64) ([]byte, error)
|
|
|
|
// filterBlock is a block of data to be processed by a filter.
|
|
type filterBlock struct {
|
|
length int // length of block
|
|
offset int // bytes to be read before start of block
|
|
reset bool // drop all existing queued filters
|
|
filter filter // filter function
|
|
}
|
|
|
|
// decoder is the interface for decoding compressed data
|
|
type decoder interface {
|
|
init(r io.ByteReader, reset bool) error // initialize decoder for current file
|
|
fill(w *window) ([]*filterBlock, error) // fill window with decoded data, returning any filters
|
|
}
|
|
|
|
// window is a sliding window buffer.
|
|
type window struct {
|
|
buf []byte
|
|
mask int // buf length mask
|
|
r int // index in buf for reads (beginning)
|
|
w int // index in buf for writes (end)
|
|
l int // length of bytes to be processed by copyBytes
|
|
o int // offset of bytes to be processed by copyBytes
|
|
}
|
|
|
|
// buffered returns the number of bytes yet to be read from window
|
|
func (w *window) buffered() int { return (w.w - w.r) & w.mask }
|
|
|
|
// available returns the number of bytes that can be written before the window is full
|
|
func (w *window) available() int { return (w.r - w.w - 1) & w.mask }
|
|
|
|
func (w *window) reset(log2size uint, clear bool) {
|
|
size := 1 << log2size
|
|
if size < minWindowSize {
|
|
size = minWindowSize
|
|
}
|
|
if size > len(w.buf) {
|
|
b := make([]byte, size)
|
|
if clear {
|
|
w.w = 0
|
|
} else if len(w.buf) > 0 {
|
|
n := copy(b, w.buf[w.w:])
|
|
n += copy(b[n:], w.buf[:w.w])
|
|
w.w = n
|
|
}
|
|
w.buf = b
|
|
w.mask = size - 1
|
|
} else if clear {
|
|
for i := range w.buf {
|
|
w.buf[i] = 0
|
|
}
|
|
w.w = 0
|
|
}
|
|
w.r = w.w
|
|
}
|
|
|
|
// writeByte writes c to the end of the window
|
|
func (w *window) writeByte(c byte) {
|
|
w.buf[w.w] = c
|
|
w.w = (w.w + 1) & w.mask
|
|
}
|
|
|
|
// copyBytes copies len bytes at off distance from the end
|
|
// to the end of the window.
|
|
func (w *window) copyBytes(len, off int) {
|
|
len &= w.mask
|
|
|
|
n := w.available()
|
|
if len > n {
|
|
// if there is not enough space availaible we copy
|
|
// as much as we can and save the offset and length
|
|
// of the remaining data to be copied later.
|
|
w.l = len - n
|
|
w.o = off
|
|
len = n
|
|
}
|
|
|
|
i := (w.w - off) & w.mask
|
|
for ; len > 0; len-- {
|
|
w.buf[w.w] = w.buf[i]
|
|
w.w = (w.w + 1) & w.mask
|
|
i = (i + 1) & w.mask
|
|
}
|
|
}
|
|
|
|
// read reads bytes from the beginning of the window into p
|
|
func (w *window) read(p []byte) (n int) {
|
|
if w.r > w.w {
|
|
n = copy(p, w.buf[w.r:])
|
|
w.r = (w.r + n) & w.mask
|
|
p = p[n:]
|
|
}
|
|
if w.r < w.w {
|
|
l := copy(p, w.buf[w.r:w.w])
|
|
w.r += l
|
|
n += l
|
|
}
|
|
if w.l > 0 && n > 0 {
|
|
// if we have successfully read data, copy any
|
|
// leftover data from a previous copyBytes.
|
|
l := w.l
|
|
w.l = 0
|
|
w.copyBytes(l, w.o)
|
|
}
|
|
return n
|
|
}
|
|
|
|
// decodeReader implements io.Reader for decoding compressed data in RAR archives.
|
|
type decodeReader struct {
|
|
win window // sliding window buffer used as decode dictionary
|
|
dec decoder // decoder being used to unpack file
|
|
tot int64 // total bytes read
|
|
buf []byte // filter input/output buffer
|
|
outbuf []byte // filter output not yet read
|
|
err error
|
|
filters []*filterBlock // list of filterBlock's, each with offset relative to previous in list
|
|
}
|
|
|
|
func (d *decodeReader) init(r io.ByteReader, dec decoder, winsize uint, reset bool) error {
|
|
if reset {
|
|
d.filters = nil
|
|
}
|
|
d.err = nil
|
|
d.outbuf = nil
|
|
d.tot = 0
|
|
d.win.reset(winsize, reset)
|
|
d.dec = dec
|
|
return d.dec.init(r, reset)
|
|
}
|
|
|
|
func (d *decodeReader) readErr() error {
|
|
err := d.err
|
|
d.err = nil
|
|
return err
|
|
}
|
|
|
|
// queueFilter adds a filterBlock to the end decodeReader's filters.
|
|
func (d *decodeReader) queueFilter(f *filterBlock) error {
|
|
if f.reset {
|
|
d.filters = nil
|
|
}
|
|
if len(d.filters) >= maxQueuedFilters {
|
|
return errTooManyFilters
|
|
}
|
|
// offset & length must be < window size
|
|
f.offset &= d.win.mask
|
|
f.length &= d.win.mask
|
|
// make offset relative to previous filter in list
|
|
for _, fb := range d.filters {
|
|
if f.offset < fb.offset {
|
|
// filter block must not start before previous filter
|
|
return errInvalidFilter
|
|
}
|
|
f.offset -= fb.offset
|
|
}
|
|
d.filters = append(d.filters, f)
|
|
return nil
|
|
}
|
|
|
|
// processFilters processes any filters valid at the current read index
|
|
// and stores the output in outbuf.
|
|
func (d *decodeReader) processFilters() (err error) {
|
|
f := d.filters[0]
|
|
if f.offset > 0 {
|
|
return nil
|
|
}
|
|
d.filters = d.filters[1:]
|
|
if d.win.buffered() < f.length {
|
|
// fill() didn't return enough bytes
|
|
err = d.readErr()
|
|
if err == nil || err == io.EOF {
|
|
return errInvalidFilter
|
|
}
|
|
return err
|
|
}
|
|
|
|
if cap(d.buf) < f.length {
|
|
d.buf = make([]byte, f.length)
|
|
}
|
|
d.outbuf = d.buf[:f.length]
|
|
n := d.win.read(d.outbuf)
|
|
for {
|
|
// run filter passing buffer and total bytes read so far
|
|
d.outbuf, err = f.filter(d.outbuf, d.tot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if cap(d.outbuf) > cap(d.buf) {
|
|
// Filter returned a bigger buffer, save it for future filters.
|
|
d.buf = d.outbuf
|
|
}
|
|
if len(d.filters) == 0 {
|
|
return nil
|
|
}
|
|
f = d.filters[0]
|
|
|
|
if f.offset != 0 {
|
|
// next filter not at current offset
|
|
f.offset -= n
|
|
return nil
|
|
}
|
|
if f.length != len(d.outbuf) {
|
|
return errInvalidFilter
|
|
}
|
|
d.filters = d.filters[1:]
|
|
|
|
if cap(d.outbuf) < cap(d.buf) {
|
|
// Filter returned a smaller buffer. Copy it back to the saved buffer
|
|
// so the next filter can make use of the larger buffer if needed.
|
|
d.outbuf = append(d.buf[:0], d.outbuf...)
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill fills the decodeReader's window
|
|
func (d *decodeReader) fill() {
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
var fl []*filterBlock
|
|
fl, d.err = d.dec.fill(&d.win) // fill window using decoder
|
|
for _, f := range fl {
|
|
err := d.queueFilter(f)
|
|
if err != nil {
|
|
d.err = err
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read decodes data and stores it in p.
|
|
func (d *decodeReader) Read(p []byte) (n int, err error) {
|
|
if len(d.outbuf) == 0 {
|
|
// no filter output, see if we need to create more
|
|
if d.win.buffered() == 0 {
|
|
// fill empty window
|
|
d.fill()
|
|
if d.win.buffered() == 0 {
|
|
return 0, d.readErr()
|
|
}
|
|
} else if len(d.filters) > 0 {
|
|
f := d.filters[0]
|
|
if f.offset == 0 && f.length > d.win.buffered() {
|
|
d.fill() // filter at current offset needs more data
|
|
}
|
|
}
|
|
if len(d.filters) > 0 {
|
|
if err := d.processFilters(); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
}
|
|
if len(d.outbuf) > 0 {
|
|
// copy filter output into p
|
|
n = copy(p, d.outbuf)
|
|
d.outbuf = d.outbuf[n:]
|
|
} else if len(d.filters) > 0 {
|
|
f := d.filters[0]
|
|
if f.offset < len(p) {
|
|
// only read data up to beginning of next filter
|
|
p = p[:f.offset]
|
|
}
|
|
n = d.win.read(p) // read directly from window
|
|
f.offset -= n // adjust first filter offset by bytes just read
|
|
} else {
|
|
n = d.win.read(p) // read directly from window
|
|
}
|
|
d.tot += int64(n)
|
|
return n, nil
|
|
}
|