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     *           &lt; 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     *           &lt; 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 &lt; 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     *           &lt; 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     *           &lt; 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     *           &lt; 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     *           &lt; 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     *           &lt; 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     *           &lt; 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     *           &lt; 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}