org.anarres.lzo.LzopInputStream.java Source code

Java tutorial

Introduction

Here is the source code for org.anarres.lzo.LzopInputStream.java

Source

/*
 * This file is part of lzo-java, an implementation of LZO in Java.
 * https://github.com/Karmasphere/lzo-java
 *
 * The Java portion of this library is:
 * Copyright (C) 2011 Shevek <shevek@anarres.org>
 * All Rights Reserved.
 *
 * The preprocessed C portion of this library is:
 * Copyright (C) 2006-2011 Markus Franz Xaver Johannes Oberhumer
 * All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with the LZO library; see the file COPYING.
 * If not, see <http://www.gnu.org/licenses/> or write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
    
 * As a special exception, the copyright holders of this file
 * give you permission to link this file with independent
 * modules to produce an executable, regardless of the license
 * terms of these independent modules, and to copy and distribute
 * the resulting executable under terms of your choice, provided
 * that you also meet, for each linked independent module,
 * the terms and conditions of the license of that module. An
 * independent module is a module which is not derived from or
 * based on this library or file. If you modify this file, you may
 * extend this exception to your version of the file, but
 * you are not obligated to do so. If you do not wish to do so,
 * delete this exception statement from your version.
 */
package org.anarres.lzo;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.zip.Adler32;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author shevek
 */
public class LzopInputStream extends LzoInputStream {

    private static final Log LOG = LogFactory.getLog(LzopInputStream.class);
    private final int flags;
    private final CRC32 c_crc32_c;
    private final CRC32 c_crc32_d;
    private final Adler32 c_adler32_c;
    private final Adler32 c_adler32_d;
    private boolean eof;

    public LzopInputStream(InputStream in) throws IOException {
        super(in, new LzoDecompressor1x());
        this.flags = readHeader();
        this.c_crc32_c = ((flags & LzopConstants.F_CRC32_C) == 0) ? null : new CRC32();
        this.c_crc32_d = ((flags & LzopConstants.F_CRC32_D) == 0) ? null : new CRC32();
        this.c_adler32_c = ((flags & LzopConstants.F_ADLER32_C) == 0) ? null : new Adler32();
        this.c_adler32_d = ((flags & LzopConstants.F_ADLER32_D) == 0) ? null : new Adler32();
        this.eof = false;
        // logState();
    }

    public int getFlags() {
        return flags;
    }

    public int getCompressedChecksumCount() {
        int out = 0;
        if (c_crc32_c != null)
            out++;
        if (c_adler32_c != null)
            out++;
        return out;
    }

    public int getUncompressedChecksumCount() {
        int out = 0;
        if (c_crc32_d != null)
            out++;
        if (c_adler32_d != null)
            out++;
        return out;
    }

    protected void logState(String when) {
        super.logState(when);
        LOG.info(when + " Flags = " + Integer.toHexString(flags));
        // LOG.info(when + " CRC32C = " + c_crc32_c);
        // LOG.info(when + " CRC32D = " + c_crc32_d);
        // LOG.info(when + " Adler32C = " + c_adler32_c);
        // LOG.info(when + " Adler32D = " + c_adler32_d);
    }

    /**
     * Read len bytes into buf, st LSB of int returned is the last byte of the
     * first word read.
     */
    private int readInt(byte[] buf, int len) throws IOException {
        readBytes(buf, 0, len);
        int ret = (0xFF & buf[0]) << 24;
        ret |= (0xFF & buf[1]) << 16;
        ret |= (0xFF & buf[2]) << 8;
        ret |= (0xFF & buf[3]);
        return (len > 3) ? ret : (ret >>> (8 * (4 - len)));
    }

    /**
     * Read bytes, update checksums, return first four bytes as an int, first
     * byte read in the MSB.
     */
    private int readHeaderItem(byte[] buf, int len, Adler32 adler, CRC32 crc32) throws IOException {
        int ret = readInt(buf, len);
        adler.update(buf, 0, len);
        crc32.update(buf, 0, len);
        Arrays.fill(buf, (byte) 0);
        return ret;
    }

