/**
* 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();
}
}