gitea/vendor/github.com/ulikunitz/xz/lzma/writer.go

210 lines
4.8 KiB
Go

// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package lzma
import (
"bufio"
"errors"
"io"
)
// MinDictCap and MaxDictCap provide the range of supported dictionary
// capacities.
const (
MinDictCap = 1 << 12
MaxDictCap = 1<<32 - 1
)
// WriterConfig defines the configuration parameter for a writer.
type WriterConfig struct {
// Properties for the encoding. If the it is nil the value
// {LC: 3, LP: 0, PB: 2} will be chosen.
Properties *Properties
// The capacity of the dictionary. If DictCap is zero, the value
// 8 MiB will be chosen.
DictCap int
// Size of the lookahead buffer; value 0 indicates default size
// 4096
BufSize int
// Match algorithm
Matcher MatchAlgorithm
// SizeInHeader indicates that the header will contain an
// explicit size.
SizeInHeader bool
// Size of the data to be encoded. A positive value will imply
// than an explicit size will be set in the header.
Size int64
// EOSMarker requests whether the EOSMarker needs to be written.
// If no explicit size is been given the EOSMarker will be
// set automatically.
EOSMarker bool
}
// fill converts zero-value fields to their explicit default values.
func (c *WriterConfig) fill() {
if c.Properties == nil {
c.Properties = &Properties{LC: 3, LP: 0, PB: 2}
}
if c.DictCap == 0 {
c.DictCap = 8 * 1024 * 1024
}
if c.BufSize == 0 {
c.BufSize = 4096
}
if c.Size > 0 {
c.SizeInHeader = true
}
if !c.SizeInHeader {
c.EOSMarker = true
}
}
// Verify checks WriterConfig for errors. Verify will replace zero
// values with default values.
func (c *WriterConfig) Verify() error {
c.fill()
var err error
if c == nil {
return errors.New("lzma: WriterConfig is nil")
}
if c.Properties == nil {
return errors.New("lzma: WriterConfig has no Properties set")
}
if err = c.Properties.verify(); err != nil {
return err
}
if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) {
return errors.New("lzma: dictionary capacity is out of range")
}
if !(maxMatchLen <= c.BufSize) {
return errors.New("lzma: lookahead buffer size too small")
}
if c.SizeInHeader {
if c.Size < 0 {
return errors.New("lzma: negative size not supported")
}
} else if !c.EOSMarker {
return errors.New("lzma: EOS marker is required")
}
if err = c.Matcher.verify(); err != nil {
return err
}
return nil
}
// header returns the header structure for this configuration.
func (c *WriterConfig) header() header {
h := header{
properties: *c.Properties,
dictCap: c.DictCap,
size: -1,
}
if c.SizeInHeader {
h.size = c.Size
}
return h
}
// Writer writes an LZMA stream in the classic format.
type Writer struct {
h header
bw io.ByteWriter
buf *bufio.Writer
e *encoder
}
// NewWriter creates a new LZMA writer for the classic format. The
// method will write the header to the underlying stream.
func (c WriterConfig) NewWriter(lzma io.Writer) (w *Writer, err error) {
if err = c.Verify(); err != nil {
return nil, err
}
w = &Writer{h: c.header()}
var ok bool
w.bw, ok = lzma.(io.ByteWriter)
if !ok {
w.buf = bufio.NewWriter(lzma)
w.bw = w.buf
}
state := newState(w.h.properties)
m, err := c.Matcher.new(w.h.dictCap)
if err != nil {
return nil, err
}
dict, err := newEncoderDict(w.h.dictCap, c.BufSize, m)
if err != nil {
return nil, err
}
var flags encoderFlags
if c.EOSMarker {
flags = eosMarker
}
if w.e, err = newEncoder(w.bw, state, dict, flags); err != nil {
return nil, err
}
if err = w.writeHeader(); err != nil {
return nil, err
}
return w, nil
}
// NewWriter creates a new LZMA writer using the classic format. The
// function writes the header to the underlying stream.
func NewWriter(lzma io.Writer) (w *Writer, err error) {
return WriterConfig{}.NewWriter(lzma)
}
// writeHeader writes the LZMA header into the stream.
func (w *Writer) writeHeader() error {
data, err := w.h.marshalBinary()
if err != nil {
return err
}
_, err = w.bw.(io.Writer).Write(data)
return err
}
// Write puts data into the Writer.
func (w *Writer) Write(p []byte) (n int, err error) {
if w.h.size >= 0 {
m := w.h.size
m -= w.e.Compressed() + int64(w.e.dict.Buffered())
if m < 0 {
m = 0
}
if m < int64(len(p)) {
p = p[:m]
err = ErrNoSpace
}
}
var werr error
if n, werr = w.e.Write(p); werr != nil {
err = werr
}
return n, err
}
// Close closes the writer stream. It ensures that all data from the
// buffer will be compressed and the LZMA stream will be finished.
func (w *Writer) Close() error {
if w.h.size >= 0 {
n := w.e.Compressed() + int64(w.e.dict.Buffered())
if n != w.h.size {
return errSize
}
}
err := w.e.Close()
if w.buf != nil {
ferr := w.buf.Flush()
if err == nil {
err = ferr
}
}
return err
}