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}