    /**
     * Read and verify an lzo header, setting relevant block checksum options
     * and ignoring most everything else.
     */
    protected int readHeader() throws IOException {
        byte[] buf = new byte[9];
        readBytes(buf, 0, 9);
        if (!Arrays.equals(buf, LzopConstants.LZOP_MAGIC))
            throw new IOException("Invalid LZO header");
        Arrays.fill(buf, (byte) 0);
        Adler32 adler = new Adler32();
        CRC32 crc32 = new CRC32();
        int hitem = readHeaderItem(buf, 2, adler, crc32); // lzop version
        if (hitem > LzopConstants.LZOP_VERSION) {
            LOG.debug("Compressed with later version of lzop: " + Integer.toHexString(hitem) + " (expected 0x"
                    + Integer.toHexString(LzopConstants.LZOP_VERSION) + ")");
        }
        hitem = readHeaderItem(buf, 2, adler, crc32); // lzo library version
        if (hitem > LzoVersion.LZO_LIBRARY_VERSION) {
            throw new IOException("Compressed with incompatible lzo version: 0x" + Integer.toHexString(hitem)
                    + " (expected 0x" + Integer.toHexString(LzoVersion.LZO_LIBRARY_VERSION) + ")");
        }
        hitem = readHeaderItem(buf, 2, adler, crc32); // lzop extract version
        if (hitem > LzopConstants.LZOP_VERSION) {
            throw new IOException("Compressed with incompatible lzop version: 0x" + Integer.toHexString(hitem)
                    + " (expected 0x" + Integer.toHexString(LzopConstants.LZOP_VERSION) + ")");
        }
        hitem = readHeaderItem(buf, 1, adler, crc32); // method
        switch (hitem) {
        case LzopConstants.M_LZO1X_1:
        case LzopConstants.M_LZO1X_1_15:
        case LzopConstants.M_LZO1X_999:
            break;
        default:
            throw new IOException("Invalid strategy " + Integer.toHexString(hitem));
        }
        readHeaderItem(buf, 1, adler, crc32); // ignore level

        // flags
        int flags = readHeaderItem(buf, 4, adler, crc32);
        boolean useCRC32 = (flags & LzopConstants.F_H_CRC32) != 0;
        boolean extraField = (flags & LzopConstants.F_H_EXTRA_FIELD) != 0;
        if ((flags & LzopConstants.F_MULTIPART) != 0)
            throw new IOException("Multipart lzop not supported");
        if ((flags & LzopConstants.F_H_FILTER) != 0)
            throw new IOException("lzop filter not supported");
        if ((flags & LzopConstants.F_RESERVED) != 0)
            throw new IOException("Unknown flags in header");
        // known !F_H_FILTER, so no optional block

        readHeaderItem(buf, 4, adler, crc32); // ignore mode
        readHeaderItem(buf, 4, adler, crc32); // ignore mtime
        readHeaderItem(buf, 4, adler, crc32); // ignore gmtdiff
        hitem = readHeaderItem(buf, 1, adler, crc32); // fn len
        if (hitem > 0) {
            byte[] tmp = (hitem > buf.length) ? new byte[hitem] : buf;
            readHeaderItem(tmp, hitem, adler, crc32); // skip filename
        }
        int checksum = (int) (useCRC32 ? crc32.getValue() : adler.getValue());
        hitem = readHeaderItem(buf, 4, adler, crc32); // read checksum
        if (hitem != checksum) {
            throw new IOException("Invalid header checksum: " + Long.toHexString(checksum) + " (expected 0x"
                    + Integer.toHexString(hitem) + ")");
        }
        if (extraField) { // lzop 1.08 ultimately ignores this
            LOG.debug("Extra header field not processed");
            adler.reset();
            crc32.reset();
            hitem = readHeaderItem(buf, 4, adler, crc32);
            readHeaderItem(new byte[hitem], hitem, adler, crc32);
            checksum = (int) (useCRC32 ? crc32.getValue() : adler.getValue());
            if (checksum != readHeaderItem(buf, 4, adler, crc32)) {
                throw new IOException("Invalid checksum for extra header field");
            }
        }

        return flags;
    }

    private int readChecksum(Checksum csum) throws IOException {
        if (csum == null)
            return 0;
        // LOG.info("Reading checksum " + csum);
        return readInt(false);
    }

    private void testChecksum(Checksum csum, int value, byte[] data, int off, int len) throws IOException {
        if (csum == null)
            return;
        csum.reset();
        csum.update(data, off, len);
        if (value != (int) csum.getValue())
            throw new IOException("Checksum failure: " + "Expected " + Integer.toHexString(value) + "; got "
                    + Long.toHexString(csum.getValue()));
    }

    @Override
    protected boolean readBlock() throws IOException {
        // logState("Before readBlock");
        if (eof)
            return false;
        int outputBufferLength = readInt(false);
        if (outputBufferLength == 0) {
            // logState("After empty readBlock");
            eof = true;
            return false;
        }
        setOutputBufferSize(outputBufferLength);
        int inputBufferLength = readInt(false);
        setInputBufferSize(inputBufferLength);
        int v_adler32_d = readChecksum(c_adler32_d);
        int v_crc32_d = readChecksum(c_crc32_d);
        // LOG.info("outputBufferLength=" + outputBufferLength + "; inputBufferLength=" + inputBufferLength);
        if (outputBufferLength == inputBufferLength) {
            outputBufferPos = 0;
            outputBufferLen.value = outputBufferLength;
            readBytes(outputBuffer, 0, outputBufferLength);
            testChecksum(c_adler32_d, v_adler32_d, outputBuffer, 0, outputBufferLength);
            testChecksum(c_crc32_d, v_crc32_d, outputBuffer, 0, outputBufferLength);
            // logState("After uncompressed readBlock");
            return true;
        }
        int v_adler32_c = readChecksum(c_adler32_c);
        int v_crc32_c = readChecksum(c_crc32_c);
        readBytes(inputBuffer, 0, inputBufferLength);
        testChecksum(c_adler32_c, v_adler32_c, inputBuffer, 0, inputBufferLength);
        testChecksum(c_crc32_c, v_crc32_c, inputBuffer, 0, inputBufferLength);
        decompress(outputBufferLength, inputBufferLength);
        testChecksum(c_adler32_d, v_adler32_d, outputBuffer, 0, outputBufferLength);
        testChecksum(c_crc32_d, v_crc32_d, outputBuffer, 0, outputBufferLength);
        // logState("After compressed readBlock");
        return true;
    }
}