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