1package zstd 2 3import ( 4 "fmt" 5 "math/bits" 6 7 "github.com/klauspost/compress/zstd/internal/xxhash" 8) 9 10type fastBase struct { 11 // cur is the offset at the start of hist 12 cur int32 13 // maximum offset. Should be at least 2x block size. 14 maxMatchOff int32 15 hist []byte 16 crc *xxhash.Digest 17 tmp [8]byte 18 blk *blockEnc 19 lastDictID uint32 20} 21 22// CRC returns the underlying CRC writer. 23func (e *fastBase) CRC() *xxhash.Digest { 24 return e.crc 25} 26 27// AppendCRC will append the CRC to the destination slice and return it. 28func (e *fastBase) AppendCRC(dst []byte) []byte { 29 crc := e.crc.Sum(e.tmp[:0]) 30 dst = append(dst, crc[7], crc[6], crc[5], crc[4]) 31 return dst 32} 33 34// WindowSize returns the window size of the encoder, 35// or a window size small enough to contain the input size, if > 0. 36func (e *fastBase) WindowSize(size int) int32 { 37 if size > 0 && size < int(e.maxMatchOff) { 38 b := int32(1) << uint(bits.Len(uint(size))) 39 // Keep minimum window. 40 if b < 1024 { 41 b = 1024 42 } 43 return b 44 } 45 return e.maxMatchOff 46} 47 48// Block returns the current block. 49func (e *fastBase) Block() *blockEnc { 50 return e.blk 51} 52 53func (e *fastBase) addBlock(src []byte) int32 { 54 if debugAsserts && e.cur > bufferReset { 55 panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, bufferReset)) 56 } 57 // check if we have space already 58 if len(e.hist)+len(src) > cap(e.hist) { 59 if cap(e.hist) == 0 { 60 l := e.maxMatchOff * 2 61 // Make it at least 1MB. 62 if l < 1<<20 { 63 l = 1 << 20 64 } 65 e.hist = make([]byte, 0, l) 66 } else { 67 if cap(e.hist) < int(e.maxMatchOff*2) { 68 panic("unexpected buffer size") 69 } 70 // Move down 71 offset := int32(len(e.hist)) - e.maxMatchOff 72 copy(e.hist[0:e.maxMatchOff], e.hist[offset:]) 73 e.cur += offset 74 e.hist = e.hist[:e.maxMatchOff] 75 } 76 } 77 s := int32(len(e.hist)) 78 e.hist = append(e.hist, src...) 79 return s 80} 81 82// useBlock will replace the block with the provided one, 83// but transfer recent offsets from the previous. 84func (e *fastBase) UseBlock(enc *blockEnc) { 85 enc.reset(e.blk) 86 e.blk = enc 87} 88 89func (e *fastBase) matchlenNoHist(s, t int32, src []byte) int32 { 90 // Extend the match to be as long as possible. 91 return int32(matchLen(src[s:], src[t:])) 92} 93 94func (e *fastBase) matchlen(s, t int32, src []byte) int32 { 95 if debugAsserts { 96 if s < 0 { 97 err := fmt.Sprintf("s (%d) < 0", s) 98 panic(err) 99 } 100 if t < 0 { 101 err := fmt.Sprintf("s (%d) < 0", s) 102 panic(err) 103 } 104 if s-t > e.maxMatchOff { 105 err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff) 106 panic(err) 107 } 108 if len(src)-int(s) > maxCompressedBlockSize { 109 panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize)) 110 } 111 } 112 113 // Extend the match to be as long as possible. 114 return int32(matchLen(src[s:], src[t:])) 115} 116 117// Reset the encoding table. 118func (e *fastBase) resetBase(d *dict, singleBlock bool) { 119 if e.blk == nil { 120 e.blk = &blockEnc{} 121 e.blk.init() 122 } else { 123 e.blk.reset(nil) 124 } 125 e.blk.initNewEncode() 126 if e.crc == nil { 127 e.crc = xxhash.New() 128 } else { 129 e.crc.Reset() 130 } 131 if (!singleBlock || d.DictContentSize() > 0) && cap(e.hist) < int(e.maxMatchOff*2)+d.DictContentSize() { 132 l := e.maxMatchOff*2 + int32(d.DictContentSize()) 133 // Make it at least 1MB. 134 if l < 1<<20 { 135 l = 1 << 20 136 } 137 e.hist = make([]byte, 0, l) 138 } 139 // We offset current position so everything will be out of reach. 140 // If above reset line, history will be purged. 141 if e.cur < bufferReset { 142 e.cur += e.maxMatchOff + int32(len(e.hist)) 143 } 144 e.hist = e.hist[:0] 145 if d != nil { 146 // Set offsets (currently not used) 147 for i, off := range d.offsets { 148 e.blk.recentOffsets[i] = uint32(off) 149 e.blk.prevRecentOffsets[i] = e.blk.recentOffsets[i] 150 } 151 // Transfer litenc. 152 e.blk.dictLitEnc = d.litEnc 153 e.hist = append(e.hist, d.content...) 154 } 155} 156