Home | History | Annotate | Download | only in common
      1 /*
      2  * DecoderUtil
      3  *
      4  * Author: Lasse Collin <lasse.collin (at) 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 {
     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 
     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 
     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 
     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 
     92     public static boolean areStreamFlagsEqual(StreamFlags a, StreamFlags b) {
     93         // backwardSize is intentionally not compared.
     94         return a.checkType == b.checkType;
     95     }
     96 
     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