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