1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 DefaultCompression
= -1
19 logMaxOffsetSize
= 15 // Standard DEFLATE
20 wideLogMaxOffsetSize
= 22 // Wide DEFLATE
21 minMatchLength
= 3 // The smallest match that the compressor looks for
22 maxMatchLength
= 258 // The longest match for the compressor
23 minOffsetSize
= 1 // The shortest offset that makes any sence
25 // The maximum number of tokens we put into a single flat block, just too
26 // stop things from getting too large.
27 maxFlateBlockTokens
= 1 << 14
28 maxStoreBlockSize
= 65535
30 hashSize
= 1 << hashBits
31 hashMask
= (1 << hashBits
) - 1
32 hashShift
= (hashBits
+ minMatchLength
- 1) / minMatchLength
35 type syncPipeReader
struct {
40 func (sr
*syncPipeReader
) CloseWithError(err os
.Error
) os
.Error
{
41 retErr
:= sr
.PipeReader
.CloseWithError(err
)
42 sr
.closeChan
<- true // finish writer close
46 type syncPipeWriter
struct {
51 type compressionLevel
struct {
52 good
, lazy
, nice
, chain
, fastSkipHashing
int
55 var levels
= []compressionLevel
{
57 // For levels 1-3 we don't bother trying with lazy matches
61 // Levels 4-9 use increasingly more lazy matching
62 // and increasingly stringent conditions for "good enough".
63 {4, 4, 16, 16, math
.MaxInt32
},
64 {8, 16, 32, 32, math
.MaxInt32
},
65 {8, 16, 128, 128, math
.MaxInt32
},
66 {8, 32, 128, 256, math
.MaxInt32
},
67 {32, 128, 258, 1024, math
.MaxInt32
},
68 {32, 258, 258, 4096, math
.MaxInt32
},
71 func (sw
*syncPipeWriter
) Close() os
.Error
{
72 err
:= sw
.PipeWriter
.Close()
73 <-sw
.closeChan
// wait for reader close
77 func syncPipe() (*syncPipeReader
, *syncPipeWriter
) {
79 sr
:= &syncPipeReader
{r
, make(chan bool, 1)}
80 sw
:= &syncPipeWriter
{w
, sr
.closeChan
}
84 type compressor
struct {
89 // (1 << logWindowSize) - 1.
92 // hashHead[hashValue] contains the largest inputIndex with the specified hash value
95 // If hashHead[hashValue] is within the current window, then
96 // hashPrev[hashHead[hashValue] & windowMask] contains the previous index
97 // with the same hash value.
100 // If we find a match of length >= niceMatch, then we don't bother searching
104 // If we find a match of length >= goodMatch, we only do a half-hearted
105 // effort at doing lazy matching starting at the next character
108 // The maximum number of chains we look at when finding a match
111 // The sliding window we use for matching
114 // The index just past the last valid character
117 // index in "window" at which current block starts
121 func (d
*compressor
) flush() os
.Error
{
126 func (d
*compressor
) fillWindow(index
int) (int, os
.Error
) {
127 wSize
:= d
.windowMask
+ 1
128 if index
>= wSize
+wSize
-(minMatchLength
+maxMatchLength
) {
129 // shift the window by wSize
130 copy(d
.window
, d
.window
[wSize
:2*wSize
])
133 if d
.blockStart
>= wSize
{
134 d
.blockStart
-= wSize
136 d
.blockStart
= math
.MaxInt32
138 for i
, h
:= range d
.hashHead
{
139 d
.hashHead
[i
] = max(h
-wSize
, -1)
141 for i
, h
:= range d
.hashPrev
{
142 d
.hashPrev
[i
] = max(h
-wSize
, -1)
147 count
, err
= io
.ReadAtLeast(d
.r
, d
.window
[d
.windowEnd
:], 1)
155 func (d
*compressor
) writeBlock(tokens
[]token
, index
int, eof
bool) os
.Error
{
156 if index
> 0 || eof
{
158 if d
.blockStart
<= index
{
159 window
= d
.window
[d
.blockStart
:index
]
162 d
.w
.writeBlock(tokens
, eof
, window
)
168 // Try to find a match starting at index whose length is greater than prevSize.
169 // We only look at chainCount possibilities before giving up.
170 func (d
*compressor
) findMatch(pos
int, prevHead
int, prevLength
int, lookahead
int) (length
, offset
int, ok
bool) {
171 win
:= d
.window
[0 : pos
+min(maxMatchLength
, lookahead
)]
173 // We quit when we get a match that's at least nice long
174 nice
:= min(d
.niceMatch
, len(win
)-pos
)
176 // If we've got a match that's good enough, only look in 1/4 the chain.
177 tries
:= d
.maxChainLength
179 if length
>= d
.goodMatch
{
185 wEnd
:= win
[pos
+length
]
186 minIndex
:= pos
- (d
.windowMask
+ 1)
188 for i
:= prevHead
; tries
> 0; tries
-- {
189 if w0
== win
[i
] && w1
== win
[i
+1] && wEnd
== win
[i
+length
] {
190 // The hash function ensures that if win[i] and win[i+1] match, win[i+2] matches
193 for pos
+n
< len(win
) && win
[i
+n
] == win
[pos
+n
] {
196 if n
> length
&& (n
> 3 || pos
-i
<= 4096) {
201 // The match is good enough that we don't try to find a better one.
208 // hashPrev[i & windowMask] has already been overwritten, so stop now.
211 if i
= d
.hashPrev
[i
&d
.windowMask
]; i
< minIndex || i
< 0 {
218 func (d
*compressor
) writeStoredBlock(buf
[]byte) os
.Error
{
219 if d
.w
.writeStoredHeader(len(buf
), false); d
.w
.err
!= nil {
226 func (d
*compressor
) storedDeflate() os
.Error
{
227 buf
:= make([]byte, maxStoreBlockSize
)
229 n
, err
:= d
.r
.Read(buf
)
231 if err
:= d
.writeStoredBlock(buf
[0:n
]); err
!= nil {
245 func (d
*compressor
) doDeflate() (err os
.Error
) {
247 d
.windowMask
= 1<<d
.logWindowSize
- 1
248 d
.hashHead
= make([]int, hashSize
)
249 d
.hashPrev
= make([]int, 1<<d
.logWindowSize
)
250 d
.window
= make([]byte, 2<<d
.logWindowSize
)
251 fillInts(d
.hashHead
, -1)
252 tokens
:= make([]token
, maxFlateBlockTokens
, maxFlateBlockTokens
+1)
256 d
.maxChainLength
= l
.chain
258 length
:= minMatchLength
- 1
260 byteAvailable
:= false
261 isFastDeflate
:= l
.fastSkipHashing
!= 0
264 if index
, err
= d
.fillWindow(index
); err
!= nil {
267 maxOffset
:= d
.windowMask
+ 1 // (1 << logWindowSize);
268 // only need to change when you refill the window
269 windowEnd
:= d
.windowEnd
270 maxInsertIndex
:= windowEnd
- (minMatchLength
- 1)
274 if index
< maxInsertIndex
{
275 hash
= int(d
.window
[index
])<<hashShift
+ int(d
.window
[index
+1])
279 if index
> windowEnd
{
280 panic("index > windowEnd")
282 lookahead
:= windowEnd
- index
283 if lookahead
< minMatchLength
+maxMatchLength
{
284 if index
, err
= d
.fillWindow(index
); err
!= nil {
287 windowEnd
= d
.windowEnd
288 if index
> windowEnd
{
289 panic("index > windowEnd")
291 maxInsertIndex
= windowEnd
- (minMatchLength
- 1)
292 lookahead
= windowEnd
- index
297 if index
< maxInsertIndex
{
299 hash
= (hash
<<hashShift
+ int(d
.window
[index
+2])) & hashMask
300 chainHead
= d
.hashHead
[hash
]
301 d
.hashPrev
[index
&d
.windowMask
] = chainHead
302 d
.hashHead
[hash
] = index
306 minIndex
:= max(index
-maxOffset
, 0)
307 length
= minMatchLength
- 1
310 if chainHead
>= minIndex
&&
311 (isFastDeflate
&& lookahead
> minMatchLength
-1 ||
312 !isFastDeflate
&& lookahead
> prevLength
&& prevLength
< lazyMatch
) {
313 if newLength
, newOffset
, ok
:= d
.findMatch(index
, chainHead
, minMatchLength
-1, lookahead
); ok
{
318 if isFastDeflate
&& length
>= minMatchLength ||
319 !isFastDeflate
&& prevLength
>= minMatchLength
&& length
<= prevLength
{
320 // There was a match at the previous step, and the current match is
321 // not better. Output the previous match.
323 tokens
[ti
] = matchToken(uint32(length
-minMatchLength
), uint32(offset
-minOffsetSize
))
325 tokens
[ti
] = matchToken(uint32(prevLength
-minMatchLength
), uint32(prevOffset
-minOffsetSize
))
328 // Insert in the hash table all strings up to the end of the match.
329 // index and index-1 are already inserted. If there is not enough
330 // lookahead, the last two strings are not inserted into the hash
332 if length
<= l
.fastSkipHashing
{
335 newIndex
= index
+ length
337 newIndex
= prevLength
- 1
339 for index
++; index
< newIndex
; index
++ {
340 if index
< maxInsertIndex
{
341 hash
= (hash
<<hashShift
+ int(d
.window
[index
+2])) & hashMask
342 // Get previous value with the same hash.
343 // Our chain should point to the previous value.
344 d
.hashPrev
[index
&d
.windowMask
] = d
.hashHead
[hash
]
345 // Set the head of the hash chain to us.
346 d
.hashHead
[hash
] = index
350 byteAvailable
= false
351 length
= minMatchLength
- 1
354 // For matches this long, we don't bother inserting each individual
355 // item into the table.
357 hash
= (int(d
.window
[index
])<<hashShift
+ int(d
.window
[index
+1]))
359 if ti
== maxFlateBlockTokens
{
360 // The block includes the current character
361 if err
= d
.writeBlock(tokens
, index
, false); err
!= nil {
367 if isFastDeflate || byteAvailable
{
372 tokens
[ti
] = literalToken(uint32(d
.window
[i
]) & 0xFF)
374 if ti
== maxFlateBlockTokens
{
375 if err
= d
.writeBlock(tokens
, i
+1, false); err
!= nil {
389 // There is still one pending token that needs to be flushed
390 tokens
[ti
] = literalToken(uint32(d
.window
[index
-1]) & 0xFF)
395 if err
= d
.writeBlock(tokens
[0:ti
], index
, false); err
!= nil {
402 func (d
*compressor
) compressor(r io
.Reader
, w io
.Writer
, level
int, logWindowSize
uint) (err os
.Error
) {
404 d
.w
= newHuffmanBitWriter(w
)
406 d
.logWindowSize
= logWindowSize
409 case level
== NoCompression
:
410 err
= d
.storedDeflate()
411 case level
== DefaultCompression
:
414 case 1 <= level
&& level
<= 9:
417 return WrongValueError
{"level", 0, 9, int32(level
)}
423 if d
.w
.writeStoredHeader(0, true); d
.w
.err
!= nil {
429 func newCompressor(w io
.Writer
, level
int, logWindowSize
uint) io
.WriteCloser
{
433 err
:= d
.compressor(pr
, w
, level
, logWindowSize
)
434 pr
.CloseWithError(err
)
439 func NewWriter(w io
.Writer
, level
int) io
.WriteCloser
{
440 return newCompressor(w
, level
, logMaxOffsetSize
)