|  | // Copyright 2016 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 | 
|  |  | 
|  | // dictDecoder implements the LZ77 sliding dictionary as used in decompression. | 
|  | // LZ77 decompresses data through sequences of two forms of commands: | 
|  | // | 
|  | //	* Literal insertions: Runs of one or more symbols are inserted into the data | 
|  | //	stream as is. This is accomplished through the writeByte method for a | 
|  | //	single symbol, or combinations of writeSlice/writeMark for multiple symbols. | 
|  | //	Any valid stream must start with a literal insertion if no preset dictionary | 
|  | //	is used. | 
|  | // | 
|  | //	* Backward copies: Runs of one or more symbols are copied from previously | 
|  | //	emitted data. Backward copies come as the tuple (dist, length) where dist | 
|  | //	determines how far back in the stream to copy from and length determines how | 
|  | //	many bytes to copy. Note that it is valid for the length to be greater than | 
|  | //	the distance. Since LZ77 uses forward copies, that situation is used to | 
|  | //	perform a form of run-length encoding on repeated runs of symbols. | 
|  | //	The writeCopy and tryWriteCopy are used to implement this command. | 
|  | // | 
|  | // For performance reasons, this implementation performs little to no sanity | 
|  | // checks about the arguments. As such, the invariants documented for each | 
|  | // method call must be respected. | 
|  | type dictDecoder struct { | 
|  | hist []byte // Sliding window history | 
|  |  | 
|  | // Invariant: 0 <= rdPos <= wrPos <= len(hist) | 
|  | wrPos int  // Current output position in buffer | 
|  | rdPos int  // Have emitted hist[:rdPos] already | 
|  | full  bool // Has a full window length been written yet? | 
|  | } | 
|  |  | 
|  | // init initializes dictDecoder to have a sliding window dictionary of the given | 
|  | // size. If a preset dict is provided, it will initialize the dictionary with | 
|  | // the contents of dict. | 
|  | func (dd *dictDecoder) init(size int, dict []byte) { | 
|  | *dd = dictDecoder{hist: dd.hist} | 
|  |  | 
|  | if cap(dd.hist) < size { | 
|  | dd.hist = make([]byte, size) | 
|  | } | 
|  | dd.hist = dd.hist[:size] | 
|  |  | 
|  | if len(dict) > len(dd.hist) { | 
|  | dict = dict[len(dict)-len(dd.hist):] | 
|  | } | 
|  | dd.wrPos = copy(dd.hist, dict) | 
|  | if dd.wrPos == len(dd.hist) { | 
|  | dd.wrPos = 0 | 
|  | dd.full = true | 
|  | } | 
|  | dd.rdPos = dd.wrPos | 
|  | } | 
|  |  | 
|  | // histSize reports the total amount of historical data in the dictionary. | 
|  | func (dd *dictDecoder) histSize() int { | 
|  | if dd.full { | 
|  | return len(dd.hist) | 
|  | } | 
|  | return dd.wrPos | 
|  | } | 
|  |  | 
|  | // availRead reports the number of bytes that can be flushed by readFlush. | 
|  | func (dd *dictDecoder) availRead() int { | 
|  | return dd.wrPos - dd.rdPos | 
|  | } | 
|  |  | 
|  | // availWrite reports the available amount of output buffer space. | 
|  | func (dd *dictDecoder) availWrite() int { | 
|  | return len(dd.hist) - dd.wrPos | 
|  | } | 
|  |  | 
|  | // writeSlice returns a slice of the available buffer to write data to. | 
|  | // | 
|  | // This invariant will be kept: len(s) <= availWrite() | 
|  | func (dd *dictDecoder) writeSlice() []byte { | 
|  | return dd.hist[dd.wrPos:] | 
|  | } | 
|  |  | 
|  | // writeMark advances the writer pointer by cnt. | 
|  | // | 
|  | // This invariant must be kept: 0 <= cnt <= availWrite() | 
|  | func (dd *dictDecoder) writeMark(cnt int) { | 
|  | dd.wrPos += cnt | 
|  | } | 
|  |  | 
|  | // writeByte writes a single byte to the dictionary. | 
|  | // | 
|  | // This invariant must be kept: 0 < availWrite() | 
|  | func (dd *dictDecoder) writeByte(c byte) { | 
|  | dd.hist[dd.wrPos] = c | 
|  | dd.wrPos++ | 
|  | } | 
|  |  | 
|  | // writeCopy copies a string at a given (dist, length) to the output. | 
|  | // This returns the number of bytes copied and may be less than the requested | 
|  | // length if the available space in the output buffer is too small. | 
|  | // | 
|  | // This invariant must be kept: 0 < dist <= histSize() | 
|  | func (dd *dictDecoder) writeCopy(dist, length int) int { | 
|  | dstBase := dd.wrPos | 
|  | dstPos := dstBase | 
|  | srcPos := dstPos - dist | 
|  | endPos := dstPos + length | 
|  | if endPos > len(dd.hist) { | 
|  | endPos = len(dd.hist) | 
|  | } | 
|  |  | 
|  | // Copy non-overlapping section after destination position. | 
|  | // | 
|  | // This section is non-overlapping in that the copy length for this section | 
|  | // is always less than or equal to the backwards distance. This can occur | 
|  | // if a distance refers to data that wraps-around in the buffer. | 
|  | // Thus, a backwards copy is performed here; that is, the exact bytes in | 
|  | // the source prior to the copy is placed in the destination. | 
|  | if srcPos < 0 { | 
|  | srcPos += len(dd.hist) | 
|  | dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:]) | 
|  | srcPos = 0 | 
|  | } | 
|  |  | 
|  | // Copy possibly overlapping section before destination position. | 
|  | // | 
|  | // This section can overlap if the copy length for this section is larger | 
|  | // than the backwards distance. This is allowed by LZ77 so that repeated | 
|  | // strings can be succinctly represented using (dist, length) pairs. | 
|  | // Thus, a forwards copy is performed here; that is, the bytes copied is | 
|  | // possibly dependent on the resulting bytes in the destination as the copy | 
|  | // progresses along. This is functionally equivalent to the following: | 
|  | // | 
|  | //	for i := 0; i < endPos-dstPos; i++ { | 
|  | //		dd.hist[dstPos+i] = dd.hist[srcPos+i] | 
|  | //	} | 
|  | //	dstPos = endPos | 
|  | // | 
|  | for dstPos < endPos { | 
|  | dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) | 
|  | } | 
|  |  | 
|  | dd.wrPos = dstPos | 
|  | return dstPos - dstBase | 
|  | } | 
|  |  | 
|  | // tryWriteCopy tries to copy a string at a given (distance, length) to the | 
|  | // output. This specialized version is optimized for short distances. | 
|  | // | 
|  | // This method is designed to be inlined for performance reasons. | 
|  | // | 
|  | // This invariant must be kept: 0 < dist <= histSize() | 
|  | func (dd *dictDecoder) tryWriteCopy(dist, length int) int { | 
|  | dstPos := dd.wrPos | 
|  | endPos := dstPos + length | 
|  | if dstPos < dist || endPos > len(dd.hist) { | 
|  | return 0 | 
|  | } | 
|  | dstBase := dstPos | 
|  | srcPos := dstPos - dist | 
|  |  | 
|  | // Copy possibly overlapping section before destination position. | 
|  | loop: | 
|  | dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) | 
|  | if dstPos < endPos { | 
|  | goto loop // Avoid for-loop so that this function can be inlined | 
|  | } | 
|  |  | 
|  | dd.wrPos = dstPos | 
|  | return dstPos - dstBase | 
|  | } | 
|  |  | 
|  | // readFlush returns a slice of the historical buffer that is ready to be | 
|  | // emitted to the user. The data returned by readFlush must be fully consumed | 
|  | // before calling any other dictDecoder methods. | 
|  | func (dd *dictDecoder) readFlush() []byte { | 
|  | toRead := dd.hist[dd.rdPos:dd.wrPos] | 
|  | dd.rdPos = dd.wrPos | 
|  | if dd.wrPos == len(dd.hist) { | 
|  | dd.wrPos, dd.rdPos = 0, 0 | 
|  | dd.full = true | 
|  | } | 
|  | return toRead | 
|  | } |