001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.io.output;
018
019import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024
025/**
026 * Implements a version of {@link AbstractByteArrayOutputStream} <b>without</b> any concurrent thread safety.
027 *
028 * @since 2.7
029 */
030//@NotThreadSafe
031public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArrayOutputStream {
032
033    /**
034     * Creates a new byte array output stream. The buffer capacity is initially
035     * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
036     */
037    public UnsynchronizedByteArrayOutputStream() {
038        this(DEFAULT_SIZE);
039    }
040
041    /**
042     * Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
043     *
044     * @param size the initial size
045     * @throws IllegalArgumentException if size is negative
046     */
047    public UnsynchronizedByteArrayOutputStream(final int size) {
048        if (size < 0) {
049            throw new IllegalArgumentException("Negative initial size: " + size);
050        }
051        needNewBuffer(size);
052    }
053
054    @Override
055    public void write(final byte[] b, final int off, final int len) {
056        if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
057            throw new IndexOutOfBoundsException(String.format("offset=%,d, length=%,d", off, len));
058        } else if (len == 0) {
059            return;
060        }
061        writeImpl(b, off, len);
062    }
063
064    @Override
065    public void write(final int b) {
066        writeImpl(b);
067    }
068
069    @Override
070    public int write(final InputStream in) throws IOException {
071        return writeImpl(in);
072    }
073
074    @Override
075    public int size() {
076        return count;
077    }
078
079    /**
080     * @see java.io.ByteArrayOutputStream#reset()
081     */
082    @Override
083    public void reset() {
084        resetImpl();
085    }
086
087    @Override
088    public void writeTo(final OutputStream out) throws IOException {
089        writeToImpl(out);
090    }
091
092    /**
093     * Fetches entire contents of an <code>InputStream</code> and represent same data as result InputStream.
094     * <p>
095     * This method is useful where,
096     * </p>
097     * <ul>
098     * <li>Source InputStream is slow.</li>
099     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
100     * <li>It has network timeout associated.</li>
101     * </ul>
102     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].<br>
103     * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
104     *
105     * @param input Stream to be fully buffered.
106     * @return A fully buffered stream.
107     * @throws IOException if an I/O error occurs
108     */
109    public static InputStream toBufferedInputStream(final InputStream input) throws IOException {
110        return toBufferedInputStream(input, DEFAULT_SIZE);
111    }
112
113    /**
114     * Fetches entire contents of an <code>InputStream</code> and represent same data as result InputStream.
115     * <p>
116     * This method is useful where,
117     * </p>
118     * <ul>
119     * <li>Source InputStream is slow.</li>
120     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
121     * <li>It has network timeout associated.</li>
122     * </ul>
123     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].<br>
124     * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
125     *
126     * @param input Stream to be fully buffered.
127     * @param size the initial buffer size
128     * @return A fully buffered stream.
129     * @throws IOException if an I/O error occurs
130     */
131    public static InputStream toBufferedInputStream(final InputStream input, final int size) throws IOException {
132        // It does not matter if a ByteArrayOutputStream is not closed as close() is a no-op
133        try (final UnsynchronizedByteArrayOutputStream output = new UnsynchronizedByteArrayOutputStream(size)) {
134            output.write(input);
135            return output.toInputStream();
136        }
137    }
138
139    @Override
140    public InputStream toInputStream() {
141        return toInputStream(UnsynchronizedByteArrayInputStream::new);
142    }
143
144    @Override
145    public byte[] toByteArray() {
146        return toByteArrayImpl();
147    }
148}