/******************************************************************************* * Copyright (c) 2007, 2012 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.compressions.impl; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.channels.ReadableByteChannel; /** * @author Hannu Niemistö * @author Tuukka Lehtonen */ public abstract class DecompressingInputStream extends InputStream { protected InputStream stream; protected ReadableByteChannel channel; protected ByteBuffer header; protected IntBuffer intHeader; protected ByteBuffer compressed; protected ByteBuffer uncompressed; protected byte[] compressedArray; public DecompressingInputStream(File file) throws FileNotFoundException { this(new FileInputStream(file)); } public DecompressingInputStream(FileInputStream stream) { this(stream, stream.getChannel()); } public DecompressingInputStream(InputStream stream) { this(stream, null); } public DecompressingInputStream(InputStream stream, ReadableByteChannel channel) { this.stream = stream; this.channel = channel; header = ByteBuffer.allocate(8); header.order(ByteOrder.LITTLE_ENDIAN); intHeader = header.asIntBuffer(); } @Override public int read() throws IOException { if(uncompressed == null || uncompressed.remaining() == 0) if(!fillBuffer()) return -1; return uncompressed.get(); } @Override public int read(byte[] b, int off, int len) throws IOException { int bytes = 0; while(len > 0) { if(uncompressed == null || uncompressed.remaining() == 0) { if(!fillBuffer()) { return bytes == 0 ? -1 : bytes; } } int s = Math.min(len, uncompressed.remaining()); uncompressed.get(b, off, s); off += s; len -= s; bytes += s; } return bytes; } @Override public void close() throws IOException { if (channel != null) { channel.close(); channel = null; } if (stream != null) { stream.close(); stream = null; } } private boolean fillBuffer() throws IOException { header.position(0); if (channel != null) { if (Channels.read(channel, header) < 8) return false; } else { if (Channels.readStream(stream, header.array(), 8) < 8) return false; } intHeader.position(0); int compressedSize = intHeader.get(); int uncompressedSize = intHeader.get(); if(uncompressedSize == 0) return false; uncompressed = ensureBufferSize(uncompressed, uncompressedSize); compressed = ensureBufferSize(compressed, compressedSize); compressed.position(0); compressed.limit(compressedSize); if (channel != null) { if (Channels.read(channel, compressed) < compressedSize) return false; } else { if (compressedArray == null || compressedArray.length < compressedSize) compressedArray = new byte[compressedSize]; if (Channels.readStream(stream, compressedArray, compressedSize) < 8) return false; compressed.put(compressedArray, 0, compressedSize); } decompress(compressed, 0, compressedSize, uncompressed, 0, uncompressedSize); uncompressed.position(0); uncompressed.limit(uncompressedSize); return true; } private ByteBuffer ensureBufferSize(ByteBuffer buffer, int minCapacity) { int oldCapacity = buffer != null ? buffer.capacity() : 0; if (buffer == null || oldCapacity < minCapacity) { int newCapacity = grow(oldCapacity, minCapacity); //System.out.println("ensureBufferSize(" + oldCapacity + ", " + minCapacity + "), new capacity " + newCapacity); buffer = allocateBuffer(newCapacity); } return buffer; } protected ByteBuffer allocateBuffer(int capacity) { return ByteBuffer.allocateDirect(capacity); } /** * @param oldCapacity current capacity of a buffer * @param minCapacity * @return new capacity */ private static int grow(int oldCapacity, int minCapacity) { // overflow-conscious code int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity < 0) // overflow throw new OutOfMemoryError(); return newCapacity; } public abstract void decompress(ByteBuffer compressed, int compressedOffset, int compressedSize, ByteBuffer uncompressed, int uncompressedOffset, int uncompressedSize) throws IOException; }