001/* 002 * Copyright 2010-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2010-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2010-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util; 037 038 039 040import java.io.IOException; 041import java.io.OutputStream; 042import java.io.Serializable; 043 044import static com.unboundid.util.UtilityMessages.*; 045 046 047 048/** 049 * This class provides an {@code OutputStream} implementation that writes data 050 * to a provided byte array. It is similar to the 051 * {@code java.io.ByteArrayOutputStream} class, except that it allows you to 052 * pass in the array that it uses, and the array will not grow over time. 053 */ 054@Mutable() 055@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 056public final class FixedArrayOutputStream 057 extends OutputStream 058 implements Serializable 059{ 060 /** 061 * The serial version UID for this serializable class. 062 */ 063 private static final long serialVersionUID = 4678108653480347534L; 064 065 066 067 // The byte array used by this class. 068 @NotNull private final byte[] array; 069 070 // The initial position for this array. 071 private final int initialPosition; 072 073 // The maximum number of bytes that may be written. 074 private final int length; 075 076 // The maximum position at which data may be written. 077 private final int maxPosition; 078 079 // The current position at which data may be written. 080 private int pos; 081 082 083 084 /** 085 * Creates a new output stream that will write data to the provided array. 086 * It will use the entire array. 087 * 088 * @param array The array to which data will be written. It must not be 089 * {@code null}. 090 */ 091 public FixedArrayOutputStream(@NotNull final byte[] array) 092 { 093 this(array, 0, array.length); 094 } 095 096 097 098 /** 099 * Creates a new output stream that will write data to the provided array. 100 * It will use the specified portion of the array. 101 * 102 * @param array The array to which data will be written. It must not be 103 * {@code null}. 104 * @param pos The position at which to start writing data. It must be 105 * greater than or equal to zero and less than or equal to the 106 * length of the array. 107 * @param len The maximum number of bytes that may be written. It must 108 * be greater than or equal to zero and less than or equal to 109 * the difference between the length of the array and the 110 * provided {@code pos} value. 111 */ 112 public FixedArrayOutputStream(@NotNull final byte[] array, final int pos, 113 final int len) 114 { 115 this.array = array; 116 this.pos = pos; 117 118 initialPosition = pos; 119 maxPosition = pos + len; 120 length = len; 121 122 Validator.ensureTrue((pos >= 0), 123 "The position must be greater than or equal to zero."); 124 Validator.ensureTrue((len >= 0), 125 "The length must be greater than or equal to zero."); 126 Validator.ensureTrue((maxPosition <= array.length), 127 "The sum of pos and len must not exceed the array length."); 128 } 129 130 131 132 /** 133 * Retrieves the backing array used by this output stream. 134 * 135 * @return The backing array used by this output stream. 136 */ 137 @NotNull() 138 public byte[] getBackingArray() 139 { 140 return array; 141 } 142 143 144 145 /** 146 * Retrieves the initial position provided when this output stream was 147 * created. 148 * 149 * @return The initial position provided when this output stream was created. 150 */ 151 public int getInitialPosition() 152 { 153 return initialPosition; 154 } 155 156 157 158 /** 159 * Retrieves the maximum number of bytes that may be written to this output 160 * stream. 161 * 162 * @return The maximum number of bytes that may be written to this output 163 * stream. 164 */ 165 public int getLength() 166 { 167 return length; 168 } 169 170 171 172 /** 173 * Retrieves the number of bytes that have been written so far to this output 174 * stream. 175 * 176 * @return The number of bytes that have been written so far to this output 177 * stream. 178 */ 179 public int getBytesWritten() 180 { 181 return (pos - initialPosition); 182 } 183 184 185 186 /** 187 * Closes this output stream. This has no effect. 188 */ 189 @Override() 190 public void close() 191 { 192 // No implementation required. 193 } 194 195 196 197 /** 198 * Flushes this output stream. This has no effect. 199 */ 200 @Override() 201 public void flush() 202 { 203 // No implementation required. 204 } 205 206 207 208 /** 209 * Writes the provided byte to this output stream. 210 * 211 * @param b The byte to be written. 212 * 213 * @throws IOException If an attempt was made to write beyond the end of the 214 * array. 215 */ 216 @Override() 217 public void write(final int b) 218 throws IOException 219 { 220 if (pos >= maxPosition) 221 { 222 throw new IOException(ERR_FIXED_ARRAY_OS_WRITE_BEYOND_END.get()); 223 } 224 225 array[pos++] = (byte) b; 226 } 227 228 229 230 /** 231 * Writes the contents of the provided array to this output stream. 232 * 233 * @param b The byte array containing the data to be written. It must not 234 * be {@code null}. 235 * 236 * @throws IOException If an attempt was made to write beyond the end of the 237 * array. 238 */ 239 @Override() 240 public void write(@NotNull final byte[] b) 241 throws IOException 242 { 243 write(b, 0, b.length); 244 } 245 246 247 248 /** 249 * Writes the contents of the provided array to this output stream. 250 * 251 * @param b The byte array containing the data to be written. It must not 252 * be {@code null}. 253 * @param off The offset within the provided array of the beginning of the 254 * data to be written. It must be greater than or equal to zero 255 * and less than or equal to the length of the provided array. 256 * @param len The number of bytes to be written. It must be greater than or 257 * equal to zero, and the sum of {@code off} and {@code len} must 258 * be less than the length of the provided array. 259 * 260 * @throws IOException If an attempt was made to write beyond the end of the 261 * array. 262 */ 263 @Override() 264 public void write(@NotNull final byte[] b, final int off, final int len) 265 throws IOException 266 { 267 Validator.ensureTrue((off >= 0), 268 "The provided offset must be greater than or equal to zero."); 269 Validator.ensureTrue((len >= 0), 270 "The provided length must be greater than or equal to zero."); 271 Validator.ensureTrue(((off + len) <= b.length), 272 "The sum of off and len must not exceed the array length."); 273 274 if ((pos + len) > maxPosition) 275 { 276 throw new IOException(ERR_FIXED_ARRAY_OS_WRITE_BEYOND_END.get()); 277 } 278 279 System.arraycopy(b, off, array, pos, len); 280 pos += len; 281 } 282}