lz4convert.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. // Copyright (c) 2022 Klaus Post. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package s2
  5. import (
  6. "encoding/binary"
  7. "errors"
  8. "fmt"
  9. )
  10. // LZ4Converter provides conversion from LZ4 blocks as defined here:
  11. // https://github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md
  12. type LZ4Converter struct {
  13. }
  14. // ErrDstTooSmall is returned when provided destination is too small.
  15. var ErrDstTooSmall = errors.New("s2: destination too small")
  16. // ConvertBlock will convert an LZ4 block and append it as an S2
  17. // block without block length to dst.
  18. // The uncompressed size is returned as well.
  19. // dst must have capacity to contain the entire compressed block.
  20. func (l *LZ4Converter) ConvertBlock(dst, src []byte) ([]byte, int, error) {
  21. if len(src) == 0 {
  22. return dst, 0, nil
  23. }
  24. const debug = false
  25. const inline = true
  26. const lz4MinMatch = 4
  27. s, d := 0, len(dst)
  28. dst = dst[:cap(dst)]
  29. if !debug && hasAmd64Asm {
  30. res, sz := cvtLZ4BlockAsm(dst[d:], src)
  31. if res < 0 {
  32. const (
  33. errCorrupt = -1
  34. errDstTooSmall = -2
  35. )
  36. switch res {
  37. case errCorrupt:
  38. return nil, 0, ErrCorrupt
  39. case errDstTooSmall:
  40. return nil, 0, ErrDstTooSmall
  41. default:
  42. return nil, 0, fmt.Errorf("unexpected result: %d", res)
  43. }
  44. }
  45. if d+sz > len(dst) {
  46. return nil, 0, ErrDstTooSmall
  47. }
  48. return dst[:d+sz], res, nil
  49. }
  50. dLimit := len(dst) - 10
  51. var lastOffset uint16
  52. var uncompressed int
  53. if debug {
  54. fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst))
  55. }
  56. for {
  57. if s >= len(src) {
  58. return dst[:d], 0, ErrCorrupt
  59. }
  60. // Read literal info
  61. token := src[s]
  62. ll := int(token >> 4)
  63. ml := int(lz4MinMatch + (token & 0xf))
  64. // If upper nibble is 15, literal length is extended
  65. if token >= 0xf0 {
  66. for {
  67. s++
  68. if s >= len(src) {
  69. if debug {
  70. fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src))
  71. }
  72. return dst[:d], 0, ErrCorrupt
  73. }
  74. val := src[s]
  75. ll += int(val)
  76. if val != 255 {
  77. break
  78. }
  79. }
  80. }
  81. // Skip past token
  82. if s+ll >= len(src) {
  83. if debug {
  84. fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src))
  85. }
  86. return nil, 0, ErrCorrupt
  87. }
  88. s++
  89. if ll > 0 {
  90. if d+ll > dLimit {
  91. return nil, 0, ErrDstTooSmall
  92. }
  93. if debug {
  94. fmt.Printf("emit %d literals\n", ll)
  95. }
  96. d += emitLiteralGo(dst[d:], src[s:s+ll])
  97. s += ll
  98. uncompressed += ll
  99. }
  100. // Check if we are done...
  101. if s == len(src) && ml == lz4MinMatch {
  102. break
  103. }
  104. // 2 byte offset
  105. if s >= len(src)-2 {
  106. if debug {
  107. fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2)
  108. }
  109. return nil, 0, ErrCorrupt
  110. }
  111. offset := binary.LittleEndian.Uint16(src[s:])
  112. s += 2
  113. if offset == 0 {
  114. if debug {
  115. fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s)
  116. }
  117. return nil, 0, ErrCorrupt
  118. }
  119. if int(offset) > uncompressed {
  120. if debug {
  121. fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed)
  122. }
  123. return nil, 0, ErrCorrupt
  124. }
  125. if ml == lz4MinMatch+15 {
  126. for {
  127. if s >= len(src) {
  128. if debug {
  129. fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
  130. }
  131. return nil, 0, ErrCorrupt
  132. }
  133. val := src[s]
  134. s++
  135. ml += int(val)
  136. if val != 255 {
  137. if s >= len(src) {
  138. if debug {
  139. fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
  140. }
  141. return nil, 0, ErrCorrupt
  142. }
  143. break
  144. }
  145. }
  146. }
  147. if offset == lastOffset {
  148. if debug {
  149. fmt.Printf("emit repeat, length: %d, offset: %d\n", ml, offset)
  150. }
  151. if !inline {
  152. d += emitRepeat16(dst[d:], offset, ml)
  153. } else {
  154. length := ml
  155. dst := dst[d:]
  156. for len(dst) > 5 {
  157. // Repeat offset, make length cheaper
  158. length -= 4
  159. if length <= 4 {
  160. dst[0] = uint8(length)<<2 | tagCopy1
  161. dst[1] = 0
  162. d += 2
  163. break
  164. }
  165. if length < 8 && offset < 2048 {
  166. // Encode WITH offset
  167. dst[1] = uint8(offset)
  168. dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1
  169. d += 2
  170. break
  171. }
  172. if length < (1<<8)+4 {
  173. length -= 4
  174. dst[2] = uint8(length)
  175. dst[1] = 0
  176. dst[0] = 5<<2 | tagCopy1
  177. d += 3
  178. break
  179. }
  180. if length < (1<<16)+(1<<8) {
  181. length -= 1 << 8
  182. dst[3] = uint8(length >> 8)
  183. dst[2] = uint8(length >> 0)
  184. dst[1] = 0
  185. dst[0] = 6<<2 | tagCopy1
  186. d += 4
  187. break
  188. }
  189. const maxRepeat = (1 << 24) - 1
  190. length -= 1 << 16
  191. left := 0
  192. if length > maxRepeat {
  193. left = length - maxRepeat + 4
  194. length = maxRepeat - 4
  195. }
  196. dst[4] = uint8(length >> 16)
  197. dst[3] = uint8(length >> 8)
  198. dst[2] = uint8(length >> 0)
  199. dst[1] = 0
  200. dst[0] = 7<<2 | tagCopy1
  201. if left > 0 {
  202. d += 5 + emitRepeat16(dst[5:], offset, left)
  203. break
  204. }
  205. d += 5
  206. break
  207. }
  208. }
  209. } else {
  210. if debug {
  211. fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset)
  212. }
  213. if !inline {
  214. d += emitCopy16(dst[d:], offset, ml)
  215. } else {
  216. length := ml
  217. dst := dst[d:]
  218. for len(dst) > 5 {
  219. // Offset no more than 2 bytes.
  220. if length > 64 {
  221. off := 3
  222. if offset < 2048 {
  223. // emit 8 bytes as tagCopy1, rest as repeats.
  224. dst[1] = uint8(offset)
  225. dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1
  226. length -= 8
  227. off = 2
  228. } else {
  229. // Emit a length 60 copy, encoded as 3 bytes.
  230. // Emit remaining as repeat value (minimum 4 bytes).
  231. dst[2] = uint8(offset >> 8)
  232. dst[1] = uint8(offset)
  233. dst[0] = 59<<2 | tagCopy2
  234. length -= 60
  235. }
  236. // Emit remaining as repeats, at least 4 bytes remain.
  237. d += off + emitRepeat16(dst[off:], offset, length)
  238. break
  239. }
  240. if length >= 12 || offset >= 2048 {
  241. // Emit the remaining copy, encoded as 3 bytes.
  242. dst[2] = uint8(offset >> 8)
  243. dst[1] = uint8(offset)
  244. dst[0] = uint8(length-1)<<2 | tagCopy2
  245. d += 3
  246. break
  247. }
  248. // Emit the remaining copy, encoded as 2 bytes.
  249. dst[1] = uint8(offset)
  250. dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
  251. d += 2
  252. break
  253. }
  254. }
  255. lastOffset = offset
  256. }
  257. uncompressed += ml
  258. if d > dLimit {
  259. return nil, 0, ErrDstTooSmall
  260. }
  261. }
  262. return dst[:d], uncompressed, nil
  263. }
  264. // ConvertBlockSnappy will convert an LZ4 block and append it
  265. // as a Snappy block without block length to dst.
  266. // The uncompressed size is returned as well.
  267. // dst must have capacity to contain the entire compressed block.
  268. func (l *LZ4Converter) ConvertBlockSnappy(dst, src []byte) ([]byte, int, error) {
  269. if len(src) == 0 {
  270. return dst, 0, nil
  271. }
  272. const debug = false
  273. const lz4MinMatch = 4
  274. s, d := 0, len(dst)
  275. dst = dst[:cap(dst)]
  276. // Use assembly when possible
  277. if !debug && hasAmd64Asm {
  278. res, sz := cvtLZ4BlockSnappyAsm(dst[d:], src)
  279. if res < 0 {
  280. const (
  281. errCorrupt = -1
  282. errDstTooSmall = -2
  283. )
  284. switch res {
  285. case errCorrupt:
  286. return nil, 0, ErrCorrupt
  287. case errDstTooSmall:
  288. return nil, 0, ErrDstTooSmall
  289. default:
  290. return nil, 0, fmt.Errorf("unexpected result: %d", res)
  291. }
  292. }
  293. if d+sz > len(dst) {
  294. return nil, 0, ErrDstTooSmall
  295. }
  296. return dst[:d+sz], res, nil
  297. }
  298. dLimit := len(dst) - 10
  299. var uncompressed int
  300. if debug {
  301. fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst))
  302. }
  303. for {
  304. if s >= len(src) {
  305. return nil, 0, ErrCorrupt
  306. }
  307. // Read literal info
  308. token := src[s]
  309. ll := int(token >> 4)
  310. ml := int(lz4MinMatch + (token & 0xf))
  311. // If upper nibble is 15, literal length is extended
  312. if token >= 0xf0 {
  313. for {
  314. s++
  315. if s >= len(src) {
  316. if debug {
  317. fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src))
  318. }
  319. return nil, 0, ErrCorrupt
  320. }
  321. val := src[s]
  322. ll += int(val)
  323. if val != 255 {
  324. break
  325. }
  326. }
  327. }
  328. // Skip past token
  329. if s+ll >= len(src) {
  330. if debug {
  331. fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src))
  332. }
  333. return nil, 0, ErrCorrupt
  334. }
  335. s++
  336. if ll > 0 {
  337. if d+ll > dLimit {
  338. return nil, 0, ErrDstTooSmall
  339. }
  340. if debug {
  341. fmt.Printf("emit %d literals\n", ll)
  342. }
  343. d += emitLiteralGo(dst[d:], src[s:s+ll])
  344. s += ll
  345. uncompressed += ll
  346. }
  347. // Check if we are done...
  348. if s == len(src) && ml == lz4MinMatch {
  349. break
  350. }
  351. // 2 byte offset
  352. if s >= len(src)-2 {
  353. if debug {
  354. fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2)
  355. }
  356. return nil, 0, ErrCorrupt
  357. }
  358. offset := binary.LittleEndian.Uint16(src[s:])
  359. s += 2
  360. if offset == 0 {
  361. if debug {
  362. fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s)
  363. }
  364. return nil, 0, ErrCorrupt
  365. }
  366. if int(offset) > uncompressed {
  367. if debug {
  368. fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed)
  369. }
  370. return nil, 0, ErrCorrupt
  371. }
  372. if ml == lz4MinMatch+15 {
  373. for {
  374. if s >= len(src) {
  375. if debug {
  376. fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
  377. }
  378. return nil, 0, ErrCorrupt
  379. }
  380. val := src[s]
  381. s++
  382. ml += int(val)
  383. if val != 255 {
  384. if s >= len(src) {
  385. if debug {
  386. fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
  387. }
  388. return nil, 0, ErrCorrupt
  389. }
  390. break
  391. }
  392. }
  393. }
  394. if debug {
  395. fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset)
  396. }
  397. length := ml
  398. // d += emitCopyNoRepeat(dst[d:], int(offset), ml)
  399. for length > 0 {
  400. if d >= dLimit {
  401. return nil, 0, ErrDstTooSmall
  402. }
  403. // Offset no more than 2 bytes.
  404. if length > 64 {
  405. // Emit a length 64 copy, encoded as 3 bytes.
  406. dst[d+2] = uint8(offset >> 8)
  407. dst[d+1] = uint8(offset)
  408. dst[d+0] = 63<<2 | tagCopy2
  409. length -= 64
  410. d += 3
  411. continue
  412. }
  413. if length >= 12 || offset >= 2048 || length < 4 {
  414. // Emit the remaining copy, encoded as 3 bytes.
  415. dst[d+2] = uint8(offset >> 8)
  416. dst[d+1] = uint8(offset)
  417. dst[d+0] = uint8(length-1)<<2 | tagCopy2
  418. d += 3
  419. break
  420. }
  421. // Emit the remaining copy, encoded as 2 bytes.
  422. dst[d+1] = uint8(offset)
  423. dst[d+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
  424. d += 2
  425. break
  426. }
  427. uncompressed += ml
  428. if d > dLimit {
  429. return nil, 0, ErrDstTooSmall
  430. }
  431. }
  432. return dst[:d], uncompressed, nil
  433. }
  434. // emitRepeat writes a repeat chunk and returns the number of bytes written.
  435. // Length must be at least 4 and < 1<<24
  436. func emitRepeat16(dst []byte, offset uint16, length int) int {
  437. // Repeat offset, make length cheaper
  438. length -= 4
  439. if length <= 4 {
  440. dst[0] = uint8(length)<<2 | tagCopy1
  441. dst[1] = 0
  442. return 2
  443. }
  444. if length < 8 && offset < 2048 {
  445. // Encode WITH offset
  446. dst[1] = uint8(offset)
  447. dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1
  448. return 2
  449. }
  450. if length < (1<<8)+4 {
  451. length -= 4
  452. dst[2] = uint8(length)
  453. dst[1] = 0
  454. dst[0] = 5<<2 | tagCopy1
  455. return 3
  456. }
  457. if length < (1<<16)+(1<<8) {
  458. length -= 1 << 8
  459. dst[3] = uint8(length >> 8)
  460. dst[2] = uint8(length >> 0)
  461. dst[1] = 0
  462. dst[0] = 6<<2 | tagCopy1
  463. return 4
  464. }
  465. const maxRepeat = (1 << 24) - 1
  466. length -= 1 << 16
  467. left := 0
  468. if length > maxRepeat {
  469. left = length - maxRepeat + 4
  470. length = maxRepeat - 4
  471. }
  472. dst[4] = uint8(length >> 16)
  473. dst[3] = uint8(length >> 8)
  474. dst[2] = uint8(length >> 0)
  475. dst[1] = 0
  476. dst[0] = 7<<2 | tagCopy1
  477. if left > 0 {
  478. return 5 + emitRepeat16(dst[5:], offset, left)
  479. }
  480. return 5
  481. }
  482. // emitCopy writes a copy chunk and returns the number of bytes written.
  483. //
  484. // It assumes that:
  485. //
  486. // dst is long enough to hold the encoded bytes
  487. // 1 <= offset && offset <= math.MaxUint16
  488. // 4 <= length && length <= math.MaxUint32
  489. func emitCopy16(dst []byte, offset uint16, length int) int {
  490. // Offset no more than 2 bytes.
  491. if length > 64 {
  492. off := 3
  493. if offset < 2048 {
  494. // emit 8 bytes as tagCopy1, rest as repeats.
  495. dst[1] = uint8(offset)
  496. dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1
  497. length -= 8
  498. off = 2
  499. } else {
  500. // Emit a length 60 copy, encoded as 3 bytes.
  501. // Emit remaining as repeat value (minimum 4 bytes).
  502. dst[2] = uint8(offset >> 8)
  503. dst[1] = uint8(offset)
  504. dst[0] = 59<<2 | tagCopy2
  505. length -= 60
  506. }
  507. // Emit remaining as repeats, at least 4 bytes remain.
  508. return off + emitRepeat16(dst[off:], offset, length)
  509. }
  510. if length >= 12 || offset >= 2048 {
  511. // Emit the remaining copy, encoded as 3 bytes.
  512. dst[2] = uint8(offset >> 8)
  513. dst[1] = uint8(offset)
  514. dst[0] = uint8(length-1)<<2 | tagCopy2
  515. return 3
  516. }
  517. // Emit the remaining copy, encoded as 2 bytes.
  518. dst[1] = uint8(offset)
  519. dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
  520. return 2
  521. }
  522. // emitLiteral writes a literal chunk and returns the number of bytes written.
  523. //
  524. // It assumes that:
  525. //
  526. // dst is long enough to hold the encoded bytes
  527. // 0 <= len(lit) && len(lit) <= math.MaxUint32
  528. func emitLiteralGo(dst, lit []byte) int {
  529. if len(lit) == 0 {
  530. return 0
  531. }
  532. i, n := 0, uint(len(lit)-1)
  533. switch {
  534. case n < 60:
  535. dst[0] = uint8(n)<<2 | tagLiteral
  536. i = 1
  537. case n < 1<<8:
  538. dst[1] = uint8(n)
  539. dst[0] = 60<<2 | tagLiteral
  540. i = 2
  541. case n < 1<<16:
  542. dst[2] = uint8(n >> 8)
  543. dst[1] = uint8(n)
  544. dst[0] = 61<<2 | tagLiteral
  545. i = 3
  546. case n < 1<<24:
  547. dst[3] = uint8(n >> 16)
  548. dst[2] = uint8(n >> 8)
  549. dst[1] = uint8(n)
  550. dst[0] = 62<<2 | tagLiteral
  551. i = 4
  552. default:
  553. dst[4] = uint8(n >> 24)
  554. dst[3] = uint8(n >> 16)
  555. dst[2] = uint8(n >> 8)
  556. dst[1] = uint8(n)
  557. dst[0] = 63<<2 | tagLiteral
  558. i = 5
  559. }
  560. return i + copy(dst[i:], lit)
  561. }