001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2009 Sun Microsystems, Inc. 015 * Portions copyright 2012-2016 ForgeRock AS. 016 */ 017package org.forgerock.opendj.ldap; 018 019import static com.forgerock.opendj.ldap.CoreMessages.*; 020 021import java.io.IOException; 022import java.io.InputStream; 023 024import com.forgerock.opendj.util.PackedLong; 025 026/** 027 * An interface for iteratively reading data from a {@link ByteSequence} . 028 * {@code ByteSequenceReader} must be created using the associated 029 * {@code ByteSequence}'s {@code asReader()} method. 030 */ 031public final class ByteSequenceReader { 032 033 /** The current position in the byte sequence. */ 034 private int pos; 035 036 /** The underlying byte sequence. */ 037 private final ByteSequence sequence; 038 039 /** 040 * The lazily allocated input stream view of this reader. Synchronization is not necessary because the stream is 041 * stateless and race conditions can be tolerated. 042 */ 043 private InputStream inputStream; 044 045 /** 046 * Creates a new byte sequence reader whose source is the provided byte 047 * sequence. 048 * <p> 049 * <b>NOTE:</b> any concurrent changes to the underlying byte sequence (if 050 * mutable) may cause subsequent reads to overrun and fail. 051 * <p> 052 * This constructor is package private: construction must be performed using 053 * {@link ByteSequence#asReader()}. 054 * 055 * @param sequence 056 * The byte sequence to be read. 057 */ 058 ByteSequenceReader(final ByteSequence sequence) { 059 this.sequence = sequence; 060 } 061 062 /** 063 * Relative read method. Reads the byte at the current position. 064 * 065 * @return The byte at this reader's current position. 066 * @throws IndexOutOfBoundsException 067 * If there are fewer bytes remaining in this reader than are 068 * required to satisfy the request, that is, if 069 * {@code remaining() < 1}. 070 */ 071 public byte readByte() { 072 final byte b = sequence.byteAt(pos); 073 pos++; 074 return b; 075 } 076 077 /** 078 * Relative bulk read method. This method transfers bytes from this reader 079 * into the given destination array. An invocation of this method of the 080 * form: 081 * 082 * <pre> 083 * src.readBytes(b); 084 * </pre> 085 * 086 * Behaves in exactly the same way as the invocation: 087 * 088 * <pre> 089 * src.readBytes(b, 0, b.length); 090 * </pre> 091 * 092 * @param b 093 * The byte array into which bytes are to be written. 094 * @throws IndexOutOfBoundsException 095 * If there are fewer bytes remaining in this reader than are 096 * required to satisfy the request, that is, if 097 * {@code remaining() < b.length}. 098 */ 099 public void readBytes(final byte[] b) { 100 readBytes(b, 0, b.length); 101 } 102 103 /** 104 * Relative bulk read method. Copies {@code length} bytes from this reader 105 * into the given array, starting at the current position of this reader and 106 * at the given {@code offset} in the array. The position of this reader is 107 * then incremented by {@code length}. In other words, an invocation of this 108 * method of the form: 109 * 110 * <pre> 111 * src.read(b, offset, length); 112 * </pre> 113 * 114 * Has exactly the same effect as the loop: 115 * 116 * <pre> 117 * for (int i = offset; i < offset + length; i++) 118 * b[i] = src.readByte(); 119 * </pre> 120 * 121 * Except that it first checks that there are sufficient bytes in this 122 * buffer and it is potentially much more efficient. 123 * 124 * @param b 125 * The byte array into which bytes are to be written. 126 * @param offset 127 * The offset within the array of the first byte to be written; 128 * must be non-negative and no larger than {@code b.length}. 129 * @param length 130 * The number of bytes to be written to the given array; must be 131 * non-negative and no larger than {@code b.length} . 132 * @throws IndexOutOfBoundsException 133 * If there are fewer bytes remaining in this reader than are 134 * required to satisfy the request, that is, if 135 * {@code remaining() < length}. 136 */ 137 public void readBytes(final byte[] b, final int offset, final int length) { 138 if (offset < 0 || length < 0 || offset + length > b.length || length > remaining()) { 139 throw new IndexOutOfBoundsException(); 140 } 141 142 sequence.subSequence(pos, pos + length).copyTo(b, offset); 143 pos += length; 144 } 145 146 /** 147 * Relative read method for reading a multi-byte BER length. Reads the next 148 * one to five bytes at this reader's current position, composing them into 149 * a integer value and then increments the position by the number of bytes 150 * read. 151 * 152 * @return The integer value representing the length at this reader's 153 * current position. 154 * @throws IndexOutOfBoundsException 155 * If there are fewer bytes remaining in this reader than are 156 * required to satisfy the request. 157 */ 158 public int readBERLength() { 159 // Make sure we have at least one byte to read. 160 int newPos = pos + 1; 161 if (newPos > sequence.length()) { 162 throw new IndexOutOfBoundsException(); 163 } 164 165 int length = sequence.byteAt(pos) & 0x7F; 166 if (length != sequence.byteAt(pos)) { 167 // Its a multi-byte length 168 final int numLengthBytes = length; 169 newPos = pos + 1 + numLengthBytes; 170 // Make sure we have the bytes needed 171 if (numLengthBytes > 4 || newPos > sequence.length()) { 172 // Shouldn't have more than 4 bytes 173 throw new IndexOutOfBoundsException(); 174 } 175 176 length = 0x00; 177 for (int i = pos + 1; i < newPos; i++) { 178 length = length << 8 | sequence.byteAt(i) & 0xFF; 179 } 180 } 181 182 pos = newPos; 183 return length; 184 } 185 186 /** 187 * Relative bulk read method. Returns a {@link ByteSequence} whose content is 188 * the next {@code length} bytes from this reader, starting at the current 189 * position of this reader. The position of this reader is then incremented 190 * by {@code length}. 191 * <p> 192 * <b>NOTE:</b> The value returned from this method should NEVER be cached 193 * as it prevents the contents of the underlying byte stream from being 194 * garbage collected. 195 * 196 * @param length 197 * The length of the byte sequence to be returned. 198 * @return The byte sequence whose content is the next {@code length} bytes 199 * from this reader. 200 * @throws IndexOutOfBoundsException 201 * If there are fewer bytes remaining in this reader than are 202 * required to satisfy the request, that is, if 203 * {@code remaining() < length}. 204 */ 205 public ByteSequence readByteSequence(final int length) { 206 final int newPos = pos + length; 207 final ByteSequence subSequence = sequence.subSequence(pos, newPos); 208 pos = newPos; 209 return subSequence; 210 } 211 212 /** 213 * Relative bulk read method. Returns a {@link ByteString} whose content is 214 * the next {@code length} bytes from this reader, starting at the current 215 * position of this reader. The position of this reader is then incremented 216 * by {@code length}. 217 * <p> 218 * An invocation of this method of the form: 219 * 220 * <pre> 221 * src.readByteString(length); 222 * </pre> 223 * 224 * Has exactly the same effect as: 225 * 226 * <pre> 227 * src.readByteSequence(length).toByteString(); 228 * </pre> 229 * 230 * <b>NOTE:</b> The value returned from this method should NEVER be cached 231 * as it prevents the contents of the underlying byte stream from being 232 * garbage collected. 233 * 234 * @param length 235 * The length of the byte string to be returned. 236 * @return The byte string whose content is the next {@code length} bytes 237 * from this reader. 238 * @throws IndexOutOfBoundsException 239 * If there are fewer bytes remaining in this reader than are 240 * required to satisfy the request, that is, if 241 * {@code remaining() < length}. 242 */ 243 public ByteString readByteString(final int length) { 244 return readByteSequence(length).toByteString(); 245 } 246 247 /** 248 * Relative read method for reading an integer value. Reads the next four 249 * bytes at this reader's current position, composing them into an integer 250 * value according to big-endian byte order, and then increments the 251 * position by four. 252 * 253 * @return The integer value at this reader's current position. 254 * @throws IndexOutOfBoundsException 255 * If there are fewer bytes remaining in this reader than are 256 * required to satisfy the request, that is, if 257 * {@code remaining() < 4}. 258 */ 259 public int readInt() { 260 if (remaining() < 4) { 261 throw new IndexOutOfBoundsException(); 262 } 263 264 int v = 0; 265 for (int i = 0; i < 4; i++) { 266 v <<= 8; 267 v |= sequence.byteAt(pos++) & 0xFF; 268 } 269 270 return v; 271 } 272 273 /** 274 * Relative read method for reading a long value. Reads the next eight bytes 275 * at this reader's current position, composing them into a long value 276 * according to big-endian byte order, and then increments the position by 277 * eight. 278 * 279 * @return The long value at this reader's current position. 280 * @throws IndexOutOfBoundsException 281 * If there are fewer bytes remaining in this reader than are 282 * required to satisfy the request, that is, if 283 * {@code remaining() < 8}. 284 */ 285 public long readLong() { 286 if (remaining() < 8) { 287 throw new IndexOutOfBoundsException(); 288 } 289 290 long v = 0; 291 for (int i = 0; i < 8; i++) { 292 v <<= 8; 293 v |= sequence.byteAt(pos++) & 0xFF; 294 } 295 296 return v; 297 } 298 299 /** 300 * Relative read method for reading a compacted long value. 301 * Compaction allows to reduce number of bytes needed to hold long types 302 * depending on its value (i.e: if value < 128, value will be encoded using one byte only). 303 * Reads the next bytes at this reader's current position, composing them into a long value 304 * according to big-endian byte order, and then increments the position by the size of the 305 * encoded long. 306 * Note that the maximum value of a compact long is 2^56. 307 * 308 * @return The long value at this reader's current position. 309 * @throws IndexOutOfBoundsException 310 * If there are fewer bytes remaining in this reader than are 311 * required to satisfy the request. 312 */ 313 public long readCompactUnsignedLong() { 314 try { 315 return PackedLong.readCompactUnsignedLong(asInputStream()); 316 } catch (IOException e) { 317 throw new IllegalStateException(e); 318 } 319 } 320 321 /** 322 * Relative read method for reading a compacted int value. 323 * Compaction allows to reduce number of bytes needed to hold int types 324 * depending on its value (i.e: if value < 128, value will be encoded using one byte only). 325 * Reads the next bytes at this reader's current position, composing them into an int value 326 * according to big-endian byte order, and then increments the position by the size of the 327 * encoded int. 328 * 329 * @return The int value at this reader's current position. 330 * @throws IndexOutOfBoundsException 331 * If there are fewer bytes remaining in this reader than are 332 * required to satisfy the request. 333 */ 334 public int readCompactUnsignedInt() { 335 long l = readCompactUnsignedLong(); 336 if (l > Integer.MAX_VALUE) { 337 throw new IllegalStateException(ERR_INVALID_COMPACTED_UNSIGNED_INT.get(Integer.MAX_VALUE, l).toString()); 338 } 339 return (int) l; 340 } 341 342 /** 343 * Relative read method for reading an short value. Reads the next 2 bytes at 344 * this reader's current position, composing them into an short value 345 * according to big-endian byte order, and then increments the position by 346 * two. 347 * 348 * @return The integer value at this reader's current position. 349 * @throws IndexOutOfBoundsException 350 * If there are fewer bytes remaining in this reader than are 351 * required to satisfy the request, that is, if 352 * {@code remaining() < 2}. 353 */ 354 public short readShort() { 355 if (remaining() < 2) { 356 throw new IndexOutOfBoundsException(); 357 } 358 359 short v = 0; 360 for (int i = 0; i < 2; i++) { 361 v <<= 8; 362 v |= sequence.byteAt(pos++) & 0xFF; 363 } 364 365 return v; 366 } 367 368 /** 369 * Relative read method for reading a UTF-8 encoded string. Reads the next 370 * number of specified bytes at this reader's current position, decoding 371 * them into a string using UTF-8 and then increments the position by the 372 * number of bytes read. If UTF-8 decoding fails, the platform's default 373 * encoding will be used. 374 * 375 * @param length 376 * The number of bytes to read and decode. 377 * @return The string value at the reader's current position. 378 * @throws IndexOutOfBoundsException 379 * If there are fewer bytes remaining in this reader than are 380 * required to satisfy the request, that is, if 381 * {@code remaining() < length}. 382 */ 383 public String readStringUtf8(final int length) { 384 if (remaining() < length) { 385 throw new IndexOutOfBoundsException(); 386 } 387 388 final int newPos = pos + length; 389 final String str = sequence.subSequence(pos, pos + length).toString(); 390 pos = newPos; 391 return str; 392 } 393 394 /** 395 * Returns this reader's position. 396 * 397 * @return The position of this reader. 398 */ 399 public int position() { 400 return pos; 401 } 402 403 /** 404 * Sets this reader's position. 405 * 406 * @param pos 407 * The new position value; must be non-negative and no larger 408 * than the length of the underlying byte sequence. 409 * @throws IndexOutOfBoundsException 410 * If the position is negative or larger than the length of the 411 * underlying byte sequence. 412 */ 413 public void position(final int pos) { 414 if (pos > sequence.length() || pos < 0) { 415 throw new IndexOutOfBoundsException(); 416 } 417 418 this.pos = pos; 419 } 420 421 /** 422 * Returns the number of bytes between the current position and the end of 423 * the underlying byte sequence. 424 * 425 * @return The number of bytes between the current position and the end of 426 * the underlying byte sequence. 427 */ 428 public int remaining() { 429 return sequence.length() - pos; 430 } 431 432 /** 433 * Rewinds this reader's position to zero. 434 * <p> 435 * An invocation of this method of the form: 436 * 437 * <pre> 438 * src.rewind(); 439 * </pre> 440 * 441 * Has exactly the same effect as: 442 * 443 * <pre> 444 * src.position(0); 445 * </pre> 446 */ 447 public void rewind() { 448 position(0); 449 } 450 451 /** 452 * Returns the byte situated at the current position. The byte is not 453 * consumed. 454 * 455 * @return the byte situated at the current position 456 * @throws IndexOutOfBoundsException 457 * If the position is negative or larger than the length of the 458 * underlying byte sequence. 459 */ 460 public byte peek() { 461 return sequence.byteAt(pos); 462 } 463 464 /** 465 * Returns the byte situated at the given offset from current position. The 466 * byte is not consumed. 467 * 468 * @param offset 469 * The offset where to look at from current position. 470 * @return the byte situated at the given offset from current position 471 * @throws IndexOutOfBoundsException 472 * If the position is negative or larger than the length of the 473 * underlying byte sequence. 474 */ 475 public byte peek(int offset) { 476 return sequence.byteAt(pos + offset); 477 } 478 479 /** 480 * Skips the given number of bytes. Negative values are allowed. 481 * <p> 482 * An invocation of this method of the form: 483 * 484 * <pre> 485 * src.skip(length); 486 * </pre> 487 * 488 * Has exactly the same effect as: 489 * 490 * <pre> 491 * src.position(position() + length); 492 * </pre> 493 * 494 * @param length 495 * The number of bytes to skip. 496 * @throws IndexOutOfBoundsException 497 * If the new position is less than 0 or greater than the length 498 * of the underlying byte sequence. 499 */ 500 public void skip(final int length) { 501 position(pos + length); 502 } 503 504 @Override 505 public String toString() { 506 return sequence.toString(); 507 } 508 509 /** 510 * Returns an {@link InputStream} from the current position in the sequence. 511 * There is only a single {@link InputStream} for a given ByteSequence, so 512 * multiple calls to {@code asInputStream()} will always return the same object. 513 * The returned {@code InputStream} does not support {@code mark()}. 514 * Calling {@code close()} does nothing. 515 * 516 * @return an {@link InputStream} from the current position in the sequence 517 */ 518 public InputStream asInputStream() { 519 if (inputStream == null) { 520 inputStream = new InputStream() { 521 @Override 522 public int read() throws IOException { 523 if (pos >= sequence.length()) { 524 return -1; 525 } 526 return sequence.byteAt(pos++) & 0xFF; 527 } 528 }; 529 } 530 return inputStream; 531 } 532}