| /* |
| * DecoderUtil |
| * |
| * Author: Lasse Collin <lasse.collin@tukaani.org> |
| * |
| * This file has been put into the public domain. |
| * You can do whatever you want with this file. |
| */ |
| |
| package org.tukaani.xz.common; |
| |
| import java.io.InputStream; |
| import java.io.IOException; |
| import java.io.EOFException; |
| import java.util.zip.CRC32; |
| import org.tukaani.xz.XZ; |
| import org.tukaani.xz.XZFormatException; |
| import org.tukaani.xz.CorruptedInputException; |
| import org.tukaani.xz.UnsupportedOptionsException; |
| |
| public class DecoderUtil extends Util { |
| public static boolean isCRC32Valid(byte[] buf, int off, int len, |
| int ref_off) { |
| CRC32 crc32 = new CRC32(); |
| crc32.update(buf, off, len); |
| long value = crc32.getValue(); |
| |
| for (int i = 0; i < 4; ++i) |
| if ((byte)(value >>> (i * 8)) != buf[ref_off + i]) |
| return false; |
| |
| return true; |
| } |
| |
| public static StreamFlags decodeStreamHeader(byte[] buf) |
| throws IOException { |
| for (int i = 0; i < XZ.HEADER_MAGIC.length; ++i) |
| if (buf[i] != XZ.HEADER_MAGIC[i]) |
| throw new XZFormatException(); |
| |
| if (!isCRC32Valid(buf, XZ.HEADER_MAGIC.length, 2, |
| XZ.HEADER_MAGIC.length + 2)) |
| throw new CorruptedInputException("XZ Stream Header is corrupt"); |
| |
| try { |
| return decodeStreamFlags(buf, XZ.HEADER_MAGIC.length); |
| } catch (UnsupportedOptionsException e) { |
| throw new UnsupportedOptionsException( |
| "Unsupported options in XZ Stream Header"); |
| } |
| } |
| |
| public static StreamFlags decodeStreamFooter(byte[] buf) |
| throws IOException { |
| if (buf[10] != XZ.FOOTER_MAGIC[0] || buf[11] != XZ.FOOTER_MAGIC[1]) { |
| // NOTE: The exception could be XZFormatException too. |
| // It depends on the situation which one is better. |
| throw new CorruptedInputException("XZ Stream Footer is corrupt"); |
| } |
| |
| if (!isCRC32Valid(buf, 4, 6, 0)) |
| throw new CorruptedInputException("XZ Stream Footer is corrupt"); |
| |
| StreamFlags streamFlags; |
| try { |
| streamFlags = decodeStreamFlags(buf, 8); |
| } catch (UnsupportedOptionsException e) { |
| throw new UnsupportedOptionsException( |
| "Unsupported options in XZ Stream Footer"); |
| } |
| |
| streamFlags.backwardSize = 0; |
| for (int i = 0; i < 4; ++i) |
| streamFlags.backwardSize |= (buf[i + 4] & 0xFF) << (i * 8); |
| |
| streamFlags.backwardSize = (streamFlags.backwardSize + 1) * 4; |
| |
| return streamFlags; |
| } |
| |
| private static StreamFlags decodeStreamFlags(byte[] buf, int off) |
| throws UnsupportedOptionsException { |
| if (buf[off] != 0x00 || (buf[off + 1] & 0xFF) >= 0x10) |
| throw new UnsupportedOptionsException(); |
| |
| StreamFlags streamFlags = new StreamFlags(); |
| streamFlags.checkType = buf[off + 1]; |
| |
| return streamFlags; |
| } |
| |
| public static boolean areStreamFlagsEqual(StreamFlags a, StreamFlags b) { |
| // backwardSize is intentionally not compared. |
| return a.checkType == b.checkType; |
| } |
| |
| public static long decodeVLI(InputStream in) throws IOException { |
| int b = in.read(); |
| if (b == -1) |
| throw new EOFException(); |
| |
| long num = b & 0x7F; |
| int i = 0; |
| |
| while ((b & 0x80) != 0x00) { |
| if (++i >= VLI_SIZE_MAX) |
| throw new CorruptedInputException(); |
| |
| b = in.read(); |
| if (b == -1) |
| throw new EOFException(); |
| |
| if (b == 0x00) |
| throw new CorruptedInputException(); |
| |
| num |= (long)(b & 0x7F) << (i * 7); |
| } |
| |
| return num; |
| } |
| } |