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