block.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. package lz4block
  2. import (
  3. "encoding/binary"
  4. "math/bits"
  5. "sync"
  6. "github.com/pierrec/lz4/v4/internal/lz4errors"
  7. )
  8. const (
  9. // The following constants are used to setup the compression algorithm.
  10. minMatch = 4 // the minimum size of the match sequence size (4 bytes)
  11. winSizeLog = 16 // LZ4 64Kb window size limit
  12. winSize = 1 << winSizeLog
  13. winMask = winSize - 1 // 64Kb window of previous data for dependent blocks
  14. // hashLog determines the size of the hash table used to quickly find a previous match position.
  15. // Its value influences the compression speed and memory usage, the lower the faster,
  16. // but at the expense of the compression ratio.
  17. // 16 seems to be the best compromise for fast compression.
  18. hashLog = 16
  19. htSize = 1 << hashLog
  20. mfLimit = 10 + minMatch // The last match cannot start within the last 14 bytes.
  21. )
  22. func recoverBlock(e *error) {
  23. if r := recover(); r != nil && *e == nil {
  24. *e = lz4errors.ErrInvalidSourceShortBuffer
  25. }
  26. }
  27. // blockHash hashes the lower five bytes of x into a value < htSize.
  28. func blockHash(x uint64) uint32 {
  29. const prime6bytes = 227718039650203
  30. x &= 1<<40 - 1
  31. return uint32((x * prime6bytes) >> (64 - hashLog))
  32. }
  33. func CompressBlockBound(n int) int {
  34. return n + n/255 + 16
  35. }
  36. func UncompressBlock(src, dst, dict []byte) (int, error) {
  37. if len(src) == 0 {
  38. return 0, nil
  39. }
  40. if di := decodeBlock(dst, src, dict); di >= 0 {
  41. return di, nil
  42. }
  43. return 0, lz4errors.ErrInvalidSourceShortBuffer
  44. }
  45. type Compressor struct {
  46. // Offsets are at most 64kiB, so we can store only the lower 16 bits of
  47. // match positions: effectively, an offset from some 64kiB block boundary.
  48. //
  49. // When we retrieve such an offset, we interpret it as relative to the last
  50. // block boundary si &^ 0xffff, or the one before, (si &^ 0xffff) - 0x10000,
  51. // depending on which of these is inside the current window. If a table
  52. // entry was generated more than 64kiB back in the input, we find out by
  53. // inspecting the input stream.
  54. table [htSize]uint16
  55. // Bitmap indicating which positions in the table are in use.
  56. // This allows us to quickly reset the table for reuse,
  57. // without having to zero everything.
  58. inUse [htSize / 32]uint32
  59. }
  60. // Get returns the position of a presumptive match for the hash h.
  61. // The match may be a false positive due to a hash collision or an old entry.
  62. // If si < winSize, the return value may be negative.
  63. func (c *Compressor) get(h uint32, si int) int {
  64. h &= htSize - 1
  65. i := 0
  66. if c.inUse[h/32]&(1<<(h%32)) != 0 {
  67. i = int(c.table[h])
  68. }
  69. i += si &^ winMask
  70. if i >= si {
  71. // Try previous 64kiB block (negative when in first block).
  72. i -= winSize
  73. }
  74. return i
  75. }
  76. func (c *Compressor) put(h uint32, si int) {
  77. h &= htSize - 1
  78. c.table[h] = uint16(si)
  79. c.inUse[h/32] |= 1 << (h % 32)
  80. }
  81. func (c *Compressor) reset() { c.inUse = [htSize / 32]uint32{} }
  82. var compressorPool = sync.Pool{New: func() interface{} { return new(Compressor) }}
  83. func CompressBlock(src, dst []byte) (int, error) {
  84. c := compressorPool.Get().(*Compressor)
  85. n, err := c.CompressBlock(src, dst)
  86. compressorPool.Put(c)
  87. return n, err
  88. }
  89. func (c *Compressor) CompressBlock(src, dst []byte) (int, error) {
  90. // Zero out reused table to avoid non-deterministic output (issue #65).
  91. c.reset()
  92. // Return 0, nil only if the destination buffer size is < CompressBlockBound.
  93. isNotCompressible := len(dst) < CompressBlockBound(len(src))
  94. // adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible.
  95. // This significantly speeds up incompressible data and usually has very small impact on compression.
  96. // bytes to skip = 1 + (bytes since last match >> adaptSkipLog)
  97. const adaptSkipLog = 7
  98. // si: Current position of the search.
  99. // anchor: Position of the current literals.
  100. var si, di, anchor int
  101. sn := len(src) - mfLimit
  102. if sn <= 0 {
  103. goto lastLiterals
  104. }
  105. // Fast scan strategy: the hash table only stores the last five-byte sequences.
  106. for si < sn {
  107. // Hash the next five bytes (sequence)...
  108. match := binary.LittleEndian.Uint64(src[si:])
  109. h := blockHash(match)
  110. h2 := blockHash(match >> 8)
  111. // We check a match at s, s+1 and s+2 and pick the first one we get.
  112. // Checking 3 only requires us to load the source one.
  113. ref := c.get(h, si)
  114. ref2 := c.get(h2, si+1)
  115. c.put(h, si)
  116. c.put(h2, si+1)
  117. offset := si - ref
  118. if offset <= 0 || offset >= winSize || uint32(match) != binary.LittleEndian.Uint32(src[ref:]) {
  119. // No match. Start calculating another hash.
  120. // The processor can usually do this out-of-order.
  121. h = blockHash(match >> 16)
  122. ref3 := c.get(h, si+2)
  123. // Check the second match at si+1
  124. si += 1
  125. offset = si - ref2
  126. if offset <= 0 || offset >= winSize || uint32(match>>8) != binary.LittleEndian.Uint32(src[ref2:]) {
  127. // No match. Check the third match at si+2
  128. si += 1
  129. offset = si - ref3
  130. c.put(h, si)
  131. if offset <= 0 || offset >= winSize || uint32(match>>16) != binary.LittleEndian.Uint32(src[ref3:]) {
  132. // Skip one extra byte (at si+3) before we check 3 matches again.
  133. si += 2 + (si-anchor)>>adaptSkipLog
  134. continue
  135. }
  136. }
  137. }
  138. // Match found.
  139. lLen := si - anchor // Literal length.
  140. // We already matched 4 bytes.
  141. mLen := 4
  142. // Extend backwards if we can, reducing literals.
  143. tOff := si - offset - 1
  144. for lLen > 0 && tOff >= 0 && src[si-1] == src[tOff] {
  145. si--
  146. tOff--
  147. lLen--
  148. mLen++
  149. }
  150. // Add the match length, so we continue search at the end.
  151. // Use mLen to store the offset base.
  152. si, mLen = si+mLen, si+minMatch
  153. // Find the longest match by looking by batches of 8 bytes.
  154. for si+8 <= sn {
  155. x := binary.LittleEndian.Uint64(src[si:]) ^ binary.LittleEndian.Uint64(src[si-offset:])
  156. if x == 0 {
  157. si += 8
  158. } else {
  159. // Stop is first non-zero byte.
  160. si += bits.TrailingZeros64(x) >> 3
  161. break
  162. }
  163. }
  164. mLen = si - mLen
  165. if di >= len(dst) {
  166. return 0, lz4errors.ErrInvalidSourceShortBuffer
  167. }
  168. if mLen < 0xF {
  169. dst[di] = byte(mLen)
  170. } else {
  171. dst[di] = 0xF
  172. }
  173. // Encode literals length.
  174. if lLen < 0xF {
  175. dst[di] |= byte(lLen << 4)
  176. } else {
  177. dst[di] |= 0xF0
  178. di++
  179. l := lLen - 0xF
  180. for ; l >= 0xFF && di < len(dst); l -= 0xFF {
  181. dst[di] = 0xFF
  182. di++
  183. }
  184. if di >= len(dst) {
  185. return 0, lz4errors.ErrInvalidSourceShortBuffer
  186. }
  187. dst[di] = byte(l)
  188. }
  189. di++
  190. // Literals.
  191. if di+lLen > len(dst) {
  192. return 0, lz4errors.ErrInvalidSourceShortBuffer
  193. }
  194. copy(dst[di:di+lLen], src[anchor:anchor+lLen])
  195. di += lLen + 2
  196. anchor = si
  197. // Encode offset.
  198. if di > len(dst) {
  199. return 0, lz4errors.ErrInvalidSourceShortBuffer
  200. }
  201. dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
  202. // Encode match length part 2.
  203. if mLen >= 0xF {
  204. for mLen -= 0xF; mLen >= 0xFF && di < len(dst); mLen -= 0xFF {
  205. dst[di] = 0xFF
  206. di++
  207. }
  208. if di >= len(dst) {
  209. return 0, lz4errors.ErrInvalidSourceShortBuffer
  210. }
  211. dst[di] = byte(mLen)
  212. di++
  213. }
  214. // Check if we can load next values.
  215. if si >= sn {
  216. break
  217. }
  218. // Hash match end-2
  219. h = blockHash(binary.LittleEndian.Uint64(src[si-2:]))
  220. c.put(h, si-2)
  221. }
  222. lastLiterals:
  223. if isNotCompressible && anchor == 0 {
  224. // Incompressible.
  225. return 0, nil
  226. }
  227. // Last literals.
  228. if di >= len(dst) {
  229. return 0, lz4errors.ErrInvalidSourceShortBuffer
  230. }
  231. lLen := len(src) - anchor
  232. if lLen < 0xF {
  233. dst[di] = byte(lLen << 4)
  234. } else {
  235. dst[di] = 0xF0
  236. di++
  237. for lLen -= 0xF; lLen >= 0xFF && di < len(dst); lLen -= 0xFF {
  238. dst[di] = 0xFF
  239. di++
  240. }
  241. if di >= len(dst) {
  242. return 0, lz4errors.ErrInvalidSourceShortBuffer
  243. }
  244. dst[di] = byte(lLen)
  245. }
  246. di++
  247. // Write the last literals.
  248. if isNotCompressible && di >= anchor {
  249. // Incompressible.
  250. return 0, nil
  251. }
  252. if di+len(src)-anchor > len(dst) {
  253. return 0, lz4errors.ErrInvalidSourceShortBuffer
  254. }
  255. di += copy(dst[di:di+len(src)-anchor], src[anchor:])
  256. return di, nil
  257. }
  258. // blockHash hashes 4 bytes into a value < winSize.
  259. func blockHashHC(x uint32) uint32 {
  260. const hasher uint32 = 2654435761 // Knuth multiplicative hash.
  261. return x * hasher >> (32 - winSizeLog)
  262. }
  263. type CompressorHC struct {
  264. // hashTable: stores the last position found for a given hash
  265. // chainTable: stores previous positions for a given hash
  266. hashTable, chainTable [htSize]int
  267. needsReset bool
  268. }
  269. var compressorHCPool = sync.Pool{New: func() interface{} { return new(CompressorHC) }}
  270. func CompressBlockHC(src, dst []byte, depth CompressionLevel) (int, error) {
  271. c := compressorHCPool.Get().(*CompressorHC)
  272. n, err := c.CompressBlock(src, dst, depth)
  273. compressorHCPool.Put(c)
  274. return n, err
  275. }
  276. func (c *CompressorHC) CompressBlock(src, dst []byte, depth CompressionLevel) (_ int, err error) {
  277. if c.needsReset {
  278. // Zero out reused table to avoid non-deterministic output (issue #65).
  279. c.hashTable = [htSize]int{}
  280. c.chainTable = [htSize]int{}
  281. }
  282. c.needsReset = true // Only false on first call.
  283. defer recoverBlock(&err)
  284. // Return 0, nil only if the destination buffer size is < CompressBlockBound.
  285. isNotCompressible := len(dst) < CompressBlockBound(len(src))
  286. // adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible.
  287. // This significantly speeds up incompressible data and usually has very small impact on compression.
  288. // bytes to skip = 1 + (bytes since last match >> adaptSkipLog)
  289. const adaptSkipLog = 7
  290. var si, di, anchor int
  291. sn := len(src) - mfLimit
  292. if sn <= 0 {
  293. goto lastLiterals
  294. }
  295. if depth == 0 {
  296. depth = winSize
  297. }
  298. for si < sn {
  299. // Hash the next 4 bytes (sequence).
  300. match := binary.LittleEndian.Uint32(src[si:])
  301. h := blockHashHC(match)
  302. // Follow the chain until out of window and give the longest match.
  303. mLen := 0
  304. offset := 0
  305. for next, try := c.hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next, try = c.chainTable[next&winMask], try-1 {
  306. // The first (mLen==0) or next byte (mLen>=minMatch) at current match length
  307. // must match to improve on the match length.
  308. if src[next+mLen] != src[si+mLen] {
  309. continue
  310. }
  311. ml := 0
  312. // Compare the current position with a previous with the same hash.
  313. for ml < sn-si {
  314. x := binary.LittleEndian.Uint64(src[next+ml:]) ^ binary.LittleEndian.Uint64(src[si+ml:])
  315. if x == 0 {
  316. ml += 8
  317. } else {
  318. // Stop is first non-zero byte.
  319. ml += bits.TrailingZeros64(x) >> 3
  320. break
  321. }
  322. }
  323. if ml < minMatch || ml <= mLen {
  324. // Match too small (<minMath) or smaller than the current match.
  325. continue
  326. }
  327. // Found a longer match, keep its position and length.
  328. mLen = ml
  329. offset = si - next
  330. // Try another previous position with the same hash.
  331. }
  332. c.chainTable[si&winMask] = c.hashTable[h]
  333. c.hashTable[h] = si
  334. // No match found.
  335. if mLen == 0 {
  336. si += 1 + (si-anchor)>>adaptSkipLog
  337. continue
  338. }
  339. // Match found.
  340. // Update hash/chain tables with overlapping bytes:
  341. // si already hashed, add everything from si+1 up to the match length.
  342. winStart := si + 1
  343. if ws := si + mLen - winSize; ws > winStart {
  344. winStart = ws
  345. }
  346. for si, ml := winStart, si+mLen; si < ml; {
  347. match >>= 8
  348. match |= uint32(src[si+3]) << 24
  349. h := blockHashHC(match)
  350. c.chainTable[si&winMask] = c.hashTable[h]
  351. c.hashTable[h] = si
  352. si++
  353. }
  354. lLen := si - anchor
  355. si += mLen
  356. mLen -= minMatch // Match length does not include minMatch.
  357. if mLen < 0xF {
  358. dst[di] = byte(mLen)
  359. } else {
  360. dst[di] = 0xF
  361. }
  362. // Encode literals length.
  363. if lLen < 0xF {
  364. dst[di] |= byte(lLen << 4)
  365. } else {
  366. dst[di] |= 0xF0
  367. di++
  368. l := lLen - 0xF
  369. for ; l >= 0xFF; l -= 0xFF {
  370. dst[di] = 0xFF
  371. di++
  372. }
  373. dst[di] = byte(l)
  374. }
  375. di++
  376. // Literals.
  377. copy(dst[di:di+lLen], src[anchor:anchor+lLen])
  378. di += lLen
  379. anchor = si
  380. // Encode offset.
  381. di += 2
  382. dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
  383. // Encode match length part 2.
  384. if mLen >= 0xF {
  385. for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
  386. dst[di] = 0xFF
  387. di++
  388. }
  389. dst[di] = byte(mLen)
  390. di++
  391. }
  392. }
  393. if isNotCompressible && anchor == 0 {
  394. // Incompressible.
  395. return 0, nil
  396. }
  397. // Last literals.
  398. lastLiterals:
  399. lLen := len(src) - anchor
  400. if lLen < 0xF {
  401. dst[di] = byte(lLen << 4)
  402. } else {
  403. dst[di] = 0xF0
  404. di++
  405. lLen -= 0xF
  406. for ; lLen >= 0xFF; lLen -= 0xFF {
  407. dst[di] = 0xFF
  408. di++
  409. }
  410. dst[di] = byte(lLen)
  411. }
  412. di++
  413. // Write the last literals.
  414. if isNotCompressible && di >= anchor {
  415. // Incompressible.
  416. return 0, nil
  417. }
  418. di += copy(dst[di:di+len(src)-anchor], src[anchor:])
  419. return di, nil
  420. }