writer.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Copyright 2014-2022 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. package lzma
  5. import (
  6. "bufio"
  7. "errors"
  8. "io"
  9. )
  10. // MinDictCap and MaxDictCap provide the range of supported dictionary
  11. // capacities.
  12. const (
  13. MinDictCap = 1 << 12
  14. MaxDictCap = 1<<32 - 1
  15. )
  16. // WriterConfig defines the configuration parameter for a writer.
  17. type WriterConfig struct {
  18. // Properties for the encoding. If the it is nil the value
  19. // {LC: 3, LP: 0, PB: 2} will be chosen.
  20. Properties *Properties
  21. // The capacity of the dictionary. If DictCap is zero, the value
  22. // 8 MiB will be chosen.
  23. DictCap int
  24. // Size of the lookahead buffer; value 0 indicates default size
  25. // 4096
  26. BufSize int
  27. // Match algorithm
  28. Matcher MatchAlgorithm
  29. // SizeInHeader indicates that the header will contain an
  30. // explicit size.
  31. SizeInHeader bool
  32. // Size of the data to be encoded. A positive value will imply
  33. // than an explicit size will be set in the header.
  34. Size int64
  35. // EOSMarker requests whether the EOSMarker needs to be written.
  36. // If no explicit size is been given the EOSMarker will be
  37. // set automatically.
  38. EOSMarker bool
  39. }
  40. // fill converts zero-value fields to their explicit default values.
  41. func (c *WriterConfig) fill() {
  42. if c.Properties == nil {
  43. c.Properties = &Properties{LC: 3, LP: 0, PB: 2}
  44. }
  45. if c.DictCap == 0 {
  46. c.DictCap = 8 * 1024 * 1024
  47. }
  48. if c.BufSize == 0 {
  49. c.BufSize = 4096
  50. }
  51. if c.Size > 0 {
  52. c.SizeInHeader = true
  53. }
  54. if !c.SizeInHeader {
  55. c.EOSMarker = true
  56. }
  57. }
  58. // Verify checks WriterConfig for errors. Verify will replace zero
  59. // values with default values.
  60. func (c *WriterConfig) Verify() error {
  61. c.fill()
  62. var err error
  63. if c == nil {
  64. return errors.New("lzma: WriterConfig is nil")
  65. }
  66. if c.Properties == nil {
  67. return errors.New("lzma: WriterConfig has no Properties set")
  68. }
  69. if err = c.Properties.verify(); err != nil {
  70. return err
  71. }
  72. if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) {
  73. return errors.New("lzma: dictionary capacity is out of range")
  74. }
  75. if !(maxMatchLen <= c.BufSize) {
  76. return errors.New("lzma: lookahead buffer size too small")
  77. }
  78. if c.SizeInHeader {
  79. if c.Size < 0 {
  80. return errors.New("lzma: negative size not supported")
  81. }
  82. } else if !c.EOSMarker {
  83. return errors.New("lzma: EOS marker is required")
  84. }
  85. if err = c.Matcher.verify(); err != nil {
  86. return err
  87. }
  88. return nil
  89. }
  90. // header returns the header structure for this configuration.
  91. func (c *WriterConfig) header() header {
  92. h := header{
  93. properties: *c.Properties,
  94. dictCap: c.DictCap,
  95. size: -1,
  96. }
  97. if c.SizeInHeader {
  98. h.size = c.Size
  99. }
  100. return h
  101. }
  102. // Writer writes an LZMA stream in the classic format.
  103. type Writer struct {
  104. h header
  105. bw io.ByteWriter
  106. buf *bufio.Writer
  107. e *encoder
  108. }
  109. // NewWriter creates a new LZMA writer for the classic format. The
  110. // method will write the header to the underlying stream.
  111. func (c WriterConfig) NewWriter(lzma io.Writer) (w *Writer, err error) {
  112. if err = c.Verify(); err != nil {
  113. return nil, err
  114. }
  115. w = &Writer{h: c.header()}
  116. var ok bool
  117. w.bw, ok = lzma.(io.ByteWriter)
  118. if !ok {
  119. w.buf = bufio.NewWriter(lzma)
  120. w.bw = w.buf
  121. }
  122. state := newState(w.h.properties)
  123. m, err := c.Matcher.new(w.h.dictCap)
  124. if err != nil {
  125. return nil, err
  126. }
  127. dict, err := newEncoderDict(w.h.dictCap, c.BufSize, m)
  128. if err != nil {
  129. return nil, err
  130. }
  131. var flags encoderFlags
  132. if c.EOSMarker {
  133. flags = eosMarker
  134. }
  135. if w.e, err = newEncoder(w.bw, state, dict, flags); err != nil {
  136. return nil, err
  137. }
  138. if err = w.writeHeader(); err != nil {
  139. return nil, err
  140. }
  141. return w, nil
  142. }
  143. // NewWriter creates a new LZMA writer using the classic format. The
  144. // function writes the header to the underlying stream.
  145. func NewWriter(lzma io.Writer) (w *Writer, err error) {
  146. return WriterConfig{}.NewWriter(lzma)
  147. }
  148. // writeHeader writes the LZMA header into the stream.
  149. func (w *Writer) writeHeader() error {
  150. data, err := w.h.marshalBinary()
  151. if err != nil {
  152. return err
  153. }
  154. _, err = w.bw.(io.Writer).Write(data)
  155. return err
  156. }
  157. // Write puts data into the Writer.
  158. func (w *Writer) Write(p []byte) (n int, err error) {
  159. if w.h.size >= 0 {
  160. m := w.h.size
  161. m -= w.e.Compressed() + int64(w.e.dict.Buffered())
  162. if m < 0 {
  163. m = 0
  164. }
  165. if m < int64(len(p)) {
  166. p = p[:m]
  167. err = ErrNoSpace
  168. }
  169. }
  170. var werr error
  171. if n, werr = w.e.Write(p); werr != nil {
  172. err = werr
  173. }
  174. return n, err
  175. }
  176. // Close closes the writer stream. It ensures that all data from the
  177. // buffer will be compressed and the LZMA stream will be finished.
  178. func (w *Writer) Close() error {
  179. if w.h.size >= 0 {
  180. n := w.e.Compressed() + int64(w.e.dict.Buffered())
  181. if n != w.h.size {
  182. return errSize
  183. }
  184. }
  185. err := w.e.Close()
  186. if w.buf != nil {
  187. ferr := w.buf.Flush()
  188. if err == nil {
  189. err = ferr
  190. }
  191. }
  192. return err
  193. }