/* * 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. *

* 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(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: * *

   * 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(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: * *
   * 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(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}. *

* 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(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}. *

* 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(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. *

* 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(int length) throws IndexOutOfBoundsException { position(pos + length); } /** * {@inheritDoc} */ @Override public String toString() { return sequence.toString(); } }