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