1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.sevenz;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24
25 import org.apache.commons.compress.MemoryLimitException;
26 import org.apache.commons.compress.utils.ByteUtils;
27 import org.apache.commons.compress.utils.FlushShieldFilterOutputStream;
28 import org.tukaani.xz.LZMA2Options;
29 import org.tukaani.xz.LZMAInputStream;
30 import org.tukaani.xz.LZMAOutputStream;
31
32 final class LZMADecoder extends AbstractCoder {
33
34 LZMADecoder() {
35 super(LZMA2Options.class, Number.class);
36 }
37
38 @Override
39 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password,
40 final int maxMemoryLimitKiB) throws IOException {
41 if (coder.properties == null) {
42 throw new IOException("Missing LZMA properties");
43 }
44 if (coder.properties.length < 1) {
45 throw new IOException("LZMA properties too short");
46 }
47 final byte propsByte = coder.properties[0];
48 final int dictSize = getDictionarySize(coder);
49 if (dictSize > LZMAInputStream.DICT_SIZE_MAX) {
50 throw new IOException("Dictionary larger than 4GiB maximum size used in " + archiveName);
51 }
52 final int memoryUsageInKiB = LZMAInputStream.getMemoryUsage(dictSize, propsByte);
53 if (memoryUsageInKiB > maxMemoryLimitKiB) {
54 throw new MemoryLimitException(memoryUsageInKiB, maxMemoryLimitKiB);
55 }
56 final LZMAInputStream lzmaIn = new LZMAInputStream(in, uncompressedLength, propsByte, dictSize);
57 lzmaIn.enableRelaxedEndCondition();
58 return lzmaIn;
59 }
60
61 @Override
62 OutputStream encode(final OutputStream out, final Object opts) throws IOException {
63
64 return new FlushShieldFilterOutputStream(new LZMAOutputStream(out, getOptions(opts), false));
65 }
66
67 private int getDictionarySize(final Coder coder) throws IllegalArgumentException {
68 return (int) ByteUtils.fromLittleEndian(coder.properties, 1, 4);
69 }
70
71 private LZMA2Options getOptions(final Object opts) throws IOException {
72 if (opts instanceof LZMA2Options) {
73 return (LZMA2Options) opts;
74 }
75 final LZMA2Options options = new LZMA2Options();
76 options.setDictSize(numberOptionOrDefault(opts));
77 return options;
78 }
79
80 @Override
81 byte[] getOptionsAsProperties(final Object opts) throws IOException {
82 final LZMA2Options options = getOptions(opts);
83 final byte props = (byte) ((options.getPb() * 5 + options.getLp()) * 9 + options.getLc());
84 final int dictSize = options.getDictSize();
85 final byte[] o = new byte[5];
86 o[0] = props;
87 ByteUtils.toLittleEndian(o, dictSize, 1, 4);
88 return o;
89 }
90
91 @Override
92 Object getOptionsFromCoder(final Coder coder, final InputStream in) throws IOException {
93 if (coder.properties == null) {
94 throw new IOException("Missing LZMA properties");
95 }
96 if (coder.properties.length < 1) {
97 throw new IOException("LZMA properties too short");
98 }
99 final byte propsByte = coder.properties[0];
100 int props = propsByte & 0xFF;
101 final int pb = props / (9 * 5);
102 props -= pb * 9 * 5;
103 final int lp = props / 9;
104 final int lc = props - lp * 9;
105 final LZMA2Options opts = new LZMA2Options();
106 opts.setPb(pb);
107 opts.setLcLp(lc, lp);
108 opts.setDictSize(getDictionarySize(coder));
109 return opts;
110 }
111
112 private int numberOptionOrDefault(final Object opts) {
113 return toInt(opts, LZMA2Options.DICT_SIZE_DEFAULT);
114 }
115 }