block.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. package lz4
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. )
  6. // block represents a frame data block.
  7. // Used when compressing or decompressing frame blocks concurrently.
  8. type block struct {
  9. compressed bool
  10. zdata []byte // compressed data
  11. data []byte // decompressed data
  12. offset int // offset within the data as with block dependency the 64Kb window is prepended to it
  13. checksum uint32 // compressed data checksum
  14. err error // error while [de]compressing
  15. }
  16. var (
  17. // ErrInvalidSource is returned by UncompressBlock when a compressed block is corrupted.
  18. ErrInvalidSource = errors.New("lz4: invalid source")
  19. // ErrShortBuffer is returned by UncompressBlock, CompressBlock or CompressBlockHC when
  20. // the supplied buffer for [de]compression is too small.
  21. ErrShortBuffer = errors.New("lz4: short buffer")
  22. )
  23. // CompressBlockBound returns the maximum size of a given buffer of size n, when not compressible.
  24. func CompressBlockBound(n int) int {
  25. return n + n/255 + 16
  26. }
  27. // UncompressBlock decompresses the source buffer into the destination one,
  28. // starting at the di index and returning the decompressed size.
  29. //
  30. // The destination buffer must be sized appropriately.
  31. //
  32. // An error is returned if the source data is invalid or the destination buffer is too small.
  33. func UncompressBlock(src, dst []byte, di int) (int, error) {
  34. si, sn, di0 := 0, len(src), di
  35. if sn == 0 {
  36. return 0, nil
  37. }
  38. for {
  39. // literals and match lengths (token)
  40. lLen := int(src[si] >> 4)
  41. mLen := int(src[si] & 0xF)
  42. if si++; si == sn {
  43. return di, ErrInvalidSource
  44. }
  45. // literals
  46. if lLen > 0 {
  47. if lLen == 0xF {
  48. for src[si] == 0xFF {
  49. lLen += 0xFF
  50. if si++; si == sn {
  51. return di - di0, ErrInvalidSource
  52. }
  53. }
  54. lLen += int(src[si])
  55. if si++; si == sn {
  56. return di - di0, ErrInvalidSource
  57. }
  58. }
  59. if len(dst)-di < lLen || si+lLen > sn {
  60. return di - di0, ErrShortBuffer
  61. }
  62. di += copy(dst[di:], src[si:si+lLen])
  63. if si += lLen; si >= sn {
  64. return di - di0, nil
  65. }
  66. }
  67. if si += 2; si >= sn {
  68. return di, ErrInvalidSource
  69. }
  70. offset := int(src[si-2]) | int(src[si-1])<<8
  71. if di-offset < 0 || offset == 0 {
  72. return di - di0, ErrInvalidSource
  73. }
  74. // match
  75. if mLen == 0xF {
  76. for src[si] == 0xFF {
  77. mLen += 0xFF
  78. if si++; si == sn {
  79. return di - di0, ErrInvalidSource
  80. }
  81. }
  82. mLen += int(src[si])
  83. if si++; si == sn {
  84. return di - di0, ErrInvalidSource
  85. }
  86. }
  87. // minimum match length is 4
  88. mLen += 4
  89. if len(dst)-di <= mLen {
  90. return di - di0, ErrShortBuffer
  91. }
  92. // copy the match (NB. match is at least 4 bytes long)
  93. // NB. past di, copy() would write old bytes instead of
  94. // the ones we just copied, so split the work into the largest chunk.
  95. for ; mLen >= offset; mLen -= offset {
  96. di += copy(dst[di:], dst[di-offset:di])
  97. }
  98. di += copy(dst[di:], dst[di-offset:di-offset+mLen])
  99. }
  100. }
  101. // CompressBlock compresses the source buffer starting at soffet into the destination one.
  102. // This is the fast version of LZ4 compression and also the default one.
  103. //
  104. // The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible.
  105. //
  106. // An error is returned if the destination buffer is too small.
  107. func CompressBlock(src, dst []byte, soffset int) (int, error) {
  108. sn, dn := len(src)-mfLimit, len(dst)
  109. if sn <= 0 || dn == 0 || soffset >= sn {
  110. return 0, nil
  111. }
  112. var si, di int
  113. // fast scan strategy:
  114. // we only need a hash table to store the last sequences (4 bytes)
  115. var hashTable [1 << hashLog]int
  116. var hashShift = uint((minMatch * 8) - hashLog)
  117. // Initialise the hash table with the first 64Kb of the input buffer
  118. // (used when compressing dependent blocks)
  119. for si < soffset {
  120. h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
  121. si++
  122. hashTable[h] = si
  123. }
  124. anchor := si
  125. fma := 1 << skipStrength
  126. for si < sn-minMatch {
  127. // hash the next 4 bytes (sequence)...
  128. h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
  129. // -1 to separate existing entries from new ones
  130. ref := hashTable[h] - 1
  131. // ...and store the position of the hash in the hash table (+1 to compensate the -1 upon saving)
  132. hashTable[h] = si + 1
  133. // no need to check the last 3 bytes in the first literal 4 bytes as
  134. // this guarantees that the next match, if any, is compressed with
  135. // a lower size, since to have some compression we must have:
  136. // ll+ml-overlap > 1 + (ll-15)/255 + (ml-4-15)/255 + 2 (uncompressed size>compressed size)
  137. // => ll+ml>3+2*overlap => ll+ml>= 4+2*overlap
  138. // and by definition we do have:
  139. // ll >= 1, ml >= 4
  140. // => ll+ml >= 5
  141. // => so overlap must be 0
  142. // the sequence is new, out of bound (64kb) or not valid: try next sequence
  143. if ref < 0 || fma&(1<<skipStrength-1) < 4 ||
  144. (si-ref)>>winSizeLog > 0 ||
  145. src[ref] != src[si] ||
  146. src[ref+1] != src[si+1] ||
  147. src[ref+2] != src[si+2] ||
  148. src[ref+3] != src[si+3] {
  149. // variable step: improves performance on non-compressible data
  150. si += fma >> skipStrength
  151. fma++
  152. continue
  153. }
  154. // match found
  155. fma = 1 << skipStrength
  156. lLen := si - anchor
  157. offset := si - ref
  158. // encode match length part 1
  159. si += minMatch
  160. mLen := si // match length has minMatch already
  161. for si <= sn && src[si] == src[si-offset] {
  162. si++
  163. }
  164. mLen = si - mLen
  165. if mLen < 0xF {
  166. dst[di] = byte(mLen)
  167. } else {
  168. dst[di] = 0xF
  169. }
  170. // encode literals length
  171. if lLen < 0xF {
  172. dst[di] |= byte(lLen << 4)
  173. } else {
  174. dst[di] |= 0xF0
  175. if di++; di == dn {
  176. return di, ErrShortBuffer
  177. }
  178. l := lLen - 0xF
  179. for ; l >= 0xFF; l -= 0xFF {
  180. dst[di] = 0xFF
  181. if di++; di == dn {
  182. return di, ErrShortBuffer
  183. }
  184. }
  185. dst[di] = byte(l)
  186. }
  187. if di++; di == dn {
  188. return di, ErrShortBuffer
  189. }
  190. // literals
  191. if di+lLen >= dn {
  192. return di, ErrShortBuffer
  193. }
  194. di += copy(dst[di:], src[anchor:anchor+lLen])
  195. anchor = si
  196. // encode offset
  197. if di += 2; di >= dn {
  198. return di, ErrShortBuffer
  199. }
  200. dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
  201. // encode match length part 2
  202. if mLen >= 0xF {
  203. for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
  204. dst[di] = 0xFF
  205. if di++; di == dn {
  206. return di, ErrShortBuffer
  207. }
  208. }
  209. dst[di] = byte(mLen)
  210. if di++; di == dn {
  211. return di, ErrShortBuffer
  212. }
  213. }
  214. }
  215. if anchor == 0 {
  216. // incompressible
  217. return 0, nil
  218. }
  219. // last literals
  220. lLen := len(src) - anchor
  221. if lLen < 0xF {
  222. dst[di] = byte(lLen << 4)
  223. } else {
  224. dst[di] = 0xF0
  225. if di++; di == dn {
  226. return di, ErrShortBuffer
  227. }
  228. lLen -= 0xF
  229. for ; lLen >= 0xFF; lLen -= 0xFF {
  230. dst[di] = 0xFF
  231. if di++; di == dn {
  232. return di, ErrShortBuffer
  233. }
  234. }
  235. dst[di] = byte(lLen)
  236. }
  237. if di++; di == dn {
  238. return di, ErrShortBuffer
  239. }
  240. // write literals
  241. src = src[anchor:]
  242. switch n := di + len(src); {
  243. case n > dn:
  244. return di, ErrShortBuffer
  245. case n >= sn:
  246. // incompressible
  247. return 0, nil
  248. }
  249. di += copy(dst[di:], src)
  250. return di, nil
  251. }
  252. // CompressBlockHC compresses the source buffer starting at soffet into the destination one.
  253. // CompressBlockHC compression ratio is better than CompressBlock but it is also slower.
  254. //
  255. // The size of the compressed data is returned. If it is 0 and no error, then the data is not compressible.
  256. //
  257. // An error is returned if the destination buffer is too small.
  258. func CompressBlockHC(src, dst []byte, soffset int) (int, error) {
  259. sn, dn := len(src)-mfLimit, len(dst)
  260. if sn <= 0 || dn == 0 || soffset >= sn {
  261. return 0, nil
  262. }
  263. var si, di int
  264. // Hash Chain strategy:
  265. // we need a hash table and a chain table
  266. // the chain table cannot contain more entries than the window size (64Kb entries)
  267. var hashTable [1 << hashLog]int
  268. var chainTable [winSize]int
  269. var hashShift = uint((minMatch * 8) - hashLog)
  270. // Initialise the hash table with the first 64Kb of the input buffer
  271. // (used when compressing dependent blocks)
  272. for si < soffset {
  273. h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
  274. chainTable[si&winMask] = hashTable[h]
  275. si++
  276. hashTable[h] = si
  277. }
  278. anchor := si
  279. for si < sn-minMatch {
  280. // hash the next 4 bytes (sequence)...
  281. h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
  282. // follow the chain until out of window and give the longest match
  283. mLen := 0
  284. offset := 0
  285. for next := hashTable[h] - 1; next > 0 && next > si-winSize; next = chainTable[next&winMask] - 1 {
  286. // the first (mLen==0) or next byte (mLen>=minMatch) at current match length must match to improve on the match length
  287. if src[next+mLen] == src[si+mLen] {
  288. for ml := 0; ; ml++ {
  289. if src[next+ml] != src[si+ml] || si+ml > sn {
  290. // found a longer match, keep its position and length
  291. if mLen < ml && ml >= minMatch {
  292. mLen = ml
  293. offset = si - next
  294. }
  295. break
  296. }
  297. }
  298. }
  299. }
  300. chainTable[si&winMask] = hashTable[h]
  301. hashTable[h] = si + 1
  302. // no match found
  303. if mLen == 0 {
  304. si++
  305. continue
  306. }
  307. // match found
  308. // update hash/chain tables with overlaping bytes:
  309. // si already hashed, add everything from si+1 up to the match length
  310. for si, ml := si+1, si+mLen; si < ml; {
  311. h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
  312. chainTable[si&winMask] = hashTable[h]
  313. si++
  314. hashTable[h] = si
  315. }
  316. lLen := si - anchor
  317. si += mLen
  318. mLen -= minMatch // match length does not include minMatch
  319. if mLen < 0xF {
  320. dst[di] = byte(mLen)
  321. } else {
  322. dst[di] = 0xF
  323. }
  324. // encode literals length
  325. if lLen < 0xF {
  326. dst[di] |= byte(lLen << 4)
  327. } else {
  328. dst[di] |= 0xF0
  329. if di++; di == dn {
  330. return di, ErrShortBuffer
  331. }
  332. l := lLen - 0xF
  333. for ; l >= 0xFF; l -= 0xFF {
  334. dst[di] = 0xFF
  335. if di++; di == dn {
  336. return di, ErrShortBuffer
  337. }
  338. }
  339. dst[di] = byte(l)
  340. }
  341. if di++; di == dn {
  342. return di, ErrShortBuffer
  343. }
  344. // literals
  345. if di+lLen >= dn {
  346. return di, ErrShortBuffer
  347. }
  348. di += copy(dst[di:], src[anchor:anchor+lLen])
  349. anchor = si
  350. // encode offset
  351. if di += 2; di >= dn {
  352. return di, ErrShortBuffer
  353. }
  354. dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
  355. // encode match length part 2
  356. if mLen >= 0xF {
  357. for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
  358. dst[di] = 0xFF
  359. if di++; di == dn {
  360. return di, ErrShortBuffer
  361. }
  362. }
  363. dst[di] = byte(mLen)
  364. if di++; di == dn {
  365. return di, ErrShortBuffer
  366. }
  367. }
  368. }
  369. if anchor == 0 {
  370. // incompressible
  371. return 0, nil
  372. }
  373. // last literals
  374. lLen := len(src) - anchor
  375. if lLen < 0xF {
  376. dst[di] = byte(lLen << 4)
  377. } else {
  378. dst[di] = 0xF0
  379. if di++; di == dn {
  380. return di, ErrShortBuffer
  381. }
  382. lLen -= 0xF
  383. for ; lLen >= 0xFF; lLen -= 0xFF {
  384. dst[di] = 0xFF
  385. if di++; di == dn {
  386. return di, ErrShortBuffer
  387. }
  388. }
  389. dst[di] = byte(lLen)
  390. }
  391. if di++; di == dn {
  392. return di, ErrShortBuffer
  393. }
  394. // write literals
  395. src = src[anchor:]
  396. switch n := di + len(src); {
  397. case n > dn:
  398. return di, ErrShortBuffer
  399. case n >= sn:
  400. // incompressible
  401. return 0, nil
  402. }
  403. di += copy(dst[di:], src)
  404. return di, nil
  405. }