/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.arakhne.afc.inputoutput.filetype; import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; /** This class defines is a buffered input stream which provides * additional reading functions which hare very useful for * a {@code MagicNumberStream}. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 14.0 * @see MagicNumberStream */ final class BufferedMagicNumberStream extends FilterInputStream { private static final int BUFFER_SIZE = 256; private byte[] buffer = new byte[0]; private int pos; /** * @param is is the input stream. */ BufferedMagicNumberStream(InputStream is) { super(is); this.pos = 0; } /** Replies the stream buffered by this object. * This buffered stream is the stream pass as parameter of * the constructor. * * @return the buffered stream. */ public InputStream getStream() { return this.in; } /** Replies the count of characters available for reading. */ private int ensureBuffer(int offset, int length) throws IOException { final int lastPos = offset + length - 1; final int desiredSize = ((lastPos / BUFFER_SIZE) + 1) * BUFFER_SIZE; final int currentSize = this.buffer.length; if (desiredSize > currentSize) { final byte[] readBuffer = new byte[desiredSize - currentSize]; final int count = this.in.read(readBuffer); if (count > 0) { final byte[] newBuffer = new byte[currentSize + count]; System.arraycopy(this.buffer, 0, newBuffer, 0, currentSize); System.arraycopy(readBuffer, 0, newBuffer, currentSize, count); this.buffer = newBuffer; } return (lastPos < this.buffer.length) ? length : length - (lastPos - this.buffer.length + 1); } return length; } /** Replies the bytes at the specified offset. * * @param offset is the position of the first byte to read. * @param length is the count of bytes to read. * @return the array of red bytes. * @throws IOException in case of problems */ public byte[] read(int offset, int length) throws IOException { if (ensureBuffer(offset, length) >= length) { final byte[] array = new byte[length]; System.arraycopy(this.buffer, offset, array, 0, length); this.pos = offset + length; return array; } throw new EOFException(); } /** Replies a byte at the specified offset. * * @param offset is the position of the byte to read. * @return the byte. * @throws IOException in case of problems */ public byte read(int offset) throws IOException { if (ensureBuffer(offset, 1) > 0) { this.pos = offset + 1; return this.buffer[offset]; } throw new EOFException(); } @Override public int read() throws IOException { if (ensureBuffer(this.pos, 1) > 0) { final int c = this.buffer[this.pos]; ++this.pos; return c; } return -1; } /** Replies the bytes until the next end of the first line (inclusive). * * @param offset is the position of the byte to read. * @return the bytes; or <code>null</code> if EOF * @throws IOException in case of problems */ public byte[] readLine(int offset) throws IOException { int lastIndex = -1; int localOffset = offset; int read; do { read = ensureBuffer(localOffset, BUFFER_SIZE); if (read <= 0) { // EOF this.pos = this.buffer.length; return null; } // NOT EOF, search for end of line final int end = localOffset + read; for (int idx = localOffset; (lastIndex == -1) && (idx < this.buffer.length) && (idx < end); ++idx) { if ((this.buffer[idx] == '\n') || (this.buffer[idx] == '\r')) { lastIndex = idx; } } localOffset += read; } while (lastIndex == -1); this.pos = lastIndex; return Arrays.copyOfRange(this.buffer, offset, lastIndex); } }