/* * 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 legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * 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 legal-notices/CDDLv1_0.txt. * 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. * Portions copyright 2012 ForgeRock AS. */ package org.forgerock.opendj.ldap; /** * 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. *
* NOTE: any concurrent changes to the underlying byte sequence (if * mutable) may cause subsequent reads to overrun and fail. *
* This constructor is package private: construction must be performed using * {@link ByteSequence#asReader()}. * * @param sequence * The byte sequence to be read. */ ByteSequenceReader(final 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() { 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: * *
* src.get(b);
*
*
* Behaves in exactly the same way as the invocation:
*
*
* src.get(b, 0, b.length);
*
*
* @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(final byte[] b) {
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:
*
*
* src.get(b, offset, length);
*
*
* Has exactly the same effect as the loop:
*
*
* for (int i = offset; i < offset + length; i++)
* b[i] = src.get();
*
*
* 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(final byte[] b, final int offset, final int length) {
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() {
// 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}.
* * NOTE: 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(final int length) { 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}. *
* An invocation of this method of the form: * *
* src.getByteString(length);
*
*
* Has exactly the same effect as:
*
*
* src.getByteSequence(length).toByteString();
*
*
* NOTE: 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(final int length) {
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() {
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() {
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() {
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(final int length) {
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(final int pos) {
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.
* * An invocation of this method of the form: * *
* src.rewind();
*
*
* Has exactly the same effect as:
*
*
* src.position(0);
*
*/
public void rewind() {
position(0);
}
/**
* Skips the given number of bytes. Negative values are allowed.
* * An invocation of this method of the form: * *
* src.skip(length);
*
*
* Has exactly the same effect as:
*
*
* src.position(position() + length);
*
*
* @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(final int length) {
position(pos + length);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return sequence.toString();
}
}