block.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. package lz4
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. )
  6. var (
  7. // ErrInvalidSourceShortBuffer is returned by UncompressBlock or CompressBLock when a compressed
  8. // block is corrupted or the destination buffer is not large enough for the uncompressed data.
  9. ErrInvalidSourceShortBuffer = errors.New("lz4: invalid source or destination buffer too short")
  10. // ErrInvalid is returned when reading an invalid LZ4 archive.
  11. ErrInvalid = errors.New("lz4: bad magic number")
  12. )
  13. // blockHash hashes 4 bytes into a value < winSize.
  14. func blockHash(x uint32) uint32 {
  15. const hasher uint32 = 2654435761 // Knuth multiplicative hash.
  16. return x * hasher >> hashShift
  17. }
  18. // CompressBlockBound returns the maximum size of a given buffer of size n, when not compressible.
  19. func CompressBlockBound(n int) int {
  20. return n + n/255 + 16
  21. }
  22. // UncompressBlock uncompresses the source buffer into the destination one,
  23. // and returns the uncompressed size.
  24. //
  25. // The destination buffer must be sized appropriately.
  26. //
  27. // An error is returned if the source data is invalid or the destination buffer is too small.
  28. func UncompressBlock(src, dst []byte) (si int, err error) {
  29. defer func() {
  30. // It is now faster to let the runtime panic and recover on out of bound slice access
  31. // than checking indices as we go along.
  32. if recover() != nil {
  33. err = ErrInvalidSourceShortBuffer
  34. }
  35. }()
  36. sn := len(src)
  37. if sn == 0 {
  38. return 0, nil
  39. }
  40. var di int
  41. for {
  42. // Literals and match lengths (token).
  43. b := int(src[si])
  44. si++
  45. // Literals.
  46. if lLen := b >> 4; lLen > 0 {
  47. if lLen == 0xF {
  48. for src[si] == 0xFF {
  49. lLen += 0xFF
  50. si++
  51. }
  52. lLen += int(src[si])
  53. si++
  54. }
  55. i := si
  56. si += lLen
  57. di += copy(dst[di:], src[i:si])
  58. if si >= sn {
  59. return di, nil
  60. }
  61. }
  62. si++
  63. _ = src[si] // Bound check elimination.
  64. offset := int(src[si-1]) | int(src[si])<<8
  65. si++
  66. // Match.
  67. mLen := b & 0xF
  68. if mLen == 0xF {
  69. for src[si] == 0xFF {
  70. mLen += 0xFF
  71. si++
  72. }
  73. mLen += int(src[si])
  74. si++
  75. }
  76. mLen += minMatch
  77. // Copy the match.
  78. i := di - offset
  79. if offset > 0 && mLen >= offset {
  80. // Efficiently copy the match dst[di-offset:di] into the dst slice.
  81. bytesToCopy := offset * (mLen / offset)
  82. expanded := dst[i:]
  83. for n := offset; n <= bytesToCopy+offset; n *= 2 {
  84. copy(expanded[n:], expanded[:n])
  85. }
  86. di += bytesToCopy
  87. mLen -= bytesToCopy
  88. }
  89. di += copy(dst[di:], dst[i:i+mLen])
  90. }
  91. }
  92. // CompressBlock compresses the source buffer into the destination one.
  93. // This is the fast version of LZ4 compression and also the default one.
  94. // The size of hashTable must be at least 64Kb.
  95. //
  96. // The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible.
  97. //
  98. // An error is returned if the destination buffer is too small.
  99. func CompressBlock(src, dst []byte, hashTable []int) (di int, err error) {
  100. defer func() {
  101. if recover() != nil {
  102. err = ErrInvalidSourceShortBuffer
  103. }
  104. }()
  105. sn, dn := len(src)-mfLimit, len(dst)
  106. if sn <= 0 || dn == 0 {
  107. return 0, nil
  108. }
  109. var si int
  110. // Fast scan strategy: the hash table only stores the last 4 bytes sequences.
  111. // const accInit = 1 << skipStrength
  112. anchor := si // Position of the current literals.
  113. // acc := accInit // Variable step: improves performance on non-compressible data.
  114. for si < sn {
  115. // Hash the next 4 bytes (sequence)...
  116. match := binary.LittleEndian.Uint32(src[si:])
  117. h := blockHash(match)
  118. ref := hashTable[h]
  119. hashTable[h] = si
  120. if ref >= sn { // Invalid reference (dirty hashtable).
  121. si++
  122. continue
  123. }
  124. offset := si - ref
  125. if offset <= 0 || offset >= winSize || // Out of window.
  126. match != binary.LittleEndian.Uint32(src[ref:]) { // Hash collision on different matches.
  127. // si += acc >> skipStrength
  128. // acc++
  129. si++
  130. continue
  131. }
  132. // Match found.
  133. // acc = accInit
  134. lLen := si - anchor // Literal length.
  135. // Encode match length part 1.
  136. si += minMatch
  137. mLen := si // Match length has minMatch already.
  138. // Find the longest match, first looking by batches of 8 bytes.
  139. for si < sn && binary.LittleEndian.Uint64(src[si:]) == binary.LittleEndian.Uint64(src[si-offset:]) {
  140. si += 8
  141. }
  142. // Then byte by byte.
  143. for si < sn && src[si] == src[si-offset] {
  144. si++
  145. }
  146. mLen = si - mLen
  147. if mLen < 0xF {
  148. dst[di] = byte(mLen)
  149. } else {
  150. dst[di] = 0xF
  151. }
  152. // Encode literals length.
  153. if lLen < 0xF {
  154. dst[di] |= byte(lLen << 4)
  155. } else {
  156. dst[di] |= 0xF0
  157. di++
  158. l := lLen - 0xF
  159. for ; l >= 0xFF; l -= 0xFF {
  160. dst[di] = 0xFF
  161. di++
  162. }
  163. dst[di] = byte(l)
  164. }
  165. di++
  166. // Literals.
  167. copy(dst[di:], src[anchor:anchor+lLen])
  168. di += lLen + 2
  169. anchor = si
  170. // Encode offset.
  171. _ = dst[di] // Bound check elimination.
  172. dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
  173. // Encode match length part 2.
  174. if mLen >= 0xF {
  175. for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
  176. dst[di] = 0xFF
  177. di++
  178. }
  179. dst[di] = byte(mLen)
  180. di++
  181. }
  182. }
  183. if anchor == 0 {
  184. // Incompressible.
  185. return 0, nil
  186. }
  187. // Last literals.
  188. lLen := len(src) - anchor
  189. if lLen < 0xF {
  190. dst[di] = byte(lLen << 4)
  191. } else {
  192. dst[di] = 0xF0
  193. di++
  194. for lLen -= 0xF; lLen >= 0xFF; lLen -= 0xFF {
  195. dst[di] = 0xFF
  196. di++
  197. }
  198. dst[di] = byte(lLen)
  199. }
  200. di++
  201. // Write the last literals.
  202. if di >= anchor {
  203. // Incompressible.
  204. return 0, nil
  205. }
  206. di += copy(dst[di:], src[anchor:])
  207. return di, nil
  208. }
  209. // CompressBlockHC compresses the source buffer src into the destination dst
  210. // with max search depth (use 0 or negative value for no max).
  211. //
  212. // CompressBlockHC compression ratio is better than CompressBlock but it is also slower.
  213. //
  214. // The size of the compressed data is returned. If it is 0 and no error, then the data is not compressible.
  215. //
  216. // An error is returned if the destination buffer is too small.
  217. func CompressBlockHC(src, dst []byte, depth int) (di int, err error) {
  218. defer func() {
  219. if recover() != nil {
  220. err = ErrInvalidSourceShortBuffer
  221. }
  222. }()
  223. sn, dn := len(src)-mfLimit, len(dst)
  224. if sn <= 0 || dn == 0 {
  225. return 0, nil
  226. }
  227. var si int
  228. // hashTable: stores the last position found for a given hash
  229. // chaingTable: stores previous positions for a given hash
  230. var hashTable, chainTable [winSize]int
  231. if depth <= 0 {
  232. depth = winSize
  233. }
  234. anchor := si
  235. for si < sn {
  236. // Hash the next 4 bytes (sequence).
  237. match := binary.LittleEndian.Uint32(src[si:])
  238. h := blockHash(match)
  239. // Follow the chain until out of window and give the longest match.
  240. mLen := 0
  241. offset := 0
  242. for next, try := hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next = chainTable[next&winMask] {
  243. // The first (mLen==0) or next byte (mLen>=minMatch) at current match length
  244. // must match to improve on the match length.
  245. if src[next+mLen] != src[si+mLen] {
  246. continue
  247. }
  248. ml := 0
  249. // Compare the current position with a previous with the same hash.
  250. for ml < sn-si && binary.LittleEndian.Uint64(src[next+ml:]) == binary.LittleEndian.Uint64(src[si+ml:]) {
  251. ml += 8
  252. }
  253. for ml < sn-si && src[next+ml] == src[si+ml] {
  254. ml++
  255. }
  256. if ml+1 < minMatch || ml <= mLen {
  257. // Match too small (<minMath) or smaller than the current match.
  258. continue
  259. }
  260. // Found a longer match, keep its position and length.
  261. mLen = ml
  262. offset = si - next
  263. // Try another previous position with the same hash.
  264. try--
  265. }
  266. chainTable[si&winMask] = hashTable[h]
  267. hashTable[h] = si
  268. // No match found.
  269. if mLen == 0 {
  270. si++
  271. continue
  272. }
  273. // Match found.
  274. // Update hash/chain tables with overlapping bytes:
  275. // si already hashed, add everything from si+1 up to the match length.
  276. winStart := si + 1
  277. if ws := si + mLen - winSize; ws > winStart {
  278. winStart = ws
  279. }
  280. for si, ml := winStart, si+mLen; si < ml; {
  281. match >>= 8
  282. match |= uint32(src[si+3]) << 24
  283. h := blockHash(match)
  284. chainTable[si&winMask] = hashTable[h]
  285. hashTable[h] = si
  286. si++
  287. }
  288. lLen := si - anchor
  289. si += mLen
  290. mLen -= minMatch // Match length does not include minMatch.
  291. if mLen < 0xF {
  292. dst[di] = byte(mLen)
  293. } else {
  294. dst[di] = 0xF
  295. }
  296. // Encode literals length.
  297. if lLen < 0xF {
  298. dst[di] |= byte(lLen << 4)
  299. } else {
  300. dst[di] |= 0xF0
  301. di++
  302. l := lLen - 0xF
  303. for ; l >= 0xFF; l -= 0xFF {
  304. dst[di] = 0xFF
  305. di++
  306. }
  307. dst[di] = byte(l)
  308. }
  309. di++
  310. // Literals.
  311. copy(dst[di:], src[anchor:anchor+lLen])
  312. di += lLen
  313. anchor = si
  314. // Encode offset.
  315. di += 2
  316. dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
  317. // Encode match length part 2.
  318. if mLen >= 0xF {
  319. for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
  320. dst[di] = 0xFF
  321. di++
  322. }
  323. dst[di] = byte(mLen)
  324. di++
  325. }
  326. }
  327. if anchor == 0 {
  328. // Incompressible.
  329. return 0, nil
  330. }
  331. // Last literals.
  332. lLen := len(src) - anchor
  333. if lLen < 0xF {
  334. dst[di] = byte(lLen << 4)
  335. } else {
  336. dst[di] = 0xF0
  337. di++
  338. lLen -= 0xF
  339. for ; lLen >= 0xFF; lLen -= 0xFF {
  340. dst[di] = 0xFF
  341. di++
  342. }
  343. dst[di] = byte(lLen)
  344. }
  345. di++
  346. // Write the last literals.
  347. if di >= anchor {
  348. // Incompressible.
  349. return 0, nil
  350. }
  351. di += copy(dst[di:], src[anchor:])
  352. return di, nil
  353. }