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 java.io.BufferedInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023
024import org.apache.commons.io.build.AbstractOrigin;
025import org.apache.commons.io.build.AbstractStreamBuilder;
026import org.apache.commons.io.function.Uncheck;
027import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
028
029/**
030 * Implements a version of {@link AbstractByteArrayOutputStream} <b>without</b> any concurrent thread safety.
031 * <p>
032 * To build an instance, use {@link Builder}.
033 * </p>
034 *
035 * @see Builder
036 * @since 2.7
037 */
038//@NotThreadSafe
039public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArrayOutputStream {
040
041    // @formatter:off
042    /**
043     * Builds a new {@link UnsynchronizedByteArrayOutputStream}.
044     *
045     * <p>
046     * Using File IO:
047     * </p>
048     * <pre>{@code
049     * UnsynchronizedByteArrayOutputStream s = UnsynchronizedByteArrayOutputStream.builder()
050     *   .setBufferSize(8192)
051     *   .get();}
052     * </pre>
053     * <p>
054     * Using NIO Path:
055     * </p>
056     * <pre>{@code
057     * UnsynchronizedByteArrayOutputStream s = UnsynchronizedByteArrayOutputStream.builder()
058     *   .setBufferSize(8192)
059     *   .get();}
060     * </pre>
061     *
062     * @see #get()
063     */
064    // @formatter:on
065    public static class Builder extends AbstractStreamBuilder<UnsynchronizedByteArrayOutputStream, Builder> {
066
067        /**
068         * Builds a new {@link UnsynchronizedByteArrayOutputStream}.
069         *
070         * <p>
071         * This builder use the following aspects:
072         * </p>
073         * <ul>
074         * <li>{@link #getBufferSize()}</li>
075         * </ul>
076         *
077         * @return a new instance.
078         * @see AbstractOrigin#getByteArray()
079         */
080        @Override
081        public UnsynchronizedByteArrayOutputStream get() {
082            return new UnsynchronizedByteArrayOutputStream(getBufferSize());
083        }
084
085    }
086
087    /**
088     * Constructs a new {@link Builder}.
089     *
090     * @return a new {@link Builder}.
091     */
092    public static Builder builder() {
093        return new Builder();
094    }
095
096    /**
097     * Fetches entire contents of an {@link InputStream} and represent same data as result InputStream.
098     * <p>
099     * This method is useful where,
100     * </p>
101     * <ul>
102     * <li>Source InputStream is slow.</li>
103     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
104     * <li>It has network timeout associated.</li>
105     * </ul>
106     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].<br>
107     * This method buffers the input internally, so there is no need to use a {@link BufferedInputStream}.
108     *
109     * @param input Stream to be fully buffered.
110     * @return A fully buffered stream.
111     * @throws IOException if an I/O error occurs.
112     */
113    public static InputStream toBufferedInputStream(final InputStream input) throws IOException {
114        return toBufferedInputStream(input, DEFAULT_SIZE);
115    }
116
117    /**
118     * Fetches entire contents of an {@link InputStream} and represent same data as result InputStream.
119     * <p>
120     * This method is useful where,
121     * </p>
122     * <ul>
123     * <li>Source InputStream is slow.</li>
124     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
125     * <li>It has network timeout associated.</li>
126     * </ul>
127     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].<br>
128     * This method buffers the input internally, so there is no need to use a {@link BufferedInputStream}.
129     *
130     * @param input Stream to be fully buffered.
131     * @param size the initial buffer size
132     * @return A fully buffered stream.
133     * @throws IOException if an I/O error occurs.
134     */
135    public static InputStream toBufferedInputStream(final InputStream input, final int size) throws IOException {
136        // It does not matter if a ByteArrayOutputStream is not closed as close() is a no-op
137        try (UnsynchronizedByteArrayOutputStream output = builder().setBufferSize(size).get()) {
138            output.write(input);
139            return output.toInputStream();
140        }
141    }
142
143    /**
144     * Constructs a new byte array output stream. The buffer capacity is initially
145     *
146     * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
147     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
148     */
149    @Deprecated
150    public UnsynchronizedByteArrayOutputStream() {
151        this(DEFAULT_SIZE);
152    }
153
154    /**
155     * Constructs a new byte array output stream, with a buffer capacity of the specified size, in bytes.
156     *
157     * @param size the initial size
158     * @throws IllegalArgumentException if size is negative
159     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}. Will be private in 3.0.0.
160     */
161    @Deprecated
162    public UnsynchronizedByteArrayOutputStream(final int size) {
163        if (size < 0) {
164            throw new IllegalArgumentException("Negative initial size: " + size);
165        }
166        needNewBuffer(size);
167    }
168
169    /**
170     * @see java.io.ByteArrayOutputStream#reset()
171     */
172    @Override
173    public void reset() {
174        resetImpl();
175    }
176
177    @Override
178    public int size() {
179        return count;
180    }
181
182    @Override
183    public byte[] toByteArray() {
184        return toByteArrayImpl();
185    }
186
187    @Override
188    public InputStream toInputStream() {
189        // @formatter:off
190        return toInputStream((buffer, offset, length) -> Uncheck
191                .get(() -> UnsynchronizedByteArrayInputStream.builder()
192                        .setByteArray(buffer)
193                        .setOffset(offset)
194                        .setLength(length)
195                        .get()));
196        // @formatter:on
197    }
198
199    @Override
200    public void write(final byte[] b, final int off, final int len) {
201        if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0) {
202            throw new IndexOutOfBoundsException(String.format("offset=%,d, length=%,d", off, len));
203        }
204        if (len == 0) {
205            return;
206        }
207        writeImpl(b, off, len);
208    }
209
210    @Override
211    public int write(final InputStream in) throws IOException {
212        return writeImpl(in);
213    }
214
215    @Override
216    public void write(final int b) {
217        writeImpl(b);
218    }
219
220    @Override
221    public void writeTo(final OutputStream out) throws IOException {
222        writeToImpl(out);
223    }
224}