View Javadoc
1   /*
2    * Copyright (c) 1997, 2012, 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  
26  package com.sun.xml.internal.ws.util;
27  
28  import java.io.ByteArrayInputStream;
29  import java.io.ByteArrayOutputStream;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import 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   */
52  public 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 }