source: code/trunk/vendor/github.com/klauspost/compress/flate/level2.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 5.7 KB
Line 
1package flate
2
3import "fmt"
4
5// fastGen maintains the table for matches,
6// and the previous byte block for level 2.
7// This is the generic implementation.
8type fastEncL2 struct {
9 fastGen
10 table [bTableSize]tableEntry
11}
12
13// EncodeL2 uses a similar algorithm to level 1, but is capable
14// of matching across blocks giving better compression at a small slowdown.
15func (e *fastEncL2) Encode(dst *tokens, src []byte) {
16 const (
17 inputMargin = 12 - 1
18 minNonLiteralBlockSize = 1 + 1 + inputMargin
19 )
20
21 if debugDeflate && e.cur < 0 {
22 panic(fmt.Sprint("e.cur < 0: ", e.cur))
23 }
24
25 // Protect against e.cur wraparound.
26 for e.cur >= bufferReset {
27 if len(e.hist) == 0 {
28 for i := range e.table[:] {
29 e.table[i] = tableEntry{}
30 }
31 e.cur = maxMatchOffset
32 break
33 }
34 // Shift down everything in the table that isn't already too far away.
35 minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
36 for i := range e.table[:] {
37 v := e.table[i].offset
38 if v <= minOff {
39 v = 0
40 } else {
41 v = v - e.cur + maxMatchOffset
42 }
43 e.table[i].offset = v
44 }
45 e.cur = maxMatchOffset
46 }
47
48 s := e.addBlock(src)
49
50 // This check isn't in the Snappy implementation, but there, the caller
51 // instead of the callee handles this case.
52 if len(src) < minNonLiteralBlockSize {
53 // We do not fill the token table.
54 // This will be picked up by caller.
55 dst.n = uint16(len(src))
56 return
57 }
58
59 // Override src
60 src = e.hist
61 nextEmit := s
62
63 // sLimit is when to stop looking for offset/length copies. The inputMargin
64 // lets us use a fast path for emitLiteral in the main loop, while we are
65 // looking for copies.
66 sLimit := int32(len(src) - inputMargin)
67
68 // nextEmit is where in src the next emitLiteral should start from.
69 cv := load3232(src, s)
70 for {
71 // When should we start skipping if we haven't found matches in a long while.
72 const skipLog = 5
73 const doEvery = 2
74
75 nextS := s
76 var candidate tableEntry
77 for {
78 nextHash := hash4u(cv, bTableBits)
79 s = nextS
80 nextS = s + doEvery + (s-nextEmit)>>skipLog
81 if nextS > sLimit {
82 goto emitRemainder
83 }
84 candidate = e.table[nextHash]
85 now := load6432(src, nextS)
86 e.table[nextHash] = tableEntry{offset: s + e.cur}
87 nextHash = hash4u(uint32(now), bTableBits)
88
89 offset := s - (candidate.offset - e.cur)
90 if offset < maxMatchOffset && cv == load3232(src, candidate.offset-e.cur) {
91 e.table[nextHash] = tableEntry{offset: nextS + e.cur}
92 break
93 }
94
95 // Do one right away...
96 cv = uint32(now)
97 s = nextS
98 nextS++
99 candidate = e.table[nextHash]
100 now >>= 8
101 e.table[nextHash] = tableEntry{offset: s + e.cur}
102
103 offset = s - (candidate.offset - e.cur)
104 if offset < maxMatchOffset && cv == load3232(src, candidate.offset-e.cur) {
105 break
106 }
107 cv = uint32(now)
108 }
109
110 // A 4-byte match has been found. We'll later see if more than 4 bytes
111 // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
112 // them as literal bytes.
113
114 // Call emitCopy, and then see if another emitCopy could be our next
115 // move. Repeat until we find no match for the input immediately after
116 // what was consumed by the last emitCopy call.
117 //
118 // If we exit this loop normally then we need to call emitLiteral next,
119 // though we don't yet know how big the literal will be. We handle that
120 // by proceeding to the next iteration of the main loop. We also can
121 // exit this loop via goto if we get close to exhausting the input.
122 for {
123 // Invariant: we have a 4-byte match at s, and no need to emit any
124 // literal bytes prior to s.
125
126 // Extend the 4-byte match as long as possible.
127 t := candidate.offset - e.cur
128 l := e.matchlenLong(s+4, t+4, src) + 4
129
130 // Extend backwards
131 for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
132 s--
133 t--
134 l++
135 }
136 if nextEmit < s {
137 emitLiteral(dst, src[nextEmit:s])
138 }
139
140 dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
141 s += l
142 nextEmit = s
143 if nextS >= s {
144 s = nextS + 1
145 }
146
147 if s >= sLimit {
148 // Index first pair after match end.
149 if int(s+l+4) < len(src) {
150 cv := load3232(src, s)
151 e.table[hash4u(cv, bTableBits)] = tableEntry{offset: s + e.cur}
152 }
153 goto emitRemainder
154 }
155
156 // Store every second hash in-between, but offset by 1.
157 for i := s - l + 2; i < s-5; i += 7 {
158 x := load6432(src, int32(i))
159 nextHash := hash4u(uint32(x), bTableBits)
160 e.table[nextHash] = tableEntry{offset: e.cur + i}
161 // Skip one
162 x >>= 16
163 nextHash = hash4u(uint32(x), bTableBits)
164 e.table[nextHash] = tableEntry{offset: e.cur + i + 2}
165 // Skip one
166 x >>= 16
167 nextHash = hash4u(uint32(x), bTableBits)
168 e.table[nextHash] = tableEntry{offset: e.cur + i + 4}
169 }
170
171 // We could immediately start working at s now, but to improve
172 // compression we first update the hash table at s-2 to s. If
173 // another emitCopy is not our next move, also calculate nextHash
174 // at s+1. At least on GOARCH=amd64, these three hash calculations
175 // are faster as one load64 call (with some shifts) instead of
176 // three load32 calls.
177 x := load6432(src, s-2)
178 o := e.cur + s - 2
179 prevHash := hash4u(uint32(x), bTableBits)
180 prevHash2 := hash4u(uint32(x>>8), bTableBits)
181 e.table[prevHash] = tableEntry{offset: o}
182 e.table[prevHash2] = tableEntry{offset: o + 1}
183 currHash := hash4u(uint32(x>>16), bTableBits)
184 candidate = e.table[currHash]
185 e.table[currHash] = tableEntry{offset: o + 2}
186
187 offset := s - (candidate.offset - e.cur)
188 if offset > maxMatchOffset || uint32(x>>16) != load3232(src, candidate.offset-e.cur) {
189 cv = uint32(x >> 24)
190 s++
191 break
192 }
193 }
194 }
195
196emitRemainder:
197 if int(nextEmit) < len(src) {
198 // If nothing was added, don't encode literals.
199 if dst.n == 0 {
200 return
201 }
202
203 emitLiteral(dst, src[nextEmit:])
204 }
205}
Note: See TracBrowser for help on using the repository browser.