mirror of
https://github.com/mudler/luet.git
synced 2025-07-13 07:04:19 +00:00
Instead of using gox on one side and an action to release, we can merge them together with goreleaser which will build for extra targets (arm, mips if needed in the future) and it also takes care of creating checksums, a source archive, and a changelog and creating a release with all the artifacts. All binaries should respect the old naming convention, so any scripts out there should still work. Signed-off-by: Itxaka <igarcia@suse.com>
493 lines
13 KiB
Go
493 lines
13 KiB
Go
// Copyright 2019+ Klaus Post. All rights reserved.
|
|
// License information can be found in the LICENSE file.
|
|
// Based on work by Yann Collet, released under BSD License.
|
|
|
|
package zstd
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
type seq struct {
|
|
litLen uint32
|
|
matchLen uint32
|
|
offset uint32
|
|
|
|
// Codes are stored here for the encoder
|
|
// so they only have to be looked up once.
|
|
llCode, mlCode, ofCode uint8
|
|
}
|
|
|
|
func (s seq) String() string {
|
|
if s.offset <= 3 {
|
|
if s.offset == 0 {
|
|
return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset: INVALID (0)")
|
|
}
|
|
return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset:", s.offset, " (repeat)")
|
|
}
|
|
return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset:", s.offset-3, " (new)")
|
|
}
|
|
|
|
type seqCompMode uint8
|
|
|
|
const (
|
|
compModePredefined seqCompMode = iota
|
|
compModeRLE
|
|
compModeFSE
|
|
compModeRepeat
|
|
)
|
|
|
|
type sequenceDec struct {
|
|
// decoder keeps track of the current state and updates it from the bitstream.
|
|
fse *fseDecoder
|
|
state fseState
|
|
repeat bool
|
|
}
|
|
|
|
// init the state of the decoder with input from stream.
|
|
func (s *sequenceDec) init(br *bitReader) error {
|
|
if s.fse == nil {
|
|
return errors.New("sequence decoder not defined")
|
|
}
|
|
s.state.init(br, s.fse.actualTableLog, s.fse.dt[:1<<s.fse.actualTableLog])
|
|
return nil
|
|
}
|
|
|
|
// sequenceDecs contains all 3 sequence decoders and their state.
|
|
type sequenceDecs struct {
|
|
litLengths sequenceDec
|
|
offsets sequenceDec
|
|
matchLengths sequenceDec
|
|
prevOffset [3]int
|
|
hist []byte
|
|
dict []byte
|
|
literals []byte
|
|
out []byte
|
|
windowSize int
|
|
maxBits uint8
|
|
}
|
|
|
|
// initialize all 3 decoders from the stream input.
|
|
func (s *sequenceDecs) initialize(br *bitReader, hist *history, literals, out []byte) error {
|
|
if err := s.litLengths.init(br); err != nil {
|
|
return errors.New("litLengths:" + err.Error())
|
|
}
|
|
if err := s.offsets.init(br); err != nil {
|
|
return errors.New("offsets:" + err.Error())
|
|
}
|
|
if err := s.matchLengths.init(br); err != nil {
|
|
return errors.New("matchLengths:" + err.Error())
|
|
}
|
|
s.literals = literals
|
|
s.hist = hist.b
|
|
s.prevOffset = hist.recentOffsets
|
|
s.maxBits = s.litLengths.fse.maxBits + s.offsets.fse.maxBits + s.matchLengths.fse.maxBits
|
|
s.windowSize = hist.windowSize
|
|
s.out = out
|
|
s.dict = nil
|
|
if hist.dict != nil {
|
|
s.dict = hist.dict.content
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// decode sequences from the stream with the provided history.
|
|
func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error {
|
|
startSize := len(s.out)
|
|
// Grab full sizes tables, to avoid bounds checks.
|
|
llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize]
|
|
llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state
|
|
|
|
for i := seqs - 1; i >= 0; i-- {
|
|
if br.overread() {
|
|
printf("reading sequence %d, exceeded available data\n", seqs-i)
|
|
return io.ErrUnexpectedEOF
|
|
}
|
|
var ll, mo, ml int
|
|
if br.off > 4+((maxOffsetBits+16+16)>>3) {
|
|
// inlined function:
|
|
// ll, mo, ml = s.nextFast(br, llState, mlState, ofState)
|
|
|
|
// Final will not read from stream.
|
|
var llB, mlB, moB uint8
|
|
ll, llB = llState.final()
|
|
ml, mlB = mlState.final()
|
|
mo, moB = ofState.final()
|
|
|
|
// extra bits are stored in reverse order.
|
|
br.fillFast()
|
|
mo += br.getBits(moB)
|
|
if s.maxBits > 32 {
|
|
br.fillFast()
|
|
}
|
|
ml += br.getBits(mlB)
|
|
ll += br.getBits(llB)
|
|
|
|
if moB > 1 {
|
|
s.prevOffset[2] = s.prevOffset[1]
|
|
s.prevOffset[1] = s.prevOffset[0]
|
|
s.prevOffset[0] = mo
|
|
} else {
|
|
// mo = s.adjustOffset(mo, ll, moB)
|
|
// Inlined for rather big speedup
|
|
if ll == 0 {
|
|
// There is an exception though, when current sequence's literals_length = 0.
|
|
// In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2,
|
|
// an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte.
|
|
mo++
|
|
}
|
|
|
|
if mo == 0 {
|
|
mo = s.prevOffset[0]
|
|
} else {
|
|
var temp int
|
|
if mo == 3 {
|
|
temp = s.prevOffset[0] - 1
|
|
} else {
|
|
temp = s.prevOffset[mo]
|
|
}
|
|
|
|
if temp == 0 {
|
|
// 0 is not valid; input is corrupted; force offset to 1
|
|
println("temp was 0")
|
|
temp = 1
|
|
}
|
|
|
|
if mo != 1 {
|
|
s.prevOffset[2] = s.prevOffset[1]
|
|
}
|
|
s.prevOffset[1] = s.prevOffset[0]
|
|
s.prevOffset[0] = temp
|
|
mo = temp
|
|
}
|
|
}
|
|
br.fillFast()
|
|
} else {
|
|
ll, mo, ml = s.next(br, llState, mlState, ofState)
|
|
br.fill()
|
|
}
|
|
|
|
if debugSequences {
|
|
println("Seq", seqs-i-1, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml)
|
|
}
|
|
|
|
if ll > len(s.literals) {
|
|
return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, len(s.literals))
|
|
}
|
|
size := ll + ml + len(s.out)
|
|
if size-startSize > maxBlockSize {
|
|
return fmt.Errorf("output (%d) bigger than max block size", size)
|
|
}
|
|
if size > cap(s.out) {
|
|
// Not enough size, which can happen under high volume block streaming conditions
|
|
// but could be if destination slice is too small for sync operations.
|
|
// over-allocating here can create a large amount of GC pressure so we try to keep
|
|
// it as contained as possible
|
|
used := len(s.out) - startSize
|
|
addBytes := 256 + ll + ml + used>>2
|
|
// Clamp to max block size.
|
|
if used+addBytes > maxBlockSize {
|
|
addBytes = maxBlockSize - used
|
|
}
|
|
s.out = append(s.out, make([]byte, addBytes)...)
|
|
s.out = s.out[:len(s.out)-addBytes]
|
|
}
|
|
if ml > maxMatchLen {
|
|
return fmt.Errorf("match len (%d) bigger than max allowed length", ml)
|
|
}
|
|
|
|
// Add literals
|
|
s.out = append(s.out, s.literals[:ll]...)
|
|
s.literals = s.literals[ll:]
|
|
out := s.out
|
|
|
|
if mo == 0 && ml > 0 {
|
|
return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml)
|
|
}
|
|
|
|
if mo > len(s.out)+len(hist) || mo > s.windowSize {
|
|
if len(s.dict) == 0 {
|
|
return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(s.out)+len(hist))
|
|
}
|
|
|
|
// we may be in dictionary.
|
|
dictO := len(s.dict) - (mo - (len(s.out) + len(hist)))
|
|
if dictO < 0 || dictO >= len(s.dict) {
|
|
return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(s.out)+len(hist))
|
|
}
|
|
end := dictO + ml
|
|
if end > len(s.dict) {
|
|
out = append(out, s.dict[dictO:]...)
|
|
mo -= len(s.dict) - dictO
|
|
ml -= len(s.dict) - dictO
|
|
} else {
|
|
out = append(out, s.dict[dictO:end]...)
|
|
mo = 0
|
|
ml = 0
|
|
}
|
|
}
|
|
|
|
// Copy from history.
|
|
// TODO: Blocks without history could be made to ignore this completely.
|
|
if v := mo - len(s.out); v > 0 {
|
|
// v is the start position in history from end.
|
|
start := len(s.hist) - v
|
|
if ml > v {
|
|
// Some goes into current block.
|
|
// Copy remainder of history
|
|
out = append(out, s.hist[start:]...)
|
|
mo -= v
|
|
ml -= v
|
|
} else {
|
|
out = append(out, s.hist[start:start+ml]...)
|
|
ml = 0
|
|
}
|
|
}
|
|
// We must be in current buffer now
|
|
if ml > 0 {
|
|
start := len(s.out) - mo
|
|
if ml <= len(s.out)-start {
|
|
// No overlap
|
|
out = append(out, s.out[start:start+ml]...)
|
|
} else {
|
|
// Overlapping copy
|
|
// Extend destination slice and copy one byte at the time.
|
|
out = out[:len(out)+ml]
|
|
src := out[start : start+ml]
|
|
// Destination is the space we just added.
|
|
dst := out[len(out)-ml:]
|
|
dst = dst[:len(src)]
|
|
for i := range src {
|
|
dst[i] = src[i]
|
|
}
|
|
}
|
|
}
|
|
s.out = out
|
|
if i == 0 {
|
|
// This is the last sequence, so we shouldn't update state.
|
|
break
|
|
}
|
|
|
|
// Manually inlined, ~ 5-20% faster
|
|
// Update all 3 states at once. Approx 20% faster.
|
|
nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits()
|
|
if nBits == 0 {
|
|
llState = llTable[llState.newState()&maxTableMask]
|
|
mlState = mlTable[mlState.newState()&maxTableMask]
|
|
ofState = ofTable[ofState.newState()&maxTableMask]
|
|
} else {
|
|
bits := br.getBitsFast(nBits)
|
|
lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31))
|
|
llState = llTable[(llState.newState()+lowBits)&maxTableMask]
|
|
|
|
lowBits = uint16(bits >> (ofState.nbBits() & 31))
|
|
lowBits &= bitMask[mlState.nbBits()&15]
|
|
mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask]
|
|
|
|
lowBits = uint16(bits) & bitMask[ofState.nbBits()&15]
|
|
ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask]
|
|
}
|
|
}
|
|
|
|
// Add final literals
|
|
s.out = append(s.out, s.literals...)
|
|
return nil
|
|
}
|
|
|
|
// update states, at least 27 bits must be available.
|
|
func (s *sequenceDecs) update(br *bitReader) {
|
|
// Max 8 bits
|
|
s.litLengths.state.next(br)
|
|
// Max 9 bits
|
|
s.matchLengths.state.next(br)
|
|
// Max 8 bits
|
|
s.offsets.state.next(br)
|
|
}
|
|
|
|
var bitMask [16]uint16
|
|
|
|
func init() {
|
|
for i := range bitMask[:] {
|
|
bitMask[i] = uint16((1 << uint(i)) - 1)
|
|
}
|
|
}
|
|
|
|
// update states, at least 27 bits must be available.
|
|
func (s *sequenceDecs) updateAlt(br *bitReader) {
|
|
// Update all 3 states at once. Approx 20% faster.
|
|
a, b, c := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state
|
|
|
|
nBits := a.nbBits() + b.nbBits() + c.nbBits()
|
|
if nBits == 0 {
|
|
s.litLengths.state.state = s.litLengths.state.dt[a.newState()]
|
|
s.matchLengths.state.state = s.matchLengths.state.dt[b.newState()]
|
|
s.offsets.state.state = s.offsets.state.dt[c.newState()]
|
|
return
|
|
}
|
|
bits := br.getBitsFast(nBits)
|
|
lowBits := uint16(bits >> ((c.nbBits() + b.nbBits()) & 31))
|
|
s.litLengths.state.state = s.litLengths.state.dt[a.newState()+lowBits]
|
|
|
|
lowBits = uint16(bits >> (c.nbBits() & 31))
|
|
lowBits &= bitMask[b.nbBits()&15]
|
|
s.matchLengths.state.state = s.matchLengths.state.dt[b.newState()+lowBits]
|
|
|
|
lowBits = uint16(bits) & bitMask[c.nbBits()&15]
|
|
s.offsets.state.state = s.offsets.state.dt[c.newState()+lowBits]
|
|
}
|
|
|
|
// nextFast will return new states when there are at least 4 unused bytes left on the stream when done.
|
|
func (s *sequenceDecs) nextFast(br *bitReader, llState, mlState, ofState decSymbol) (ll, mo, ml int) {
|
|
// Final will not read from stream.
|
|
ll, llB := llState.final()
|
|
ml, mlB := mlState.final()
|
|
mo, moB := ofState.final()
|
|
|
|
// extra bits are stored in reverse order.
|
|
br.fillFast()
|
|
mo += br.getBits(moB)
|
|
if s.maxBits > 32 {
|
|
br.fillFast()
|
|
}
|
|
ml += br.getBits(mlB)
|
|
ll += br.getBits(llB)
|
|
|
|
if moB > 1 {
|
|
s.prevOffset[2] = s.prevOffset[1]
|
|
s.prevOffset[1] = s.prevOffset[0]
|
|
s.prevOffset[0] = mo
|
|
return
|
|
}
|
|
// mo = s.adjustOffset(mo, ll, moB)
|
|
// Inlined for rather big speedup
|
|
if ll == 0 {
|
|
// There is an exception though, when current sequence's literals_length = 0.
|
|
// In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2,
|
|
// an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte.
|
|
mo++
|
|
}
|
|
|
|
if mo == 0 {
|
|
mo = s.prevOffset[0]
|
|
return
|
|
}
|
|
var temp int
|
|
if mo == 3 {
|
|
temp = s.prevOffset[0] - 1
|
|
} else {
|
|
temp = s.prevOffset[mo]
|
|
}
|
|
|
|
if temp == 0 {
|
|
// 0 is not valid; input is corrupted; force offset to 1
|
|
println("temp was 0")
|
|
temp = 1
|
|
}
|
|
|
|
if mo != 1 {
|
|
s.prevOffset[2] = s.prevOffset[1]
|
|
}
|
|
s.prevOffset[1] = s.prevOffset[0]
|
|
s.prevOffset[0] = temp
|
|
mo = temp
|
|
return
|
|
}
|
|
|
|
func (s *sequenceDecs) next(br *bitReader, llState, mlState, ofState decSymbol) (ll, mo, ml int) {
|
|
// Final will not read from stream.
|
|
ll, llB := llState.final()
|
|
ml, mlB := mlState.final()
|
|
mo, moB := ofState.final()
|
|
|
|
// extra bits are stored in reverse order.
|
|
br.fill()
|
|
if s.maxBits <= 32 {
|
|
mo += br.getBits(moB)
|
|
ml += br.getBits(mlB)
|
|
ll += br.getBits(llB)
|
|
} else {
|
|
mo += br.getBits(moB)
|
|
br.fill()
|
|
// matchlength+literal length, max 32 bits
|
|
ml += br.getBits(mlB)
|
|
ll += br.getBits(llB)
|
|
|
|
}
|
|
mo = s.adjustOffset(mo, ll, moB)
|
|
return
|
|
}
|
|
|
|
func (s *sequenceDecs) adjustOffset(offset, litLen int, offsetB uint8) int {
|
|
if offsetB > 1 {
|
|
s.prevOffset[2] = s.prevOffset[1]
|
|
s.prevOffset[1] = s.prevOffset[0]
|
|
s.prevOffset[0] = offset
|
|
return offset
|
|
}
|
|
|
|
if litLen == 0 {
|
|
// There is an exception though, when current sequence's literals_length = 0.
|
|
// In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2,
|
|
// an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte.
|
|
offset++
|
|
}
|
|
|
|
if offset == 0 {
|
|
return s.prevOffset[0]
|
|
}
|
|
var temp int
|
|
if offset == 3 {
|
|
temp = s.prevOffset[0] - 1
|
|
} else {
|
|
temp = s.prevOffset[offset]
|
|
}
|
|
|
|
if temp == 0 {
|
|
// 0 is not valid; input is corrupted; force offset to 1
|
|
println("temp was 0")
|
|
temp = 1
|
|
}
|
|
|
|
if offset != 1 {
|
|
s.prevOffset[2] = s.prevOffset[1]
|
|
}
|
|
s.prevOffset[1] = s.prevOffset[0]
|
|
s.prevOffset[0] = temp
|
|
return temp
|
|
}
|
|
|
|
// mergeHistory will merge history.
|
|
func (s *sequenceDecs) mergeHistory(hist *sequenceDecs) (*sequenceDecs, error) {
|
|
for i := uint(0); i < 3; i++ {
|
|
var sNew, sHist *sequenceDec
|
|
switch i {
|
|
default:
|
|
// same as "case 0":
|
|
sNew = &s.litLengths
|
|
sHist = &hist.litLengths
|
|
case 1:
|
|
sNew = &s.offsets
|
|
sHist = &hist.offsets
|
|
case 2:
|
|
sNew = &s.matchLengths
|
|
sHist = &hist.matchLengths
|
|
}
|
|
if sNew.repeat {
|
|
if sHist.fse == nil {
|
|
return nil, fmt.Errorf("sequence stream %d, repeat requested, but no history", i)
|
|
}
|
|
continue
|
|
}
|
|
if sNew.fse == nil {
|
|
return nil, fmt.Errorf("sequence stream %d, no fse found", i)
|
|
}
|
|
if sHist.fse != nil && !sHist.fse.preDefined {
|
|
fseDecoderPool.Put(sHist.fse)
|
|
}
|
|
sHist.fse = sNew.fse
|
|
}
|
|
return hist, nil
|
|
}
|