blob: 18bb353d22b88d40006077eb5542584c72105be2 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flate
import (
"io"
"math"
"os"
)
const (
NoCompression = 0
BestSpeed = 1
fastCompression = 3
BestCompression = 9
DefaultCompression = -1
logMaxOffsetSize = 15 // Standard DEFLATE
wideLogMaxOffsetSize = 22 // Wide DEFLATE
minMatchLength = 3 // The smallest match that the deflater looks for
maxMatchLength = 258 // The longest match for the deflater
minOffsetSize = 1 // The shortest offset that makes any sence
// The maximum number of tokens we put into a single flat block, just too
// stop things from getting too large.
maxFlateBlockTokens = 1 << 14
maxStoreBlockSize = 65535
hashBits = 15
hashSize = 1 << hashBits
hashMask = (1 << hashBits) - 1
hashShift = (hashBits + minMatchLength - 1) / minMatchLength
)
type syncPipeReader struct {
*io.PipeReader
closeChan chan bool
}
func (sr *syncPipeReader) CloseWithError(err os.Error) os.Error {
retErr := sr.PipeReader.CloseWithError(err)
sr.closeChan <- true // finish writer close
return retErr
}
type syncPipeWriter struct {
*io.PipeWriter
closeChan chan bool
}
type compressionLevel struct {
good, lazy, nice, chain, fastSkipHashing int
}
var levels = []compressionLevel{
compressionLevel{}, // 0
// For levels 1-3 we don't bother trying with lazy matches
compressionLevel{3, 0, 8, 4, 4},
compressionLevel{3, 0, 16, 8, 5},
compressionLevel{3, 0, 32, 32, 6},
// Levels 4-9 use increasingly more lazy matching
// and increasingly stringent conditions for "good enough".
compressionLevel{4, 4, 16, 16, math.MaxInt32},
compressionLevel{8, 16, 32, 32, math.MaxInt32},
compressionLevel{8, 16, 128, 128, math.MaxInt32},
compressionLevel{8, 32, 128, 256, math.MaxInt32},
compressionLevel{32, 128, 258, 1024, math.MaxInt32},
compressionLevel{32, 258, 258, 4096, math.MaxInt32},
}
func (sw *syncPipeWriter) Close() os.Error {
err := sw.PipeWriter.Close()
<-sw.closeChan // wait for reader close
return err
}
func syncPipe() (*syncPipeReader, *syncPipeWriter) {
r, w := io.Pipe()
sr := &syncPipeReader{r, make(chan bool, 1)}
sw := &syncPipeWriter{w, sr.closeChan}
return sr, sw
}
type deflater struct {
level int
logWindowSize uint
w *huffmanBitWriter
r io.Reader
// (1 << logWindowSize) - 1.
windowMask int
// hashHead[hashValue] contains the largest inputIndex with the specified hash value
hashHead []int
// If hashHead[hashValue] is within the current window, then
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
// with the same hash value.
hashPrev []int
// If we find a match of length >= niceMatch, then we don't bother searching
// any further.
niceMatch int
// If we find a match of length >= goodMatch, we only do a half-hearted
// effort at doing lazy matching starting at the next character
goodMatch int
// The maximum number of chains we look at when finding a match
maxChainLength int
// The sliding window we use for matching
window []byte
// The index just past the last valid character
windowEnd int
// index in "window" at which current block starts
blockStart int
}
func (d *deflater) flush() os.Error {
d.w.flush()
return d.w.err
}
func (d *deflater) fillWindow(index int) (int, os.Error) {
wSize := d.windowMask + 1
if index >= wSize+wSize-(minMatchLength+maxMatchLength) {
// shift the window by wSize
copy(d.window, d.window[wSize:2*wSize])
index -= wSize
d.windowEnd -= wSize
if d.blockStart >= wSize {
d.blockStart -= wSize
} else {
d.blockStart = math.MaxInt32
}
for i, h := range d.hashHead {
d.hashHead[i] = max(h-wSize, -1)
}
for i, h := range d.hashPrev {
d.hashPrev[i] = max(h-wSize, -1)
}
}
var count int
var err os.Error
count, err = io.ReadAtLeast(d.r, d.window[d.windowEnd:], 1)
d.windowEnd += count
if err == os.EOF {
return index, nil
}
return index, err
}
func (d *deflater) writeBlock(tokens []token, index int, eof bool) os.Error {
if index > 0 || eof {
var window []byte
if d.blockStart <= index {
window = d.window[d.blockStart:index]
}
d.blockStart = index
d.w.writeBlock(tokens, eof, window)
return d.w.err
}
return nil
}
// Try to find a match starting at index whose length is greater than prevSize.
// We only look at chainCount possibilities before giving up.
func (d *deflater) findMatch(pos int, prevHead int, prevLength int, lookahead int) (length, offset int, ok bool) {
win := d.window[0 : pos+min(maxMatchLength, lookahead)]
// We quit when we get a match that's at least nice long
nice := min(d.niceMatch, len(win)-pos)
// If we've got a match that's good enough, only look in 1/4 the chain.
tries := d.maxChainLength
length = prevLength
if length >= d.goodMatch {
tries >>= 2
}
w0 := win[pos]
w1 := win[pos+1]
wEnd := win[pos+length]
minIndex := pos - (d.windowMask + 1)
for i := prevHead; tries > 0; tries-- {
if w0 == win[i] && w1 == win[i+1] && wEnd == win[i+length] {
// The hash function ensures that if win[i] and win[i+1] match, win[i+2] matches
n := 3
for pos+n < len(win) && win[i+n] == win[pos+n] {
n++
}
if n > length && (n > 3 || pos-i <= 4096) {
length = n
offset = pos - i
ok = true
if n >= nice {
// The match is good enough that we don't try to find a better one.
break
}
wEnd = win[pos+n]
}
}
if i == minIndex {
// hashPrev[i & windowMask] has already been overwritten, so stop now.
break
}
if i = d.hashPrev[i&d.windowMask]; i < minIndex || i < 0 {
break
}
}
return
}
func (d *deflater) writeStoredBlock(buf []byte) os.Error {
if d.w.writeStoredHeader(len(buf), false); d.w.err != nil {
return d.w.err
}
d.w.writeBytes(buf)
return d.w.err
}
func (d *deflater) storedDeflate() os.Error {
buf := make([]byte, maxStoreBlockSize)
for {
n, err := d.r.Read(buf)
if n > 0 {
if err := d.writeStoredBlock(buf[0:n]); err != nil {
return err
}
}
if err != nil {
if err == os.EOF {
break
}
return err
}
}
return nil
}
func (d *deflater) doDeflate() (err os.Error) {
// init
d.windowMask = 1<<d.logWindowSize - 1
d.hashHead = make([]int, hashSize)
d.hashPrev = make([]int, 1<<d.logWindowSize)
d.window = make([]byte, 2<<d.logWindowSize)
fillInts(d.hashHead, -1)
tokens := make([]token, maxFlateBlockTokens, maxFlateBlockTokens+1)
l := levels[d.level]
d.goodMatch = l.good
d.niceMatch = l.nice
d.maxChainLength = l.chain
lazyMatch := l.lazy
length := minMatchLength - 1
offset := 0
byteAvailable := false
isFastDeflate := l.fastSkipHashing != 0
index := 0
// run
if index, err = d.fillWindow(index); err != nil {
return
}
maxOffset := d.windowMask + 1 // (1 << logWindowSize);
// only need to change when you refill the window
windowEnd := d.windowEnd
maxInsertIndex := windowEnd - (minMatchLength - 1)
ti := 0
hash := int(0)
if index < maxInsertIndex {
hash = int(d.window[index])<<hashShift + int(d.window[index+1])
}
chainHead := -1
for {
if index > windowEnd {
panic("index > windowEnd")
}
lookahead := windowEnd - index
if lookahead < minMatchLength+maxMatchLength {
if index, err = d.fillWindow(index); err != nil {
return
}
windowEnd = d.windowEnd
if index > windowEnd {
panic("index > windowEnd")
}
maxInsertIndex = windowEnd - (minMatchLength - 1)
lookahead = windowEnd - index
if lookahead == 0 {
break
}
}
if index < maxInsertIndex {
// Update the hash
hash = (hash<<hashShift + int(d.window[index+2])) & hashMask
chainHead = d.hashHead[hash]
d.hashPrev[index&d.windowMask] = chainHead
d.hashHead[hash] = index
}
prevLength := length
prevOffset := offset
minIndex := max(index-maxOffset, 0)
length = minMatchLength - 1
offset = 0
if chainHead >= minIndex &&
(isFastDeflate && lookahead > minMatchLength-1 ||
!isFastDeflate && lookahead > prevLength && prevLength < lazyMatch) {
if newLength, newOffset, ok := d.findMatch(index, chainHead, minMatchLength-1, lookahead); ok {
length = newLength
offset = newOffset
}
}
if isFastDeflate && length >= minMatchLength ||
!isFastDeflate && prevLength >= minMatchLength && length <= prevLength {
// There was a match at the previous step, and the current match is
// not better. Output the previous match.
if isFastDeflate {
tokens[ti] = matchToken(uint32(length-minMatchLength), uint32(offset-minOffsetSize))
} else {
tokens[ti] = matchToken(uint32(prevLength-minMatchLength), uint32(prevOffset-minOffsetSize))
}
ti++
// Insert in the hash table all strings up to the end of the match.
// index and index-1 are already inserted. If there is not enough
// lookahead, the last two strings are not inserted into the hash
// table.
if length <= l.fastSkipHashing {
var newIndex int
if isFastDeflate {
newIndex = index + length
} else {
newIndex = prevLength - 1
}
for index++; index < newIndex; index++ {
if index < maxInsertIndex {
hash = (hash<<hashShift + int(d.window[index+2])) & hashMask
// Get previous value with the same hash.
// Our chain should point to the previous value.
d.hashPrev[index&d.windowMask] = d.hashHead[hash]
// Set the head of the hash chain to us.
d.hashHead[hash] = index
}
}
if !isFastDeflate {
byteAvailable = false
length = minMatchLength - 1
}
} else {
// For matches this long, we don't bother inserting each individual
// item into the table.
index += length
hash = (int(d.window[index])<<hashShift + int(d.window[index+1]))
}
if ti == maxFlateBlockTokens {
// The block includes the current character
if err = d.writeBlock(tokens, index, false); err != nil {
return
}
ti = 0
}
} else {
if isFastDeflate || byteAvailable {
i := index - 1
if isFastDeflate {
i = index
}
tokens[ti] = literalToken(uint32(d.window[i]) & 0xFF)
ti++
if ti == maxFlateBlockTokens {
if err = d.writeBlock(tokens, i+1, false); err != nil {
return
}
ti = 0
}
}
index++
if !isFastDeflate {
byteAvailable = true
}
}
}
if byteAvailable {
// There is still one pending token that needs to be flushed
tokens[ti] = literalToken(uint32(d.window[index-1]) & 0xFF)
ti++
}
if ti > 0 {
if err = d.writeBlock(tokens[0:ti], index, false); err != nil {
return
}
}
return
}
func (d *deflater) deflater(r io.Reader, w io.Writer, level int, logWindowSize uint) (err os.Error) {
d.r = r
d.w = newHuffmanBitWriter(w)
d.level = level
d.logWindowSize = logWindowSize
switch {
case level == NoCompression:
err = d.storedDeflate()
case level == DefaultCompression:
d.level = 6
fallthrough
case 1 <= level && level <= 9:
err = d.doDeflate()
default:
return WrongValueError{"level", 0, 9, int32(level)}
}
if err != nil {
return err
}
if d.w.writeStoredHeader(0, true); d.w.err != nil {
return d.w.err
}
return d.flush()
}
func newDeflater(w io.Writer, level int, logWindowSize uint) io.WriteCloser {
var d deflater
pr, pw := syncPipe()
go func() {
err := d.deflater(pr, w, level, logWindowSize)
pr.CloseWithError(err)
}()
return pw
}
func NewDeflater(w io.Writer, level int) io.WriteCloser {
return newDeflater(w, level, logMaxOffsetSize)
}