/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.facebook.infrastructure.io; import org.apache.commons.lang.ArrayUtils; import java.io.*; import java.nio.ByteBuffer; import java.util.Random; /** * An implementation of the DataInputStream interface. This instance is completely thread * unsafe. * * Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com ) */ public final class DataInputBuffer extends DataInputStream { /* * This is a clone of the ByteArrayInputStream class w/o any * method being synchronized. */ public static class FastByteArrayInputStream extends InputStream { /** * An array of bytes that was provided by the creator of the stream. * Elements <code>buf[0]</code> through <code>buf[count-1]</code> are * the only bytes that can ever be read from the stream; element * <code>buf[pos]</code> is the next byte to be read. */ protected byte buf[]; /** * The index of the next character to read from the input stream buffer. * This value should always be nonnegative and not larger than the value of * <code>count</code>. The next byte to be read from the input stream * buffer will be <code>buf[pos]</code>. */ protected int pos; /** * The currently marked position in the stream. ByteArrayInputStream objects * are marked at position zero by default when constructed. They may be * marked at another position within the buffer by the <code>mark()</code> * method. The current buffer position is set to this point by the * <code>reset()</code> method. * <p> * If no mark has been set, then the value of mark is the offset passed to * the constructor (or 0 if the offset was not supplied). * * @since JDK1.1 */ protected int mark = 0; /** * The index one greater than the last valid character in the input stream * buffer. This value should always be nonnegative and not larger than the * length of <code>buf</code>. It is one greater than the position of the * last byte within <code>buf</code> that can ever be read from the input * stream buffer. */ protected int count; public FastByteArrayInputStream() { buf = ArrayUtils.EMPTY_BYTE_ARRAY; } /** * Creates a <code>ByteArrayInputStream</code> so that it uses * <code>buf</code> as its buffer array. The buffer array is not copied. * The initial value of <code>pos</code> is <code>0</code> and the * initial value of <code>count</code> is the length of <code>buf</code>. * * @param buf * the input buffer. */ public FastByteArrayInputStream(byte buf[]) { this.buf = buf; this.pos = 0; this.count = buf.length; } /** * Creates <code>ByteArrayInputStream</code> that uses <code>buf</code> * as its buffer array. The initial value of <code>pos</code> is * <code>offset</code> and the initial value of <code>count</code> is * the minimum of <code>offset+length</code> and <code>buf.length</code>. * The buffer array is not copied. The buffer's mark is set to the specified * offset. * * @param buf * the input buffer. * @param offset * the offset in the buffer of the first byte to read. * @param length * the maximum number of bytes to read from the buffer. */ public FastByteArrayInputStream(byte buf[], int offset, int length) { this.buf = buf; this.pos = offset; this.count = Math.min(offset + length, buf.length); this.mark = offset; } public final void setBytes(byte[] bytes) { buf = bytes; pos = 0; count = bytes.length; } /** * Reads the next byte of data from this input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the * stream has been reached, the value <code>-1</code> is returned. * <p> * This <code>read</code> method cannot block. * * @return the next byte of data, or <code>-1</code> if the end of the * stream has been reached. */ public final int read() { return (pos < count) ? ( buf[pos++] & 0xFF ) : -1; } /** * Reads up to <code>len</code> bytes of data into an array of bytes from * this input stream. If <code>pos</code> equals <code>count</code>, * then <code>-1</code> is returned to indicate end of file. Otherwise, * the number <code>k</code> of bytes read is equal to the smaller of * <code>len</code> and <code>count-pos</code>. If <code>k</code> is * positive, then bytes <code>buf[pos]</code> through * <code>buf[pos+k-1]</code> are copied into <code>b[off]</code> through * <code>b[off+k-1]</code> in the manner performed by * <code>System.arraycopy</code>. The value <code>k</code> is added * into <code>pos</code> and <code>k</code> is returned. * <p> * This <code>read</code> method cannot block. * * @param b * the buffer into which the data is read. * @param off * the start offset in the destination array <code>b</code> * @param len * the maximum number of bytes read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of the * stream has been reached. * @exception NullPointerException * If <code>b</code> is <code>null</code>. * @exception IndexOutOfBoundsException * If <code>off</code> is negative, <code>len</code> is * negative, or <code>len</code> is greater than * <code>b.length - off</code> */ public final int read(byte b[], int off, int len) { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } if (pos >= count) { return -1; } if (pos + len > count) { len = count - pos; } if (len <= 0) { return 0; } System.arraycopy(buf, pos, b, off, len); pos += len; return len; } /** * Skips <code>n</code> bytes of input from this input stream. Fewer bytes * might be skipped if the end of the input stream is reached. The actual * number <code>k</code> of bytes to be skipped is equal to the smaller of * <code>n</code> and <code>count-pos</code>. The value <code>k</code> * is added into <code>pos</code> and <code>k</code> is returned. * * @param n * the number of bytes to be skipped. * @return the actual number of bytes skipped. */ public final long skip(long n) { if (pos + n > count) { n = count - pos; } if (n < 0) { return 0; } pos += n; return n; } /** * Returns the number of remaining bytes that can be read (or skipped over) * from this input stream. * <p> * The value returned is <code>count - pos</code>, which is the * number of bytes remaining to be read from the input buffer. * * @return the number of remaining bytes that can be read (or skipped over) * from this input stream without blocking. */ public final int available() { return count - pos; } /** * Tests if this <code>InputStream</code> supports mark/reset. The * <code>markSupported</code> method of <code>ByteArrayInputStream</code> * always returns <code>true</code>. * * @since JDK1.1 */ public final boolean markSupported() { return true; } /** * Set the current marked position in the stream. ByteArrayInputStream * objects are marked at position zero by default when constructed. They may * be marked at another position within the buffer by this method. * <p> * If no mark has been set, then the value of the mark is the offset passed * to the constructor (or 0 if the offset was not supplied). * * <p> * Note: The <code>readAheadLimit</code> for this class has no meaning. * * @since JDK1.1 */ public final void mark(int readAheadLimit) { mark = pos; } /** * Resets the buffer to the marked position. The marked position is 0 unless * another position was marked or an offset was specified in the * constructor. */ public final void reset() { pos = mark; } /** * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in * this class can be called after the stream has been closed without * generating an <tt>IOException</tt>. * <p> */ public final void close() throws IOException { } } private static class Buffer extends FastByteArrayInputStream { public Buffer() { super(new byte[] {}); } public void reset(byte[] input, int start, int length) { this.buf = input; this.count = start + length; this.mark = start; this.pos = start; } public int getPosition() { return pos; } public int getLength() { return count; } } private Buffer buffer; /** Constructs a new empty buffer. */ public DataInputBuffer() { this(new Buffer()); } private DataInputBuffer(Buffer buffer) { super(buffer); this.buffer = buffer; } /** Resets the data that the buffer reads. */ public void reset(byte[] input, int length) { buffer.reset(input, 0, length); } /** Resets the data that the buffer reads. */ public void reset(byte[] input, int start, int length) { buffer.reset(input, start, length); } /** Returns the current position in the input. */ public int getPosition() { return buffer.getPosition(); } /** Returns the length of the input. */ public int getLength() { return buffer.getLength(); } }