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        }
059        if (len == 0) {
060            return;
061        }
062        writeImpl(b, off, len);
063    }
064
065    @Override
066    public void write(final int b) {
067        writeImpl(b);
068    }
069
070    @Override
071    public int write(final InputStream in) throws IOException {
072        return writeImpl(in);
073    }
074
075    @Override
076    public int size() {
077        return count;
078    }
079
080    /**
081     * @see java.io.ByteArrayOutputStream#reset()
082     */
083    @Override
084    public void reset() {
085        resetImpl();
086    }
087
088    @Override
089    public void writeTo(final OutputStream out) throws IOException {
090        writeToImpl(out);
091    }
092
093    /**
094     * Fetches entire contents of an {@code InputStream} and represent same data as result InputStream.
095     * <p>
096     * This method is useful where,
097     * </p>
098     * <ul>
099     * <li>Source InputStream is slow.</li>
100     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
101     * <li>It has network timeout associated.</li>
102     * </ul>
103     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].<br>
104     * This method buffers the input internally, so there is no need to use a {@code BufferedInputStream}.
105     *
106     * @param input Stream to be fully buffered.
107     * @return A fully buffered stream.
108     * @throws IOException if an I/O error occurs.
109     */
110    public static InputStream toBufferedInputStream(final InputStream input) throws IOException {
111        return toBufferedInputStream(input, DEFAULT_SIZE);
112    }
113
114    /**
115     * Fetches entire contents of an {@code InputStream} and represent same data as result InputStream.
116     * <p>
117     * This method is useful where,
118     * </p>
119     * <ul>
120     * <li>Source InputStream is slow.</li>
121     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
122     * <li>It has network timeout associated.</li>
123     * </ul>
124     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].<br>
125     * This method buffers the input internally, so there is no need to use a {@code BufferedInputStream}.
126     *
127     * @param input Stream to be fully buffered.
128     * @param size the initial buffer size
129     * @return A fully buffered stream.
130     * @throws IOException if an I/O error occurs.
131     */
132    public static InputStream toBufferedInputStream(final InputStream input, final int size) throws IOException {
133        // It does not matter if a ByteArrayOutputStream is not closed as close() is a no-op
134        try (final UnsynchronizedByteArrayOutputStream output = new UnsynchronizedByteArrayOutputStream(size)) {
135            output.write(input);
136            return output.toInputStream();
137        }
138    }
139
140    @Override
141    public InputStream toInputStream() {
142        return toInputStream(UnsynchronizedByteArrayInputStream::new);
143    }
144
145    @Override
146    public byte[] toByteArray() {
147        return toByteArrayImpl();
148    }
149}