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