/**
* Copyright (c) 2011-2012, Thilo Planz. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package v7db.files;
import static java.util.zip.Deflater.BEST_COMPRESSION;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.Inflater;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Compression {
// minimum overhead: 10 byte header, 8 byte trailer
static final int GZIP_STORAGE_OVERHEAD = 18;
static Logger log = LoggerFactory.getLogger(Compression.class);
/**
* @return 0, if the "deflated" data fills the whole output array
*/
static int deflate(byte[] data, int off, int len, byte[] out) {
Deflater deflater = new Deflater(BEST_COMPRESSION, true);
deflater.setInput(data, off, len);
deflater.finish();
int size = deflater.deflate(out);
if (size == 0 || size == out.length)
return 0;
return size;
}
/**
* @return null, if the "gzipped" data is larger than the input (or there
* has been an exception)
*/
public static byte[] gzip(byte[] data, int off, int len) {
if (len < GZIP_STORAGE_OVERHEAD)
return null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(data.length);
GZIPOutputStream gz = new GZIPOutputStream(baos);
gz.write(data, off, len);
gz.close();
if (baos.size() >= len)
return null;
return baos.toByteArray();
} catch (Exception e) {
log.error("failed to gzip byte array", e);
return null;
}
}
/**
* assumes that the result buffer has exactly the needed size
*
* @throws DataFormatException
*/
static void inflate(byte[] data, int off, int len, byte[] out)
throws DataFormatException {
Inflater inflater = new Inflater(true);
inflater.setInput(data, off, len);
int size = inflater.inflate(out);
if (size != out.length)
throw new DataFormatException("unexpected size of deflated data: "
+ size + " instead of " + out.length);
}
static void gunzip(InputStream in, OutputStream out) throws IOException {
IOUtils.copy(new GZIPInputStream(in), out);
}
/**
* input must be in zip format, i.e. first a local header, and then the data
* segment.
*
*/
static InputStream unzip(InputStream in) throws IOException {
ZipInputStream zip = new ZipInputStream(in);
zip.getNextEntry();
return zip;
}
/**
* @return the compressed file (delete after use), or null, if the
* "compressed" data is bigger than the input (or if there was an
* exception)
*/
static File gzip(File data) {
File file = null;
try {
file = File.createTempFile(data.getName(), ".gz");
OutputStream out = new GZIPOutputStream(new FileOutputStream(file));
long size = FileUtils.copyFile(data, out);
out.close();
long cSize = file.length();
if (cSize < size) {
return file;
}
file.delete();
return null;
} catch (Exception e) {
log.error("failed to gzip file " + data, e);
if (file != null)
file.delete();
return null;
}
}
}