level6.go (8615B)
1 package flate 2 3 import "fmt" 4 5 type fastEncL6 struct { 6 fastGen 7 table [tableSize]tableEntry 8 bTable [tableSize]tableEntryPrev 9 } 10 11 func (e *fastEncL6) Encode(dst *tokens, src []byte) { 12 const ( 13 inputMargin = 12 - 1 14 minNonLiteralBlockSize = 1 + 1 + inputMargin 15 hashShortBytes = 4 16 ) 17 if debugDeflate && e.cur < 0 { 18 panic(fmt.Sprint("e.cur < 0: ", e.cur)) 19 } 20 21 // Protect against e.cur wraparound. 22 for e.cur >= bufferReset { 23 if len(e.hist) == 0 { 24 for i := range e.table[:] { 25 e.table[i] = tableEntry{} 26 } 27 for i := range e.bTable[:] { 28 e.bTable[i] = tableEntryPrev{} 29 } 30 e.cur = maxMatchOffset 31 break 32 } 33 // Shift down everything in the table that isn't already too far away. 34 minOff := e.cur + int32(len(e.hist)) - maxMatchOffset 35 for i := range e.table[:] { 36 v := e.table[i].offset 37 if v <= minOff { 38 v = 0 39 } else { 40 v = v - e.cur + maxMatchOffset 41 } 42 e.table[i].offset = v 43 } 44 for i := range e.bTable[:] { 45 v := e.bTable[i] 46 if v.Cur.offset <= minOff { 47 v.Cur.offset = 0 48 v.Prev.offset = 0 49 } else { 50 v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset 51 if v.Prev.offset <= minOff { 52 v.Prev.offset = 0 53 } else { 54 v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset 55 } 56 } 57 e.bTable[i] = v 58 } 59 e.cur = maxMatchOffset 60 } 61 62 s := e.addBlock(src) 63 64 // This check isn't in the Snappy implementation, but there, the caller 65 // instead of the callee handles this case. 66 if len(src) < minNonLiteralBlockSize { 67 // We do not fill the token table. 68 // This will be picked up by caller. 69 dst.n = uint16(len(src)) 70 return 71 } 72 73 // Override src 74 src = e.hist 75 nextEmit := s 76 77 // sLimit is when to stop looking for offset/length copies. The inputMargin 78 // lets us use a fast path for emitLiteral in the main loop, while we are 79 // looking for copies. 80 sLimit := int32(len(src) - inputMargin) 81 82 // nextEmit is where in src the next emitLiteral should start from. 83 cv := load6432(src, s) 84 // Repeat MUST be > 1 and within range 85 repeat := int32(1) 86 for { 87 const skipLog = 7 88 const doEvery = 1 89 90 nextS := s 91 var l int32 92 var t int32 93 for { 94 nextHashS := hashLen(cv, tableBits, hashShortBytes) 95 nextHashL := hash7(cv, tableBits) 96 s = nextS 97 nextS = s + doEvery + (s-nextEmit)>>skipLog 98 if nextS > sLimit { 99 goto emitRemainder 100 } 101 // Fetch a short+long candidate 102 sCandidate := e.table[nextHashS] 103 lCandidate := e.bTable[nextHashL] 104 next := load6432(src, nextS) 105 entry := tableEntry{offset: s + e.cur} 106 e.table[nextHashS] = entry 107 eLong := &e.bTable[nextHashL] 108 eLong.Cur, eLong.Prev = entry, eLong.Cur 109 110 // Calculate hashes of 'next' 111 nextHashS = hashLen(next, tableBits, hashShortBytes) 112 nextHashL = hash7(next, tableBits) 113 114 t = lCandidate.Cur.offset - e.cur 115 if s-t < maxMatchOffset { 116 if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) { 117 // Long candidate matches at least 4 bytes. 118 119 // Store the next match 120 e.table[nextHashS] = tableEntry{offset: nextS + e.cur} 121 eLong := &e.bTable[nextHashL] 122 eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur 123 124 // Check the previous long candidate as well. 125 t2 := lCandidate.Prev.offset - e.cur 126 if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { 127 l = e.matchlen(s+4, t+4, src) + 4 128 ml1 := e.matchlen(s+4, t2+4, src) + 4 129 if ml1 > l { 130 t = t2 131 l = ml1 132 break 133 } 134 } 135 break 136 } 137 // Current value did not match, but check if previous long value does. 138 t = lCandidate.Prev.offset - e.cur 139 if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { 140 // Store the next match 141 e.table[nextHashS] = tableEntry{offset: nextS + e.cur} 142 eLong := &e.bTable[nextHashL] 143 eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur 144 break 145 } 146 } 147 148 t = sCandidate.offset - e.cur 149 if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) { 150 // Found a 4 match... 151 l = e.matchlen(s+4, t+4, src) + 4 152 153 // Look up next long candidate (at nextS) 154 lCandidate = e.bTable[nextHashL] 155 156 // Store the next match 157 e.table[nextHashS] = tableEntry{offset: nextS + e.cur} 158 eLong := &e.bTable[nextHashL] 159 eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur 160 161 // Check repeat at s + repOff 162 const repOff = 1 163 t2 := s - repeat + repOff 164 if load3232(src, t2) == uint32(cv>>(8*repOff)) { 165 ml := e.matchlen(s+4+repOff, t2+4, src) + 4 166 if ml > l { 167 t = t2 168 l = ml 169 s += repOff 170 // Not worth checking more. 171 break 172 } 173 } 174 175 // If the next long is a candidate, use that... 176 t2 = lCandidate.Cur.offset - e.cur 177 if nextS-t2 < maxMatchOffset { 178 if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) { 179 ml := e.matchlen(nextS+4, t2+4, src) + 4 180 if ml > l { 181 t = t2 182 s = nextS 183 l = ml 184 // This is ok, but check previous as well. 185 } 186 } 187 // If the previous long is a candidate, use that... 188 t2 = lCandidate.Prev.offset - e.cur 189 if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) { 190 ml := e.matchlen(nextS+4, t2+4, src) + 4 191 if ml > l { 192 t = t2 193 s = nextS 194 l = ml 195 break 196 } 197 } 198 } 199 break 200 } 201 cv = next 202 } 203 204 // A 4-byte match has been found. We'll later see if more than 4 bytes 205 // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit 206 // them as literal bytes. 207 208 // Extend the 4-byte match as long as possible. 209 if l == 0 { 210 l = e.matchlenLong(s+4, t+4, src) + 4 211 } else if l == maxMatchLength { 212 l += e.matchlenLong(s+l, t+l, src) 213 } 214 215 // Try to locate a better match by checking the end-of-match... 216 if sAt := s + l; sAt < sLimit { 217 // Allow some bytes at the beginning to mismatch. 218 // Sweet spot is 2/3 bytes depending on input. 219 // 3 is only a little better when it is but sometimes a lot worse. 220 // The skipped bytes are tested in Extend backwards, 221 // and still picked up as part of the match if they do. 222 const skipBeginning = 2 223 eLong := &e.bTable[hash7(load6432(src, sAt), tableBits)] 224 // Test current 225 t2 := eLong.Cur.offset - e.cur - l + skipBeginning 226 s2 := s + skipBeginning 227 off := s2 - t2 228 if off < maxMatchOffset { 229 if off > 0 && t2 >= 0 { 230 if l2 := e.matchlenLong(s2, t2, src); l2 > l { 231 t = t2 232 l = l2 233 s = s2 234 } 235 } 236 // Test next: 237 t2 = eLong.Prev.offset - e.cur - l + skipBeginning 238 off := s2 - t2 239 if off > 0 && off < maxMatchOffset && t2 >= 0 { 240 if l2 := e.matchlenLong(s2, t2, src); l2 > l { 241 t = t2 242 l = l2 243 s = s2 244 } 245 } 246 } 247 } 248 249 // Extend backwards 250 for t > 0 && s > nextEmit && src[t-1] == src[s-1] { 251 s-- 252 t-- 253 l++ 254 } 255 if nextEmit < s { 256 if false { 257 emitLiteral(dst, src[nextEmit:s]) 258 } else { 259 for _, v := range src[nextEmit:s] { 260 dst.tokens[dst.n] = token(v) 261 dst.litHist[v]++ 262 dst.n++ 263 } 264 } 265 } 266 if false { 267 if t >= s { 268 panic(fmt.Sprintln("s-t", s, t)) 269 } 270 if (s - t) > maxMatchOffset { 271 panic(fmt.Sprintln("mmo", s-t)) 272 } 273 if l < baseMatchLength { 274 panic("bml") 275 } 276 } 277 278 dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) 279 repeat = s - t 280 s += l 281 nextEmit = s 282 if nextS >= s { 283 s = nextS + 1 284 } 285 286 if s >= sLimit { 287 // Index after match end. 288 for i := nextS + 1; i < int32(len(src))-8; i += 2 { 289 cv := load6432(src, i) 290 e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: i + e.cur} 291 eLong := &e.bTable[hash7(cv, tableBits)] 292 eLong.Cur, eLong.Prev = tableEntry{offset: i + e.cur}, eLong.Cur 293 } 294 goto emitRemainder 295 } 296 297 // Store every long hash in-between and every second short. 298 if true { 299 for i := nextS + 1; i < s-1; i += 2 { 300 cv := load6432(src, i) 301 t := tableEntry{offset: i + e.cur} 302 t2 := tableEntry{offset: t.offset + 1} 303 eLong := &e.bTable[hash7(cv, tableBits)] 304 eLong2 := &e.bTable[hash7(cv>>8, tableBits)] 305 e.table[hashLen(cv, tableBits, hashShortBytes)] = t 306 eLong.Cur, eLong.Prev = t, eLong.Cur 307 eLong2.Cur, eLong2.Prev = t2, eLong2.Cur 308 } 309 } 310 311 // We could immediately start working at s now, but to improve 312 // compression we first update the hash table at s-1 and at s. 313 cv = load6432(src, s) 314 } 315 316 emitRemainder: 317 if int(nextEmit) < len(src) { 318 // If nothing was added, don't encode literals. 319 if dst.n == 0 { 320 return 321 } 322 323 emitLiteral(dst, src[nextEmit:]) 324 } 325 }