Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+89
-89
@@ -1,89 +1,89 @@
|
||||
# Huff0 entropy compression
|
||||
|
||||
This package provides Huff0 encoding and decoding as used in zstd.
|
||||
|
||||
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
|
||||
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
|
||||
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
|
||||
|
||||
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
|
||||
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
|
||||
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
|
||||
|
||||
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
|
||||
|
||||
## News
|
||||
|
||||
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
|
||||
|
||||
This ensures that most functionality is well tested.
|
||||
|
||||
# Usage
|
||||
|
||||
This package provides a low level interface that allows to compress single independent blocks.
|
||||
|
||||
Each block is separate, and there is no built in integrity checks.
|
||||
This means that the caller should keep track of block sizes and also do checksums if needed.
|
||||
|
||||
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
|
||||
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
|
||||
You must provide input and will receive the output and maybe an error.
|
||||
|
||||
These error values can be returned:
|
||||
|
||||
| Error | Description |
|
||||
|---------------------|-----------------------------------------------------------------------------|
|
||||
| `<nil>` | Everything ok, output is returned |
|
||||
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
|
||||
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
|
||||
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
|
||||
| `(error)` | An internal error occurred. |
|
||||
|
||||
|
||||
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
|
||||
|
||||
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
|
||||
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
|
||||
object can be used for both.
|
||||
|
||||
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
|
||||
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
|
||||
|
||||
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
|
||||
|
||||
## Tables and re-use
|
||||
|
||||
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
|
||||
|
||||
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
|
||||
that controls this behaviour. See the documentation for details. This can be altered between each block.
|
||||
|
||||
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
|
||||
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
|
||||
based on the boolean reported back from the CompressXX call.
|
||||
|
||||
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
|
||||
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
|
||||
|
||||
## Decompressing
|
||||
|
||||
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
|
||||
This will initialize the decoding tables.
|
||||
You can supply the complete block to `ReadTable` and it will return the data part of the block
|
||||
which can be given to the decompressor.
|
||||
|
||||
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
|
||||
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
|
||||
|
||||
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
|
||||
|
||||
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
|
||||
your input was likely corrupted.
|
||||
|
||||
It is important to note that a successful decoding does *not* mean your output matches your original input.
|
||||
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
|
||||
changes will likely not be accepted. If in doubt open an issue before writing the PR.
|
||||
# Huff0 entropy compression
|
||||
|
||||
This package provides Huff0 encoding and decoding as used in zstd.
|
||||
|
||||
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
|
||||
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
|
||||
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
|
||||
|
||||
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
|
||||
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
|
||||
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
|
||||
|
||||
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
|
||||
|
||||
## News
|
||||
|
||||
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
|
||||
|
||||
This ensures that most functionality is well tested.
|
||||
|
||||
# Usage
|
||||
|
||||
This package provides a low level interface that allows to compress single independent blocks.
|
||||
|
||||
Each block is separate, and there is no built in integrity checks.
|
||||
This means that the caller should keep track of block sizes and also do checksums if needed.
|
||||
|
||||
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
|
||||
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
|
||||
You must provide input and will receive the output and maybe an error.
|
||||
|
||||
These error values can be returned:
|
||||
|
||||
| Error | Description |
|
||||
|---------------------|-----------------------------------------------------------------------------|
|
||||
| `<nil>` | Everything ok, output is returned |
|
||||
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
|
||||
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
|
||||
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
|
||||
| `(error)` | An internal error occurred. |
|
||||
|
||||
|
||||
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
|
||||
|
||||
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
|
||||
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
|
||||
object can be used for both.
|
||||
|
||||
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
|
||||
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
|
||||
|
||||
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
|
||||
|
||||
## Tables and re-use
|
||||
|
||||
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
|
||||
|
||||
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
|
||||
that controls this behaviour. See the documentation for details. This can be altered between each block.
|
||||
|
||||
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
|
||||
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
|
||||
based on the boolean reported back from the CompressXX call.
|
||||
|
||||
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
|
||||
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
|
||||
|
||||
## Decompressing
|
||||
|
||||
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
|
||||
This will initialize the decoding tables.
|
||||
You can supply the complete block to `ReadTable` and it will return the data part of the block
|
||||
which can be given to the decompressor.
|
||||
|
||||
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
|
||||
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
|
||||
|
||||
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
|
||||
|
||||
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
|
||||
your input was likely corrupted.
|
||||
|
||||
It is important to note that a successful decoding does *not* mean your output matches your original input.
|
||||
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
|
||||
changes will likely not be accepted. If in doubt open an issue before writing the PR.
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
package huff0
|
||||
|
||||
import "errors"
|
||||
|
||||
// BuildCTable builds a Huffman compression table from a precomputed symbol
|
||||
// histogram and installs it as the previous (reuse) table on s.
|
||||
//
|
||||
// After this call:
|
||||
// - EstimateSize/CanUseTable can probe the table against other histograms.
|
||||
// - Compress1X/Compress4X with Reuse = ReusePolicyMust will encode without
|
||||
// emitting a new table header.
|
||||
// - TransferCTable can hand the table to a sibling Scratch.
|
||||
//
|
||||
// count[i] is the number of occurrences of symbol i. The histogram must have
|
||||
// at least 2 distinct non-zero symbols; ErrUseRLE is returned for a single
|
||||
// symbol and an error is returned for an empty histogram.
|
||||
func (s *Scratch) BuildCTable(count *[256]uint32) error {
|
||||
if s == nil {
|
||||
return errors.New("huff0: BuildCTable on nil Scratch")
|
||||
}
|
||||
if count == nil {
|
||||
return errors.New("huff0: nil count passed to BuildCTable")
|
||||
}
|
||||
var err error
|
||||
s, err = s.prepare(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.count = *count
|
||||
var total, maxCount int
|
||||
var symLen uint16
|
||||
for i, v := range s.count {
|
||||
total += int(v)
|
||||
if int(v) > maxCount {
|
||||
maxCount = int(v)
|
||||
}
|
||||
if v != 0 {
|
||||
symLen = uint16(i) + 1
|
||||
}
|
||||
}
|
||||
if total == 0 {
|
||||
return errors.New("huff0: empty histogram")
|
||||
}
|
||||
if symLen < 2 || maxCount == total {
|
||||
return ErrUseRLE
|
||||
}
|
||||
// huff0's internal rank table assumes total ≤ BlockSizeMax (it uses
|
||||
// highBit32(count+1) + 1 as a rank index into a fixed-size array).
|
||||
// Histograms summed across multiple blocks can exceed that; scale the
|
||||
// counts down preserving the distribution. Non-zero entries round up so
|
||||
// rare symbols stay representable.
|
||||
if total > BlockSizeMax {
|
||||
shift := uint(0)
|
||||
for total>>shift > BlockSizeMax {
|
||||
shift++
|
||||
}
|
||||
round := uint32(1<<shift) - 1
|
||||
var newTotal, newMax int
|
||||
for i, v := range s.count {
|
||||
if v == 0 {
|
||||
continue
|
||||
}
|
||||
scaled := (v + round) >> shift
|
||||
if scaled == 0 {
|
||||
scaled = 1
|
||||
}
|
||||
s.count[i] = scaled
|
||||
newTotal += int(scaled)
|
||||
if int(scaled) > newMax {
|
||||
newMax = int(scaled)
|
||||
}
|
||||
}
|
||||
total = newTotal
|
||||
maxCount = newMax
|
||||
if maxCount == total {
|
||||
return ErrUseRLE
|
||||
}
|
||||
}
|
||||
s.symbolLen = symLen
|
||||
s.maxCount = maxCount
|
||||
s.srcLen = total
|
||||
if err := s.buildCTable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if cap(s.prevTable) < len(s.cTable) {
|
||||
s.prevTable = make(cTable, 0, maxSymbolValue+1)
|
||||
}
|
||||
s.prevTable = s.prevTable[:len(s.cTable)]
|
||||
copy(s.prevTable, s.cTable)
|
||||
s.prevTableLog = s.actualTableLog
|
||||
// Force the next Compress* to recount from real input.
|
||||
s.clearCount = true
|
||||
s.maxCount = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// EstimateSize returns an estimated compressed payload size in bytes for the
|
||||
// supplied histogram using the table currently stored in prevTable. It returns
|
||||
// -1 when the table cannot encode every non-zero symbol of hist (i.e. when
|
||||
// CanUseTable would return false). The estimate excludes the table header.
|
||||
func (s *Scratch) EstimateSize(hist *[256]uint32) int {
|
||||
if s == nil || hist == nil || len(s.prevTable) == 0 {
|
||||
return -1
|
||||
}
|
||||
pt := s.prevTable
|
||||
nbBits := uint32(7)
|
||||
for i, v := range hist {
|
||||
if v == 0 {
|
||||
continue
|
||||
}
|
||||
if i >= len(pt) || pt[i].nBits == 0 {
|
||||
return -1
|
||||
}
|
||||
nbBits += uint32(pt[i].nBits) * v
|
||||
}
|
||||
return int(nbBits >> 3)
|
||||
}
|
||||
|
||||
// CanUseTable reports whether the table in prevTable can encode every
|
||||
// non-zero symbol present in hist.
|
||||
func (s *Scratch) CanUseTable(hist *[256]uint32) bool {
|
||||
if s == nil || hist == nil || len(s.prevTable) == 0 {
|
||||
return false
|
||||
}
|
||||
pt := s.prevTable
|
||||
for i, v := range hist {
|
||||
if v == 0 {
|
||||
continue
|
||||
}
|
||||
if i >= len(pt) || pt[i].nBits == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AppendTable serializes the table currently stored in prevTable (e.g. as
|
||||
// installed by BuildCTable or carried over from a previous Compress call)
|
||||
// into a self-delimiting zstd-style header and appends it to dst. The
|
||||
// returned slice can be parsed back by ReadTable.
|
||||
func (s *Scratch) AppendTable(dst []byte) ([]byte, error) {
|
||||
if s == nil || len(s.prevTable) == 0 {
|
||||
return dst, errors.New("huff0: AppendTable with empty table")
|
||||
}
|
||||
// cTable.write reads s.actualTableLog, s.symbolLen, s.huffWeight, s.fse
|
||||
// and writes into s.Out. Save/restore Out so we don't disturb in-flight
|
||||
// compression buffers.
|
||||
saveOut := s.Out
|
||||
saveTL := s.actualTableLog
|
||||
saveSL := s.symbolLen
|
||||
if s.fse == nil {
|
||||
// Lazily init in case AppendTable is called on a fresh Scratch.
|
||||
if _, err := s.prepare(nil); err != nil {
|
||||
return dst, err
|
||||
}
|
||||
saveOut = s.Out
|
||||
}
|
||||
s.Out = s.Out[:0]
|
||||
s.actualTableLog = s.prevTableLog
|
||||
s.symbolLen = uint16(len(s.prevTable))
|
||||
if err := s.prevTable.write(s); err != nil {
|
||||
s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL
|
||||
return dst, err
|
||||
}
|
||||
dst = append(dst, s.Out...)
|
||||
s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL
|
||||
return dst, nil
|
||||
}
|
||||
+15
-189
@@ -1,223 +1,49 @@
|
||||
//go:build amd64 && !appengine && !noasm && gc
|
||||
// +build amd64,!appengine,!noasm,gc
|
||||
|
||||
// This file contains the specialisation of Decoder.Decompress4X
|
||||
// and Decoder.Decompress1X that use an asm implementation of thir main loops.
|
||||
// amd64 stubs and dispatch for the asm loops used by decompress_asm.go.
|
||||
package huff0
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/klauspost/compress/internal/cpuinfo"
|
||||
)
|
||||
|
||||
// decompress4x_main_loop_x86 is an x86 assembler implementation
|
||||
// decompress4x_main_loop_amd64 is an x86 assembler implementation
|
||||
// of Decompress4X when tablelog > 8.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress4x_main_loop_amd64(ctx *decompress4xContext)
|
||||
|
||||
// decompress4x_8b_loop_x86 is an x86 assembler implementation
|
||||
// decompress4x_8b_main_loop_amd64 is an x86 assembler implementation
|
||||
// of Decompress4X when tablelog <= 8 which decodes 4 entries
|
||||
// per loop.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext)
|
||||
|
||||
// fallback8BitSize is the size where using Go version is faster.
|
||||
const fallback8BitSize = 800
|
||||
|
||||
type decompress4xContext struct {
|
||||
pbr *[4]bitReaderShifted
|
||||
peekBits uint8
|
||||
out *byte
|
||||
dstEvery int
|
||||
tbl *dEntrySingle
|
||||
decoded int
|
||||
limit *byte
|
||||
}
|
||||
|
||||
// Decompress4X will decompress a 4X encoded stream.
|
||||
// The length of the supplied input must match the end of a block exactly.
|
||||
// The *capacity* of the dst slice must match the destination size of
|
||||
// the uncompressed data exactly.
|
||||
func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
|
||||
if len(d.dt.single) == 0 {
|
||||
return nil, errors.New("no table loaded")
|
||||
}
|
||||
if len(src) < 6+(4*1) {
|
||||
return nil, errors.New("input too small")
|
||||
}
|
||||
|
||||
use8BitTables := d.actualTableLog <= 8
|
||||
if cap(dst) < fallback8BitSize && use8BitTables {
|
||||
return d.decompress4X8bit(dst, src)
|
||||
}
|
||||
|
||||
var br [4]bitReaderShifted
|
||||
// Decode "jump table"
|
||||
start := 6
|
||||
for i := range 3 {
|
||||
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
|
||||
if start+length >= len(src) {
|
||||
return nil, errors.New("truncated input (or invalid offset)")
|
||||
}
|
||||
err := br[i].init(src[start : start+length])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start += length
|
||||
}
|
||||
err := br[3].init(src[start:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// destination, offset to match first output
|
||||
dstSize := cap(dst)
|
||||
dst = dst[:dstSize]
|
||||
out := dst
|
||||
dstEvery := (dstSize + 3) / 4
|
||||
|
||||
const tlSize = 1 << tableLogMax
|
||||
const tlMask = tlSize - 1
|
||||
single := d.dt.single[:tlSize]
|
||||
|
||||
var decoded int
|
||||
|
||||
if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) {
|
||||
ctx := decompress4xContext{
|
||||
pbr: &br,
|
||||
peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast()
|
||||
out: &out[0],
|
||||
dstEvery: dstEvery,
|
||||
tbl: &single[0],
|
||||
limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last.
|
||||
}
|
||||
if use8BitTables {
|
||||
decompress4x_8b_main_loop_amd64(&ctx)
|
||||
} else {
|
||||
decompress4x_main_loop_amd64(&ctx)
|
||||
}
|
||||
|
||||
decoded = ctx.decoded
|
||||
out = out[decoded/4:]
|
||||
}
|
||||
|
||||
// Decode remaining.
|
||||
remainBytes := dstEvery - (decoded / 4)
|
||||
for i := range br {
|
||||
offset := dstEvery * i
|
||||
endsAt := min(offset+remainBytes, len(out))
|
||||
br := &br[i]
|
||||
bitsLeft := br.remaining()
|
||||
for bitsLeft > 0 {
|
||||
br.fill()
|
||||
if offset >= endsAt {
|
||||
return nil, errors.New("corruption detected: stream overrun 4")
|
||||
}
|
||||
|
||||
// Read value and increment offset.
|
||||
val := br.peekBitsFast(d.actualTableLog)
|
||||
v := single[val&tlMask].entry
|
||||
nBits := uint8(v)
|
||||
br.advance(nBits)
|
||||
bitsLeft -= uint(nBits)
|
||||
out[offset] = uint8(v >> 8)
|
||||
offset++
|
||||
}
|
||||
if offset != endsAt {
|
||||
return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt)
|
||||
}
|
||||
decoded += offset - dstEvery*i
|
||||
err = br.close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if dstSize != decoded {
|
||||
return nil, errors.New("corruption detected: short output block")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// decompress4x_main_loop_x86 is an x86 assembler implementation
|
||||
// decompress1x_main_loop_amd64 is an x86 assembler implementation
|
||||
// of Decompress1X when tablelog > 8.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress1x_main_loop_amd64(ctx *decompress1xContext)
|
||||
|
||||
// decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation
|
||||
// decompress1x_main_loop_bmi2 is an x86 with BMI2 assembler implementation
|
||||
// of Decompress1X when tablelog > 8.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress1x_main_loop_bmi2(ctx *decompress1xContext)
|
||||
|
||||
type decompress1xContext struct {
|
||||
pbr *bitReaderShifted
|
||||
peekBits uint8
|
||||
out *byte
|
||||
outCap int
|
||||
tbl *dEntrySingle
|
||||
decoded int
|
||||
func decompress4x_main_loop_asm(ctx *decompress4xContext) {
|
||||
decompress4x_main_loop_amd64(ctx)
|
||||
}
|
||||
|
||||
// Error reported by asm implementations
|
||||
const error_max_decoded_size_exeeded = -1
|
||||
func decompress4x_8b_main_loop_asm(ctx *decompress4xContext) {
|
||||
decompress4x_8b_main_loop_amd64(ctx)
|
||||
}
|
||||
|
||||
// Decompress1X will decompress a 1X encoded stream.
|
||||
// The cap of the output buffer will be the maximum decompressed size.
|
||||
// The length of the supplied input must match the end of a block exactly.
|
||||
func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
|
||||
if len(d.dt.single) == 0 {
|
||||
return nil, errors.New("no table loaded")
|
||||
func decompress1x_main_loop_asm(ctx *decompress1xContext) {
|
||||
if cpuinfo.HasBMI2() {
|
||||
decompress1x_main_loop_bmi2(ctx)
|
||||
} else {
|
||||
decompress1x_main_loop_amd64(ctx)
|
||||
}
|
||||
var br bitReaderShifted
|
||||
err := br.init(src)
|
||||
if err != nil {
|
||||
return dst, err
|
||||
}
|
||||
maxDecodedSize := cap(dst)
|
||||
dst = dst[:maxDecodedSize]
|
||||
|
||||
const tlSize = 1 << tableLogMax
|
||||
const tlMask = tlSize - 1
|
||||
|
||||
if maxDecodedSize >= 4 {
|
||||
ctx := decompress1xContext{
|
||||
pbr: &br,
|
||||
out: &dst[0],
|
||||
outCap: maxDecodedSize,
|
||||
peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast()
|
||||
tbl: &d.dt.single[0],
|
||||
}
|
||||
|
||||
if cpuinfo.HasBMI2() {
|
||||
decompress1x_main_loop_bmi2(&ctx)
|
||||
} else {
|
||||
decompress1x_main_loop_amd64(&ctx)
|
||||
}
|
||||
if ctx.decoded == error_max_decoded_size_exeeded {
|
||||
return nil, ErrMaxDecodedSizeExceeded
|
||||
}
|
||||
|
||||
dst = dst[:ctx.decoded]
|
||||
}
|
||||
|
||||
// br < 8, so uint8 is fine
|
||||
bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead
|
||||
for bitsLeft > 0 {
|
||||
br.fill()
|
||||
if len(dst) >= maxDecodedSize {
|
||||
br.close()
|
||||
return nil, ErrMaxDecodedSizeExceeded
|
||||
}
|
||||
v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask]
|
||||
nBits := uint8(v.entry)
|
||||
br.advance(nBits)
|
||||
bitsLeft -= nBits
|
||||
dst = append(dst, uint8(v.entry>>8))
|
||||
}
|
||||
return dst, br.close()
|
||||
}
|
||||
+105
-105
@@ -1,6 +1,6 @@
|
||||
// Code generated by command: go run gen.go -out ../decompress_amd64.s -pkg=huff0. DO NOT EDIT.
|
||||
// Code generated by command: go run gen.go -out ../decompress.s -arch amd64,arm64 -pkg=huff0. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && !appengine && !noasm && gc
|
||||
//go:build !appengine && !noasm && gc
|
||||
|
||||
// func decompress4x_main_loop_amd64(ctx *decompress4xContext)
|
||||
TEXT ·decompress4x_main_loop_amd64(SB), $0-8
|
||||
@@ -47,12 +47,12 @@ skip_fill0:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br0.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br0.peekTopBits(peekBits)
|
||||
MOVQ DI, CX
|
||||
@@ -60,7 +60,7 @@ skip_fill0:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br0.advance(uint8(v1.entry))
|
||||
MOVB CH, AH
|
||||
@@ -104,12 +104,12 @@ skip_fill1:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br1.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br1.peekTopBits(peekBits)
|
||||
MOVQ DI, CX
|
||||
@@ -117,7 +117,7 @@ skip_fill1:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br1.advance(uint8(v1.entry))
|
||||
MOVB CH, AH
|
||||
@@ -161,12 +161,12 @@ skip_fill2:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br2.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br2.peekTopBits(peekBits)
|
||||
MOVQ DI, CX
|
||||
@@ -174,7 +174,7 @@ skip_fill2:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br2.advance(uint8(v1.entry))
|
||||
MOVB CH, AH
|
||||
@@ -218,12 +218,12 @@ skip_fill3:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br3.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br3.peekTopBits(peekBits)
|
||||
MOVQ DI, CX
|
||||
@@ -231,7 +231,7 @@ skip_fill3:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br3.advance(uint8(v1.entry))
|
||||
MOVB CH, AH
|
||||
@@ -259,13 +259,13 @@ skip_fill3:
|
||||
// func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext)
|
||||
TEXT ·decompress4x_8b_main_loop_amd64(SB), $0-8
|
||||
// Preload values
|
||||
MOVQ ctx+0(FP), CX
|
||||
MOVBQZX 8(CX), DI
|
||||
MOVQ 16(CX), BX
|
||||
MOVQ 48(CX), SI
|
||||
MOVQ 24(CX), R8
|
||||
MOVQ 32(CX), R9
|
||||
MOVQ (CX), R10
|
||||
MOVQ ctx+0(FP), AX
|
||||
MOVBQZX 8(AX), DI
|
||||
MOVQ 16(AX), BX
|
||||
MOVQ 48(AX), SI
|
||||
MOVQ 24(AX), R8
|
||||
MOVQ 32(AX), R9
|
||||
MOVQ (AX), R10
|
||||
|
||||
// Main loop
|
||||
main_loop:
|
||||
@@ -278,35 +278,35 @@ main_loop:
|
||||
MOVBQZX 40(R10), R12
|
||||
CMPQ R12, $0x20
|
||||
JBE skip_fill0
|
||||
MOVQ 24(R10), R13
|
||||
MOVQ 24(R10), AX
|
||||
SUBQ $0x20, R12
|
||||
SUBQ $0x04, R13
|
||||
MOVQ (R10), R14
|
||||
SUBQ $0x04, AX
|
||||
MOVQ (R10), R13
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVL (R13)(R14*1), R14
|
||||
MOVL (AX)(R13*1), R13
|
||||
MOVQ R12, CX
|
||||
SHLQ CL, R14
|
||||
MOVQ R13, 24(R10)
|
||||
ORQ R14, R11
|
||||
SHLQ CL, R13
|
||||
MOVQ AX, 24(R10)
|
||||
ORQ R13, R11
|
||||
|
||||
// exhausted += (br0.off < 4)
|
||||
CMPQ R13, $0x04
|
||||
CMPQ AX, $0x04
|
||||
ADCB $+0, DL
|
||||
|
||||
skip_fill0:
|
||||
// val0 := br0.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
MOVQ R11, AX
|
||||
MOVQ DI, CX
|
||||
SHRQ CL, R13
|
||||
SHRQ CL, AX
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(AX*2), CX
|
||||
|
||||
// br0.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br0.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
@@ -314,7 +314,7 @@ skip_fill0:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br0.advance(uint8(v1.entry)
|
||||
MOVB CH, AH
|
||||
@@ -328,7 +328,7 @@ skip_fill0:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br0.advance(uint8(v2.entry)
|
||||
MOVB CH, AH
|
||||
@@ -341,7 +341,7 @@ skip_fill0:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br0.advance(uint8(v3.entry)
|
||||
MOVB CH, AL
|
||||
@@ -365,35 +365,35 @@ skip_fill0:
|
||||
MOVBQZX 88(R10), R12
|
||||
CMPQ R12, $0x20
|
||||
JBE skip_fill1
|
||||
MOVQ 72(R10), R13
|
||||
MOVQ 72(R10), AX
|
||||
SUBQ $0x20, R12
|
||||
SUBQ $0x04, R13
|
||||
MOVQ 48(R10), R14
|
||||
SUBQ $0x04, AX
|
||||
MOVQ 48(R10), R13
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVL (R13)(R14*1), R14
|
||||
MOVL (AX)(R13*1), R13
|
||||
MOVQ R12, CX
|
||||
SHLQ CL, R14
|
||||
MOVQ R13, 72(R10)
|
||||
ORQ R14, R11
|
||||
SHLQ CL, R13
|
||||
MOVQ AX, 72(R10)
|
||||
ORQ R13, R11
|
||||
|
||||
// exhausted += (br1.off < 4)
|
||||
CMPQ R13, $0x04
|
||||
CMPQ AX, $0x04
|
||||
ADCB $+0, DL
|
||||
|
||||
skip_fill1:
|
||||
// val0 := br1.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
MOVQ R11, AX
|
||||
MOVQ DI, CX
|
||||
SHRQ CL, R13
|
||||
SHRQ CL, AX
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(AX*2), CX
|
||||
|
||||
// br1.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br1.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
@@ -401,7 +401,7 @@ skip_fill1:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br1.advance(uint8(v1.entry)
|
||||
MOVB CH, AH
|
||||
@@ -415,7 +415,7 @@ skip_fill1:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br1.advance(uint8(v2.entry)
|
||||
MOVB CH, AH
|
||||
@@ -428,7 +428,7 @@ skip_fill1:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br1.advance(uint8(v3.entry)
|
||||
MOVB CH, AL
|
||||
@@ -452,35 +452,35 @@ skip_fill1:
|
||||
MOVBQZX 136(R10), R12
|
||||
CMPQ R12, $0x20
|
||||
JBE skip_fill2
|
||||
MOVQ 120(R10), R13
|
||||
MOVQ 120(R10), AX
|
||||
SUBQ $0x20, R12
|
||||
SUBQ $0x04, R13
|
||||
MOVQ 96(R10), R14
|
||||
SUBQ $0x04, AX
|
||||
MOVQ 96(R10), R13
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVL (R13)(R14*1), R14
|
||||
MOVL (AX)(R13*1), R13
|
||||
MOVQ R12, CX
|
||||
SHLQ CL, R14
|
||||
MOVQ R13, 120(R10)
|
||||
ORQ R14, R11
|
||||
SHLQ CL, R13
|
||||
MOVQ AX, 120(R10)
|
||||
ORQ R13, R11
|
||||
|
||||
// exhausted += (br2.off < 4)
|
||||
CMPQ R13, $0x04
|
||||
CMPQ AX, $0x04
|
||||
ADCB $+0, DL
|
||||
|
||||
skip_fill2:
|
||||
// val0 := br2.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
MOVQ R11, AX
|
||||
MOVQ DI, CX
|
||||
SHRQ CL, R13
|
||||
SHRQ CL, AX
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(AX*2), CX
|
||||
|
||||
// br2.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br2.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
@@ -488,7 +488,7 @@ skip_fill2:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br2.advance(uint8(v1.entry)
|
||||
MOVB CH, AH
|
||||
@@ -502,7 +502,7 @@ skip_fill2:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br2.advance(uint8(v2.entry)
|
||||
MOVB CH, AH
|
||||
@@ -515,7 +515,7 @@ skip_fill2:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br2.advance(uint8(v3.entry)
|
||||
MOVB CH, AL
|
||||
@@ -539,35 +539,35 @@ skip_fill2:
|
||||
MOVBQZX 184(R10), R12
|
||||
CMPQ R12, $0x20
|
||||
JBE skip_fill3
|
||||
MOVQ 168(R10), R13
|
||||
MOVQ 168(R10), AX
|
||||
SUBQ $0x20, R12
|
||||
SUBQ $0x04, R13
|
||||
MOVQ 144(R10), R14
|
||||
SUBQ $0x04, AX
|
||||
MOVQ 144(R10), R13
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVL (R13)(R14*1), R14
|
||||
MOVL (AX)(R13*1), R13
|
||||
MOVQ R12, CX
|
||||
SHLQ CL, R14
|
||||
MOVQ R13, 168(R10)
|
||||
ORQ R14, R11
|
||||
SHLQ CL, R13
|
||||
MOVQ AX, 168(R10)
|
||||
ORQ R13, R11
|
||||
|
||||
// exhausted += (br3.off < 4)
|
||||
CMPQ R13, $0x04
|
||||
CMPQ AX, $0x04
|
||||
ADCB $+0, DL
|
||||
|
||||
skip_fill3:
|
||||
// val0 := br3.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
MOVQ R11, AX
|
||||
MOVQ DI, CX
|
||||
SHRQ CL, R13
|
||||
SHRQ CL, AX
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(AX*2), CX
|
||||
|
||||
// br3.advance(uint8(v0.entry)
|
||||
MOVB CH, AL
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
MOVBLZX CH, AX
|
||||
SHLQ CL, R11
|
||||
ADDB CL, R12
|
||||
|
||||
// val1 := br3.peekTopBits(peekBits)
|
||||
MOVQ R11, R13
|
||||
@@ -575,7 +575,7 @@ skip_fill3:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br3.advance(uint8(v1.entry)
|
||||
MOVB CH, AH
|
||||
@@ -589,7 +589,7 @@ skip_fill3:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br3.advance(uint8(v2.entry)
|
||||
MOVB CH, AH
|
||||
@@ -602,7 +602,7 @@ skip_fill3:
|
||||
SHRQ CL, R13
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVW (R9)(R13*2), CX
|
||||
MOVWQZX (R9)(R13*2), CX
|
||||
|
||||
// br3.advance(uint8(v3.entry)
|
||||
MOVB CH, AL
|
||||
@@ -667,7 +667,7 @@ bitReader_fillFast_1_end:
|
||||
MOVQ DI, CX
|
||||
MOVQ R10, R12
|
||||
SHRQ CL, R12
|
||||
MOVW (SI)(R12*2), CX
|
||||
MOVWQZX (SI)(R12*2), CX
|
||||
MOVB CH, AL
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
@@ -675,7 +675,7 @@ bitReader_fillFast_1_end:
|
||||
MOVQ DI, CX
|
||||
MOVQ R10, R12
|
||||
SHRQ CL, R12
|
||||
MOVW (SI)(R12*2), CX
|
||||
MOVWQZX (SI)(R12*2), CX
|
||||
MOVB CH, AH
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
@@ -694,7 +694,7 @@ bitReader_fillFast_2_end:
|
||||
MOVQ DI, CX
|
||||
MOVQ R10, R12
|
||||
SHRQ CL, R12
|
||||
MOVW (SI)(R12*2), CX
|
||||
MOVWQZX (SI)(R12*2), CX
|
||||
MOVB CH, AH
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
@@ -702,7 +702,7 @@ bitReader_fillFast_2_end:
|
||||
MOVQ DI, CX
|
||||
MOVQ R10, R12
|
||||
SHRQ CL, R12
|
||||
MOVW (SI)(R12*2), CX
|
||||
MOVWQZX (SI)(R12*2), CX
|
||||
MOVB CH, AL
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
@@ -769,13 +769,13 @@ main_loop:
|
||||
|
||||
bitReader_fillFast_1_end:
|
||||
SHRXQ DI, R10, CX
|
||||
MOVW (SI)(CX*2), CX
|
||||
MOVWQZX (SI)(CX*2), CX
|
||||
MOVB CH, AL
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
SHLXQ CX, R10, R10
|
||||
SHRXQ DI, R10, CX
|
||||
MOVW (SI)(CX*2), CX
|
||||
MOVWQZX (SI)(CX*2), CX
|
||||
MOVB CH, AH
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
@@ -791,13 +791,13 @@ bitReader_fillFast_1_end:
|
||||
|
||||
bitReader_fillFast_2_end:
|
||||
SHRXQ DI, R10, CX
|
||||
MOVW (SI)(CX*2), CX
|
||||
MOVWQZX (SI)(CX*2), CX
|
||||
MOVB CH, AH
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
SHLXQ CX, R10, R10
|
||||
SHRXQ DI, R10, CX
|
||||
MOVW (SI)(CX*2), CX
|
||||
MOVWQZX (SI)(CX*2), CX
|
||||
MOVB CH, AL
|
||||
MOVBQZX CL, CX
|
||||
ADDQ CX, R11
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//go:build arm64 && !appengine && !noasm && gc
|
||||
|
||||
// arm64 stubs and dispatch for the asm loops used by decompress_asm.go.
|
||||
// The asm (decompress_arm64.s) is generated by the avo arm64 lowering
|
||||
// printer from the same source as the amd64 asm; see _generate/gen.go.
|
||||
package huff0
|
||||
|
||||
// decompress4x_main_loop_arm64 is an arm64 assembler implementation
|
||||
// of Decompress4X when tablelog > 8.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress4x_main_loop_arm64(ctx *decompress4xContext)
|
||||
|
||||
// decompress4x_8b_main_loop_arm64 is an arm64 assembler implementation
|
||||
// of Decompress4X when tablelog <= 8 which decodes 4 entries
|
||||
// per loop.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress4x_8b_main_loop_arm64(ctx *decompress4xContext)
|
||||
|
||||
// decompress1x_main_loop_arm64 is an arm64 assembler implementation
|
||||
// of Decompress1X when tablelog > 8.
|
||||
//
|
||||
//go:noescape
|
||||
func decompress1x_main_loop_arm64(ctx *decompress1xContext)
|
||||
|
||||
func decompress4x_main_loop_asm(ctx *decompress4xContext) {
|
||||
decompress4x_main_loop_arm64(ctx)
|
||||
}
|
||||
|
||||
func decompress4x_8b_main_loop_asm(ctx *decompress4xContext) {
|
||||
decompress4x_8b_main_loop_arm64(ctx)
|
||||
}
|
||||
|
||||
func decompress1x_main_loop_asm(ctx *decompress1xContext) {
|
||||
decompress1x_main_loop_arm64(ctx)
|
||||
}
|
||||
+796
@@ -0,0 +1,796 @@
|
||||
// Code generated by command: go run gen.go -out ../decompress.s -arch amd64,arm64 -pkg=huff0. DO NOT EDIT.
|
||||
// EXPERIMENTAL arm64 output lowered from an amd64 avo program.
|
||||
|
||||
//go:build arm64 && (!appengine && !noasm && gc)
|
||||
|
||||
// func decompress4x_main_loop_amd64(ctx *decompress4xContext)
|
||||
TEXT ·decompress4x_main_loop_arm64(SB), $0-8
|
||||
// Preload values
|
||||
MOVD ctx+0(FP), R0
|
||||
MOVBU 8(R0), R6
|
||||
MOVD 16(R0), R3
|
||||
MOVD 48(R0), R5
|
||||
MOVD 24(R0), R7
|
||||
MOVD 32(R0), R8
|
||||
MOVD (R0), R9
|
||||
|
||||
// Main loop
|
||||
main_loop:
|
||||
MOVD $0, R2
|
||||
CMP R5, R3
|
||||
CSET GE, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
// br0.fillFast32()
|
||||
MOVD 32(R9), R10
|
||||
MOVBU 40(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill0
|
||||
MOVD 24(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD (R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 24(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br0.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill0:
|
||||
// val0 := br0.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br0.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br0.peekTopBits(peekBits)
|
||||
MOVD R6, R1
|
||||
MOVD R10, R12
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br0.advance(uint8(v1.entry))
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// these two writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
MOVH R0, (R3)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 32(R9)
|
||||
MOVB R11, 40(R9)
|
||||
|
||||
// br1.fillFast32()
|
||||
MOVD 80(R9), R10
|
||||
MOVBU 88(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill1
|
||||
MOVD 72(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD 48(R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 72(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br1.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill1:
|
||||
// val0 := br1.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br1.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br1.peekTopBits(peekBits)
|
||||
MOVD R6, R1
|
||||
MOVD R10, R12
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br1.advance(uint8(v1.entry))
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// these two writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
MOVH R0, (R3)(R7)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 80(R9)
|
||||
MOVB R11, 88(R9)
|
||||
|
||||
// br2.fillFast32()
|
||||
MOVD 128(R9), R10
|
||||
MOVBU 136(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill2
|
||||
MOVD 120(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD 96(R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 120(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br2.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill2:
|
||||
// val0 := br2.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br2.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br2.peekTopBits(peekBits)
|
||||
MOVD R6, R1
|
||||
MOVD R10, R12
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br2.advance(uint8(v1.entry))
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// these two writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
MOVH R0, (R3)(R7<<1)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 128(R9)
|
||||
MOVB R11, 136(R9)
|
||||
|
||||
// br3.fillFast32()
|
||||
MOVD 176(R9), R10
|
||||
MOVBU 184(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill3
|
||||
MOVD 168(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD 144(R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 168(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br3.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill3:
|
||||
// val0 := br3.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br3.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br3.peekTopBits(peekBits)
|
||||
MOVD R6, R1
|
||||
MOVD R10, R12
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val1&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br3.advance(uint8(v1.entry))
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// these two writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
ADD R7<<1, R7, R1
|
||||
MOVH R0, (R3)(R1)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 176(R9)
|
||||
MOVB R11, 184(R9)
|
||||
ADD $0x02, R3, R3
|
||||
TST $0xff, R2
|
||||
BEQ main_loop
|
||||
MOVD ctx+0(FP), R0
|
||||
MOVD 16(R0), R16
|
||||
SUB R16, R3, R3
|
||||
LSL $0x02, R3, R3
|
||||
MOVD R3, 40(R0)
|
||||
RET
|
||||
|
||||
// func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext)
|
||||
TEXT ·decompress4x_8b_main_loop_arm64(SB), $0-8
|
||||
// Preload values
|
||||
MOVD ctx+0(FP), R0
|
||||
MOVBU 8(R0), R6
|
||||
MOVD 16(R0), R3
|
||||
MOVD 48(R0), R5
|
||||
MOVD 24(R0), R7
|
||||
MOVD 32(R0), R8
|
||||
MOVD (R0), R9
|
||||
|
||||
// Main loop
|
||||
main_loop:
|
||||
MOVD $0, R2
|
||||
CMP R5, R3
|
||||
CSET GE, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
// br0.fillFast32()
|
||||
MOVD 32(R9), R10
|
||||
MOVBU 40(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill0
|
||||
MOVD 24(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD (R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 24(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br0.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill0:
|
||||
// val0 := br0.peekTopBits(peekBits)
|
||||
MOVD R10, R0
|
||||
MOVD R6, R1
|
||||
LSR R1, R0, R0
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R0<<1), R1
|
||||
|
||||
// br0.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br0.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br0.advance(uint8(v1.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// val2 := br0.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br0.advance(uint8(v2.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val3 := br0.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br0.advance(uint8(v3.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $0, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// these four writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
// out[id * dstEvery + 3] = uint8(v2.entry >> 8)
|
||||
// out[id * dstEvery + 4] = uint8(v3.entry >> 8)
|
||||
MOVW R0, (R3)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 32(R9)
|
||||
MOVB R11, 40(R9)
|
||||
|
||||
// br1.fillFast32()
|
||||
MOVD 80(R9), R10
|
||||
MOVBU 88(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill1
|
||||
MOVD 72(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD 48(R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 72(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br1.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill1:
|
||||
// val0 := br1.peekTopBits(peekBits)
|
||||
MOVD R10, R0
|
||||
MOVD R6, R1
|
||||
LSR R1, R0, R0
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R0<<1), R1
|
||||
|
||||
// br1.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br1.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br1.advance(uint8(v1.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// val2 := br1.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br1.advance(uint8(v2.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val3 := br1.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br1.advance(uint8(v3.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $0, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// these four writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
// out[id * dstEvery + 3] = uint8(v2.entry >> 8)
|
||||
// out[id * dstEvery + 4] = uint8(v3.entry >> 8)
|
||||
MOVW R0, (R3)(R7)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 80(R9)
|
||||
MOVB R11, 88(R9)
|
||||
|
||||
// br2.fillFast32()
|
||||
MOVD 128(R9), R10
|
||||
MOVBU 136(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill2
|
||||
MOVD 120(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD 96(R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 120(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br2.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill2:
|
||||
// val0 := br2.peekTopBits(peekBits)
|
||||
MOVD R10, R0
|
||||
MOVD R6, R1
|
||||
LSR R1, R0, R0
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R0<<1), R1
|
||||
|
||||
// br2.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br2.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br2.advance(uint8(v1.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// val2 := br2.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br2.advance(uint8(v2.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val3 := br2.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br2.advance(uint8(v3.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $0, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// these four writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
// out[id * dstEvery + 3] = uint8(v2.entry >> 8)
|
||||
// out[id * dstEvery + 4] = uint8(v3.entry >> 8)
|
||||
ADD R7<<1, R3, R15
|
||||
MOVW R0, (R15)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 128(R9)
|
||||
MOVB R11, 136(R9)
|
||||
|
||||
// br3.fillFast32()
|
||||
MOVD 176(R9), R10
|
||||
MOVBU 184(R9), R11
|
||||
CMP $0x20, R11
|
||||
BLS skip_fill3
|
||||
MOVD 168(R9), R0
|
||||
SUB $0x20, R11, R11
|
||||
SUB $0x04, R0, R0
|
||||
MOVD 144(R9), R12
|
||||
|
||||
// b.value |= uint64(low) << (b.bitsRead & 63)
|
||||
MOVWU (R0)(R12), R12
|
||||
MOVD R11, R1
|
||||
LSL R1, R12, R12
|
||||
MOVD R0, 168(R9)
|
||||
ORR R12, R10, R10
|
||||
|
||||
// exhausted += (br3.off < 4)
|
||||
CMP $0x04, R0
|
||||
CSINC HS, R2, R2, R16
|
||||
BFI $0, R16, $8, R2
|
||||
|
||||
skip_fill3:
|
||||
// val0 := br3.peekTopBits(peekBits)
|
||||
MOVD R10, R0
|
||||
MOVD R6, R1
|
||||
LSR R1, R0, R0
|
||||
|
||||
// v0 := table[val0&mask]
|
||||
MOVHU (R8)(R0<<1), R1
|
||||
|
||||
// br3.advance(uint8(v0.entry)
|
||||
UBFX $8, R1, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val1 := br3.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v1 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br3.advance(uint8(v1.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// val2 := br3.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v2 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br3.advance(uint8(v2.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
|
||||
// val3 := br3.peekTopBits(peekBits)
|
||||
MOVD R10, R12
|
||||
MOVD R6, R1
|
||||
LSR R1, R12, R12
|
||||
|
||||
// v3 := table[val0&mask]
|
||||
MOVHU (R8)(R12<<1), R1
|
||||
|
||||
// br3.advance(uint8(v3.entry)
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $0, R16, $8, R0
|
||||
LSL R1, R10, R10
|
||||
ADD R1, R11, R15
|
||||
BFI $0, R15, $8, R11
|
||||
REVW R0, R0
|
||||
|
||||
// these four writes get coalesced
|
||||
// out[id * dstEvery + 0] = uint8(v0.entry >> 8)
|
||||
// out[id * dstEvery + 1] = uint8(v1.entry >> 8)
|
||||
// out[id * dstEvery + 3] = uint8(v2.entry >> 8)
|
||||
// out[id * dstEvery + 4] = uint8(v3.entry >> 8)
|
||||
ADD R7<<1, R7, R1
|
||||
MOVW R0, (R3)(R1)
|
||||
|
||||
// update the bitreader structure
|
||||
MOVD R10, 176(R9)
|
||||
MOVB R11, 184(R9)
|
||||
ADD $0x04, R3, R3
|
||||
TST $0xff, R2
|
||||
BEQ main_loop
|
||||
MOVD ctx+0(FP), R0
|
||||
MOVD 16(R0), R16
|
||||
SUB R16, R3, R3
|
||||
LSL $0x02, R3, R3
|
||||
MOVD R3, 40(R0)
|
||||
RET
|
||||
|
||||
// func decompress1x_main_loop_amd64(ctx *decompress1xContext)
|
||||
TEXT ·decompress1x_main_loop_arm64(SB), $0-8
|
||||
MOVD ctx+0(FP), R1
|
||||
MOVD 16(R1), R2
|
||||
MOVD 24(R1), R3
|
||||
CMP $0x04, R3
|
||||
BLO error_max_decoded_size_exceeded
|
||||
ADD R3, R2, R3
|
||||
MOVD (R1), R5
|
||||
MOVD (R5), R7
|
||||
MOVD 24(R5), R8
|
||||
MOVD 32(R5), R9
|
||||
MOVBU 40(R5), R10
|
||||
MOVD 32(R1), R5
|
||||
MOVBU 8(R1), R6
|
||||
JMP loop_condition
|
||||
|
||||
main_loop:
|
||||
// Check if we have room for 4 bytes in the output buffer
|
||||
ADD $4, R2, R1
|
||||
CMP R3, R1
|
||||
BGE error_max_decoded_size_exceeded
|
||||
|
||||
// Decode 4 values
|
||||
CMP $0x20, R10
|
||||
BLT bitReader_fillFast_1_end
|
||||
SUB $0x20, R10, R10
|
||||
SUB $0x04, R8, R8
|
||||
MOVWU (R7)(R8), R11
|
||||
MOVD R10, R1
|
||||
LSL R1, R11, R11
|
||||
ORR R11, R9, R9
|
||||
|
||||
bitReader_fillFast_1_end:
|
||||
MOVD R6, R1
|
||||
MOVD R9, R11
|
||||
LSR R1, R11, R11
|
||||
MOVHU (R5)(R11<<1), R1
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $0, R16, $8, R0
|
||||
MOVBU R1, R1
|
||||
ADD R1, R10, R10
|
||||
LSL R1, R9, R9
|
||||
MOVD R6, R1
|
||||
MOVD R9, R11
|
||||
LSR R1, R11, R11
|
||||
MOVHU (R5)(R11<<1), R1
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
MOVBU R1, R1
|
||||
ADD R1, R10, R10
|
||||
LSL R1, R9, R9
|
||||
REVW R0, R0
|
||||
CMP $0x20, R10
|
||||
BLT bitReader_fillFast_2_end
|
||||
SUB $0x20, R10, R10
|
||||
SUB $0x04, R8, R8
|
||||
MOVWU (R7)(R8), R11
|
||||
MOVD R10, R1
|
||||
LSL R1, R11, R11
|
||||
ORR R11, R9, R9
|
||||
|
||||
bitReader_fillFast_2_end:
|
||||
MOVD R6, R1
|
||||
MOVD R9, R11
|
||||
LSR R1, R11, R11
|
||||
MOVHU (R5)(R11<<1), R1
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $8, R16, $8, R0
|
||||
MOVBU R1, R1
|
||||
ADD R1, R10, R10
|
||||
LSL R1, R9, R9
|
||||
MOVD R6, R1
|
||||
MOVD R9, R11
|
||||
LSR R1, R11, R11
|
||||
MOVHU (R5)(R11<<1), R1
|
||||
UBFX $8, R1, $8, R16
|
||||
BFI $0, R16, $8, R0
|
||||
MOVBU R1, R1
|
||||
ADD R1, R10, R10
|
||||
LSL R1, R9, R9
|
||||
REVW R0, R0
|
||||
|
||||
// Store the decoded values
|
||||
MOVW R0, (R2)
|
||||
ADD $0x04, R2, R2
|
||||
|
||||
loop_condition:
|
||||
CMP $0x08, R8
|
||||
BGE main_loop
|
||||
|
||||
// Update ctx structure
|
||||
MOVD ctx+0(FP), R0
|
||||
MOVD 16(R0), R16
|
||||
SUB R16, R2, R2
|
||||
MOVD R2, 40(R0)
|
||||
MOVD (R0), R0
|
||||
MOVD R8, 24(R0)
|
||||
MOVD R9, 32(R0)
|
||||
MOVB R10, 40(R0)
|
||||
RET
|
||||
|
||||
// Report error
|
||||
error_max_decoded_size_exceeded:
|
||||
MOVD ctx+0(FP), R0
|
||||
MOVD $-1, R1
|
||||
MOVD R1, 40(R0)
|
||||
RET
|
||||
|
||||
// skipped decompress1x_main_loop_bmi2 (generic twin preferred on arm64)
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
//go:build (amd64 || arm64) && !appengine && !noasm && gc
|
||||
|
||||
// This file contains the specialisation of Decoder.Decompress4X
|
||||
// and Decoder.Decompress1X that use an asm implementation of their main loops.
|
||||
// The asm function stubs and any per-arch dispatch live in decompress_amd64.go
|
||||
// and decompress_arm64.go.
|
||||
package huff0
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// fallback8BitSize is the size where using Go version is faster.
|
||||
const fallback8BitSize = 800
|
||||
|
||||
type decompress4xContext struct {
|
||||
pbr *[4]bitReaderShifted
|
||||
peekBits uint8
|
||||
out *byte
|
||||
dstEvery int
|
||||
tbl *dEntrySingle
|
||||
decoded int
|
||||
limit *byte
|
||||
}
|
||||
|
||||
// Decompress4X will decompress a 4X encoded stream.
|
||||
// The length of the supplied input must match the end of a block exactly.
|
||||
// The *capacity* of the dst slice must match the destination size of
|
||||
// the uncompressed data exactly.
|
||||
func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
|
||||
if len(d.dt.single) == 0 {
|
||||
return nil, errors.New("no table loaded")
|
||||
}
|
||||
if len(src) < 6+(4*1) {
|
||||
return nil, errors.New("input too small")
|
||||
}
|
||||
|
||||
use8BitTables := d.actualTableLog <= 8
|
||||
if cap(dst) < fallback8BitSize && use8BitTables {
|
||||
return d.decompress4X8bit(dst, src)
|
||||
}
|
||||
|
||||
var br [4]bitReaderShifted
|
||||
// Decode "jump table"
|
||||
start := 6
|
||||
for i := range 3 {
|
||||
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
|
||||
if start+length >= len(src) {
|
||||
return nil, errors.New("truncated input (or invalid offset)")
|
||||
}
|
||||
err := br[i].init(src[start : start+length])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start += length
|
||||
}
|
||||
err := br[3].init(src[start:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// destination, offset to match first output
|
||||
dstSize := cap(dst)
|
||||
dst = dst[:dstSize]
|
||||
out := dst
|
||||
dstEvery := (dstSize + 3) / 4
|
||||
|
||||
const tlSize = 1 << tableLogMax
|
||||
const tlMask = tlSize - 1
|
||||
single := d.dt.single[:tlSize]
|
||||
|
||||
var decoded int
|
||||
|
||||
if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) {
|
||||
ctx := decompress4xContext{
|
||||
pbr: &br,
|
||||
peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast()
|
||||
out: &out[0],
|
||||
dstEvery: dstEvery,
|
||||
tbl: &single[0],
|
||||
limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last.
|
||||
}
|
||||
if use8BitTables {
|
||||
decompress4x_8b_main_loop_asm(&ctx)
|
||||
} else {
|
||||
decompress4x_main_loop_asm(&ctx)
|
||||
}
|
||||
|
||||
decoded = ctx.decoded
|
||||
out = out[decoded/4:]
|
||||
}
|
||||
|
||||
// Decode remaining.
|
||||
remainBytes := dstEvery - (decoded / 4)
|
||||
for i := range br {
|
||||
offset := dstEvery * i
|
||||
endsAt := min(offset+remainBytes, len(out))
|
||||
br := &br[i]
|
||||
bitsLeft := br.remaining()
|
||||
for bitsLeft > 0 {
|
||||
br.fill()
|
||||
if offset >= endsAt {
|
||||
return nil, errors.New("corruption detected: stream overrun 4")
|
||||
}
|
||||
|
||||
// Read value and increment offset.
|
||||
val := br.peekBitsFast(d.actualTableLog)
|
||||
v := single[val&tlMask].entry
|
||||
nBits := uint8(v)
|
||||
br.advance(nBits)
|
||||
bitsLeft -= uint(nBits)
|
||||
out[offset] = uint8(v >> 8)
|
||||
offset++
|
||||
}
|
||||
if offset != endsAt {
|
||||
return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt)
|
||||
}
|
||||
decoded += offset - dstEvery*i
|
||||
err = br.close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if dstSize != decoded {
|
||||
return nil, errors.New("corruption detected: short output block")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
type decompress1xContext struct {
|
||||
pbr *bitReaderShifted
|
||||
peekBits uint8
|
||||
out *byte
|
||||
outCap int
|
||||
tbl *dEntrySingle
|
||||
decoded int
|
||||
}
|
||||
|
||||
// Error reported by asm implementations
|
||||
const error_max_decoded_size_exeeded = -1
|
||||
|
||||
// Decompress1X will decompress a 1X encoded stream.
|
||||
// The cap of the output buffer will be the maximum decompressed size.
|
||||
// The length of the supplied input must match the end of a block exactly.
|
||||
func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
|
||||
if len(d.dt.single) == 0 {
|
||||
return nil, errors.New("no table loaded")
|
||||
}
|
||||
var br bitReaderShifted
|
||||
err := br.init(src)
|
||||
if err != nil {
|
||||
return dst, err
|
||||
}
|
||||
maxDecodedSize := cap(dst)
|
||||
dst = dst[:maxDecodedSize]
|
||||
|
||||
const tlSize = 1 << tableLogMax
|
||||
const tlMask = tlSize - 1
|
||||
|
||||
if maxDecodedSize >= 4 {
|
||||
ctx := decompress1xContext{
|
||||
pbr: &br,
|
||||
out: &dst[0],
|
||||
outCap: maxDecodedSize,
|
||||
peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast()
|
||||
tbl: &d.dt.single[0],
|
||||
}
|
||||
|
||||
decompress1x_main_loop_asm(&ctx)
|
||||
if ctx.decoded == error_max_decoded_size_exeeded {
|
||||
return nil, ErrMaxDecodedSizeExceeded
|
||||
}
|
||||
|
||||
dst = dst[:ctx.decoded]
|
||||
}
|
||||
|
||||
// br < 8, so uint8 is fine
|
||||
bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead
|
||||
for bitsLeft > 0 {
|
||||
br.fill()
|
||||
if len(dst) >= maxDecodedSize {
|
||||
br.close()
|
||||
return nil, ErrMaxDecodedSizeExceeded
|
||||
}
|
||||
v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask]
|
||||
nBits := uint8(v.entry)
|
||||
br.advance(nBits)
|
||||
bitsLeft -= nBits
|
||||
dst = append(dst, uint8(v.entry>>8))
|
||||
}
|
||||
return dst, br.close()
|
||||
}
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
//go:build !amd64 || appengine || !gc || noasm
|
||||
// +build !amd64 appengine !gc noasm
|
||||
//go:build (!amd64 && !arm64) || appengine || !gc || noasm
|
||||
|
||||
// This file contains a generic implementation of Decoder.Decompress4X.
|
||||
package huff0
|
||||
|
||||
Reference in new issue
Block a user