1// Copyright 2014-2017 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
5package xz
6
7import (
8	"errors"
9	"fmt"
10	"io"
11
12	"github.com/ulikunitz/xz/lzma"
13)
14
15// LZMA filter constants.
16const (
17	lzmaFilterID  = 0x21
18	lzmaFilterLen = 3
19)
20
21// lzmaFilter declares the LZMA2 filter information stored in an xz
22// block header.
23type lzmaFilter struct {
24	dictCap int64
25}
26
27// String returns a representation of the LZMA filter.
28func (f lzmaFilter) String() string {
29	return fmt.Sprintf("LZMA dict cap %#x", f.dictCap)
30}
31
32// id returns the ID for the LZMA2 filter.
33func (f lzmaFilter) id() uint64 { return lzmaFilterID }
34
35// MarshalBinary converts the lzmaFilter in its encoded representation.
36func (f lzmaFilter) MarshalBinary() (data []byte, err error) {
37	c := lzma.EncodeDictCap(f.dictCap)
38	return []byte{lzmaFilterID, 1, c}, nil
39}
40
41// UnmarshalBinary unmarshals the given data representation of the LZMA2
42// filter.
43func (f *lzmaFilter) UnmarshalBinary(data []byte) error {
44	if len(data) != lzmaFilterLen {
45		return errors.New("xz: data for LZMA2 filter has wrong length")
46	}
47	if data[0] != lzmaFilterID {
48		return errors.New("xz: wrong LZMA2 filter id")
49	}
50	if data[1] != 1 {
51		return errors.New("xz: wrong LZMA2 filter size")
52	}
53	dc, err := lzma.DecodeDictCap(data[2])
54	if err != nil {
55		return errors.New("xz: wrong LZMA2 dictionary size property")
56	}
57
58	f.dictCap = dc
59	return nil
60}
61
62// reader creates a new reader for the LZMA2 filter.
63func (f lzmaFilter) reader(r io.Reader, c *ReaderConfig) (fr io.Reader,
64	err error) {
65
66	config := new(lzma.Reader2Config)
67	if c != nil {
68		config.DictCap = c.DictCap
69	}
70	dc := int(f.dictCap)
71	if dc < 1 {
72		return nil, errors.New("xz: LZMA2 filter parameter " +
73			"dictionary capacity overflow")
74	}
75	if dc > config.DictCap {
76		config.DictCap = dc
77	}
78
79	fr, err = config.NewReader2(r)
80	if err != nil {
81		return nil, err
82	}
83	return fr, nil
84}
85
86// writeCloser creates a io.WriteCloser for the LZMA2 filter.
87func (f lzmaFilter) writeCloser(w io.WriteCloser, c *WriterConfig,
88) (fw io.WriteCloser, err error) {
89	config := new(lzma.Writer2Config)
90	if c != nil {
91		*config = lzma.Writer2Config{
92			Properties: c.Properties,
93			DictCap:    c.DictCap,
94			BufSize:    c.BufSize,
95			Matcher:    c.Matcher,
96		}
97	}
98
99	dc := int(f.dictCap)
100	if dc < 1 {
101		return nil, errors.New("xz: LZMA2 filter parameter " +
102			"dictionary capacity overflow")
103	}
104	if dc > config.DictCap {
105		config.DictCap = dc
106	}
107
108	fw, err = config.NewWriter2(w)
109	if err != nil {
110		return nil, err
111	}
112	return fw, nil
113}
114
115// last returns true, because an LZMA2 filter must be the last filter in
116// the filter list.
117func (f lzmaFilter) last() bool { return true }
118