| //go:build !amd64 || appengine || !gc || noasm |
| // +build !amd64 appengine !gc noasm |
| |
| // This file contains a generic implementation of Decoder.Decompress4X. |
| package huff0 |
| |
| import ( |
| "errors" |
| "fmt" |
| ) |
| |
| // 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") |
| } |
| if use8BitTables && d.actualTableLog <= 8 { |
| return d.decompress4X8bit(dst, src) |
| } |
| |
| var br [4]bitReaderShifted |
| // Decode "jump table" |
| start := 6 |
| for i := 0; i < 3; i++ { |
| 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] |
| |
| // Use temp table to avoid bound checks/append penalty. |
| buf := d.buffer() |
| var off uint8 |
| var decoded int |
| |
| // Decode 2 values from each decoder/loop. |
| const bufoff = 256 |
| for { |
| if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { |
| break |
| } |
| |
| { |
| const stream = 0 |
| const stream2 = 1 |
| br[stream].fillFast() |
| br[stream2].fillFast() |
| |
| val := br[stream].peekBitsFast(d.actualTableLog) |
| val2 := br[stream2].peekBitsFast(d.actualTableLog) |
| v := single[val&tlMask] |
| v2 := single[val2&tlMask] |
| br[stream].advance(uint8(v.entry)) |
| br[stream2].advance(uint8(v2.entry)) |
| buf[stream][off] = uint8(v.entry >> 8) |
| buf[stream2][off] = uint8(v2.entry >> 8) |
| |
| val = br[stream].peekBitsFast(d.actualTableLog) |
| val2 = br[stream2].peekBitsFast(d.actualTableLog) |
| v = single[val&tlMask] |
| v2 = single[val2&tlMask] |
| br[stream].advance(uint8(v.entry)) |
| br[stream2].advance(uint8(v2.entry)) |
| buf[stream][off+1] = uint8(v.entry >> 8) |
| buf[stream2][off+1] = uint8(v2.entry >> 8) |
| } |
| |
| { |
| const stream = 2 |
| const stream2 = 3 |
| br[stream].fillFast() |
| br[stream2].fillFast() |
| |
| val := br[stream].peekBitsFast(d.actualTableLog) |
| val2 := br[stream2].peekBitsFast(d.actualTableLog) |
| v := single[val&tlMask] |
| v2 := single[val2&tlMask] |
| br[stream].advance(uint8(v.entry)) |
| br[stream2].advance(uint8(v2.entry)) |
| buf[stream][off] = uint8(v.entry >> 8) |
| buf[stream2][off] = uint8(v2.entry >> 8) |
| |
| val = br[stream].peekBitsFast(d.actualTableLog) |
| val2 = br[stream2].peekBitsFast(d.actualTableLog) |
| v = single[val&tlMask] |
| v2 = single[val2&tlMask] |
| br[stream].advance(uint8(v.entry)) |
| br[stream2].advance(uint8(v2.entry)) |
| buf[stream][off+1] = uint8(v.entry >> 8) |
| buf[stream2][off+1] = uint8(v2.entry >> 8) |
| } |
| |
| off += 2 |
| |
| if off == 0 { |
| if bufoff > dstEvery { |
| d.bufs.Put(buf) |
| return nil, errors.New("corruption detected: stream overrun 1") |
| } |
| copy(out, buf[0][:]) |
| copy(out[dstEvery:], buf[1][:]) |
| copy(out[dstEvery*2:], buf[2][:]) |
| copy(out[dstEvery*3:], buf[3][:]) |
| out = out[bufoff:] |
| decoded += bufoff * 4 |
| // There must at least be 3 buffers left. |
| if len(out) < dstEvery*3 { |
| d.bufs.Put(buf) |
| return nil, errors.New("corruption detected: stream overrun 2") |
| } |
| } |
| } |
| if off > 0 { |
| ioff := int(off) |
| if len(out) < dstEvery*3+ioff { |
| d.bufs.Put(buf) |
| return nil, errors.New("corruption detected: stream overrun 3") |
| } |
| copy(out, buf[0][:off]) |
| copy(out[dstEvery:], buf[1][:off]) |
| copy(out[dstEvery*2:], buf[2][:off]) |
| copy(out[dstEvery*3:], buf[3][:off]) |
| decoded += int(off) * 4 |
| out = out[off:] |
| } |
| |
| // Decode remaining. |
| remainBytes := dstEvery - (decoded / 4) |
| for i := range br { |
| offset := dstEvery * i |
| endsAt := offset + remainBytes |
| if endsAt > len(out) { |
| endsAt = len(out) |
| } |
| br := &br[i] |
| bitsLeft := br.remaining() |
| for bitsLeft > 0 { |
| br.fill() |
| if offset >= endsAt { |
| d.bufs.Put(buf) |
| 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 { |
| d.bufs.Put(buf) |
| 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 |
| } |
| } |
| d.bufs.Put(buf) |
| if dstSize != decoded { |
| return nil, errors.New("corruption detected: short output block") |
| } |
| return dst, nil |
| } |
| |
| // 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") |
| } |
| if use8BitTables && d.actualTableLog <= 8 { |
| return d.decompress1X8Bit(dst, src) |
| } |
| var br bitReaderShifted |
| err := br.init(src) |
| if err != nil { |
| return dst, err |
| } |
| maxDecodedSize := cap(dst) |
| dst = dst[:0] |
| |
| // Avoid bounds check by always having full sized table. |
| const tlSize = 1 << tableLogMax |
| const tlMask = tlSize - 1 |
| dt := d.dt.single[:tlSize] |
| |
| // Use temp table to avoid bound checks/append penalty. |
| bufs := d.buffer() |
| buf := &bufs[0] |
| var off uint8 |
| |
| for br.off >= 8 { |
| br.fillFast() |
| v := dt[br.peekBitsFast(d.actualTableLog)&tlMask] |
| br.advance(uint8(v.entry)) |
| buf[off+0] = uint8(v.entry >> 8) |
| |
| v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] |
| br.advance(uint8(v.entry)) |
| buf[off+1] = uint8(v.entry >> 8) |
| |
| // Refill |
| br.fillFast() |
| |
| v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] |
| br.advance(uint8(v.entry)) |
| buf[off+2] = uint8(v.entry >> 8) |
| |
| v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] |
| br.advance(uint8(v.entry)) |
| buf[off+3] = uint8(v.entry >> 8) |
| |
| off += 4 |
| if off == 0 { |
| if len(dst)+256 > maxDecodedSize { |
| br.close() |
| d.bufs.Put(bufs) |
| return nil, ErrMaxDecodedSizeExceeded |
| } |
| dst = append(dst, buf[:]...) |
| } |
| } |
| |
| if len(dst)+int(off) > maxDecodedSize { |
| d.bufs.Put(bufs) |
| br.close() |
| return nil, ErrMaxDecodedSizeExceeded |
| } |
| dst = append(dst, buf[:off]...) |
| |
| // br < 8, so uint8 is fine |
| bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead |
| for bitsLeft > 0 { |
| br.fill() |
| if false && br.bitsRead >= 32 { |
| if br.off >= 4 { |
| v := br.in[br.off-4:] |
| v = v[:4] |
| low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) |
| br.value = (br.value << 32) | uint64(low) |
| br.bitsRead -= 32 |
| br.off -= 4 |
| } else { |
| for br.off > 0 { |
| br.value = (br.value << 8) | uint64(br.in[br.off-1]) |
| br.bitsRead -= 8 |
| br.off-- |
| } |
| } |
| } |
| if len(dst) >= maxDecodedSize { |
| d.bufs.Put(bufs) |
| 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)) |
| } |
| d.bufs.Put(bufs) |
| return dst, br.close() |
| } |