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