| // 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 ( |
| "errors" |
| "fmt" |
| "io" |
| ) |
| |
| // matcher is an interface that supports the identification of the next |
| // operation. |
| type matcher interface { |
| io.Writer |
| SetDict(d *encoderDict) |
| NextOp(rep [4]uint32) operation |
| } |
| |
| // encoderDict provides the dictionary of the encoder. It includes an |
| // additional buffer atop of the actual dictionary. |
| type encoderDict struct { |
| buf buffer |
| m matcher |
| head int64 |
| capacity int |
| // preallocated array |
| data [maxMatchLen]byte |
| } |
| |
| // newEncoderDict creates the encoder dictionary. The argument bufSize |
| // defines the size of the additional buffer. |
| func newEncoderDict(dictCap, bufSize int, m matcher) (d *encoderDict, err error) { |
| if !(1 <= dictCap && int64(dictCap) <= MaxDictCap) { |
| return nil, errors.New( |
| "lzma: dictionary capacity out of range") |
| } |
| if bufSize < 1 { |
| return nil, errors.New( |
| "lzma: buffer size must be larger than zero") |
| } |
| d = &encoderDict{ |
| buf: *newBuffer(dictCap + bufSize), |
| capacity: dictCap, |
| m: m, |
| } |
| m.SetDict(d) |
| return d, nil |
| } |
| |
| // Discard discards n bytes. Note that n must not be larger than |
| // MaxMatchLen. |
| func (d *encoderDict) Discard(n int) { |
| p := d.data[:n] |
| k, _ := d.buf.Read(p) |
| if k < n { |
| panic(fmt.Errorf("lzma: can't discard %d bytes", n)) |
| } |
| d.head += int64(n) |
| d.m.Write(p) |
| } |
| |
| // Len returns the data available in the encoder dictionary. |
| func (d *encoderDict) Len() int { |
| n := d.buf.Available() |
| if int64(n) > d.head { |
| return int(d.head) |
| } |
| return n |
| } |
| |
| // DictLen returns the actual length of data in the dictionary. |
| func (d *encoderDict) DictLen() int { |
| if d.head < int64(d.capacity) { |
| return int(d.head) |
| } |
| return d.capacity |
| } |
| |
| // Available returns the number of bytes that can be written by a |
| // following Write call. |
| func (d *encoderDict) Available() int { |
| return d.buf.Available() - d.DictLen() |
| } |
| |
| // Write writes data into the dictionary buffer. Note that the position |
| // of the dictionary head will not be moved. If there is not enough |
| // space in the buffer ErrNoSpace will be returned. |
| func (d *encoderDict) Write(p []byte) (n int, err error) { |
| m := d.Available() |
| if len(p) > m { |
| p = p[:m] |
| err = ErrNoSpace |
| } |
| var e error |
| if n, e = d.buf.Write(p); e != nil { |
| err = e |
| } |
| return n, err |
| } |
| |
| // Pos returns the position of the head. |
| func (d *encoderDict) Pos() int64 { return d.head } |
| |
| // ByteAt returns the byte at the given distance. |
| func (d *encoderDict) ByteAt(distance int) byte { |
| if !(0 < distance && distance <= d.Len()) { |
| return 0 |
| } |
| i := d.buf.rear - distance |
| if i < 0 { |
| i += len(d.buf.data) |
| } |
| return d.buf.data[i] |
| } |
| |
| // CopyN copies the last n bytes from the dictionary into the provided |
| // writer. This is used for copying uncompressed data into an |
| // uncompressed segment. |
| func (d *encoderDict) CopyN(w io.Writer, n int) (written int, err error) { |
| if n <= 0 { |
| return 0, nil |
| } |
| m := d.Len() |
| if n > m { |
| n = m |
| err = ErrNoSpace |
| } |
| i := d.buf.rear - n |
| var e error |
| if i < 0 { |
| i += len(d.buf.data) |
| if written, e = w.Write(d.buf.data[i:]); e != nil { |
| return written, e |
| } |
| i = 0 |
| } |
| var k int |
| k, e = w.Write(d.buf.data[i:d.buf.rear]) |
| written += k |
| if e != nil { |
| err = e |
| } |
| return written, err |
| } |
| |
| // Buffered returns the number of bytes in the buffer. |
| func (d *encoderDict) Buffered() int { return d.buf.Buffered() } |