package flate import "fmt" type fastEncL5 struct { fastGen table [tableSize]tableEntry bTable [tableSize]tableEntryPrev } func (e *fastEncL5) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin ) if debugDecode && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.bTable[:] { e.bTable[i] = tableEntryPrev{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } for i := range e.bTable[:] { v := e.bTable[i] if v.Cur.offset <= minOff { v.Cur.offset = 0 v.Prev.offset = 0 } else { v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset if v.Prev.offset <= minOff { v.Prev.offset = 0 } else { v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset } } e.bTable[i] = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { const skipLog = 6 const doEvery = 1 nextS := s var l int32 var t int32 for { nextHashS := hash4x64(cv, tableBits) nextHashL := hash7(cv, tableBits) s = nextS nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } // Fetch a short+long candidate sCandidate := e.table[nextHashS] lCandidate := e.bTable[nextHashL] next := load6432(src, nextS) entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.table[nextHashS] = entry eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = entry, eLong.Cur nextHashS = hash4x64(next, tableBits) nextHashL = hash7(next, tableBits) t = lCandidate.Cur.offset - e.cur if s-t < maxMatchOffset { if uint32(cv) == lCandidate.Cur.val { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur, val: uint32(next)} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur, val: uint32(next)}, eLong.Cur t2 := lCandidate.Prev.offset - e.cur if s-t2 < maxMatchOffset && uint32(cv) == lCandidate.Prev.val { l = e.matchlen(s+4, t+4, src) + 4 ml1 := e.matchlen(s+4, t2+4, src) + 4 if ml1 > l { t = t2 l = ml1 break } } break } t = lCandidate.Prev.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == lCandidate.Prev.val { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur, val: uint32(next)} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur, val: uint32(next)}, eLong.Cur break } } t = sCandidate.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == sCandidate.val { // Found a 4 match... l = e.matchlen(s+4, t+4, src) + 4 lCandidate = e.bTable[nextHashL] // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur, val: uint32(next)} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur, val: uint32(next)}, eLong.Cur // If the next long is a candidate, use that... t2 := lCandidate.Cur.offset - e.cur if nextS-t2 < maxMatchOffset { if lCandidate.Cur.val == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } // If the previous long is a candidate, use that... t2 = lCandidate.Prev.offset - e.cur if nextS-t2 < maxMatchOffset && lCandidate.Prev.val == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } } break } cv = next } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. // Extend the 4-byte match as long as possible. if l == 0 { l = e.matchlenLong(s+4, t+4, src) + 4 } else if l == maxMatchLength { l += e.matchlenLong(s+l, t+l, src) } // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { emitLiteral(dst, src[nextEmit:s]) } if false { if t >= s { panic(fmt.Sprintln("s-t", s, t)) } if (s - t) > maxMatchOffset { panic(fmt.Sprintln("mmo", s-t)) } if l < baseMatchLength { panic("bml") } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { goto emitRemainder } // Store every 3rd hash in-between. if true { const hashEvery = 3 i := s - l + 1 if i < s-1 { cv := load6432(src, i) t := tableEntry{offset: i + e.cur, val: uint32(cv)} e.table[hash4x64(cv, tableBits)] = t eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur // Do an long at i+1 cv >>= 8 t = tableEntry{offset: t.offset + 1, val: uint32(cv)} eLong = &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur // We only have enough bits for a short entry at i+2 cv >>= 8 t = tableEntry{offset: t.offset + 1, val: uint32(cv)} e.table[hash4x64(cv, tableBits)] = t // Skip one - otherwise we risk hitting 's' i += 4 for ; i < s-1; i += hashEvery { cv := load6432(src, i) t := tableEntry{offset: i + e.cur, val: uint32(cv)} t2 := tableEntry{offset: t.offset + 1, val: uint32(cv >> 8)} eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur e.table[hash4u(t2.val, tableBits)] = t2 } } } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-1 and at s. x := load6432(src, s-1) o := e.cur + s - 1 prevHashS := hash4x64(x, tableBits) prevHashL := hash7(x, tableBits) e.table[prevHashS] = tableEntry{offset: o, val: uint32(x)} eLong := &e.bTable[prevHashL] eLong.Cur, eLong.Prev = tableEntry{offset: o, val: uint32(x)}, eLong.Cur cv = x >> 8 } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } }