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