/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.process.protocol; import java.io.IOException; import java.io.InputStream; import org.jboss.as.process.logging.ProcessLogger; import org.jboss.marshalling.ByteInput; import org.jboss.marshalling.Marshalling; /** * Byte input implementation that reads bytes in chunks. Each chunk is started with a {@code CHUNK_START} header followed * by the length of the chunk. At the end of all the chunks it will run into a {@code END} byte, which will appear as the end * of the stream. This is used when you need to ensure a consumer of the input can not read more than necessary. This is handy * if the consumer of the stream is prone to over-buffering. Note this will only work for byte streams that were written using a * {@link ChunkyByteOutput}. * * @author John Bailey */ public class ChunkyByteInput extends InputStream implements ByteInput { public static final int CHUNK_START = 0x98; public static final int END = 0x99; private ByteInput input; private int remaining = 0; private boolean finished; public ChunkyByteInput(final ByteInput byteInput) { input = byteInput; } public ChunkyByteInput(final InputStream inputStream) { input = Marshalling.createByteInput(inputStream); } public ChunkyByteInput(final InputStream inputStream, final int remaining) { input = Marshalling.createByteInput(inputStream); this.remaining = remaining; } /** * {@inheritDoc} */ public int read() throws IOException { if (remaining == 0) { startChunk(); } if (remaining < 1) { return remaining; } this.remaining--; return input.read(); } /** * {@inheritDoc} */ public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /** * {@inheritDoc} */ public int read(final byte[] b, int off, int len) throws IOException { int ret = 0; while (len != 0) { if (remaining == 0) { startChunk(); } if (remaining < 1) { return ret; } int toRead = Math.min(len, remaining); int read = input.read(b, off, toRead); this.remaining -= read; len -= read; off += read; ret += read; if (read < toRead) { return ret; } } return ret; } /** * {@inheritDoc} */ public long skip(final long n) throws IOException { if (remaining == 0) { startChunk(); } if (remaining < 1) { return 0; } final long toSkip = n < remaining ? n : remaining; final long ret = input.skip(toSkip); this.remaining = remaining - (int) ret; return ret; } public int available() throws IOException { return remaining; } public void close() throws IOException { // Don't close the underlying input while (!finished) { final int current = input.read(); switch (current) { case -1: return; case END: finished = true; break; } } } private void startChunk() throws IOException { final int current = input.read(); switch (current) { case -1: remaining = -1; break; case CHUNK_START: remaining = readInt(); break; case END: remaining = -1; finished = true; break; default: throw ProcessLogger.ROOT_LOGGER.invalidStartChunk(current); } } private int readInt() throws IOException { return input.read() << 24 | (input.read() & 0xff) << 16 | (input.read() & 0xff) << 8 | (input.read() & 0xff); } }