]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/ulikunitz/xz/writer.go
Merge branch 'fix_read_test' of github.com:alexandreFre/terraform-provider-statuscake
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / ulikunitz / xz / writer.go
CommitLineData
15c0b25d
AP
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 xz
6
7import (
8 "errors"
9 "hash"
10 "io"
11
12 "github.com/ulikunitz/xz/lzma"
13)
14
15// WriterConfig describe the parameters for an xz writer.
16type 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.
28func (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.
48func (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.
72func (c *WriterConfig) filters() []filter {
73 return []filter{&lzmaFilter{int64(c.DictCap)}}
74}
75
76// maxInt64 defines the maximum 64-bit signed integer.
77const maxInt64 = 1<<63 - 1
78
79// verifyFilters checks the filter list for the length and the right
80// sequence of filters.
81func 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.
101func (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.
117type nopWCloser struct {
118 io.Writer
119}
120
121// Close returns nil and doesn't do anything else.
122func (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.
128func nopWriteCloser(w io.Writer) io.WriteCloser {
129 return nopWCloser{w}
130}
131
132// Writer compresses data written to it. It is an io.WriteCloser.
133type 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.
145func (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.
159func (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.
169func 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.
174func (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.
199func (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.
220func (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.
245type countingWriter struct {
246 w io.Writer
247 n int64
248}
249
250// Write writes data to the countingWriter.
251func (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.
261type 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.
276func (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.
293func (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.
316func (bw *blockWriter) compressedSize() int64 {
317 return bw.cxz.n
318}
319
320// uncompressedSize returns the number of data written to the
321// blockWriter
322func (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.
328func (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.
340func (bw *blockWriter) record() record {
341 return record{bw.unpaddedSize(), bw.uncompressedSize()}
342}
343
344var errClosed = errors.New("xz: writer already closed")
345
346var errNoSpace = errors.New("xz: no space")
347
348// Write writes uncompressed data to the block writer.
349func (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.
370func (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}