Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
6.9 KiB
290 lines
6.9 KiB
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 |
|
}
|
|
|