/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
|
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
|
* add the following below this CDDL HEADER, with the fields enclosed
|
* by brackets "[]" replaced with your own identifying information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2009 Sun Microsystems, Inc.
|
*/
|
package org.opends.sdk;
|
|
|
|
|
/**
|
* An interface for iteratively reading date from a {@link ByteSequence}
|
* . {@code ByteSequenceReader} must be created using the associated
|
* {@code ByteSequence}'s {@code asReader()} method.
|
*/
|
public final class ByteSequenceReader
|
{
|
|
// The current position in the byte sequence.
|
private int pos = 0;
|
|
// The underlying byte sequence.
|
private final ByteSequence sequence;
|
|
|
|
/**
|
* Creates a new byte sequence reader whose source is the provided
|
* byte sequence.
|
* <p>
|
* <b>NOTE:</b> any concurrent changes to the underlying byte sequence
|
* (if mutable) may cause subsequent reads to overrun and fail.
|
* <p>
|
* This constructor is package private: construction must be performed
|
* using {@link ByteSequence#asReader()}.
|
*
|
* @param sequence
|
* The byte sequence to be read.
|
*/
|
ByteSequenceReader(ByteSequence sequence)
|
{
|
this.sequence = sequence;
|
}
|
|
|
|
/**
|
* Relative get method. Reads the byte at the current position.
|
*
|
* @return The byte at this reader's current position.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < 1}.
|
*/
|
public byte get() throws IndexOutOfBoundsException
|
{
|
final byte b = sequence.byteAt(pos);
|
pos++;
|
return b;
|
}
|
|
|
|
/**
|
* Relative bulk get method. This method transfers bytes from this
|
* reader into the given destination array. An invocation of this
|
* method of the form:
|
*
|
* <pre>
|
* src.get(b);
|
* </pre>
|
*
|
* Behaves in exactly the same way as the invocation:
|
*
|
* <pre>
|
* src.get(b, 0, b.length);
|
* </pre>
|
*
|
* @param b
|
* The byte array into which bytes are to be written.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < b.length}.
|
*/
|
public void get(byte[] b) throws IndexOutOfBoundsException
|
{
|
get(b, 0, b.length);
|
}
|
|
|
|
/**
|
* Relative bulk get method. Copies {@code length} bytes from this
|
* reader into the given array, starting at the current position of
|
* this reader and at the given {@code offset} in the array. The
|
* position of this reader is then incremented by {@code length}. In
|
* other words, an invocation of this method of the form:
|
*
|
* <pre>
|
* src.get(b, offset, length);
|
* </pre>
|
*
|
* Has exactly the same effect as the loop:
|
*
|
* <pre>
|
* for (int i = offset; i < offset + length; i++)
|
* b[i] = src.get();
|
* </pre>
|
*
|
* Except that it first checks that there are sufficient bytes in this
|
* buffer and it is potentially much more efficient.
|
*
|
* @param b
|
* The byte array into which bytes are to be written.
|
* @param offset
|
* The offset within the array of the first byte to be
|
* written; must be non-negative and no larger than {@code
|
* b.length}.
|
* @param length
|
* The number of bytes to be written to the given array; must
|
* be non-negative and no larger than {@code b.length} .
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < length}.
|
*/
|
public void get(byte[] b, int offset, int length)
|
throws IndexOutOfBoundsException
|
{
|
if (offset < 0 || length < 0 || offset + length > b.length
|
|| length > remaining())
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
sequence.subSequence(pos, pos + length).copyTo(b, offset);
|
pos += length;
|
}
|
|
|
|
/**
|
* Relative get method for reading a multi-byte BER length. Reads the
|
* next one to five bytes at this reader's current position, composing
|
* them into a integer value and then increments the position by the
|
* number of bytes read.
|
*
|
* @return The integer value representing the length at this reader's
|
* current position.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request.
|
*/
|
public int getBERLength() throws IndexOutOfBoundsException
|
{
|
// Make sure we have at least one byte to read.
|
int newPos = pos + 1;
|
if (newPos > sequence.length())
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
int length = sequence.byteAt(pos) & 0x7F;
|
if (length != sequence.byteAt(pos))
|
{
|
// Its a multi-byte length
|
final int numLengthBytes = length;
|
newPos = pos + 1 + numLengthBytes;
|
// Make sure we have the bytes needed
|
if (numLengthBytes > 4 || newPos > sequence.length())
|
{
|
// Shouldn't have more than 4 bytes
|
throw new IndexOutOfBoundsException();
|
}
|
|
length = 0x00;
|
for (int i = pos + 1; i < newPos; i++)
|
{
|
length = length << 8 | sequence.byteAt(i) & 0xFF;
|
}
|
}
|
|
pos = newPos;
|
return length;
|
}
|
|
|
|
/**
|
* Relative bulk get method. Returns a {@link ByteSequence} whose
|
* content is the next {@code length} bytes from this reader, starting
|
* at the current position of this reader. The position of this reader
|
* is then incremented by {@code length}.
|
* <p>
|
* <b>NOTE:</b> The value returned from this method should NEVER be
|
* cached as it prevents the contents of the underlying byte stream
|
* from being garbage collected.
|
*
|
* @param length
|
* The length of the byte sequence to be returned.
|
* @return The byte sequence whose content is the next {@code length}
|
* bytes from this reader.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < length}.
|
*/
|
public ByteSequence getByteSequence(int length)
|
throws IndexOutOfBoundsException
|
{
|
final int newPos = pos + length;
|
final ByteSequence subSequence = sequence.subSequence(pos, newPos);
|
pos = newPos;
|
return subSequence;
|
}
|
|
|
|
/**
|
* Relative bulk get method. Returns a {@link ByteString} whose
|
* content is the next {@code length} bytes from this reader, starting
|
* at the current position of this reader. The position of this reader
|
* is then incremented by {@code length}.
|
* <p>
|
* An invocation of this method of the form:
|
*
|
* <pre>
|
* src.getByteString(length);
|
* </pre>
|
*
|
* Has exactly the same effect as:
|
*
|
* <pre>
|
* src.getByteSequence(length).toByteString();
|
* </pre>
|
*
|
* <b>NOTE:</b> The value returned from this method should NEVER be
|
* cached as it prevents the contents of the underlying byte stream
|
* from being garbage collected.
|
*
|
* @param length
|
* The length of the byte string to be returned.
|
* @return The byte string whose content is the next {@code length}
|
* bytes from this reader.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < length}.
|
*/
|
public ByteString getByteString(int length)
|
throws IndexOutOfBoundsException
|
{
|
return getByteSequence(length).toByteString();
|
}
|
|
|
|
/**
|
* Relative get method for reading an integer value. Reads the next
|
* four bytes at this reader's current position, composing them into
|
* an integer value according to big-endian byte order, and then
|
* increments the position by four.
|
*
|
* @return The integer value at this reader's current position.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < 4}.
|
*/
|
public int getInt() throws IndexOutOfBoundsException
|
{
|
if (remaining() < 4)
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
int v = 0;
|
for (int i = 0; i < 4; i++)
|
{
|
v <<= 8;
|
v |= sequence.byteAt(pos++) & 0xFF;
|
}
|
|
return v;
|
}
|
|
|
|
/**
|
* Relative get method for reading a long value. Reads the next eight
|
* bytes at this reader's current position, composing them into a long
|
* value according to big-endian byte order, and then increments the
|
* position by eight.
|
*
|
* @return The long value at this reader's current position.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < 8}.
|
*/
|
public long getLong() throws IndexOutOfBoundsException
|
{
|
if (remaining() < 8)
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
long v = 0;
|
for (int i = 0; i < 8; i++)
|
{
|
v <<= 8;
|
v |= sequence.byteAt(pos++) & 0xFF;
|
}
|
|
return v;
|
}
|
|
|
|
/**
|
* Relative get method for reading an short value. Reads the next 2
|
* bytes at this reader's current position, composing them into an
|
* short value according to big-endian byte order, and then increments
|
* the position by two.
|
*
|
* @return The integer value at this reader's current position.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < 2}.
|
*/
|
public short getShort() throws IndexOutOfBoundsException
|
{
|
if (remaining() < 2)
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
short v = 0;
|
for (int i = 0; i < 2; i++)
|
{
|
v <<= 8;
|
v |= sequence.byteAt(pos++) & 0xFF;
|
}
|
|
return v;
|
}
|
|
|
|
/**
|
* Relative get method for reading a UTF-8 encoded string. Reads the
|
* next number of specified bytes at this reader's current position,
|
* decoding them into a string using UTF-8 and then increments the
|
* position by the number of bytes read. If UTF-8 decoding fails, the
|
* platform's default encoding will be used.
|
*
|
* @param length
|
* The number of bytes to read and decode.
|
* @return The string value at the reader's current position.
|
* @throws IndexOutOfBoundsException
|
* If there are fewer bytes remaining in this reader than
|
* are required to satisfy the request, that is, if {@code
|
* remaining() < length}.
|
*/
|
public String getString(int length) throws IndexOutOfBoundsException
|
{
|
if (remaining() < length)
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
final int newPos = pos + length;
|
final String str =
|
sequence.subSequence(pos, pos + length).toString();
|
pos = newPos;
|
return str;
|
}
|
|
|
|
/**
|
* Returns this reader's position.
|
*
|
* @return The position of this reader.
|
*/
|
public int position()
|
{
|
return pos;
|
}
|
|
|
|
/**
|
* Sets this reader's position.
|
*
|
* @param pos
|
* The new position value; must be non-negative and no larger
|
* than the length of the underlying byte sequence.
|
* @throws IndexOutOfBoundsException
|
* If the position is negative or larger than the length of
|
* the underlying byte sequence.
|
*/
|
public void position(int pos) throws IndexOutOfBoundsException
|
{
|
if (pos > sequence.length() || pos < 0)
|
{
|
throw new IndexOutOfBoundsException();
|
}
|
|
this.pos = pos;
|
}
|
|
|
|
/**
|
* Returns the number of bytes between the current position and the
|
* end of the underlying byte sequence.
|
*
|
* @return The number of bytes between the current position and the
|
* end of the underlying byte sequence.
|
*/
|
public int remaining()
|
{
|
return sequence.length() - pos;
|
}
|
|
|
|
/**
|
* Rewinds this reader's position to zero.
|
* <p>
|
* An invocation of this method of the form:
|
*
|
* <pre>
|
* src.rewind();
|
* </pre>
|
*
|
* Has exactly the same effect as:
|
*
|
* <pre>
|
* src.position(0);
|
* </pre>
|
*/
|
public void rewind()
|
{
|
position(0);
|
}
|
|
|
|
/**
|
* Skips the given number of bytes. Negative values are allowed.
|
* <p>
|
* An invocation of this method of the form:
|
*
|
* <pre>
|
* src.skip(length);
|
* </pre>
|
*
|
* Has exactly the same effect as:
|
*
|
* <pre>
|
* src.position(position() + length);
|
* </pre>
|
*
|
* @param length
|
* The number of bytes to skip.
|
* @throws IndexOutOfBoundsException
|
* If the new position is less than 0 or greater than the
|
* length of the underlying byte sequence.
|
*/
|
public void skip(int length) throws IndexOutOfBoundsException
|
{
|
position(pos + length);
|
}
|
|
|
|
/**
|
* {@inheritDoc}
|
*/
|
@Override
|
public String toString()
|
{
|
return sequence.toString();
|
}
|
}
|