writer2.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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. "bytes"
  7. "errors"
  8. "io"
  9. )
  10. // Writer2Config is used to create a Writer2 using parameters.
  11. type Writer2Config struct {
  12. // The properties for the encoding. If the it is nil the value
  13. // {LC: 3, LP: 0, PB: 2} will be chosen.
  14. Properties *Properties
  15. // The capacity of the dictionary. If DictCap is zero, the value
  16. // 8 MiB will be chosen.
  17. DictCap int
  18. // Size of the lookahead buffer; value 0 indicates default size
  19. // 4096
  20. BufSize int
  21. // Match algorithm
  22. Matcher MatchAlgorithm
  23. }
  24. // fill replaces zero values with default values.
  25. func (c *Writer2Config) fill() {
  26. if c.Properties == nil {
  27. c.Properties = &Properties{LC: 3, LP: 0, PB: 2}
  28. }
  29. if c.DictCap == 0 {
  30. c.DictCap = 8 * 1024 * 1024
  31. }
  32. if c.BufSize == 0 {
  33. c.BufSize = 4096
  34. }
  35. }
  36. // Verify checks the Writer2Config for correctness. Zero values will be
  37. // replaced by default values.
  38. func (c *Writer2Config) Verify() error {
  39. c.fill()
  40. var err error
  41. if c == nil {
  42. return errors.New("lzma: WriterConfig is nil")
  43. }
  44. if c.Properties == nil {
  45. return errors.New("lzma: WriterConfig has no Properties set")
  46. }
  47. if err = c.Properties.verify(); err != nil {
  48. return err
  49. }
  50. if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) {
  51. return errors.New("lzma: dictionary capacity is out of range")
  52. }
  53. if !(maxMatchLen <= c.BufSize) {
  54. return errors.New("lzma: lookahead buffer size too small")
  55. }
  56. if c.Properties.LC+c.Properties.LP > 4 {
  57. return errors.New("lzma: sum of lc and lp exceeds 4")
  58. }
  59. if err = c.Matcher.verify(); err != nil {
  60. return err
  61. }
  62. return nil
  63. }
  64. // Writer2 supports the creation of an LZMA2 stream. But note that
  65. // written data is buffered, so call Flush or Close to write data to the
  66. // underlying writer. The Close method writes the end-of-stream marker
  67. // to the stream. So you may be able to concatenate the output of two
  68. // writers as long the output of the first writer has only been flushed
  69. // but not closed.
  70. //
  71. // Any change to the fields Properties, DictCap must be done before the
  72. // first call to Write, Flush or Close.
  73. type Writer2 struct {
  74. w io.Writer
  75. start *state
  76. encoder *encoder
  77. cstate chunkState
  78. ctype chunkType
  79. buf bytes.Buffer
  80. lbw LimitedByteWriter
  81. }
  82. // NewWriter2 creates an LZMA2 chunk sequence writer with the default
  83. // parameters and options.
  84. func NewWriter2(lzma2 io.Writer) (w *Writer2, err error) {
  85. return Writer2Config{}.NewWriter2(lzma2)
  86. }
  87. // NewWriter2 creates a new LZMA2 writer using the given configuration.
  88. func (c Writer2Config) NewWriter2(lzma2 io.Writer) (w *Writer2, err error) {
  89. if err = c.Verify(); err != nil {
  90. return nil, err
  91. }
  92. w = &Writer2{
  93. w: lzma2,
  94. start: newState(*c.Properties),
  95. cstate: start,
  96. ctype: start.defaultChunkType(),
  97. }
  98. w.buf.Grow(maxCompressed)
  99. w.lbw = LimitedByteWriter{BW: &w.buf, N: maxCompressed}
  100. m, err := c.Matcher.new(c.DictCap)
  101. if err != nil {
  102. return nil, err
  103. }
  104. d, err := newEncoderDict(c.DictCap, c.BufSize, m)
  105. if err != nil {
  106. return nil, err
  107. }
  108. w.encoder, err = newEncoder(&w.lbw, cloneState(w.start), d, 0)
  109. if err != nil {
  110. return nil, err
  111. }
  112. return w, nil
  113. }
  114. // written returns the number of bytes written to the current chunk
  115. func (w *Writer2) written() int {
  116. if w.encoder == nil {
  117. return 0
  118. }
  119. return int(w.encoder.Compressed()) + w.encoder.dict.Buffered()
  120. }
  121. // errClosed indicates that the writer is closed.
  122. var errClosed = errors.New("lzma: writer closed")
  123. // Writes data to LZMA2 stream. Note that written data will be buffered.
  124. // Use Flush or Close to ensure that data is written to the underlying
  125. // writer.
  126. func (w *Writer2) Write(p []byte) (n int, err error) {
  127. if w.cstate == stop {
  128. return 0, errClosed
  129. }
  130. for n < len(p) {
  131. m := maxUncompressed - w.written()
  132. if m <= 0 {
  133. panic("lzma: maxUncompressed reached")
  134. }
  135. var q []byte
  136. if n+m < len(p) {
  137. q = p[n : n+m]
  138. } else {
  139. q = p[n:]
  140. }
  141. k, err := w.encoder.Write(q)
  142. n += k
  143. if err != nil && err != ErrLimit {
  144. return n, err
  145. }
  146. if err == ErrLimit || k == m {
  147. if err = w.flushChunk(); err != nil {
  148. return n, err
  149. }
  150. }
  151. }
  152. return n, nil
  153. }
  154. // writeUncompressedChunk writes an uncompressed chunk to the LZMA2
  155. // stream.
  156. func (w *Writer2) writeUncompressedChunk() error {
  157. u := w.encoder.Compressed()
  158. if u <= 0 {
  159. return errors.New("lzma: can't write empty uncompressed chunk")
  160. }
  161. if u > maxUncompressed {
  162. panic("overrun of uncompressed data limit")
  163. }
  164. switch w.ctype {
  165. case cLRND:
  166. w.ctype = cUD
  167. default:
  168. w.ctype = cU
  169. }
  170. w.encoder.state = w.start
  171. header := chunkHeader{
  172. ctype: w.ctype,
  173. uncompressed: uint32(u - 1),
  174. }
  175. hdata, err := header.MarshalBinary()
  176. if err != nil {
  177. return err
  178. }
  179. if _, err = w.w.Write(hdata); err != nil {
  180. return err
  181. }
  182. _, err = w.encoder.dict.CopyN(w.w, int(u))
  183. return err
  184. }
  185. // writeCompressedChunk writes a compressed chunk to the underlying
  186. // writer.
  187. func (w *Writer2) writeCompressedChunk() error {
  188. if w.ctype == cU || w.ctype == cUD {
  189. panic("chunk type uncompressed")
  190. }
  191. u := w.encoder.Compressed()
  192. if u <= 0 {
  193. return errors.New("writeCompressedChunk: empty chunk")
  194. }
  195. if u > maxUncompressed {
  196. panic("overrun of uncompressed data limit")
  197. }
  198. c := w.buf.Len()
  199. if c <= 0 {
  200. panic("no compressed data")
  201. }
  202. if c > maxCompressed {
  203. panic("overrun of compressed data limit")
  204. }
  205. header := chunkHeader{
  206. ctype: w.ctype,
  207. uncompressed: uint32(u - 1),
  208. compressed: uint16(c - 1),
  209. props: w.encoder.state.Properties,
  210. }
  211. hdata, err := header.MarshalBinary()
  212. if err != nil {
  213. return err
  214. }
  215. if _, err = w.w.Write(hdata); err != nil {
  216. return err
  217. }
  218. _, err = io.Copy(w.w, &w.buf)
  219. return err
  220. }
  221. // writes a single chunk to the underlying writer.
  222. func (w *Writer2) writeChunk() error {
  223. u := int(uncompressedHeaderLen + w.encoder.Compressed())
  224. c := headerLen(w.ctype) + w.buf.Len()
  225. if u < c {
  226. return w.writeUncompressedChunk()
  227. }
  228. return w.writeCompressedChunk()
  229. }
  230. // flushChunk terminates the current chunk. The encoder will be reset
  231. // to support the next chunk.
  232. func (w *Writer2) flushChunk() error {
  233. if w.written() == 0 {
  234. return nil
  235. }
  236. var err error
  237. if err = w.encoder.Close(); err != nil {
  238. return err
  239. }
  240. if err = w.writeChunk(); err != nil {
  241. return err
  242. }
  243. w.buf.Reset()
  244. w.lbw.N = maxCompressed
  245. if err = w.encoder.Reopen(&w.lbw); err != nil {
  246. return err
  247. }
  248. if err = w.cstate.next(w.ctype); err != nil {
  249. return err
  250. }
  251. w.ctype = w.cstate.defaultChunkType()
  252. w.start = cloneState(w.encoder.state)
  253. return nil
  254. }
  255. // Flush writes all buffered data out to the underlying stream. This
  256. // could result in multiple chunks to be created.
  257. func (w *Writer2) Flush() error {
  258. if w.cstate == stop {
  259. return errClosed
  260. }
  261. for w.written() > 0 {
  262. if err := w.flushChunk(); err != nil {
  263. return err
  264. }
  265. }
  266. return nil
  267. }
  268. // Close terminates the LZMA2 stream with an EOS chunk.
  269. func (w *Writer2) Close() error {
  270. if w.cstate == stop {
  271. return errClosed
  272. }
  273. if err := w.Flush(); err != nil {
  274. return nil
  275. }
  276. // write zero byte EOS chunk
  277. _, err := w.w.Write([]byte{0})
  278. if err != nil {
  279. return err
  280. }
  281. w.cstate = stop
  282. return nil
  283. }