package zstd
import (
"errors"
"fmt"
"io"
)
type seq struct {
litLen uint32
matchLen uint32
offset uint32
llCode, mlCode, ofCode uint8
}
type seqVals struct {
ll, ml, mo int
}
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 {
fse *fseDecoder
state fseState
repeat bool
}
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
}
type sequenceDecs struct {
litLengths sequenceDec
offsets sequenceDec
matchLengths sequenceDec
prevOffset [3 ]int
dict []byte
literals []byte
out []byte
nSeqs int
br *bitReader
seqSize int
windowSize int
maxBits uint8
maxSyncLen uint64
}
func (s *sequenceDecs ) initialize (br *bitReader , hist *history , 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 .br = br
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
}
func (s *sequenceDecs ) freeDecoders () {
if f := s .litLengths .fse ; f != nil && !f .preDefined {
fseDecoderPool .Put (f )
s .litLengths .fse = nil
}
if f := s .offsets .fse ; f != nil && !f .preDefined {
fseDecoderPool .Put (f )
s .offsets .fse = nil
}
if f := s .matchLengths .fse ; f != nil && !f .preDefined {
fseDecoderPool .Put (f )
s .matchLengths .fse = nil
}
}
func (s *sequenceDecs ) execute (seqs []seqVals , hist []byte ) error {
if len (s .dict ) == 0 {
return s .executeSimple (seqs , hist )
}
if len (s .out )+s .seqSize > cap (s .out ) {
addBytes := s .seqSize + len (s .out )
s .out = append (s .out , make ([]byte , addBytes )...)
s .out = s .out [:len (s .out )-addBytes ]
}
if debugDecoder {
printf ("Execute %d seqs with hist %d, dict %d, literals: %d into %d bytes\n" , len (seqs ), len (hist ), len (s .dict ), len (s .literals ), s .seqSize )
}
var t = len (s .out )
out := s .out [:t +s .seqSize ]
for _ , seq := range seqs {
copy (out [t :], s .literals [:seq .ll ])
t += seq .ll
s .literals = s .literals [seq .ll :]
if seq .mo > t +len (hist ) || seq .mo > s .windowSize {
if len (s .dict ) == 0 {
return fmt .Errorf ("match offset (%d) bigger than current history (%d)" , seq .mo , t +len (hist ))
}
dictO := len (s .dict ) - (seq .mo - (t + len (hist )))
if dictO < 0 || dictO >= len (s .dict ) {
return fmt .Errorf ("match offset (%d) bigger than current history+dict (%d)" , seq .mo , t +len (hist )+len (s .dict ))
}
end := dictO + seq .ml
if end > len (s .dict ) {
n := len (s .dict ) - dictO
copy (out [t :], s .dict [dictO :])
t += n
seq .ml -= n
} else {
copy (out [t :], s .dict [dictO :end ])
t += end - dictO
continue
}
}
if v := seq .mo - t ; v > 0 {
start := len (hist ) - v
if seq .ml > v {
copy (out [t :], hist [start :])
t += v
seq .ml -= v
} else {
copy (out [t :], hist [start :start +seq .ml ])
t += seq .ml
continue
}
}
if seq .ml > 0 {
start := t - seq .mo
if seq .ml <= t -start {
copy (out [t :], out [start :start +seq .ml ])
t += seq .ml
continue
} else {
src := out [start : start +seq .ml ]
dst := out [t :]
dst = dst [:len (src )]
t += len (src )
for i := range src {
dst [i ] = src [i ]
}
}
}
}
copy (out [t :], s .literals )
if debugDecoder {
t += len (s .literals )
if t != len (out ) {
panic (fmt .Errorf ("length mismatch, want %d, got %d, ss: %d" , len (out ), t , s .seqSize ))
}
}
s .out = out
return nil
}
func (s *sequenceDecs ) decodeSync (hist []byte ) error {
supported , err := s .decodeSyncSimple (hist )
if supported {
return err
}
br := s .br
seqs := s .nSeqs
startSize := len (s .out )
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
out := s .out
maxBlockSize := min (s .windowSize , maxCompressedBlockSize )
if debugDecoder {
println ("decodeSync: decoding" , seqs , "sequences" , br .remain (), "bits remain on stream" )
}
for i := seqs - 1 ; i >= 0 ; i -- {
if br .overread () {
printf ("reading sequence %d, exceeded available data. Overread by %d\n" , seqs -i , -br .remain ())
return io .ErrUnexpectedEOF
}
var ll , mo , ml int
if br .cursor > 4 +((maxOffsetBits +16 +16 )>>3 ) {
var llB , mlB , moB uint8
ll , llB = llState .final ()
ml , mlB = mlState .final ()
mo , moB = ofState .final ()
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 {
if ll == 0 {
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 {
println ("WARNING: 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 (out )
if size -startSize > maxBlockSize {
return fmt .Errorf ("output bigger than max block size (%d)" , maxBlockSize )
}
if size > cap (out ) {
used := len (out ) - startSize
addBytes := 256 + ll + ml + used >>2
if used +addBytes > maxBlockSize {
addBytes = maxBlockSize - used
}
out = append (out , make ([]byte , addBytes )...)
out = out [:len (out )-addBytes ]
}
if ml > maxMatchLen {
return fmt .Errorf ("match len (%d) bigger than max allowed length" , ml )
}
out = append (out , s .literals [:ll ]...)
s .literals = s .literals [ll :]
if mo == 0 && ml > 0 {
return fmt .Errorf ("zero matchoff and matchlen (%d) > 0" , ml )
}
if mo > len (out )+len (hist ) || mo > s .windowSize {
if len (s .dict ) == 0 {
return fmt .Errorf ("match offset (%d) bigger than current history (%d)" , mo , len (out )+len (hist )-startSize )
}
dictO := len (s .dict ) - (mo - (len (out ) + len (hist )))
if dictO < 0 || dictO >= len (s .dict ) {
return fmt .Errorf ("match offset (%d) bigger than current history (%d)" , mo , len (out )+len (hist )-startSize )
}
end := dictO + ml
if end > len (s .dict ) {
out = append (out , s .dict [dictO :]...)
ml -= len (s .dict ) - dictO
} else {
out = append (out , s .dict [dictO :end ]...)
mo = 0
ml = 0
}
}
if v := mo - len (out ); v > 0 {
start := len (hist ) - v
if ml > v {
out = append (out , hist [start :]...)
ml -= v
} else {
out = append (out , hist [start :start +ml ]...)
ml = 0
}
}
if ml > 0 {
start := len (out ) - mo
if ml <= len (out )-start {
out = append (out , out [start :start +ml ]...)
} else {
out = out [:len (out )+ml ]
src := out [start : start +ml ]
dst := out [len (out )-ml :]
dst = dst [:len (src )]
for i := range src {
dst [i ] = src [i ]
}
}
}
if i == 0 {
break
}
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 .get32BitsFast (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 ]
}
}
if size := len (s .literals ) + len (out ) - startSize ; size > maxBlockSize {
return fmt .Errorf ("output bigger than max block size (%d)" , maxBlockSize )
}
s .out = append (out , s .literals ...)
return br .close ()
}
var bitMask [16 ]uint16
func init() {
for i := range bitMask [:] {
bitMask [i ] = uint16 ((1 << uint (i )) - 1 )
}
}
func (s *sequenceDecs ) next (br *bitReader , llState , mlState , ofState decSymbol ) (ll , mo , ml int ) {
ll , llB := llState .final ()
ml , mlB := mlState .final ()
mo , moB := ofState .final ()
br .fill ()
mo += br .getBits (moB )
if s .maxBits > 32 {
br .fill ()
}
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 {
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 {
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .