1 /*
2  * DecoderUtil
3  *
4  * Author: Lasse Collin <lasse.collin@tukaani.org>
5  *
6  * This file has been put into the public domain.
7  * You can do whatever you want with this file.
8  */
9 
10 package org.tukaani.xz.common;
11 
12 import java.io.InputStream;
13 import java.io.IOException;
14 import java.io.EOFException;
15 import java.util.zip.CRC32;
16 import org.tukaani.xz.XZ;
17 import org.tukaani.xz.XZFormatException;
18 import org.tukaani.xz.CorruptedInputException;
19 import org.tukaani.xz.UnsupportedOptionsException;
20 
21 public class DecoderUtil extends Util {
isCRC32Valid(byte[] buf, int off, int len, int ref_off)22     public static boolean isCRC32Valid(byte[] buf, int off, int len,
23                                        int ref_off) {
24         CRC32 crc32 = new CRC32();
25         crc32.update(buf, off, len);
26         long value = crc32.getValue();
27 
28         for (int i = 0; i < 4; ++i)
29             if ((byte)(value >>> (i * 8)) != buf[ref_off + i])
30                 return false;
31 
32         return true;
33     }
34 
decodeStreamHeader(byte[] buf)35     public static StreamFlags decodeStreamHeader(byte[] buf)
36             throws IOException {
37         for (int i = 0; i < XZ.HEADER_MAGIC.length; ++i)
38             if (buf[i] != XZ.HEADER_MAGIC[i])
39                 throw new XZFormatException();
40 
41         if (!isCRC32Valid(buf, XZ.HEADER_MAGIC.length, 2,
42                           XZ.HEADER_MAGIC.length + 2))
43             throw new CorruptedInputException("XZ Stream Header is corrupt");
44 
45         try {
46             return decodeStreamFlags(buf, XZ.HEADER_MAGIC.length);
47         } catch (UnsupportedOptionsException e) {
48             throw new UnsupportedOptionsException(
49                     "Unsupported options in XZ Stream Header");
50         }
51     }
52 
decodeStreamFooter(byte[] buf)53     public static StreamFlags decodeStreamFooter(byte[] buf)
54             throws IOException {
55         if (buf[10] != XZ.FOOTER_MAGIC[0] || buf[11] != XZ.FOOTER_MAGIC[1]) {
56             // NOTE: The exception could be XZFormatException too.
57             // It depends on the situation which one is better.
58             throw new CorruptedInputException("XZ Stream Footer is corrupt");
59         }
60 
61         if (!isCRC32Valid(buf, 4, 6, 0))
62             throw new CorruptedInputException("XZ Stream Footer is corrupt");
63 
64         StreamFlags streamFlags;
65         try {
66             streamFlags = decodeStreamFlags(buf, 8);
67         } catch (UnsupportedOptionsException e) {
68             throw new UnsupportedOptionsException(
69                     "Unsupported options in XZ Stream Footer");
70         }
71 
72         streamFlags.backwardSize = 0;
73         for (int i = 0; i < 4; ++i)
74             streamFlags.backwardSize |= (buf[i + 4] & 0xFF) << (i * 8);
75 
76         streamFlags.backwardSize = (streamFlags.backwardSize + 1) * 4;
77 
78         return streamFlags;
79     }
80 
decodeStreamFlags(byte[] buf, int off)81     private static StreamFlags decodeStreamFlags(byte[] buf, int off)
82             throws UnsupportedOptionsException {
83         if (buf[off] != 0x00 || (buf[off + 1] & 0xFF) >= 0x10)
84             throw new UnsupportedOptionsException();
85 
86         StreamFlags streamFlags = new StreamFlags();
87         streamFlags.checkType = buf[off + 1];
88 
89         return streamFlags;
90     }
91 
areStreamFlagsEqual(StreamFlags a, StreamFlags b)92     public static boolean areStreamFlagsEqual(StreamFlags a, StreamFlags b) {
93         // backwardSize is intentionally not compared.
94         return a.checkType == b.checkType;
95     }
96 
decodeVLI(InputStream in)97     public static long decodeVLI(InputStream in) throws IOException {
98         int b = in.read();
99         if (b == -1)
100             throw new EOFException();
101 
102         long num = b & 0x7F;
103         int i = 0;
104 
105         while ((b & 0x80) != 0x00) {
106             if (++i >= VLI_SIZE_MAX)
107                 throw new CorruptedInputException();
108 
109             b = in.read();
110             if (b == -1)
111                 throw new EOFException();
112 
113             if (b == 0x00)
114                 throw new CorruptedInputException();
115 
116             num |= (long)(b & 0x7F) << (i * 7);
117         }
118 
119         return num;
120     }
121 }
122