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