| //go:build !amd64 || appengine || !gc || noasm |
| // +build !amd64 appengine !gc noasm |
| |
| package zstd |
| |
| import ( |
| "fmt" |
| "io" |
| ) |
| |
| // decode sequences from the stream with the provided history but without dictionary. |
| func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { |
| return false, nil |
| } |
| |
| // decode sequences from the stream without the provided history. |
| func (s *sequenceDecs) decode(seqs []seqVals) error { |
| br := s.br |
| |
| // 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 |
| s.seqSize = 0 |
| litRemain := len(s.literals) |
| |
| maxBlockSize := maxCompressedBlockSize |
| if s.windowSize < maxBlockSize { |
| maxBlockSize = s.windowSize |
| } |
| for i := range seqs { |
| 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("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 { |
| if br.overread() { |
| if debugDecoder { |
| printf("reading sequence %d, exceeded available data\n", i) |
| } |
| return io.ErrUnexpectedEOF |
| } |
| ll, mo, ml = s.next(br, llState, mlState, ofState) |
| br.fill() |
| } |
| |
| if debugSequences { |
| println("Seq", i, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) |
| } |
| // Evaluate. |
| // We might be doing this async, so do it early. |
| if mo == 0 && ml > 0 { |
| return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) |
| } |
| if ml > maxMatchLen { |
| return fmt.Errorf("match len (%d) bigger than max allowed length", ml) |
| } |
| s.seqSize += ll + ml |
| if s.seqSize > maxBlockSize { |
| return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) |
| } |
| litRemain -= ll |
| if litRemain < 0 { |
| return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, litRemain+ll) |
| } |
| seqs[i] = seqVals{ |
| ll: ll, |
| ml: ml, |
| mo: mo, |
| } |
| if i == len(seqs)-1 { |
| // 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.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] |
| } |
| } |
| s.seqSize += litRemain |
| if s.seqSize > maxBlockSize { |
| return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) |
| } |
| err := br.close() |
| if err != nil { |
| printf("Closing sequences: %v, %+v\n", err, *br) |
| } |
| return err |
| } |
| |
| // executeSimple handles cases when a dictionary is not used. |
| func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { |
| // Ensure we have enough output size... |
| 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 literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) |
| } |
| |
| var t = len(s.out) |
| out := s.out[:t+s.seqSize] |
| |
| for _, seq := range seqs { |
| // Add literals |
| copy(out[t:], s.literals[:seq.ll]) |
| t += seq.ll |
| s.literals = s.literals[seq.ll:] |
| |
| // Malformed input |
| if seq.mo > t+len(hist) || seq.mo > s.windowSize { |
| return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist)) |
| } |
| |
| // Copy from history. |
| if v := seq.mo - t; v > 0 { |
| // v is the start position in history from end. |
| start := len(hist) - v |
| if seq.ml > v { |
| // Some goes into the current block. |
| // Copy remainder of history |
| copy(out[t:], hist[start:]) |
| t += v |
| seq.ml -= v |
| } else { |
| copy(out[t:], hist[start:start+seq.ml]) |
| t += seq.ml |
| continue |
| } |
| } |
| |
| // We must be in the current buffer now |
| if seq.ml > 0 { |
| start := t - seq.mo |
| if seq.ml <= t-start { |
| // No overlap |
| copy(out[t:], out[start:start+seq.ml]) |
| t += seq.ml |
| } else { |
| // Overlapping copy |
| // Extend destination slice and copy one byte at the time. |
| src := out[start : start+seq.ml] |
| dst := out[t:] |
| dst = dst[:len(src)] |
| t += len(src) |
| // Destination is the space we just added. |
| for i := range src { |
| dst[i] = src[i] |
| } |
| } |
| } |
| } |
| // Add final literals |
| 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 |
| } |