]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/ulikunitz/xz/writer.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / ulikunitz / xz / writer.go
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 xz
6
7 import (
8 "errors"
9 "hash"
10 "io"
11
12 "github.com/ulikunitz/xz/lzma"
13 )
14
15 // WriterConfig describe the parameters for an xz writer.
16 type WriterConfig struct {
17 Properties *lzma.Properties
18 DictCap int
19 BufSize int
20 BlockSize int64
21 // checksum method: CRC32, CRC64 or SHA256
22 CheckSum byte
23 // match algorithm
24 Matcher lzma.MatchAlgorithm
25 }
26
27 // fill replaces zero values with default values.
28 func (c *WriterConfig) fill() {
29 if c.Properties == nil {
30 c.Properties = &lzma.Properties{LC: 3, LP: 0, PB: 2}
31 }
32 if c.DictCap == 0 {
33 c.DictCap = 8 * 1024 * 1024
34 }
35 if c.BufSize == 0 {
36 c.BufSize = 4096
37 }
38 if c.BlockSize == 0 {
39 c.BlockSize = maxInt64
40 }
41 if c.CheckSum == 0 {
42 c.CheckSum = CRC64
43 }
44 }
45
46 // Verify checks the configuration for errors. Zero values will be
47 // replaced by default values.
48 func (c *WriterConfig) Verify() error {
49 if c == nil {
50 return errors.New("xz: writer configuration is nil")
51 }
52 c.fill()
53 lc := lzma.Writer2Config{
54 Properties: c.Properties,
55 DictCap: c.DictCap,
56 BufSize: c.BufSize,
57 Matcher: c.Matcher,
58 }
59 if err := lc.Verify(); err != nil {
60 return err
61 }
62 if c.BlockSize <= 0 {
63 return errors.New("xz: block size out of range")
64 }
65 if err := verifyFlags(c.CheckSum); err != nil {
66 return err
67 }
68 return nil
69 }
70
71 // filters creates the filter list for the given parameters.
72 func (c *WriterConfig) filters() []filter {
73 return []filter{&lzmaFilter{int64(c.DictCap)}}
74 }
75
76 // maxInt64 defines the maximum 64-bit signed integer.
77 const maxInt64 = 1<<63 - 1
78
79 // verifyFilters checks the filter list for the length and the right
80 // sequence of filters.
81 func verifyFilters(f []filter) error {
82 if len(f) == 0 {
83 return errors.New("xz: no filters")
84 }
85 if len(f) > 4 {
86 return errors.New("xz: more than four filters")
87 }
88 for _, g := range f[:len(f)-1] {
89 if g.last() {
90 return errors.New("xz: last filter is not last")
91 }
92 }
93 if !f[len(f)-1].last() {
94 return errors.New("xz: wrong last filter")
95 }
96 return nil
97 }
98
99 // newFilterWriteCloser converts a filter list into a WriteCloser that
100 // can be used by a blockWriter.
101 func (c *WriterConfig) newFilterWriteCloser(w io.Writer, f []filter) (fw io.WriteCloser, err error) {
102 if err = verifyFilters(f); err != nil {
103 return nil, err
104 }
105 fw = nopWriteCloser(w)
106 for i := len(f) - 1; i >= 0; i-- {
107 fw, err = f[i].writeCloser(fw, c)
108 if err != nil {
109 return nil, err
110 }
111 }
112 return fw, nil
113 }
114
115 // nopWCloser implements a WriteCloser with a Close method not doing
116 // anything.
117 type nopWCloser struct {
118 io.Writer
119 }
120
121 // Close returns nil and doesn't do anything else.
122 func (c nopWCloser) Close() error {
123 return nil
124 }
125
126 // nopWriteCloser converts the Writer into a WriteCloser with a Close
127 // function that does nothing beside returning nil.
128 func nopWriteCloser(w io.Writer) io.WriteCloser {
129 return nopWCloser{w}
130 }
131
132 // Writer compresses data written to it. It is an io.WriteCloser.
133 type Writer struct {
134 WriterConfig
135
136 xz io.Writer
137 bw *blockWriter
138 newHash func() hash.Hash
139 h header
140 index []record
141 closed bool
142 }
143
144 // newBlockWriter creates a new block writer writes the header out.
145 func (w *Writer) newBlockWriter() error {
146 var err error
147 w.bw, err = w.WriterConfig.newBlockWriter(w.xz, w.newHash())
148 if err != nil {
149 return err
150 }
151 if err = w.bw.writeHeader(w.xz); err != nil {
152 return err
153 }
154 return nil
155 }
156
157 // closeBlockWriter closes a block writer and records the sizes in the
158 // index.
159 func (w *Writer) closeBlockWriter() error {
160 var err error
161 if err = w.bw.Close(); err != nil {
162 return err
163 }
164 w.index = append(w.index, w.bw.record())
165 return nil
166 }
167
168 // NewWriter creates a new xz writer using default parameters.
169 func NewWriter(xz io.Writer) (w *Writer, err error) {
170 return WriterConfig{}.NewWriter(xz)
171 }
172
173 // NewWriter creates a new Writer using the given configuration parameters.
174 func (c WriterConfig) NewWriter(xz io.Writer) (w *Writer, err error) {
175 if err = c.Verify(); err != nil {
176 return nil, err
177 }
178 w = &Writer{
179 WriterConfig: c,
180 xz: xz,
181 h: header{c.CheckSum},
182 index: make([]record, 0, 4),
183 }
184 if w.newHash, err = newHashFunc(c.CheckSum); err != nil {
185 return nil, err
186 }
187 data, err := w.h.MarshalBinary()
188 if _, err = xz.Write(data); err != nil {
189 return nil, err
190 }
191 if err = w.newBlockWriter(); err != nil {
192 return nil, err
193 }
194 return w, nil
195
196 }
197
198 // Write compresses the uncompressed data provided.
199 func (w *Writer) Write(p []byte) (n int, err error) {
200 if w.closed {
201 return 0, errClosed
202 }
203 for {
204 k, err := w.bw.Write(p[n:])
205 n += k
206 if err != errNoSpace {
207 return n, err
208 }
209 if err = w.closeBlockWriter(); err != nil {
210 return n, err
211 }
212 if err = w.newBlockWriter(); err != nil {
213 return n, err
214 }
215 }
216 }
217
218 // Close closes the writer and adds the footer to the Writer. Close
219 // doesn't close the underlying writer.
220 func (w *Writer) Close() error {
221 if w.closed {
222 return errClosed
223 }
224 w.closed = true
225 var err error
226 if err = w.closeBlockWriter(); err != nil {
227 return err
228 }
229
230 f := footer{flags: w.h.flags}
231 if f.indexSize, err = writeIndex(w.xz, w.index); err != nil {
232 return err
233 }
234 data, err := f.MarshalBinary()
235 if err != nil {
236 return err
237 }
238 if _, err = w.xz.Write(data); err != nil {
239 return err
240 }
241 return nil
242 }
243
244 // countingWriter is a writer that counts all data written to it.
245 type countingWriter struct {
246 w io.Writer
247 n int64
248 }
249
250 // Write writes data to the countingWriter.
251 func (cw *countingWriter) Write(p []byte) (n int, err error) {
252 n, err = cw.w.Write(p)
253 cw.n += int64(n)
254 if err == nil && cw.n < 0 {
255 return n, errors.New("xz: counter overflow")
256 }
257 return
258 }
259
260 // blockWriter is writes a single block.
261 type blockWriter struct {
262 cxz countingWriter
263 // mw combines io.WriteCloser w and the hash.
264 mw io.Writer
265 w io.WriteCloser
266 n int64
267 blockSize int64
268 closed bool
269 headerLen int
270
271 filters []filter
272 hash hash.Hash
273 }
274
275 // newBlockWriter creates a new block writer.
276 func (c *WriterConfig) newBlockWriter(xz io.Writer, hash hash.Hash) (bw *blockWriter, err error) {
277 bw = &blockWriter{
278 cxz: countingWriter{w: xz},
279 blockSize: c.BlockSize,
280 filters: c.filters(),
281 hash: hash,
282 }
283 bw.w, err = c.newFilterWriteCloser(&bw.cxz, bw.filters)
284 if err != nil {
285 return nil, err
286 }
287 bw.mw = io.MultiWriter(bw.w, bw.hash)
288 return bw, nil
289 }
290
291 // writeHeader writes the header. If the function is called after Close
292 // the commpressedSize and uncompressedSize fields will be filled.
293 func (bw *blockWriter) writeHeader(w io.Writer) error {
294 h := blockHeader{
295 compressedSize: -1,
296 uncompressedSize: -1,
297 filters: bw.filters,
298 }
299 if bw.closed {
300 h.compressedSize = bw.compressedSize()
301 h.uncompressedSize = bw.uncompressedSize()
302 }
303 data, err := h.MarshalBinary()
304 if err != nil {
305 return err
306 }
307 if _, err = w.Write(data); err != nil {
308 return err
309 }
310 bw.headerLen = len(data)
311 return nil
312 }
313
314 // compressed size returns the amount of data written to the underlying
315 // stream.
316 func (bw *blockWriter) compressedSize() int64 {
317 return bw.cxz.n
318 }
319
320 // uncompressedSize returns the number of data written to the
321 // blockWriter
322 func (bw *blockWriter) uncompressedSize() int64 {
323 return bw.n
324 }
325
326 // unpaddedSize returns the sum of the header length, the uncompressed
327 // size of the block and the hash size.
328 func (bw *blockWriter) unpaddedSize() int64 {
329 if bw.headerLen <= 0 {
330 panic("xz: block header not written")
331 }
332 n := int64(bw.headerLen)
333 n += bw.compressedSize()
334 n += int64(bw.hash.Size())
335 return n
336 }
337
338 // record returns the record for the current stream. Call Close before
339 // calling this method.
340 func (bw *blockWriter) record() record {
341 return record{bw.unpaddedSize(), bw.uncompressedSize()}
342 }
343
344 var errClosed = errors.New("xz: writer already closed")
345
346 var errNoSpace = errors.New("xz: no space")
347
348 // Write writes uncompressed data to the block writer.
349 func (bw *blockWriter) Write(p []byte) (n int, err error) {
350 if bw.closed {
351 return 0, errClosed
352 }
353
354 t := bw.blockSize - bw.n
355 if int64(len(p)) > t {
356 err = errNoSpace
357 p = p[:t]
358 }
359
360 var werr error
361 n, werr = bw.mw.Write(p)
362 bw.n += int64(n)
363 if werr != nil {
364 return n, werr
365 }
366 return n, err
367 }
368
369 // Close closes the writer.
370 func (bw *blockWriter) Close() error {
371 if bw.closed {
372 return errClosed
373 }
374 bw.closed = true
375 if err := bw.w.Close(); err != nil {
376 return err
377 }
378 s := bw.hash.Size()
379 k := padLen(bw.cxz.n)
380 p := make([]byte, k+s)
381 bw.hash.Sum(p[k:k])
382 if _, err := bw.cxz.w.Write(p); err != nil {
383 return err
384 }
385 return nil
386 }