Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
6.5 KiB
277 lines
6.5 KiB
// Copyright 2014-2021 Ulrich Kunitz. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package lzma |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"io" |
|
) |
|
|
|
// decoder decodes a raw LZMA stream without any header. |
|
type decoder struct { |
|
// dictionary; the rear pointer of the buffer will be used for |
|
// reading the data. |
|
Dict *decoderDict |
|
// decoder state |
|
State *state |
|
// range decoder |
|
rd *rangeDecoder |
|
// start stores the head value of the dictionary for the LZMA |
|
// stream |
|
start int64 |
|
// size of uncompressed data |
|
size int64 |
|
// end-of-stream encountered |
|
eos bool |
|
// EOS marker found |
|
eosMarker bool |
|
} |
|
|
|
// newDecoder creates a new decoder instance. The parameter size provides |
|
// the expected byte size of the decompressed data. If the size is |
|
// unknown use a negative value. In that case the decoder will look for |
|
// a terminating end-of-stream marker. |
|
func newDecoder(br io.ByteReader, state *state, dict *decoderDict, size int64) (d *decoder, err error) { |
|
rd, err := newRangeDecoder(br) |
|
if err != nil { |
|
return nil, err |
|
} |
|
d = &decoder{ |
|
State: state, |
|
Dict: dict, |
|
rd: rd, |
|
size: size, |
|
start: dict.pos(), |
|
} |
|
return d, nil |
|
} |
|
|
|
// Reopen restarts the decoder with a new byte reader and a new size. Reopen |
|
// resets the Decompressed counter to zero. |
|
func (d *decoder) Reopen(br io.ByteReader, size int64) error { |
|
var err error |
|
if d.rd, err = newRangeDecoder(br); err != nil { |
|
return err |
|
} |
|
d.start = d.Dict.pos() |
|
d.size = size |
|
d.eos = false |
|
return nil |
|
} |
|
|
|
// decodeLiteral decodes a single literal from the LZMA stream. |
|
func (d *decoder) decodeLiteral() (op operation, err error) { |
|
litState := d.State.litState(d.Dict.byteAt(1), d.Dict.head) |
|
match := d.Dict.byteAt(int(d.State.rep[0]) + 1) |
|
s, err := d.State.litCodec.Decode(d.rd, d.State.state, match, litState) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return lit{s}, nil |
|
} |
|
|
|
// errEOS indicates that an EOS marker has been found. |
|
var errEOS = errors.New("EOS marker found") |
|
|
|
// readOp decodes the next operation from the compressed stream. It |
|
// returns the operation. If an explicit end of stream marker is |
|
// identified the eos error is returned. |
|
func (d *decoder) readOp() (op operation, err error) { |
|
// Value of the end of stream (EOS) marker |
|
const eosDist = 1<<32 - 1 |
|
|
|
state, state2, posState := d.State.states(d.Dict.head) |
|
|
|
b, err := d.State.isMatch[state2].Decode(d.rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if b == 0 { |
|
// literal |
|
op, err := d.decodeLiteral() |
|
if err != nil { |
|
return nil, err |
|
} |
|
d.State.updateStateLiteral() |
|
return op, nil |
|
} |
|
b, err = d.State.isRep[state].Decode(d.rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if b == 0 { |
|
// simple match |
|
d.State.rep[3], d.State.rep[2], d.State.rep[1] = |
|
d.State.rep[2], d.State.rep[1], d.State.rep[0] |
|
|
|
d.State.updateStateMatch() |
|
// The length decoder returns the length offset. |
|
n, err := d.State.lenCodec.Decode(d.rd, posState) |
|
if err != nil { |
|
return nil, err |
|
} |
|
// The dist decoder returns the distance offset. The actual |
|
// distance is 1 higher. |
|
d.State.rep[0], err = d.State.distCodec.Decode(d.rd, n) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if d.State.rep[0] == eosDist { |
|
d.eosMarker = true |
|
return nil, errEOS |
|
} |
|
op = match{n: int(n) + minMatchLen, |
|
distance: int64(d.State.rep[0]) + minDistance} |
|
return op, nil |
|
} |
|
b, err = d.State.isRepG0[state].Decode(d.rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
dist := d.State.rep[0] |
|
if b == 0 { |
|
// rep match 0 |
|
b, err = d.State.isRepG0Long[state2].Decode(d.rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if b == 0 { |
|
d.State.updateStateShortRep() |
|
op = match{n: 1, distance: int64(dist) + minDistance} |
|
return op, nil |
|
} |
|
} else { |
|
b, err = d.State.isRepG1[state].Decode(d.rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if b == 0 { |
|
dist = d.State.rep[1] |
|
} else { |
|
b, err = d.State.isRepG2[state].Decode(d.rd) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if b == 0 { |
|
dist = d.State.rep[2] |
|
} else { |
|
dist = d.State.rep[3] |
|
d.State.rep[3] = d.State.rep[2] |
|
} |
|
d.State.rep[2] = d.State.rep[1] |
|
} |
|
d.State.rep[1] = d.State.rep[0] |
|
d.State.rep[0] = dist |
|
} |
|
n, err := d.State.repLenCodec.Decode(d.rd, posState) |
|
if err != nil { |
|
return nil, err |
|
} |
|
d.State.updateStateRep() |
|
op = match{n: int(n) + minMatchLen, distance: int64(dist) + minDistance} |
|
return op, nil |
|
} |
|
|
|
// apply takes the operation and transforms the decoder dictionary accordingly. |
|
func (d *decoder) apply(op operation) error { |
|
var err error |
|
switch x := op.(type) { |
|
case match: |
|
err = d.Dict.writeMatch(x.distance, x.n) |
|
case lit: |
|
err = d.Dict.WriteByte(x.b) |
|
default: |
|
panic("op is neither a match nor a literal") |
|
} |
|
return err |
|
} |
|
|
|
// decompress fills the dictionary unless no space for new data is |
|
// available. If the end of the LZMA stream has been reached io.EOF will |
|
// be returned. |
|
func (d *decoder) decompress() error { |
|
if d.eos { |
|
return io.EOF |
|
} |
|
for d.Dict.Available() >= maxMatchLen { |
|
op, err := d.readOp() |
|
switch err { |
|
case nil: |
|
// break |
|
case errEOS: |
|
d.eos = true |
|
if !d.rd.possiblyAtEnd() { |
|
return errDataAfterEOS |
|
} |
|
if d.size >= 0 && d.size != d.Decompressed() { |
|
return errSize |
|
} |
|
return io.EOF |
|
case io.EOF: |
|
d.eos = true |
|
return io.ErrUnexpectedEOF |
|
default: |
|
return err |
|
} |
|
if err = d.apply(op); err != nil { |
|
return err |
|
} |
|
if d.size >= 0 && d.Decompressed() >= d.size { |
|
d.eos = true |
|
if d.Decompressed() > d.size { |
|
return errSize |
|
} |
|
if !d.rd.possiblyAtEnd() { |
|
switch _, err = d.readOp(); err { |
|
case nil: |
|
return errSize |
|
case io.EOF: |
|
return io.ErrUnexpectedEOF |
|
case errEOS: |
|
break |
|
default: |
|
return err |
|
} |
|
} |
|
return io.EOF |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
// Errors that may be returned while decoding data. |
|
var ( |
|
errDataAfterEOS = errors.New("lzma: data after end of stream marker") |
|
errSize = errors.New("lzma: wrong uncompressed data size") |
|
) |
|
|
|
// Read reads data from the buffer. If no more data is available io.EOF is |
|
// returned. |
|
func (d *decoder) Read(p []byte) (n int, err error) { |
|
var k int |
|
for { |
|
// Read of decoder dict never returns an error. |
|
k, err = d.Dict.Read(p[n:]) |
|
if err != nil { |
|
panic(fmt.Errorf("dictionary read error %s", err)) |
|
} |
|
if k == 0 && d.eos { |
|
return n, io.EOF |
|
} |
|
n += k |
|
if n >= len(p) { |
|
return n, nil |
|
} |
|
if err = d.decompress(); err != nil && err != io.EOF { |
|
return n, err |
|
} |
|
} |
|
} |
|
|
|
// Decompressed returns the number of bytes decompressed by the decoder. |
|
func (d *decoder) Decompressed() int64 { |
|
return d.Dict.pos() - d.start |
|
}
|
|
|