func CompressBlockHC(src, dst []byte, depth int) (_ int, err error) {
	defer recoverBlock(&err)
	const adaptSkipLog = 7
	sn, dn := len(src)-mfLimit, len(dst)
	if sn <= 0 || dn == 0 {
		return 0, nil
	}
	var si, di int
	var hashTable, chainTable [winSize]int
	if depth <= 0 {
		depth = winSize
	}
	anchor := si
	for si < sn {
		match := binary.LittleEndian.Uint32(src[si:])
		h := blockHashHC(match)
		mLen := 0
		offset := 0
		for next, try := hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next = chainTable[next&winMask] {
			if src[next+mLen] != src[si+mLen] {
				continue
			}
			ml := 0
			for ml < sn-si {
				x := binary.LittleEndian.Uint64(src[next+ml:]) ^ binary.LittleEndian.Uint64(src[si+ml:])
				if x == 0 {
					ml += 8
				} else {
					ml += bits.TrailingZeros64(x) >> 3
					break
				}
			}
			if ml < minMatch || ml <= mLen {
				continue
			}
			mLen = ml
			offset = si - next
			try--
		}
		chainTable[si&winMask] = hashTable[h]
		hashTable[h] = si
		if mLen == 0 {
			si += 1 + (si-anchor)>>adaptSkipLog
			continue
		}
		winStart := si + 1
		if ws := si + mLen - winSize; ws > winStart {
			winStart = ws
		}
		for si, ml := winStart, si+mLen; si < ml; {
			match >>= 8
			match |= uint32(src[si+3]) << 24
			h := blockHashHC(match)
			chainTable[si&winMask] = hashTable[h]
			hashTable[h] = si
			si++
		}
		lLen := si - anchor
		si += mLen
		mLen -= minMatch  
		if mLen < 0xF {
			dst[di] = byte(mLen)
		} else {
			dst[di] = 0xF
		}
		if lLen < 0xF {
			dst[di] |= byte(lLen << 4)
		} else {
			dst[di] |= 0xF0
			di++
			l := lLen - 0xF
			for ; l >= 0xFF; l -= 0xFF {
				dst[di] = 0xFF
				di++
			}
			dst[di] = byte(l)
		}
		di++
		copy(dst[di:di+lLen], src[anchor:anchor+lLen])
		di += lLen
		anchor = si
		di += 2
		dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
		if mLen >= 0xF {
			for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
				dst[di] = 0xFF
				di++
			}
			dst[di] = byte(mLen)
			di++
		}
	}
	if anchor == 0 {
		return 0, nil
	}
	lLen := len(src) - anchor
	if lLen < 0xF {
		dst[di] = byte(lLen << 4)
	} else {
		dst[di] = 0xF0
		di++
		lLen -= 0xF
		for ; lLen >= 0xFF; lLen -= 0xFF {
			dst[di] = 0xFF
			di++
		}
		dst[di] = byte(lLen)
	}
	di++
	if di >= anchor {
		return 0, nil
	}
	di += copy(dst[di:di+len(src)-anchor], src[anchor:])
	return di, nil
}
CompressBlockHC compresses the source buffer src into the destination dst
with max search depth (use 0 or negative value for no max).

CompressBlockHC compression ratio is better than CompressBlock but it is also slower.

The size of the compressed data is returned. If it is 0 and no error, then the data is not compressible.

An error is returned if the destination buffer is too small.
package lz4
import (
	"encoding/binary"
	"fmt"
	"math/bits"
)
func blockHash(x uint64) uint32 {
	const prime6bytes = 227718039650203
	return uint32(((x << (64 - 48)) * prime6bytes) >> (64 - hashLog))
}
func CompressBlockBound(n int) int {
	return n + n/255 + 16
}
func UncompressBlock(src, dst []byte) (int, error) {
	if len(src) == 0 {
		return 0, nil
	}
	if di := decodeBlock(dst, src); di >= 0 {
		return di, nil
	}
	return 0, ErrInvalidSourceShortBuffer
}
func CompressBlock(src, dst []byte, hashTable []int) (_ int, err error) {
	if len(hashTable) < htSize {
		return 0, fmt.Errorf("hash table too small, should be at least %d in size", htSize)
	}
	defer recoverBlock(&err)
	const adaptSkipLog = 7
	sn, dn := len(src)-mfLimit, len(dst)
	if sn <= 0 || dn == 0 {
		return 0, nil
	}
	hashTable = hashTable[:htSize]
	var si, di, anchor int
	for si < sn {
		match := binary.LittleEndian.Uint64(src[si:])
		h := blockHash(match)
		h2 := blockHash(match >> 8)
		ref := hashTable[h]
		ref2 := hashTable[h2]
		hashTable[h] = si
		hashTable[h2] = si + 1
		offset := si - ref
		if offset <= 0 || offset >= winSize ||  
			uint32(match) != binary.LittleEndian.Uint32(src[ref:]) {  
			h = blockHash(match >> 16)
			ref = hashTable[h]
			si += 1
			offset = si - ref2
			if offset <= 0 || offset >= winSize ||
				uint32(match>>8) != binary.LittleEndian.Uint32(src[ref2:]) {
				si += 1
				offset = si - ref
				hashTable[h] = si
				if offset <= 0 || offset >= winSize ||
					uint32(match>>16) != binary.LittleEndian.Uint32(src[ref:]) {
					si += 2 + (si-anchor)>>adaptSkipLog
					continue
				}
			}
		}
		lLen := si - anchor  
		mLen := 4
		tOff := si - offset - 1
		for lLen > 0 && tOff >= 0 && src[si-1] == src[tOff] {
			si--
			tOff--
			lLen--
			mLen++
		}
		si, mLen = si+mLen, si+minMatch
		for si+8 < sn {
			x := binary.LittleEndian.Uint64(src[si:]) ^ binary.LittleEndian.Uint64(src[si-offset:])
			if x == 0 {
				si += 8
			} else {
				si += bits.TrailingZeros64(x) >> 3
				break
			}
		}
		mLen = si - mLen
		if mLen < 0xF {
			dst[di] = byte(mLen)
		} else {
			dst[di] = 0xF
		}
		if lLen < 0xF {
			dst[di] |= byte(lLen << 4)
		} else {
			dst[di] |= 0xF0
			di++
			l := lLen - 0xF
			for ; l >= 0xFF; l -= 0xFF {
				dst[di] = 0xFF
				di++
			}
			dst[di] = byte(l)
		}
		di++
		copy(dst[di:di+lLen], src[anchor:anchor+lLen])
		di += lLen + 2
		anchor = si
		_ = dst[di]  
		dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
		if mLen >= 0xF {
			for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
				dst[di] = 0xFF
				di++
			}
			dst[di] = byte(mLen)
			di++
		}
		if si >= sn {
			break
		}
		h = blockHash(binary.LittleEndian.Uint64(src[si-2:]))
		hashTable[h] = si - 2
	}
	if anchor == 0 {
		return 0, nil
	}
	lLen := len(src) - anchor
	if lLen < 0xF {
		dst[di] = byte(lLen << 4)
	} else {
		dst[di] = 0xF0
		di++
		for lLen -= 0xF; lLen >= 0xFF; lLen -= 0xFF {
			dst[di] = 0xFF
			di++
		}
		dst[di] = byte(lLen)
	}
	di++
	if di >= anchor {
		return 0, nil
	}
	di += copy(dst[di:di+len(src)-anchor], src[anchor:])
	return di, nil
}
func blockHashHC(x uint32) uint32 {
	const hasher uint32 = 2654435761  
	return x * hasher >> (32 - winSizeLog)
}