You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

writer.go 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. // Copyright 2014-2021 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 xz
  5. import (
  6. "errors"
  7. "fmt"
  8. "hash"
  9. "io"
  10. "github.com/ulikunitz/xz/lzma"
  11. )
  12. // WriterConfig describe the parameters for an xz writer.
  13. type WriterConfig struct {
  14. Properties *lzma.Properties
  15. DictCap int
  16. BufSize int
  17. BlockSize int64
  18. // checksum method: CRC32, CRC64 or SHA256 (default: CRC64)
  19. CheckSum byte
  20. // Forces NoChecksum (default: false)
  21. NoCheckSum bool
  22. // match algorithm
  23. Matcher lzma.MatchAlgorithm
  24. }
  25. // fill replaces zero values with default values.
  26. func (c *WriterConfig) fill() {
  27. if c.Properties == nil {
  28. c.Properties = &lzma.Properties{LC: 3, LP: 0, PB: 2}
  29. }
  30. if c.DictCap == 0 {
  31. c.DictCap = 8 * 1024 * 1024
  32. }
  33. if c.BufSize == 0 {
  34. c.BufSize = 4096
  35. }
  36. if c.BlockSize == 0 {
  37. c.BlockSize = maxInt64
  38. }
  39. if c.CheckSum == 0 {
  40. c.CheckSum = CRC64
  41. }
  42. if c.NoCheckSum {
  43. c.CheckSum = None
  44. }
  45. }
  46. // Verify checks the configuration for errors. Zero values will be
  47. // replaced by default values.
  48. func (c *WriterConfig) Verify() error {
  49. if c == nil {
  50. return errors.New("xz: writer configuration is nil")
  51. }
  52. c.fill()
  53. lc := lzma.Writer2Config{
  54. Properties: c.Properties,
  55. DictCap: c.DictCap,
  56. BufSize: c.BufSize,
  57. Matcher: c.Matcher,
  58. }
  59. if err := lc.Verify(); err != nil {
  60. return err
  61. }
  62. if c.BlockSize <= 0 {
  63. return errors.New("xz: block size out of range")
  64. }
  65. if err := verifyFlags(c.CheckSum); err != nil {
  66. return err
  67. }
  68. return nil
  69. }
  70. // filters creates the filter list for the given parameters.
  71. func (c *WriterConfig) filters() []filter {
  72. return []filter{&lzmaFilter{int64(c.DictCap)}}
  73. }
  74. // maxInt64 defines the maximum 64-bit signed integer.
  75. const maxInt64 = 1<<63 - 1
  76. // verifyFilters checks the filter list for the length and the right
  77. // sequence of filters.
  78. func verifyFilters(f []filter) error {
  79. if len(f) == 0 {
  80. return errors.New("xz: no filters")
  81. }
  82. if len(f) > 4 {
  83. return errors.New("xz: more than four filters")
  84. }
  85. for _, g := range f[:len(f)-1] {
  86. if g.last() {
  87. return errors.New("xz: last filter is not last")
  88. }
  89. }
  90. if !f[len(f)-1].last() {
  91. return errors.New("xz: wrong last filter")
  92. }
  93. return nil
  94. }
  95. // newFilterWriteCloser converts a filter list into a WriteCloser that
  96. // can be used by a blockWriter.
  97. func (c *WriterConfig) newFilterWriteCloser(w io.Writer, f []filter) (fw io.WriteCloser, err error) {
  98. if err = verifyFilters(f); err != nil {
  99. return nil, err
  100. }
  101. fw = nopWriteCloser(w)
  102. for i := len(f) - 1; i >= 0; i-- {
  103. fw, err = f[i].writeCloser(fw, c)
  104. if err != nil {
  105. return nil, err
  106. }
  107. }
  108. return fw, nil
  109. }
  110. // nopWCloser implements a WriteCloser with a Close method not doing
  111. // anything.
  112. type nopWCloser struct {
  113. io.Writer
  114. }
  115. // Close returns nil and doesn't do anything else.
  116. func (c nopWCloser) Close() error {
  117. return nil
  118. }
  119. // nopWriteCloser converts the Writer into a WriteCloser with a Close
  120. // function that does nothing beside returning nil.
  121. func nopWriteCloser(w io.Writer) io.WriteCloser {
  122. return nopWCloser{w}
  123. }
  124. // Writer compresses data written to it. It is an io.WriteCloser.
  125. type Writer struct {
  126. WriterConfig
  127. xz io.Writer
  128. bw *blockWriter
  129. newHash func() hash.Hash
  130. h header
  131. index []record
  132. closed bool
  133. }
  134. // newBlockWriter creates a new block writer writes the header out.
  135. func (w *Writer) newBlockWriter() error {
  136. var err error
  137. w.bw, err = w.WriterConfig.newBlockWriter(w.xz, w.newHash())
  138. if err != nil {
  139. return err
  140. }
  141. if err = w.bw.writeHeader(w.xz); err != nil {
  142. return err
  143. }
  144. return nil
  145. }
  146. // closeBlockWriter closes a block writer and records the sizes in the
  147. // index.
  148. func (w *Writer) closeBlockWriter() error {
  149. var err error
  150. if err = w.bw.Close(); err != nil {
  151. return err
  152. }
  153. w.index = append(w.index, w.bw.record())
  154. return nil
  155. }
  156. // NewWriter creates a new xz writer using default parameters.
  157. func NewWriter(xz io.Writer) (w *Writer, err error) {
  158. return WriterConfig{}.NewWriter(xz)
  159. }
  160. // NewWriter creates a new Writer using the given configuration parameters.
  161. func (c WriterConfig) NewWriter(xz io.Writer) (w *Writer, err error) {
  162. if err = c.Verify(); err != nil {
  163. return nil, err
  164. }
  165. w = &Writer{
  166. WriterConfig: c,
  167. xz: xz,
  168. h: header{c.CheckSum},
  169. index: make([]record, 0, 4),
  170. }
  171. if w.newHash, err = newHashFunc(c.CheckSum); err != nil {
  172. return nil, err
  173. }
  174. data, err := w.h.MarshalBinary()
  175. if err != nil {
  176. return nil, fmt.Errorf("w.h.MarshalBinary(): error %w", err)
  177. }
  178. if _, err = xz.Write(data); err != nil {
  179. return nil, err
  180. }
  181. if err = w.newBlockWriter(); err != nil {
  182. return nil, err
  183. }
  184. return w, nil
  185. }
  186. // Write compresses the uncompressed data provided.
  187. func (w *Writer) Write(p []byte) (n int, err error) {
  188. if w.closed {
  189. return 0, errClosed
  190. }
  191. for {
  192. k, err := w.bw.Write(p[n:])
  193. n += k
  194. if err != errNoSpace {
  195. return n, err
  196. }
  197. if err = w.closeBlockWriter(); err != nil {
  198. return n, err
  199. }
  200. if err = w.newBlockWriter(); err != nil {
  201. return n, err
  202. }
  203. }
  204. }
  205. // Close closes the writer and adds the footer to the Writer. Close
  206. // doesn't close the underlying writer.
  207. func (w *Writer) Close() error {
  208. if w.closed {
  209. return errClosed
  210. }
  211. w.closed = true
  212. var err error
  213. if err = w.closeBlockWriter(); err != nil {
  214. return err
  215. }
  216. f := footer{flags: w.h.flags}
  217. if f.indexSize, err = writeIndex(w.xz, w.index); err != nil {
  218. return err
  219. }
  220. data, err := f.MarshalBinary()
  221. if err != nil {
  222. return err
  223. }
  224. if _, err = w.xz.Write(data); err != nil {
  225. return err
  226. }
  227. return nil
  228. }
  229. // countingWriter is a writer that counts all data written to it.
  230. type countingWriter struct {
  231. w io.Writer
  232. n int64
  233. }
  234. // Write writes data to the countingWriter.
  235. func (cw *countingWriter) Write(p []byte) (n int, err error) {
  236. n, err = cw.w.Write(p)
  237. cw.n += int64(n)
  238. if err == nil && cw.n < 0 {
  239. return n, errors.New("xz: counter overflow")
  240. }
  241. return
  242. }
  243. // blockWriter is writes a single block.
  244. type blockWriter struct {
  245. cxz countingWriter
  246. // mw combines io.WriteCloser w and the hash.
  247. mw io.Writer
  248. w io.WriteCloser
  249. n int64
  250. blockSize int64
  251. closed bool
  252. headerLen int
  253. filters []filter
  254. hash hash.Hash
  255. }
  256. // newBlockWriter creates a new block writer.
  257. func (c *WriterConfig) newBlockWriter(xz io.Writer, hash hash.Hash) (bw *blockWriter, err error) {
  258. bw = &blockWriter{
  259. cxz: countingWriter{w: xz},
  260. blockSize: c.BlockSize,
  261. filters: c.filters(),
  262. hash: hash,
  263. }
  264. bw.w, err = c.newFilterWriteCloser(&bw.cxz, bw.filters)
  265. if err != nil {
  266. return nil, err
  267. }
  268. if bw.hash.Size() != 0 {
  269. bw.mw = io.MultiWriter(bw.w, bw.hash)
  270. } else {
  271. bw.mw = bw.w
  272. }
  273. return bw, nil
  274. }
  275. // writeHeader writes the header. If the function is called after Close
  276. // the commpressedSize and uncompressedSize fields will be filled.
  277. func (bw *blockWriter) writeHeader(w io.Writer) error {
  278. h := blockHeader{
  279. compressedSize: -1,
  280. uncompressedSize: -1,
  281. filters: bw.filters,
  282. }
  283. if bw.closed {
  284. h.compressedSize = bw.compressedSize()
  285. h.uncompressedSize = bw.uncompressedSize()
  286. }
  287. data, err := h.MarshalBinary()
  288. if err != nil {
  289. return err
  290. }
  291. if _, err = w.Write(data); err != nil {
  292. return err
  293. }
  294. bw.headerLen = len(data)
  295. return nil
  296. }
  297. // compressed size returns the amount of data written to the underlying
  298. // stream.
  299. func (bw *blockWriter) compressedSize() int64 {
  300. return bw.cxz.n
  301. }
  302. // uncompressedSize returns the number of data written to the
  303. // blockWriter
  304. func (bw *blockWriter) uncompressedSize() int64 {
  305. return bw.n
  306. }
  307. // unpaddedSize returns the sum of the header length, the uncompressed
  308. // size of the block and the hash size.
  309. func (bw *blockWriter) unpaddedSize() int64 {
  310. if bw.headerLen <= 0 {
  311. panic("xz: block header not written")
  312. }
  313. n := int64(bw.headerLen)
  314. n += bw.compressedSize()
  315. n += int64(bw.hash.Size())
  316. return n
  317. }
  318. // record returns the record for the current stream. Call Close before
  319. // calling this method.
  320. func (bw *blockWriter) record() record {
  321. return record{bw.unpaddedSize(), bw.uncompressedSize()}
  322. }
  323. var errClosed = errors.New("xz: writer already closed")
  324. var errNoSpace = errors.New("xz: no space")
  325. // Write writes uncompressed data to the block writer.
  326. func (bw *blockWriter) Write(p []byte) (n int, err error) {
  327. if bw.closed {
  328. return 0, errClosed
  329. }
  330. t := bw.blockSize - bw.n
  331. if int64(len(p)) > t {
  332. err = errNoSpace
  333. p = p[:t]
  334. }
  335. var werr error
  336. n, werr = bw.mw.Write(p)
  337. bw.n += int64(n)
  338. if werr != nil {
  339. return n, werr
  340. }
  341. return n, err
  342. }
  343. // Close closes the writer.
  344. func (bw *blockWriter) Close() error {
  345. if bw.closed {
  346. return errClosed
  347. }
  348. bw.closed = true
  349. if err := bw.w.Close(); err != nil {
  350. return err
  351. }
  352. s := bw.hash.Size()
  353. k := padLen(bw.cxz.n)
  354. p := make([]byte, k+s)
  355. bw.hash.Sum(p[k:k])
  356. if _, err := bw.cxz.w.Write(p); err != nil {
  357. return err
  358. }
  359. return nil
  360. }