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