LZX: Split table into two parts

This commit is contained in:
John Starks
2016-05-12 19:47:52 -07:00
parent d69c9fb04e
commit 69b46ecca8
+79 -52
View File
@@ -18,6 +18,8 @@ const (
lencodecount = 249 lencodecount = 249
lenshift = 9 lenshift = 9
codemask = 0x1ff codemask = 0x1ff
tablebits = 9
tablesize = 1 << tablebits
maxBlockSize = 32768 maxBlockSize = 32768
windowSize = 32768 windowSize = 32768
@@ -141,8 +143,9 @@ func (f *decompressor) getBits(n byte) uint16 {
} }
type huffman struct { type huffman struct {
table []uint16 extra [][]uint16
maxbits byte maxbits byte
table [tablesize]uint16
} }
// buildTable builds a huffman decoding table from a slice of code lengths, // buildTable builds a huffman decoding table from a slice of code lengths,
@@ -179,53 +182,78 @@ func buildTable(codelens []byte) *huffman {
// Build a table for code lookup. For code sizes < max, // Build a table for code lookup. For code sizes < max,
// put all possible suffixes for the code into the table, too. // put all possible suffixes for the code into the table, too.
// Typically a huffman implementation will only do this up to // For max > tablebits, split long codes into additional tables
// a small code length maximum, then fall back to a different // of suffixes of max-tablebits length.
// mechanism; this would probably improve performance. h := &huffman{maxbits: max}
table := make([]uint16, 1<<max) if max > tablebits {
for i, cl := range codelens { core := first[tablebits+1] / 2 // Number of codes that fit without extra tables
if cl != 0 { nextra := 1<<tablebits - core // Number of extra entries
code := first[cl] h.extra = make([][]uint16, nextra)
extendedCode := code << (max - cl) for code := core; code < 1<<tablebits; code++ {
for j := uint(0); j < 1<<(max-cl); j++ { h.table[code] = uint16(code - core)
table[extendedCode+j] = uint16(cl)<<lenshift | uint16(i) h.extra[code-core] = make([]uint16, 1<<(max-tablebits))
}
first[cl]++
} }
} }
return &huffman{ for i, cl := range codelens {
table: table, if cl != 0 {
maxbits: max, code := first[cl]
first[cl]++
v := uint16(cl)<<lenshift | uint16(i)
if cl <= tablebits {
extendedCode := code << (tablebits - cl)
for j := uint(0); j < 1<<(tablebits-cl); j++ {
h.table[extendedCode+j] = v
}
} else {
prefix := code >> (cl - tablebits)
suffix := code & (1<<(cl-tablebits) - 1)
extendedCode := suffix << (max - cl)
for j := uint(0); j < 1<<(max-cl); j++ {
h.extra[h.table[prefix]][extendedCode+j] = v
}
}
}
} }
return h
} }
// getCode retrieves the next code using the provided // getCode retrieves the next code using the provided
// huffman tree. It sets f.err on error. // huffman tree. It sets f.err on error.
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. if f.nbits < maxTreePathLen {
f.fail(errCorrupt) f.feed()
return 0 }
}
if f.nbits < maxTreePathLen { // For codes with length < tablebits, it doesn't matter
f.feed() // what the remainder of the bits used for table lookup
} // are, since entries with all possible suffixes were
// For codes with length < h.maxbits, it doesn't matter // added to the table.
// what the remainder of the bits used for table lookup c := h.table[f.c>>(32-tablebits)]
// are, since entries with all possible suffixes were if c >= 1<<lenshift {
// added to the table. // The code is already in c.
c := h.table[f.c>>(32-h.maxbits)] } else {
n := byte(c >> lenshift) c = h.extra[c][f.c<<tablebits>>(32-(h.maxbits-tablebits))]
if f.nbits < n { }
n := byte(c >> lenshift)
if f.nbits >= n {
// Only consume the length of the code, not the maximum
// code length.
f.c <<= n
f.nbits -= n
return c & codemask
}
f.fail(io.ErrUnexpectedEOF) f.fail(io.ErrUnexpectedEOF)
return 0 return 0
} }
// Only consume the length of the code, not the maximum
// code length. // This is an empty tree. It should not be used.
f.c <<= n f.fail(errCorrupt)
f.nbits -= n return 0
return c & codemask
} }
// mod17 computes the value mod 17. // mod17 computes the value mod 17.
@@ -419,10 +447,11 @@ func (f *decompressor) readTrees(readAligned bool) (main *huffman, length *huffm
// readCompressedBlock decodes a compressed block, writing into the window // readCompressedBlock decodes a compressed block, writing into the window
// starting at start and ending at end, and using the provided huffman trees. // starting at start and ending at end, and using the provided huffman trees.
func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, haligned *huffman) (int, error) { func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, haligned *huffman) (int, error) {
for i := start; i < end; { i := start
for i < end {
main := f.getCode(hmain) main := f.getCode(hmain)
if f.err != nil { if f.err != nil {
return int(i - start), f.err break
} }
if main < 256 { if main < 256 {
// Literal byte. // Literal byte.
@@ -433,16 +462,13 @@ func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, ha
// This is a match backward in the window. Determine // This is a match backward in the window. Determine
// the offset and dlength. // the offset and dlength.
lenheader := (main - 256) % 8 matchlen := (main - 256) % 8
slot := (main - 256) / 8 slot := (main - 256) / 8
// The length is either the low bits of the code, // The length is either the low bits of the code,
// or if this is 7, is encoded with the length tree. // or if this is 7, is encoded with the length tree.
var matchlen uint16 if matchlen == 7 {
if lenheader == 7 { matchlen += f.getCode(hlength)
matchlen = f.getCode(hlength) + 7
} else {
matchlen = lenheader
} }
matchlen += 2 matchlen += 2
@@ -478,16 +504,17 @@ func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, ha
f.lru[0] = matchoffset f.lru[0] = matchoffset
} }
if matchoffset > i || matchlen > end-i { if matchoffset <= i && matchlen <= end-i {
return int(i - start), errCorrupt copyend := i + matchlen
for ; i < copyend; i++ {
f.window[i] = f.window[i-matchoffset]
}
} else {
f.fail(errCorrupt)
break
} }
for j := uint16(0); j < matchlen; j++ {
f.window[i+j] = f.window[i+j-matchoffset]
}
i += matchlen
} }
return int(end - start), nil return int(i - start), f.err
} }
// readBlock decodes the current block and returns the number of uncompressed bytes. // readBlock decodes the current block and returns the number of uncompressed bytes.