decode_other.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. //go:build (!amd64 && !arm && !arm64) || appengine || !gc || noasm
  2. // +build !amd64,!arm,!arm64 appengine !gc noasm
  3. package lz4block
  4. import (
  5. "encoding/binary"
  6. )
  7. func decodeBlock(dst, src, dict []byte) (ret int) {
  8. // Restrict capacities so we don't read or write out of bounds.
  9. dst = dst[:len(dst):len(dst)]
  10. src = src[:len(src):len(src)]
  11. const hasError = -2
  12. if len(src) == 0 {
  13. return hasError
  14. }
  15. defer func() {
  16. if recover() != nil {
  17. ret = hasError
  18. }
  19. }()
  20. var si, di uint
  21. for si < uint(len(src)) {
  22. // Literals and match lengths (token).
  23. b := uint(src[si])
  24. si++
  25. // Literals.
  26. if lLen := b >> 4; lLen > 0 {
  27. switch {
  28. case lLen < 0xF && si+16 < uint(len(src)):
  29. // Shortcut 1
  30. // if we have enough room in src and dst, and the literals length
  31. // is small enough (0..14) then copy all 16 bytes, even if not all
  32. // are part of the literals.
  33. copy(dst[di:], src[si:si+16])
  34. si += lLen
  35. di += lLen
  36. if mLen := b & 0xF; mLen < 0xF {
  37. // Shortcut 2
  38. // if the match length (4..18) fits within the literals, then copy
  39. // all 18 bytes, even if not all are part of the literals.
  40. mLen += 4
  41. if offset := u16(src[si:]); mLen <= offset && offset < di {
  42. i := di - offset
  43. end := i + 18
  44. copy(dst[di:], dst[i:end])
  45. si += 2
  46. di += mLen
  47. continue
  48. }
  49. }
  50. case lLen == 0xF:
  51. for {
  52. x := uint(src[si])
  53. if lLen += x; int(lLen) < 0 {
  54. return hasError
  55. }
  56. si++
  57. if x != 0xFF {
  58. break
  59. }
  60. }
  61. fallthrough
  62. default:
  63. copy(dst[di:di+lLen], src[si:si+lLen])
  64. si += lLen
  65. di += lLen
  66. }
  67. }
  68. mLen := b & 0xF
  69. if si == uint(len(src)) && mLen == 0 {
  70. break
  71. } else if si >= uint(len(src)) {
  72. return hasError
  73. }
  74. offset := u16(src[si:])
  75. if offset == 0 {
  76. return hasError
  77. }
  78. si += 2
  79. // Match.
  80. mLen += minMatch
  81. if mLen == minMatch+0xF {
  82. for {
  83. x := uint(src[si])
  84. if mLen += x; int(mLen) < 0 {
  85. return hasError
  86. }
  87. si++
  88. if x != 0xFF {
  89. break
  90. }
  91. }
  92. }
  93. // Copy the match.
  94. if di < offset {
  95. // The match is beyond our block, meaning the first part
  96. // is in the dictionary.
  97. fromDict := dict[uint(len(dict))+di-offset:]
  98. n := uint(copy(dst[di:di+mLen], fromDict))
  99. di += n
  100. if mLen -= n; mLen == 0 {
  101. continue
  102. }
  103. // We copied n = offset-di bytes from the dictionary,
  104. // then set di = di+n = offset, so the following code
  105. // copies from dst[di-offset:] = dst[0:].
  106. }
  107. expanded := dst[di-offset:]
  108. if mLen > offset {
  109. // Efficiently copy the match dst[di-offset:di] into the dst slice.
  110. bytesToCopy := offset * (mLen / offset)
  111. for n := offset; n <= bytesToCopy+offset; n *= 2 {
  112. copy(expanded[n:], expanded[:n])
  113. }
  114. di += bytesToCopy
  115. mLen -= bytesToCopy
  116. }
  117. di += uint(copy(dst[di:di+mLen], expanded[:mLen]))
  118. }
  119. return int(di)
  120. }
  121. func u16(p []byte) uint { return uint(binary.LittleEndian.Uint16(p)) }