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