aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/ulikunitz/xz/lzma/reader2.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ulikunitz/xz/lzma/reader2.go')
-rw-r--r--vendor/github.com/ulikunitz/xz/lzma/reader2.go232
1 files changed, 232 insertions, 0 deletions
diff --git a/vendor/github.com/ulikunitz/xz/lzma/reader2.go b/vendor/github.com/ulikunitz/xz/lzma/reader2.go
new file mode 100644
index 0000000..a55cfaa
--- /dev/null
+++ b/vendor/github.com/ulikunitz/xz/lzma/reader2.go
@@ -0,0 +1,232 @@
1// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package lzma
6
7import (
8 "errors"
9 "io"
10
11 "github.com/ulikunitz/xz/internal/xlog"
12)
13
14// Reader2Config stores the parameters for the LZMA2 reader.
15// format.
16type Reader2Config struct {
17 DictCap int
18}
19
20// fill converts the zero values of the configuration to the default values.
21func (c *Reader2Config) fill() {
22 if c.DictCap == 0 {
23 c.DictCap = 8 * 1024 * 1024
24 }
25}
26
27// Verify checks the reader configuration for errors. Zero configuration values
28// will be replaced by default values.
29func (c *Reader2Config) Verify() error {
30 c.fill()
31 if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) {
32 return errors.New("lzma: dictionary capacity is out of range")
33 }
34 return nil
35}
36
37// Reader2 supports the reading of LZMA2 chunk sequences. Note that the
38// first chunk should have a dictionary reset and the first compressed
39// chunk a properties reset. The chunk sequence may not be terminated by
40// an end-of-stream chunk.
41type Reader2 struct {
42 r io.Reader
43 err error
44
45 dict *decoderDict
46 ur *uncompressedReader
47 decoder *decoder
48 chunkReader io.Reader
49
50 cstate chunkState
51 ctype chunkType
52}
53
54// NewReader2 creates a reader for an LZMA2 chunk sequence.
55func NewReader2(lzma2 io.Reader) (r *Reader2, err error) {
56 return Reader2Config{}.NewReader2(lzma2)
57}
58
59// NewReader2 creates an LZMA2 reader using the given configuration.
60func (c Reader2Config) NewReader2(lzma2 io.Reader) (r *Reader2, err error) {
61 if err = c.Verify(); err != nil {
62 return nil, err
63 }
64 r = &Reader2{r: lzma2, cstate: start}
65 r.dict, err = newDecoderDict(c.DictCap)
66 if err != nil {
67 return nil, err
68 }
69 if err = r.startChunk(); err != nil {
70 r.err = err
71 }
72 return r, nil
73}
74
75// uncompressed tests whether the chunk type specifies an uncompressed
76// chunk.
77func uncompressed(ctype chunkType) bool {
78 return ctype == cU || ctype == cUD
79}
80
81// startChunk parses a new chunk.
82func (r *Reader2) startChunk() error {
83 r.chunkReader = nil
84 header, err := readChunkHeader(r.r)
85 if err != nil {
86 if err == io.EOF {
87 err = io.ErrUnexpectedEOF
88 }
89 return err
90 }
91 xlog.Debugf("chunk header %v", header)
92 if err = r.cstate.next(header.ctype); err != nil {
93 return err
94 }
95 if r.cstate == stop {
96 return io.EOF
97 }
98 if header.ctype == cUD || header.ctype == cLRND {
99 r.dict.Reset()
100 }
101 size := int64(header.uncompressed) + 1
102 if uncompressed(header.ctype) {
103 if r.ur != nil {
104 r.ur.Reopen(r.r, size)
105 } else {
106 r.ur = newUncompressedReader(r.r, r.dict, size)
107 }
108 r.chunkReader = r.ur
109 return nil
110 }
111 br := ByteReader(io.LimitReader(r.r, int64(header.compressed)+1))
112 if r.decoder == nil {
113 state := newState(header.props)
114 r.decoder, err = newDecoder(br, state, r.dict, size)
115 if err != nil {
116 return err
117 }
118 r.chunkReader = r.decoder
119 return nil
120 }
121 switch header.ctype {
122 case cLR:
123 r.decoder.State.Reset()
124 case cLRN, cLRND:
125 r.decoder.State = newState(header.props)
126 }
127 err = r.decoder.Reopen(br, size)
128 if err != nil {
129 return err
130 }
131 r.chunkReader = r.decoder
132 return nil
133}
134
135// Read reads data from the LZMA2 chunk sequence.
136func (r *Reader2) Read(p []byte) (n int, err error) {
137 if r.err != nil {
138 return 0, r.err
139 }
140 for n < len(p) {
141 var k int
142 k, err = r.chunkReader.Read(p[n:])
143 n += k
144 if err != nil {
145 if err == io.EOF {
146 err = r.startChunk()
147 if err == nil {
148 continue
149 }
150 }
151 r.err = err
152 return n, err
153 }
154 if k == 0 {
155 r.err = errors.New("lzma: Reader2 doesn't get data")
156 return n, r.err
157 }
158 }
159 return n, nil
160}
161
162// EOS returns whether the LZMA2 stream has been terminated by an
163// end-of-stream chunk.
164func (r *Reader2) EOS() bool {
165 return r.cstate == stop
166}
167
168// uncompressedReader is used to read uncompressed chunks.
169type uncompressedReader struct {
170 lr io.LimitedReader
171 Dict *decoderDict
172 eof bool
173 err error
174}
175
176// newUncompressedReader initializes a new uncompressedReader.
177func newUncompressedReader(r io.Reader, dict *decoderDict, size int64) *uncompressedReader {
178 ur := &uncompressedReader{
179 lr: io.LimitedReader{R: r, N: size},
180 Dict: dict,
181 }
182 return ur
183}
184
185// Reopen reinitializes an uncompressed reader.
186func (ur *uncompressedReader) Reopen(r io.Reader, size int64) {
187 ur.err = nil
188 ur.eof = false
189 ur.lr = io.LimitedReader{R: r, N: size}
190}
191
192// fill reads uncompressed data into the dictionary.
193func (ur *uncompressedReader) fill() error {
194 if !ur.eof {
195 n, err := io.CopyN(ur.Dict, &ur.lr, int64(ur.Dict.Available()))
196 if err != io.EOF {
197 return err
198 }
199 ur.eof = true
200 if n > 0 {
201 return nil
202 }
203 }
204 if ur.lr.N != 0 {
205 return io.ErrUnexpectedEOF
206 }
207 return io.EOF
208}
209
210// Read reads uncompressed data from the limited reader.
211func (ur *uncompressedReader) Read(p []byte) (n int, err error) {
212 if ur.err != nil {
213 return 0, ur.err
214 }
215 for {
216 var k int
217 k, err = ur.Dict.Read(p[n:])
218 n += k
219 if n >= len(p) {
220 return n, nil
221 }
222 if err != nil {
223 break
224 }
225 err = ur.fill()
226 if err != nil {
227 break
228 }
229 }
230 ur.err = err
231 return n, err
232}