diff options
Diffstat (limited to 'vendor/github.com/ulikunitz/xz/lzma/writer2.go')
-rw-r--r-- | vendor/github.com/ulikunitz/xz/lzma/writer2.go | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/vendor/github.com/ulikunitz/xz/lzma/writer2.go b/vendor/github.com/ulikunitz/xz/lzma/writer2.go new file mode 100644 index 0000000..7c1afe1 --- /dev/null +++ b/vendor/github.com/ulikunitz/xz/lzma/writer2.go | |||
@@ -0,0 +1,305 @@ | |||
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 | |||
5 | package lzma | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "errors" | ||
10 | "io" | ||
11 | ) | ||
12 | |||
13 | // Writer2Config is used to create a Writer2 using parameters. | ||
14 | type Writer2Config struct { | ||
15 | // The properties for the encoding. If the it is nil the value | ||
16 | // {LC: 3, LP: 0, PB: 2} will be chosen. | ||
17 | Properties *Properties | ||
18 | // The capacity of the dictionary. If DictCap is zero, the value | ||
19 | // 8 MiB will be chosen. | ||
20 | DictCap int | ||
21 | // Size of the lookahead buffer; value 0 indicates default size | ||
22 | // 4096 | ||
23 | BufSize int | ||
24 | // Match algorithm | ||
25 | Matcher MatchAlgorithm | ||
26 | } | ||
27 | |||
28 | // fill replaces zero values with default values. | ||
29 | func (c *Writer2Config) fill() { | ||
30 | if c.Properties == nil { | ||
31 | c.Properties = &Properties{LC: 3, LP: 0, PB: 2} | ||
32 | } | ||
33 | if c.DictCap == 0 { | ||
34 | c.DictCap = 8 * 1024 * 1024 | ||
35 | } | ||
36 | if c.BufSize == 0 { | ||
37 | c.BufSize = 4096 | ||
38 | } | ||
39 | } | ||
40 | |||
41 | // Verify checks the Writer2Config for correctness. Zero values will be | ||
42 | // replaced by default values. | ||
43 | func (c *Writer2Config) Verify() error { | ||
44 | c.fill() | ||
45 | var err error | ||
46 | if c == nil { | ||
47 | return errors.New("lzma: WriterConfig is nil") | ||
48 | } | ||
49 | if c.Properties == nil { | ||
50 | return errors.New("lzma: WriterConfig has no Properties set") | ||
51 | } | ||
52 | if err = c.Properties.verify(); err != nil { | ||
53 | return err | ||
54 | } | ||
55 | if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) { | ||
56 | return errors.New("lzma: dictionary capacity is out of range") | ||
57 | } | ||
58 | if !(maxMatchLen <= c.BufSize) { | ||
59 | return errors.New("lzma: lookahead buffer size too small") | ||
60 | } | ||
61 | if c.Properties.LC+c.Properties.LP > 4 { | ||
62 | return errors.New("lzma: sum of lc and lp exceeds 4") | ||
63 | } | ||
64 | if err = c.Matcher.verify(); err != nil { | ||
65 | return err | ||
66 | } | ||
67 | return nil | ||
68 | } | ||
69 | |||
70 | // Writer2 supports the creation of an LZMA2 stream. But note that | ||
71 | // written data is buffered, so call Flush or Close to write data to the | ||
72 | // underlying writer. The Close method writes the end-of-stream marker | ||
73 | // to the stream. So you may be able to concatenate the output of two | ||
74 | // writers as long the output of the first writer has only been flushed | ||
75 | // but not closed. | ||
76 | // | ||
77 | // Any change to the fields Properties, DictCap must be done before the | ||
78 | // first call to Write, Flush or Close. | ||
79 | type Writer2 struct { | ||
80 | w io.Writer | ||
81 | |||
82 | start *state | ||
83 | encoder *encoder | ||
84 | |||
85 | cstate chunkState | ||
86 | ctype chunkType | ||
87 | |||
88 | buf bytes.Buffer | ||
89 | lbw LimitedByteWriter | ||
90 | } | ||
91 | |||
92 | // NewWriter2 creates an LZMA2 chunk sequence writer with the default | ||
93 | // parameters and options. | ||
94 | func NewWriter2(lzma2 io.Writer) (w *Writer2, err error) { | ||
95 | return Writer2Config{}.NewWriter2(lzma2) | ||
96 | } | ||
97 | |||
98 | // NewWriter2 creates a new LZMA2 writer using the given configuration. | ||
99 | func (c Writer2Config) NewWriter2(lzma2 io.Writer) (w *Writer2, err error) { | ||
100 | if err = c.Verify(); err != nil { | ||
101 | return nil, err | ||
102 | } | ||
103 | w = &Writer2{ | ||
104 | w: lzma2, | ||
105 | start: newState(*c.Properties), | ||
106 | cstate: start, | ||
107 | ctype: start.defaultChunkType(), | ||
108 | } | ||
109 | w.buf.Grow(maxCompressed) | ||
110 | w.lbw = LimitedByteWriter{BW: &w.buf, N: maxCompressed} | ||
111 | m, err := c.Matcher.new(c.DictCap) | ||
112 | if err != nil { | ||
113 | return nil, err | ||
114 | } | ||
115 | d, err := newEncoderDict(c.DictCap, c.BufSize, m) | ||
116 | if err != nil { | ||
117 | return nil, err | ||
118 | } | ||
119 | w.encoder, err = newEncoder(&w.lbw, cloneState(w.start), d, 0) | ||
120 | if err != nil { | ||
121 | return nil, err | ||
122 | } | ||
123 | return w, nil | ||
124 | } | ||
125 | |||
126 | // written returns the number of bytes written to the current chunk | ||
127 | func (w *Writer2) written() int { | ||
128 | if w.encoder == nil { | ||
129 | return 0 | ||
130 | } | ||
131 | return int(w.encoder.Compressed()) + w.encoder.dict.Buffered() | ||
132 | } | ||
133 | |||
134 | // errClosed indicates that the writer is closed. | ||
135 | var errClosed = errors.New("lzma: writer closed") | ||
136 | |||
137 | // Writes data to LZMA2 stream. Note that written data will be buffered. | ||
138 | // Use Flush or Close to ensure that data is written to the underlying | ||
139 | // writer. | ||
140 | func (w *Writer2) Write(p []byte) (n int, err error) { | ||
141 | if w.cstate == stop { | ||
142 | return 0, errClosed | ||
143 | } | ||
144 | for n < len(p) { | ||
145 | m := maxUncompressed - w.written() | ||
146 | if m <= 0 { | ||
147 | panic("lzma: maxUncompressed reached") | ||
148 | } | ||
149 | var q []byte | ||
150 | if n+m < len(p) { | ||
151 | q = p[n : n+m] | ||
152 | } else { | ||
153 | q = p[n:] | ||
154 | } | ||
155 | k, err := w.encoder.Write(q) | ||
156 | n += k | ||
157 | if err != nil && err != ErrLimit { | ||
158 | return n, err | ||
159 | } | ||
160 | if err == ErrLimit || k == m { | ||
161 | if err = w.flushChunk(); err != nil { | ||
162 | return n, err | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | return n, nil | ||
167 | } | ||
168 | |||
169 | // writeUncompressedChunk writes an uncompressed chunk to the LZMA2 | ||
170 | // stream. | ||
171 | func (w *Writer2) writeUncompressedChunk() error { | ||
172 | u := w.encoder.Compressed() | ||
173 | if u <= 0 { | ||
174 | return errors.New("lzma: can't write empty uncompressed chunk") | ||
175 | } | ||
176 | if u > maxUncompressed { | ||
177 | panic("overrun of uncompressed data limit") | ||
178 | } | ||
179 | switch w.ctype { | ||
180 | case cLRND: | ||
181 | w.ctype = cUD | ||
182 | default: | ||
183 | w.ctype = cU | ||
184 | } | ||
185 | w.encoder.state = w.start | ||
186 | |||
187 | header := chunkHeader{ | ||
188 | ctype: w.ctype, | ||
189 | uncompressed: uint32(u - 1), | ||
190 | } | ||
191 | hdata, err := header.MarshalBinary() | ||
192 | if err != nil { | ||
193 | return err | ||
194 | } | ||
195 | if _, err = w.w.Write(hdata); err != nil { | ||
196 | return err | ||
197 | } | ||
198 | _, err = w.encoder.dict.CopyN(w.w, int(u)) | ||
199 | return err | ||
200 | } | ||
201 | |||
202 | // writeCompressedChunk writes a compressed chunk to the underlying | ||
203 | // writer. | ||
204 | func (w *Writer2) writeCompressedChunk() error { | ||
205 | if w.ctype == cU || w.ctype == cUD { | ||
206 | panic("chunk type uncompressed") | ||
207 | } | ||
208 | |||
209 | u := w.encoder.Compressed() | ||
210 | if u <= 0 { | ||
211 | return errors.New("writeCompressedChunk: empty chunk") | ||
212 | } | ||
213 | if u > maxUncompressed { | ||
214 | panic("overrun of uncompressed data limit") | ||
215 | } | ||
216 | c := w.buf.Len() | ||
217 | if c <= 0 { | ||
218 | panic("no compressed data") | ||
219 | } | ||
220 | if c > maxCompressed { | ||
221 | panic("overrun of compressed data limit") | ||
222 | } | ||
223 | header := chunkHeader{ | ||
224 | ctype: w.ctype, | ||
225 | uncompressed: uint32(u - 1), | ||
226 | compressed: uint16(c - 1), | ||
227 | props: w.encoder.state.Properties, | ||
228 | } | ||
229 | hdata, err := header.MarshalBinary() | ||
230 | if err != nil { | ||
231 | return err | ||
232 | } | ||
233 | if _, err = w.w.Write(hdata); err != nil { | ||
234 | return err | ||
235 | } | ||
236 | _, err = io.Copy(w.w, &w.buf) | ||
237 | return err | ||
238 | } | ||
239 | |||
240 | // writes a single chunk to the underlying writer. | ||
241 | func (w *Writer2) writeChunk() error { | ||
242 | u := int(uncompressedHeaderLen + w.encoder.Compressed()) | ||
243 | c := headerLen(w.ctype) + w.buf.Len() | ||
244 | if u < c { | ||
245 | return w.writeUncompressedChunk() | ||
246 | } | ||
247 | return w.writeCompressedChunk() | ||
248 | } | ||
249 | |||
250 | // flushChunk terminates the current chunk. The encoder will be reset | ||
251 | // to support the next chunk. | ||
252 | func (w *Writer2) flushChunk() error { | ||
253 | if w.written() == 0 { | ||
254 | return nil | ||
255 | } | ||
256 | var err error | ||
257 | if err = w.encoder.Close(); err != nil { | ||
258 | return err | ||
259 | } | ||
260 | if err = w.writeChunk(); err != nil { | ||
261 | return err | ||
262 | } | ||
263 | w.buf.Reset() | ||
264 | w.lbw.N = maxCompressed | ||
265 | if err = w.encoder.Reopen(&w.lbw); err != nil { | ||
266 | return err | ||
267 | } | ||
268 | if err = w.cstate.next(w.ctype); err != nil { | ||
269 | return err | ||
270 | } | ||
271 | w.ctype = w.cstate.defaultChunkType() | ||
272 | w.start = cloneState(w.encoder.state) | ||
273 | return nil | ||
274 | } | ||
275 | |||
276 | // Flush writes all buffered data out to the underlying stream. This | ||
277 | // could result in multiple chunks to be created. | ||
278 | func (w *Writer2) Flush() error { | ||
279 | if w.cstate == stop { | ||
280 | return errClosed | ||
281 | } | ||
282 | for w.written() > 0 { | ||
283 | if err := w.flushChunk(); err != nil { | ||
284 | return err | ||
285 | } | ||
286 | } | ||
287 | return nil | ||
288 | } | ||
289 | |||
290 | // Close terminates the LZMA2 stream with an EOS chunk. | ||
291 | func (w *Writer2) Close() error { | ||
292 | if w.cstate == stop { | ||
293 | return errClosed | ||
294 | } | ||
295 | if err := w.Flush(); err != nil { | ||
296 | return nil | ||
297 | } | ||
298 | // write zero byte EOS chunk | ||
299 | _, err := w.w.Write([]byte{0}) | ||
300 | if err != nil { | ||
301 | return err | ||
302 | } | ||
303 | w.cstate = stop | ||
304 | return nil | ||
305 | } | ||