mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-09-06 09:07:18 -04:00
LZX: Perform buffering internally
This commit is contained in:
+68
-34
@@ -6,7 +6,6 @@
|
|||||||
package lzx
|
package lzx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -60,7 +59,7 @@ type Reader interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type decompressor struct {
|
type decompressor struct {
|
||||||
r Reader
|
r io.Reader
|
||||||
err error
|
err error
|
||||||
unaligned bool
|
unaligned bool
|
||||||
nbits byte
|
nbits byte
|
||||||
@@ -71,28 +70,59 @@ type decompressor struct {
|
|||||||
mainlens [maincodecount]byte
|
mainlens [maincodecount]byte
|
||||||
lenlens [lencodecount]byte
|
lenlens [lencodecount]byte
|
||||||
window [windowSize]byte
|
window [windowSize]byte
|
||||||
|
b []byte
|
||||||
|
bv int
|
||||||
|
bo int
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func (f *decompressor) fail(err error) {
|
||||||
|
if f.err == nil {
|
||||||
|
f.err = err
|
||||||
|
}
|
||||||
|
f.bo = 0
|
||||||
|
f.bv = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) ensureAtLeast(n int) error {
|
||||||
|
if f.bv-f.bo >= n {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.err != nil {
|
||||||
|
return f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.bv != f.bo {
|
||||||
|
copy(f.b[:f.bv-f.bo], f.b[f.bo:f.bv])
|
||||||
|
}
|
||||||
|
n, err := io.ReadAtLeast(f.r, f.b[f.bv-f.bo:], n)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else {
|
||||||
|
f.fail(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.bv = f.bv - f.bo + n
|
||||||
|
f.bo = 0
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// feed retrieves another 16-bit word from the stream and consumes
|
// feed retrieves another 16-bit word from the stream and consumes
|
||||||
// it into f.c. It returns false if there are no more bytes available.
|
// it into f.c. It returns false if there are no more bytes available.
|
||||||
// Otherwise, on error, it sets f.err.
|
// Otherwise, on error, it sets f.err.
|
||||||
func (f *decompressor) feed() bool {
|
func (f *decompressor) feed() bool {
|
||||||
if f.err != nil {
|
err := f.ensureAtLeast(2)
|
||||||
return true
|
|
||||||
}
|
|
||||||
var b0, b1 byte
|
|
||||||
b0, err := f.r.ReadByte()
|
|
||||||
if err == nil {
|
|
||||||
b1, err = f.r.ReadByte()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.ErrUnexpectedEOF {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
f.err = err
|
|
||||||
}
|
}
|
||||||
f.c |= (uint32(b1)<<8 | uint32(b0)) << (16 - f.nbits)
|
f.c |= (uint32(f.b[f.bo+1])<<8 | uint32(f.b[f.bo])) << (16 - f.nbits)
|
||||||
f.nbits += 16
|
f.nbits += 16
|
||||||
|
f.bo += 2
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +131,7 @@ func (f *decompressor) feed() bool {
|
|||||||
func (f *decompressor) getBits(n byte) uint16 {
|
func (f *decompressor) getBits(n byte) uint16 {
|
||||||
if f.nbits < n {
|
if f.nbits < n {
|
||||||
if !f.feed() {
|
if !f.feed() {
|
||||||
f.err = io.ErrUnexpectedEOF
|
f.fail(io.ErrUnexpectedEOF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := uint16(f.c >> (32 - n))
|
c := uint16(f.c >> (32 - n))
|
||||||
@@ -175,7 +205,7 @@ func buildTable(codelens []byte) *huffman {
|
|||||||
func (f *decompressor) getCode(h *huffman) uint16 {
|
func (f *decompressor) getCode(h *huffman) uint16 {
|
||||||
if h.maxbits == 0 {
|
if h.maxbits == 0 {
|
||||||
// This is an empty tree. It should not be used.
|
// This is an empty tree. It should not be used.
|
||||||
f.err = errCorrupt
|
f.fail(errCorrupt)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if f.nbits < maxTreePathLen {
|
if f.nbits < maxTreePathLen {
|
||||||
@@ -188,7 +218,7 @@ func (f *decompressor) getCode(h *huffman) uint16 {
|
|||||||
c := h.table[f.c>>(32-h.maxbits)]
|
c := h.table[f.c>>(32-h.maxbits)]
|
||||||
n := byte(c >> lenshift)
|
n := byte(c >> lenshift)
|
||||||
if f.nbits < n {
|
if f.nbits < n {
|
||||||
f.err = io.ErrUnexpectedEOF
|
f.fail(io.ErrUnexpectedEOF)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
// Only consume the length of the code, not the maximum
|
// Only consume the length of the code, not the maximum
|
||||||
@@ -279,13 +309,11 @@ func (f *decompressor) readBlockHeader() (byte, uint16, error) {
|
|||||||
// If the previous block was an unaligned uncompressed block, restore
|
// If the previous block was an unaligned uncompressed block, restore
|
||||||
// 2-byte alignment.
|
// 2-byte alignment.
|
||||||
if f.unaligned {
|
if f.unaligned {
|
||||||
_, err := f.r.ReadByte()
|
err := f.ensureAtLeast(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
f.bo++
|
||||||
f.unaligned = false
|
f.unaligned = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,19 +349,17 @@ func (f *decompressor) readBlockHeader() (byte, uint16, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.getBits(n)
|
f.getBits(n)
|
||||||
if f.err != nil {
|
|
||||||
return 0, 0, f.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the LRU values for the next block.
|
// Read the LRU values for the next block.
|
||||||
var lru [12]byte
|
err := f.ensureAtLeast(12)
|
||||||
_, err := io.ReadFull(f.r, lru[:])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
f.lru[0] = uint16(binary.LittleEndian.Uint32(lru[0:4]))
|
|
||||||
f.lru[1] = uint16(binary.LittleEndian.Uint32(lru[4:8]))
|
f.lru[0] = uint16(binary.LittleEndian.Uint32(f.b[f.bo : f.bo+4]))
|
||||||
f.lru[2] = uint16(binary.LittleEndian.Uint32(lru[8:12]))
|
f.lru[1] = uint16(binary.LittleEndian.Uint32(f.b[f.bo+4 : f.bo+8]))
|
||||||
|
f.lru[2] = uint16(binary.LittleEndian.Uint32(f.b[f.bo+8 : f.bo+12]))
|
||||||
|
f.bo += 12
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0, 0, errCorrupt
|
return 0, 0, errCorrupt
|
||||||
@@ -476,7 +502,18 @@ func (f *decompressor) readBlock(start uint16) (int, error) {
|
|||||||
// Remember to realign the byte stream at the next block.
|
// Remember to realign the byte stream at the next block.
|
||||||
f.unaligned = true
|
f.unaligned = true
|
||||||
}
|
}
|
||||||
return io.ReadFull(f.r, f.window[start:start+size])
|
copied := 0
|
||||||
|
if f.bo < f.bv {
|
||||||
|
copied = int(size)
|
||||||
|
s := int(start)
|
||||||
|
if copied > f.bv-f.bo {
|
||||||
|
copied = f.bv - f.bo
|
||||||
|
}
|
||||||
|
copy(f.window[s:s+copied], f.b[f.bo:f.bo+copied])
|
||||||
|
f.bo += copied
|
||||||
|
}
|
||||||
|
n, err := io.ReadFull(f.r, f.window[start+uint16(copied):start+size])
|
||||||
|
return copied + n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hmain, hlength, haligned, err := f.readTrees(blockType == alignedOffsetBlock)
|
hmain, hlength, haligned, err := f.readTrees(blockType == alignedOffsetBlock)
|
||||||
@@ -543,11 +580,8 @@ func NewReader(r io.Reader, uncompressedSize int) (io.ReadCloser, error) {
|
|||||||
f := &decompressor{
|
f := &decompressor{
|
||||||
lru: [3]uint16{1, 1, 1},
|
lru: [3]uint16{1, 1, 1},
|
||||||
uncompressed: uncompressedSize,
|
uncompressed: uncompressedSize,
|
||||||
}
|
b: make([]byte, 4096),
|
||||||
if br, ok := r.(Reader); ok {
|
r: r,
|
||||||
f.r = br
|
|
||||||
} else {
|
|
||||||
f.r = bufio.NewReader(r)
|
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user