001/* 002 * This file is part of lzo-java, an implementation of LZO in Java. 003 * https://github.com/shevek/lzo-java 004 * 005 * The Java portion of this library is: 006 * Copyright (C) 2011 Shevek <shevek@anarres.org> 007 * All Rights Reserved. 008 * 009 * The preprocessed C portion of this library is: 010 * Copyright (C) 2006-2011 Markus Franz Xaver Johannes Oberhumer 011 * All Rights Reserved. 012 * 013 * This library is free software; you can redistribute it and/or 014 * modify it under the terms of the GNU General Public License 015 * as published by the Free Software Foundation; either version 016 * 2 of the License, or (at your option) any later version. 017 * 018 * This library is distributed in the hope that it will be useful, 019 * but WITHOUT ANY WARRANTY; without even the implied warranty 020 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 021 * See the GNU General Public License for more details. 022 * 023 * You should have received a copy of the GNU General Public 024 * License along with the LZO library; see the file COPYING. 025 * If not, see <http://www.gnu.org/licenses/> or write to the 026 * Free Software Foundation, Inc., 51 Franklin Street, Fifth 027 * Floor, Boston, MA 02110-1301, USA. 028 029 * As a special exception, the copyright holders of this file 030 * give you permission to link this file with independent 031 * modules to produce an executable, regardless of the license 032 * terms of these independent modules, and to copy and distribute 033 * the resulting executable under terms of your choice, provided 034 * that you also meet, for each linked independent module, 035 * the terms and conditions of the license of that module. An 036 * independent module is a module which is not derived from or 037 * based on this library or file. If you modify this file, you may 038 * extend this exception to your version of the file, but 039 * you are not obligated to do so. If you do not wish to do so, 040 * delete this exception statement from your version. 041 */ 042package org.anarres.lzo; 043 044import java.io.IOException; 045import java.io.InputStream; 046import java.util.Arrays; 047import java.util.zip.Adler32; 048import java.util.zip.CRC32; 049import java.util.zip.Checksum; 050import javax.annotation.CheckForNull; 051import javax.annotation.Nonnegative; 052import javax.annotation.Nonnull; 053import org.apache.commons.logging.Log; 054import org.apache.commons.logging.LogFactory; 055 056/** 057 * 058 * @author shevek 059 */ 060public class LzopInputStream extends LzoInputStream { 061 062 private static final Log LOG = LogFactory.getLog(LzopInputStream.class); 063 private final int flags; 064 private final CRC32 c_crc32_c; 065 private final CRC32 c_crc32_d; 066 private final Adler32 c_adler32_c; 067 private final Adler32 c_adler32_d; 068 private boolean eof; 069 070 public LzopInputStream(@Nonnull InputStream in) throws IOException { 071 super(in, new LzoDecompressor1x()); 072 this.flags = readHeader(); 073 this.c_crc32_c = ((flags & LzopConstants.F_CRC32_C) == 0) ? null : new CRC32(); 074 this.c_crc32_d = ((flags & LzopConstants.F_CRC32_D) == 0) ? null : new CRC32(); 075 this.c_adler32_c = ((flags & LzopConstants.F_ADLER32_C) == 0) ? null : new Adler32(); 076 this.c_adler32_d = ((flags & LzopConstants.F_ADLER32_D) == 0) ? null : new Adler32(); 077 this.eof = false; 078 // logState(); 079 } 080 081 public int getFlags() { 082 return flags; 083 } 084 085 @Nonnegative 086 public int getCompressedChecksumCount() { 087 int out = 0; 088 if (c_crc32_c != null) 089 out++; 090 if (c_adler32_c != null) 091 out++; 092 return out; 093 } 094 095 @Nonnegative 096 public int getUncompressedChecksumCount() { 097 int out = 0; 098 if (c_crc32_d != null) 099 out++; 100 if (c_adler32_d != null) 101 out++; 102 return out; 103 } 104 105 @Override 106 protected void logState(@Nonnull String when) { 107 super.logState(when); 108 LOG.info(when + " Flags = " + Integer.toHexString(flags)); 109 // LOG.info(when + " CRC32C = " + c_crc32_c); 110 // LOG.info(when + " CRC32D = " + c_crc32_d); 111 // LOG.info(when + " Adler32C = " + c_adler32_c); 112 // LOG.info(when + " Adler32D = " + c_adler32_d); 113 } 114 115 /** 116 * Read len bytes into buf, st LSB of int returned is the last byte of the 117 * first word read. 118 */ 119 // @Nonnegative ? 120 private int readInt(@Nonnull byte[] buf, @Nonnegative int len) 121 throws IOException { 122 readBytes(buf, 0, len); 123 int ret = (0xFF & buf[0]) << 24; 124 ret |= (0xFF & buf[1]) << 16; 125 ret |= (0xFF & buf[2]) << 8; 126 ret |= (0xFF & buf[3]); 127 return (len > 3) ? ret : (ret >>> (8 * (4 - len))); 128 } 129 130 /** 131 * Read bytes, update checksums, return first four bytes as an int, first 132 * byte read in the MSB. 133 */ 134 // @Nonnegative ? 135 private int readHeaderItem(@Nonnull byte[] buf, @Nonnegative int len, @Nonnull Adler32 adler, @Nonnull CRC32 crc32) throws IOException { 136 int ret = readInt(buf, len); 137 adler.update(buf, 0, len); 138 crc32.update(buf, 0, len); 139 Arrays.fill(buf, (byte) 0); 140 return ret; 141 } 142 143 /** 144 * Read and verify an lzo header, setting relevant block checksum options 145 * and ignoring most everything else. 146 */ 147 protected int readHeader() throws IOException { 148 byte[] buf = new byte[9]; 149 readBytes(buf, 0, 9); 150 if (!Arrays.equals(buf, LzopConstants.LZOP_MAGIC)) 151 throw new IOException("Invalid LZO header"); 152 Arrays.fill(buf, (byte) 0); 153 Adler32 adler = new Adler32(); 154 CRC32 crc32 = new CRC32(); 155 int hitem = readHeaderItem(buf, 2, adler, crc32); // lzop version 156 if (hitem > LzopConstants.LZOP_VERSION) { 157 LOG.debug("Compressed with later version of lzop: " 158 + Integer.toHexString(hitem) + " (expected 0x" 159 + Integer.toHexString(LzopConstants.LZOP_VERSION) + ")"); 160 } 161 hitem = readHeaderItem(buf, 2, adler, crc32); // lzo library version 162 if (hitem > LzoVersion.LZO_LIBRARY_VERSION) { 163 throw new IOException("Compressed with incompatible lzo version: 0x" 164 + Integer.toHexString(hitem) + " (expected 0x" 165 + Integer.toHexString(LzoVersion.LZO_LIBRARY_VERSION) + ")"); 166 } 167 hitem = readHeaderItem(buf, 2, adler, crc32); // lzop extract version 168 if (hitem > LzopConstants.LZOP_VERSION) { 169 throw new IOException("Compressed with incompatible lzop version: 0x" 170 + Integer.toHexString(hitem) + " (expected 0x" 171 + Integer.toHexString(LzopConstants.LZOP_VERSION) + ")"); 172 } 173 hitem = readHeaderItem(buf, 1, adler, crc32); // method 174 switch (hitem) { 175 case LzopConstants.M_LZO1X_1: 176 case LzopConstants.M_LZO1X_1_15: 177 case LzopConstants.M_LZO1X_999: 178 break; 179 default: 180 throw new IOException("Invalid strategy " + Integer.toHexString(hitem)); 181 } 182 readHeaderItem(buf, 1, adler, crc32); // ignore level 183 184 // flags 185 int flags = readHeaderItem(buf, 4, adler, crc32); 186 boolean useCRC32 = (flags & LzopConstants.F_H_CRC32) != 0; 187 boolean extraField = (flags & LzopConstants.F_H_EXTRA_FIELD) != 0; 188 if ((flags & LzopConstants.F_MULTIPART) != 0) 189 throw new IOException("Multipart lzop not supported"); 190 if ((flags & LzopConstants.F_H_FILTER) != 0) 191 throw new IOException("lzop filter not supported"); 192 if ((flags & LzopConstants.F_RESERVED) != 0) 193 throw new IOException("Unknown flags in header"); 194 // known !F_H_FILTER, so no optional block 195 196 readHeaderItem(buf, 4, adler, crc32); // ignore mode 197 readHeaderItem(buf, 4, adler, crc32); // ignore mtime 198 readHeaderItem(buf, 4, adler, crc32); // ignore gmtdiff 199 hitem = readHeaderItem(buf, 1, adler, crc32); // fn len 200 if (hitem > 0) { 201 byte[] tmp = (hitem > buf.length) ? new byte[hitem] : buf; 202 readHeaderItem(tmp, hitem, adler, crc32); // skip filename 203 } 204 int checksum = (int) (useCRC32 ? crc32.getValue() : adler.getValue()); 205 hitem = readHeaderItem(buf, 4, adler, crc32); // read checksum 206 if (hitem != checksum) { 207 throw new IOException("Invalid header checksum: " 208 + Long.toHexString(checksum) + " (expected 0x" 209 + Integer.toHexString(hitem) + ")"); 210 } 211 if (extraField) { // lzop 1.08 ultimately ignores this 212 LOG.debug("Extra header field not processed"); 213 adler.reset(); 214 crc32.reset(); 215 hitem = readHeaderItem(buf, 4, adler, crc32); 216 readHeaderItem(new byte[hitem], hitem, adler, crc32); 217 checksum = (int) (useCRC32 ? crc32.getValue() : adler.getValue()); 218 if (checksum != readHeaderItem(buf, 4, adler, crc32)) { 219 throw new IOException("Invalid checksum for extra header field"); 220 } 221 } 222 223 return flags; 224 } 225 226 private int readChecksum(@CheckForNull Checksum csum) throws IOException { 227 if (csum == null) 228 return 0; 229 // LOG.info("Reading checksum " + csum); 230 return readInt(false); 231 } 232 233 private void testChecksum(@CheckForNull Checksum csum, int value, @Nonnull byte[] data, @Nonnegative int off, @Nonnegative int len) throws IOException { 234 if (csum == null) 235 return; 236 csum.reset(); 237 csum.update(data, off, len); 238 if (value != (int) csum.getValue()) 239 throw new IOException("Checksum failure: " 240 + "Expected " + Integer.toHexString(value) 241 + "; got " + Long.toHexString(csum.getValue())); 242 } 243 244 @Override 245 protected boolean readBlock() throws IOException { 246 // logState("Before readBlock"); 247 if (eof) 248 return false; 249 int outputBufferLength = readInt(false); 250 if (outputBufferLength == 0) { 251 // logState("After empty readBlock"); 252 eof = true; 253 return false; 254 } 255 setOutputBufferSize(outputBufferLength); 256 int inputBufferLength = readInt(false); 257 setInputBufferSize(inputBufferLength); 258 int v_adler32_d = readChecksum(c_adler32_d); 259 int v_crc32_d = readChecksum(c_crc32_d); 260 // LOG.info("outputBufferLength=" + outputBufferLength + "; inputBufferLength=" + inputBufferLength); 261 if (outputBufferLength == inputBufferLength) { 262 outputBufferPos = 0; 263 outputBufferLen.value = outputBufferLength; 264 readBytes(outputBuffer, 0, outputBufferLength); 265 testChecksum(c_adler32_d, v_adler32_d, outputBuffer, 0, outputBufferLength); 266 testChecksum(c_crc32_d, v_crc32_d, outputBuffer, 0, outputBufferLength); 267 // logState("After uncompressed readBlock"); 268 return true; 269 } 270 int v_adler32_c = readChecksum(c_adler32_c); 271 int v_crc32_c = readChecksum(c_crc32_c); 272 readBytes(inputBuffer, 0, inputBufferLength); 273 testChecksum(c_adler32_c, v_adler32_c, inputBuffer, 0, inputBufferLength); 274 testChecksum(c_crc32_c, v_crc32_c, inputBuffer, 0, inputBufferLength); 275 decompress(outputBufferLength, inputBufferLength); 276 testChecksum(c_adler32_d, v_adler32_d, outputBuffer, 0, outputBufferLength); 277 testChecksum(c_crc32_d, v_crc32_d, outputBuffer, 0, outputBufferLength); 278 // logState("After compressed readBlock"); 279 return true; 280 } 281}