1/* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.ws.util; 27 28import java.io.ByteArrayInputStream; 29import java.io.ByteArrayOutputStream; 30import java.io.IOException; 31import java.io.InputStream; 32import java.io.OutputStream; 33 34/** 35 * Read/write buffer that stores a sequence of bytes. 36 * 37 * <p> 38 * It works in a way similar to {@link ByteArrayOutputStream} but 39 * this class works better in the following ways: 40 * 41 * <ol> 42 * <li>no synchronization 43 * <li>offers a {@link #newInputStream()} that creates a new {@link InputStream} 44 * that won't cause buffer reallocation. 45 * <li>less parameter correctness checking 46 * <li>offers a {@link #write(InputStream)} method that reads the entirety of the 47 * given {@link InputStream} without using a temporary buffer. 48 * </ol> 49 * 50 * @author Kohsuke Kawaguchi 51 */ 52public class ByteArrayBuffer extends OutputStream { 53 /** 54 * The buffer where data is stored. 55 */ 56 protected byte[] buf; 57 58 /** 59 * The number of valid bytes in the buffer. 60 */ 61 private int count; 62 63 private static final int CHUNK_SIZE = 4096; 64 65 /** 66 * Creates a new byte array output stream. The buffer capacity is 67 * initially 32 bytes, though its size increases if necessary. 68 */ 69 public ByteArrayBuffer() { 70 this(32); 71 } 72 73 /** 74 * Creates a new byte array output stream, with a buffer capacity of 75 * the specified size, in bytes. 76 * 77 * @param size the initial size. 78 * @throws IllegalArgumentException if size is negative. 79 */ 80 public ByteArrayBuffer(int size) { 81 if (size <= 0) 82 throw new IllegalArgumentException(); 83 buf = new byte[size]; 84 } 85 86 public ByteArrayBuffer(byte[] data) { 87 this(data,data.length); 88 } 89 90 public ByteArrayBuffer(byte[] data, int length) { 91 this.buf = data; 92 this.count = length; 93 } 94 95 /** 96 * Reads all the data of the given {@link InputStream} and appends them 97 * into this buffer. 98 * 99 * @throws IOException 100 * if the read operation fails with an {@link IOException}. 101 */ 102 public final void write(InputStream in) throws IOException { 103 while(true) { 104 int cap = buf.length-count; // the remaining buffer space 105 int sz = in.read(buf,count,cap); 106 if(sz<0) return; // hit EOS 107 count += sz; 108 109 110 if(cap==sz) 111 ensureCapacity(buf.length*2); // buffer filled up. 112 } 113 } 114 115 public final void write(int b) { 116 int newcount = count + 1; 117 ensureCapacity(newcount); 118 buf[count] = (byte) b; 119 count = newcount; 120 } 121 122 public final void write(byte b[], int off, int len) { 123 int newcount = count + len; 124 ensureCapacity(newcount); 125 System.arraycopy(b, off, buf, count, len); 126 count = newcount; 127 } 128 129 private void ensureCapacity(int newcount) { 130 if (newcount > buf.length) { 131 byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; 132 System.arraycopy(buf, 0, newbuf, 0, count); 133 buf = newbuf; 134 } 135 } 136 137 public final void writeTo(OutputStream out) throws IOException { 138 // Instead of writing out.write(buf, 0, count) 139 // Writing it in chunks that would help larger payloads 140 // Also if out is System.out on windows, it doesn't show on the console 141 // for larger data. 142 int remaining = count; 143 int off = 0; 144 while(remaining > 0) { 145 int chunk = (remaining > CHUNK_SIZE) ? CHUNK_SIZE : remaining; 146 out.write(buf, off, chunk); 147 remaining -= chunk; 148 off += chunk; 149 } 150 } 151 152 public final void reset() { 153 count = 0; 154 } 155 156 /** 157 * Gets the <b>copy</b> of exact-size byte[] that represents the written data. 158 * 159 * <p> 160 * Since this method needs to allocate a new byte[], this method will be costly. 161 * 162 * @deprecated 163 * this method causes a buffer reallocation. Use it only when 164 * you have to. 165 */ 166 public final byte[] toByteArray() { 167 byte newbuf[] = new byte[count]; 168 System.arraycopy(buf, 0, newbuf, 0, count); 169 return newbuf; 170 } 171 172 public final int size() { 173 return count; 174 } 175 176 /** 177 * Gets the underlying buffer that this {@link ByteArrayBuffer} uses. 178 * It's never small than its {@link #size()}. 179 * 180 * Use with caution. 181 */ 182 public final byte[] getRawData() { 183 return buf; 184 } 185 186 public void close() throws IOException { 187 } 188 189 /** 190 * Creates a new {@link InputStream} that reads from this buffer. 191 */ 192 public final InputStream newInputStream() { 193 return new ByteArrayInputStream(buf,0,count); 194 } 195 196 /** 197 * Creates a new {@link InputStream} that reads a part of this bfufer. 198 */ 199 public final InputStream newInputStream(int start, int length) { 200 return new ByteArrayInputStream(buf,start,length); 201 } 202 203 /** 204 * Decodes the contents of this buffer by the default encoding 205 * and returns it as a string. 206 * 207 * <p> 208 * Meant to aid debugging, but no more. 209 */ 210 public String toString() { 211 return new String(buf, 0, count); 212 } 213} 214