decompress_amd64.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //go:build amd64 && !appengine && !noasm && gc
  2. // This file contains the specialisation of Decoder.Decompress4X
  3. // and Decoder.Decompress1X that use an asm implementation of thir main loops.
  4. package huff0
  5. import (
  6. "errors"
  7. "fmt"
  8. "github.com/klauspost/compress/internal/cpuinfo"
  9. )
  10. // decompress4x_main_loop_x86 is an x86 assembler implementation
  11. // of Decompress4X when tablelog > 8.
  12. //
  13. //go:noescape
  14. func decompress4x_main_loop_amd64(ctx *decompress4xContext)
  15. // decompress4x_8b_loop_x86 is an x86 assembler implementation
  16. // of Decompress4X when tablelog <= 8 which decodes 4 entries
  17. // per loop.
  18. //
  19. //go:noescape
  20. func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext)
  21. // fallback8BitSize is the size where using Go version is faster.
  22. const fallback8BitSize = 800
  23. type decompress4xContext struct {
  24. pbr *[4]bitReaderShifted
  25. peekBits uint8
  26. out *byte
  27. dstEvery int
  28. tbl *dEntrySingle
  29. decoded int
  30. limit *byte
  31. }
  32. // Decompress4X will decompress a 4X encoded stream.
  33. // The length of the supplied input must match the end of a block exactly.
  34. // The *capacity* of the dst slice must match the destination size of
  35. // the uncompressed data exactly.
  36. func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
  37. if len(d.dt.single) == 0 {
  38. return nil, errors.New("no table loaded")
  39. }
  40. if len(src) < 6+(4*1) {
  41. return nil, errors.New("input too small")
  42. }
  43. use8BitTables := d.actualTableLog <= 8
  44. if cap(dst) < fallback8BitSize && use8BitTables {
  45. return d.decompress4X8bit(dst, src)
  46. }
  47. var br [4]bitReaderShifted
  48. // Decode "jump table"
  49. start := 6
  50. for i := range 3 {
  51. length := int(src[i*2]) | (int(src[i*2+1]) << 8)
  52. if start+length >= len(src) {
  53. return nil, errors.New("truncated input (or invalid offset)")
  54. }
  55. err := br[i].init(src[start : start+length])
  56. if err != nil {
  57. return nil, err
  58. }
  59. start += length
  60. }
  61. err := br[3].init(src[start:])
  62. if err != nil {
  63. return nil, err
  64. }
  65. // destination, offset to match first output
  66. dstSize := cap(dst)
  67. dst = dst[:dstSize]
  68. out := dst
  69. dstEvery := (dstSize + 3) / 4
  70. const tlSize = 1 << tableLogMax
  71. const tlMask = tlSize - 1
  72. single := d.dt.single[:tlSize]
  73. var decoded int
  74. if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) {
  75. ctx := decompress4xContext{
  76. pbr: &br,
  77. peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast()
  78. out: &out[0],
  79. dstEvery: dstEvery,
  80. tbl: &single[0],
  81. limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last.
  82. }
  83. if use8BitTables {
  84. decompress4x_8b_main_loop_amd64(&ctx)
  85. } else {
  86. decompress4x_main_loop_amd64(&ctx)
  87. }
  88. decoded = ctx.decoded
  89. out = out[decoded/4:]
  90. }
  91. // Decode remaining.
  92. remainBytes := dstEvery - (decoded / 4)
  93. for i := range br {
  94. offset := dstEvery * i
  95. endsAt := min(offset+remainBytes, len(out))
  96. br := &br[i]
  97. bitsLeft := br.remaining()
  98. for bitsLeft > 0 {
  99. br.fill()
  100. if offset >= endsAt {
  101. return nil, errors.New("corruption detected: stream overrun 4")
  102. }
  103. // Read value and increment offset.
  104. val := br.peekBitsFast(d.actualTableLog)
  105. v := single[val&tlMask].entry
  106. nBits := uint8(v)
  107. br.advance(nBits)
  108. bitsLeft -= uint(nBits)
  109. out[offset] = uint8(v >> 8)
  110. offset++
  111. }
  112. if offset != endsAt {
  113. return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt)
  114. }
  115. decoded += offset - dstEvery*i
  116. err = br.close()
  117. if err != nil {
  118. return nil, err
  119. }
  120. }
  121. if dstSize != decoded {
  122. return nil, errors.New("corruption detected: short output block")
  123. }
  124. return dst, nil
  125. }
  126. // decompress4x_main_loop_x86 is an x86 assembler implementation
  127. // of Decompress1X when tablelog > 8.
  128. //
  129. //go:noescape
  130. func decompress1x_main_loop_amd64(ctx *decompress1xContext)
  131. // decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation
  132. // of Decompress1X when tablelog > 8.
  133. //
  134. //go:noescape
  135. func decompress1x_main_loop_bmi2(ctx *decompress1xContext)
  136. type decompress1xContext struct {
  137. pbr *bitReaderShifted
  138. peekBits uint8
  139. out *byte
  140. outCap int
  141. tbl *dEntrySingle
  142. decoded int
  143. }
  144. // Error reported by asm implementations
  145. const error_max_decoded_size_exeeded = -1
  146. // Decompress1X will decompress a 1X encoded stream.
  147. // The cap of the output buffer will be the maximum decompressed size.
  148. // The length of the supplied input must match the end of a block exactly.
  149. func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
  150. if len(d.dt.single) == 0 {
  151. return nil, errors.New("no table loaded")
  152. }
  153. var br bitReaderShifted
  154. err := br.init(src)
  155. if err != nil {
  156. return dst, err
  157. }
  158. maxDecodedSize := cap(dst)
  159. dst = dst[:maxDecodedSize]
  160. const tlSize = 1 << tableLogMax
  161. const tlMask = tlSize - 1
  162. if maxDecodedSize >= 4 {
  163. ctx := decompress1xContext{
  164. pbr: &br,
  165. out: &dst[0],
  166. outCap: maxDecodedSize,
  167. peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast()
  168. tbl: &d.dt.single[0],
  169. }
  170. if cpuinfo.HasBMI2() {
  171. decompress1x_main_loop_bmi2(&ctx)
  172. } else {
  173. decompress1x_main_loop_amd64(&ctx)
  174. }
  175. if ctx.decoded == error_max_decoded_size_exeeded {
  176. return nil, ErrMaxDecodedSizeExceeded
  177. }
  178. dst = dst[:ctx.decoded]
  179. }
  180. // br < 8, so uint8 is fine
  181. bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead
  182. for bitsLeft > 0 {
  183. br.fill()
  184. if len(dst) >= maxDecodedSize {
  185. br.close()
  186. return nil, ErrMaxDecodedSizeExceeded
  187. }
  188. v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask]
  189. nBits := uint8(v.entry)
  190. br.advance(nBits)
  191. bitsLeft -= nBits
  192. dst = append(dst, uint8(v.entry>>8))
  193. }
  194. return dst, br.close()
  195. }