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