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.tukaani.xz.FinishableWrapperOutputStream;
27 import org.tukaani.xz.LZMA2InputStream;
28 import org.tukaani.xz.LZMA2Options;
29
30 final class LZMA2Decoder extends AbstractCoder {
31
32 LZMA2Decoder() {
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 maxMemoryLimitKiB) throws IOException {
39 try {
40 final int dictionarySize = getDictionarySize(coder);
41 final int memoryUsageInKiB = LZMA2InputStream.getMemoryUsage(dictionarySize);
42 if (memoryUsageInKiB > maxMemoryLimitKiB) {
43 throw new MemoryLimitException(memoryUsageInKiB, maxMemoryLimitKiB);
44 }
45 return new LZMA2InputStream(in, dictionarySize);
46 } catch (final IllegalArgumentException ex) {
47 throw new IOException(ex);
48 }
49 }
50
51 @SuppressWarnings("resource")
52 @Override
53 OutputStream encode(final OutputStream out, final Object opts) throws IOException {
54 return getOptions(opts).getOutputStream(new FinishableWrapperOutputStream(out));
55 }
56
57 private int getDictionarySize(final Coder coder) throws IOException {
58 if (coder.properties == null) {
59 throw new IOException("Missing LZMA2 properties");
60 }
61 if (coder.properties.length < 1) {
62 throw new IOException("LZMA2 properties too short");
63 }
64 final int dictionarySizeBits = 0xff & coder.properties[0];
65 if ((dictionarySizeBits & ~0x3f) != 0) {
66 throw new IOException("Unsupported LZMA2 property bits");
67 }
68 if (dictionarySizeBits > 40) {
69 throw new IOException("Dictionary larger than 4GiB maximum size");
70 }
71 if (dictionarySizeBits == 40) {
72 return 0xFFFFffff;
73 }
74 return (2 | dictionarySizeBits & 0x1) << dictionarySizeBits / 2 + 11;
75 }
76
77 private int getDictSize(final Object opts) {
78 if (opts instanceof LZMA2Options) {
79 return ((LZMA2Options) opts).getDictSize();
80 }
81 return numberOptionOrDefault(opts);
82 }
83
84 private LZMA2Options getOptions(final Object opts) throws IOException {
85 if (opts instanceof LZMA2Options) {
86 return (LZMA2Options) opts;
87 }
88 final LZMA2Options options = new LZMA2Options();
89 options.setDictSize(numberOptionOrDefault(opts));
90 return options;
91 }
92
93 @Override
94 byte[] getOptionsAsProperties(final Object opts) {
95 final int dictSize = getDictSize(opts);
96 final int lead = Integer.numberOfLeadingZeros(dictSize);
97 final int secondBit = (dictSize >>> 30 - lead) - 2;
98 return new byte[] { (byte) ((19 - lead) * 2 + secondBit) };
99 }
100
101 @Override
102 Object getOptionsFromCoder(final Coder coder, final InputStream in) throws IOException {
103 return getDictionarySize(coder);
104 }
105
106 private int numberOptionOrDefault(final Object opts) {
107 return toInt(opts, LZMA2Options.DICT_SIZE_DEFAULT);
108 }
109 }