mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-25 03:06:10 +01:00
210 lines
4.8 KiB
Go
Vendored
210 lines
4.8 KiB
Go
Vendored
// Copyright 2014-2021 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
|
|
}
|