/*
* 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.OutputStream;
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;
import org.apache.hadoop.io.DataOutputBuffer;
/**
*
* @author shevek
*/
public class LzopOutputStream extends LzoOutputStream {
private static final Log LOG = LogFactory.getLog(LzopOutputStream.class);
private final long 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 closed = false;
/**
* Constructs a new LzopOutputStream.
*
* I recommend limiting flags to the following unless you REALLY know what
* you are doing:
* <ul>
* <li>{@link LzopConstants#F_ADLER32_C}</li>
* <li>{@link LzopConstants#F_ADLER32_D}</li>
* <li>{@link LzopConstants#F_CRC32_C}</li>
* <li>{@link LzopConstants#F_CRC32_D}</li>
* </ul>
*/
public LzopOutputStream(OutputStream out, LzoCompressor compressor, int inputBufferSize, long flags) throws IOException {
super(out, compressor, inputBufferSize);
this.flags = flags;
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();
writeLzopHeader();
}
public LzopOutputStream(OutputStream out, LzoCompressor compressor, int inputBufferSize) throws IOException {
this(out, compressor, inputBufferSize, 0L);
}
public LzopOutputStream(OutputStream out, LzoCompressor compressor) throws IOException {
this(out, compressor, 256 * 1024);
}
/**
* Writes an lzop-compatible header to the OutputStream provided.
*/
protected void writeLzopHeader() throws IOException {
DataOutputBuffer dob = new DataOutputBuffer();
try {
dob.writeShort(LzopConstants.LZOP_VERSION);
dob.writeShort(LzoVersion.LZO_LIBRARY_VERSION);
dob.writeShort(LzopConstants.LZOP_COMPAT_VERSION);
switch (getAlgorithm()) {
case LZO1X:
// case LZO1X_1:
dob.writeByte(LzopConstants.M_LZO1X_1);
dob.writeByte(5);
break;
/*
case LZO1X_15:
dob.writeByte(LzopConstants.M_LZO1X_1_15);
dob.writeByte(1);
break;
case LZO1X_999:
dob.writeByte(LzopConstants.M_LZO1X_999);
dob.writeByte(9);
break;
*/
default:
throw new IOException("Incompatible lzop algorithm " + getAlgorithm());
}
long mask = LzopConstants.F_ADLER32_C | LzopConstants.F_ADLER32_D;
mask = mask | LzopConstants.F_CRC32_C | LzopConstants.F_CRC32_D;
dob.writeInt((int) (flags & mask & 0xFFFFFFFF)); // all flags 0
dob.writeInt(33188); // mode
dob.writeInt((int) (System.currentTimeMillis() / 1000)); // mtime
dob.writeInt(0); // gmtdiff ignored
dob.writeByte(0); // no filename
Adler32 headerChecksum = new Adler32();
headerChecksum.update(dob.getData(), 0, dob.getLength());
int hc = (int) headerChecksum.getValue();
dob.writeInt(hc);
out.write(LzopConstants.LZOP_MAGIC);
out.write(dob.getData(), 0, dob.getLength());
} finally {
dob.close();
}
}
private void writeChecksum(Checksum csum, byte[] data, int off, int len) throws IOException {
if (csum == null)
return;
csum.reset();
csum.update(data, off, len);
long value = csum.getValue();
// LOG.info("Writing checksum " + csum);
writeInt((int) (value & 0xFFFFFFFF));
}
@Override
protected void writeBlock(byte[] inputData, int inputPos, int inputLen, byte[] outputData, int outputPos, int outputLen) throws IOException {
// LOG.info("inputLen=" + inputLen + "; outputLen=" + outputLen);
writeInt(inputLen);
if (outputLen < inputLen)
writeInt(outputLen);
else
writeInt(inputLen);
// This is where we put checksums, if any.
writeChecksum(c_adler32_d, inputData, inputPos, inputLen);
writeChecksum(c_crc32_d, inputData, inputPos, inputLen);
if (outputLen < inputLen) {
writeChecksum(c_adler32_c, outputData, outputPos, outputLen);
writeChecksum(c_crc32_c, outputData, outputPos, outputLen);
}
if (outputLen < inputLen)
out.write(outputData, outputPos, outputLen);
else
out.write(inputData, inputPos, inputLen);
}
/**
* Writes a null word to the underlying output stream, then closes it.
*/
@Override
public void close() throws IOException {
if (!closed) {
flush();
out.write(new byte[]{0, 0, 0, 0});
super.close();
closed = true;
}
}
}