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 2010–2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2014 ForgeRock AS.
016 */
017
018package org.forgerock.openig.io;
019
020import java.io.IOException;
021
022/**
023 * Wraps a byte array with a stream that can branch to perform divergent reads.
024 */
025public class ByteArrayBranchingStream extends BranchingInputStream {
026    /** Branch that this was spawned from, or {@code null} if this is the trunk. */
027    private ByteArrayBranchingStream parent = null;
028
029    /** The index of the next byte to read from the byte array. */
030    private int position = 0;
031
032    /** The currently marked position in the stream. */
033    private int mark = -1;
034
035    /** The byte array to expose as the input stream. */
036    private byte[] data;
037
038    /**
039     * Constructs a new branching input stream that wraps a byte array.
040     *
041     * @param data byte array to wrap with the branching input stream.
042     */
043    public ByteArrayBranchingStream(byte[] data) {
044        this.data = data;
045    }
046
047    @Override
048    public ByteArrayBranchingStream branch() {
049        ByteArrayBranchingStream branch = new ByteArrayBranchingStream(data);
050        branch.position = this.position;
051        branch.parent = this;
052        return branch;
053    }
054
055    @Override
056    public ByteArrayBranchingStream parent() {
057        return parent;
058    }
059
060    /**
061     * Reads the next byte of data from the input stream.
062     *
063     * @return the next byte of data, or {@code -1} if the end of the stream is reached.
064     */
065    @Override
066    public synchronized int read() {
067        return (position < data.length ? data[position++] & 0xff : -1);
068    }
069
070    /**
071     * Reads some number of bytes from the input stream and stores them into the buffer
072     * array {@code b}.
073     *
074     * @param b the buffer into which the data is read.
075     * @return the total number of bytes read into the buffer, or {@code -1} is there is no more data because the
076     * end of the stream has been reached.
077     */
078    @Override
079    public int read(byte[] b) {
080        return read(b, 0, b.length);
081    }
082
083    /**
084     * Reads up to {@code len} bytes of data from the input stream into an array of bytes.
085     *
086     * @param b the buffer into which the data is read.
087     * @param off the start offset in array {@code b} at which the data is written.
088     * @param len the maximum number of bytes to read.
089     * @return the total number of bytes read into the buffer, or {@code -1} if there is no more data because the
090     * end of the stream has been reached.
091     */
092    @Override
093    public synchronized int read(byte[] b, int off, int len) {
094        if (off < 0 || len < 0 || len > b.length - off) {
095            throw new IndexOutOfBoundsException();
096        }
097        if (position >= data.length) {
098            // end of stream has been reached
099            return -1;
100        }
101        len = Math.min(len, data.length - position);
102        System.arraycopy(data, position, b, off, len);
103        position += len;
104        return len;
105    }
106
107    /**
108     * Skips over and discards {@code n} bytes of data from this input stream.
109     *
110     * @param n the number of bytes to be skipped.
111     * @return the actual number of bytes skipped.
112     */
113    @Override
114    public synchronized long skip(long n) {
115        if (n <= 0) {
116            return 0;
117        }
118        n = Math.min(n, data.length - position);
119        position += n;
120        return n;
121    }
122
123    /**
124     * Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without
125     * blocking by the next invocation of a method for this input stream.
126     *
127     * @return an estimate of the number of bytes that can be read.
128     */
129    @Override
130    public synchronized int available() {
131        return data.length - position;
132    }
133
134    /**
135     * Returns {@code true} unconditionally; mark and reset are supported.
136     *
137     * @return {@code true} unconditionally.
138     */
139    @Override
140    public boolean markSupported() {
141        return true;
142    }
143
144    /**
145     * Marks the current position in this input stream.
146     *
147     * @param readlimit the maximum limit of bytes that can be read before the mark position becomes invalid.
148     */
149    @Override
150    public void mark(int readlimit) {
151        mark = position;
152    }
153
154    /**
155     * Repositions this stream to the position at the time the {@code mark} method was last
156     * called on this input stream.
157     *
158     * @throws IOException if the position was not previously marked.
159     */
160    @Override
161    public synchronized void reset() throws IOException {
162        if (mark < 0) {
163            throw new IOException("position was not marked");
164        }
165        position = mark;
166    }
167
168    /**
169     * Has no effect.
170     */
171    @Override
172    public void close() {
173    }
174}