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.
12 "github.com/ulikunitz/xz/lzma"
15 // WriterConfig describe the parameters for an xz writer.
16 type WriterConfig struct {
17 Properties *lzma.Properties
21 // checksum method: CRC32, CRC64 or SHA256
24 Matcher lzma.MatchAlgorithm
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}
33 c.DictCap = 8 * 1024 * 1024
39 c.BlockSize = maxInt64
46 // Verify checks the configuration for errors. Zero values will be
47 // replaced by default values.
48 func (c *WriterConfig) Verify() error {
50 return errors.New("xz: writer configuration is nil")
53 lc := lzma.Writer2Config{
54 Properties: c.Properties,
59 if err := lc.Verify(); err != nil {
63 return errors.New("xz: block size out of range")
65 if err := verifyFlags(c.CheckSum); err != nil {
71 // filters creates the filter list for the given parameters.
72 func (c *WriterConfig) filters() []filter {
73 return []filter{&lzmaFilter{int64(c.DictCap)}}
76 // maxInt64 defines the maximum 64-bit signed integer.
77 const maxInt64 = 1<<63 - 1
79 // verifyFilters checks the filter list for the length and the right
80 // sequence of filters.
81 func verifyFilters(f []filter) error {
83 return errors.New("xz: no filters")
86 return errors.New("xz: more than four filters")
88 for _, g := range f[:len(f)-1] {
90 return errors.New("xz: last filter is not last")
93 if !f[len(f)-1].last() {
94 return errors.New("xz: wrong last filter")
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 {
105 fw = nopWriteCloser(w)
106 for i := len(f) - 1; i >= 0; i-- {
107 fw, err = f[i].writeCloser(fw, c)
115 // nopWCloser implements a WriteCloser with a Close method not doing
117 type nopWCloser struct {
121 // Close returns nil and doesn't do anything else.
122 func (c nopWCloser) Close() error {
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 {
132 // Writer compresses data written to it. It is an io.WriteCloser.
138 newHash func() hash.Hash
144 // newBlockWriter creates a new block writer writes the header out.
145 func (w *Writer) newBlockWriter() error {
147 w.bw, err = w.WriterConfig.newBlockWriter(w.xz, w.newHash())
151 if err = w.bw.writeHeader(w.xz); err != nil {
157 // closeBlockWriter closes a block writer and records the sizes in the
159 func (w *Writer) closeBlockWriter() error {
161 if err = w.bw.Close(); err != nil {
164 w.index = append(w.index, w.bw.record())
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)
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 {
181 h: header{c.CheckSum},
182 index: make([]record, 0, 4),
184 if w.newHash, err = newHashFunc(c.CheckSum); err != nil {
187 data, err := w.h.MarshalBinary()
188 if _, err = xz.Write(data); err != nil {
191 if err = w.newBlockWriter(); err != nil {
198 // Write compresses the uncompressed data provided.
199 func (w *Writer) Write(p []byte) (n int, err error) {
204 k, err := w.bw.Write(p[n:])
206 if err != errNoSpace {
209 if err = w.closeBlockWriter(); err != nil {
212 if err = w.newBlockWriter(); err != nil {
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 {
226 if err = w.closeBlockWriter(); err != nil {
230 f := footer{flags: w.h.flags}
231 if f.indexSize, err = writeIndex(w.xz, w.index); err != nil {
234 data, err := f.MarshalBinary()
238 if _, err = w.xz.Write(data); err != nil {
244 // countingWriter is a writer that counts all data written to it.
245 type countingWriter struct {
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)
254 if err == nil && cw.n < 0 {
255 return n, errors.New("xz: counter overflow")
260 // blockWriter is writes a single block.
261 type blockWriter struct {
263 // mw combines io.WriteCloser w and the hash.
275 // newBlockWriter creates a new block writer.
276 func (c *WriterConfig) newBlockWriter(xz io.Writer, hash hash.Hash) (bw *blockWriter, err error) {
278 cxz: countingWriter{w: xz},
279 blockSize: c.BlockSize,
280 filters: c.filters(),
283 bw.w, err = c.newFilterWriteCloser(&bw.cxz, bw.filters)
287 bw.mw = io.MultiWriter(bw.w, bw.hash)
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 {
296 uncompressedSize: -1,
300 h.compressedSize = bw.compressedSize()
301 h.uncompressedSize = bw.uncompressedSize()
303 data, err := h.MarshalBinary()
307 if _, err = w.Write(data); err != nil {
310 bw.headerLen = len(data)
314 // compressed size returns the amount of data written to the underlying
316 func (bw *blockWriter) compressedSize() int64 {
320 // uncompressedSize returns the number of data written to the
322 func (bw *blockWriter) uncompressedSize() int64 {
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")
332 n := int64(bw.headerLen)
333 n += bw.compressedSize()
334 n += int64(bw.hash.Size())
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()}
344 var errClosed = errors.New("xz: writer already closed")
346 var errNoSpace = errors.New("xz: no space")
348 // Write writes uncompressed data to the block writer.
349 func (bw *blockWriter) Write(p []byte) (n int, err error) {
354 t := bw.blockSize - bw.n
355 if int64(len(p)) > t {
361 n, werr = bw.mw.Write(p)
369 // Close closes the writer.
370 func (bw *blockWriter) Close() error {
375 if err := bw.w.Close(); err != nil {
379 k := padLen(bw.cxz.n)
380 p := make([]byte, k+s)
382 if _, err := bw.cxz.w.Write(p); err != nil {