001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.utils;
020
021import java.io.ByteArrayOutputStream;
022import java.io.Closeable;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.OutputStream;
026
027/**
028 * Utility functions
029 * @Immutable (has mutable data but it is write-only)
030 */
031public final class IOUtils {
032
033    private static final int COPY_BUF_SIZE = 8024;
034    private static final int SKIP_BUF_SIZE = 4096;
035    
036    // This buffer does not need to be synchronised because it is write only; the contents are ignored
037    // Does not affect Immutability
038    private static final byte[] SKIP_BUF = new byte[SKIP_BUF_SIZE];
039
040    /** Private constructor to prevent instantiation of this utility class. */
041    private IOUtils(){
042    }
043
044    /**
045     * Copies the content of a InputStream into an OutputStream.
046     * Uses a default buffer size of 8024 bytes.
047     *
048     * @param input
049     *            the InputStream to copy
050     * @param output
051     *            the target Stream
052     * @throws IOException
053     *             if an error occurs
054     */
055    public static long copy(final InputStream input, final OutputStream output) throws IOException {
056        return copy(input, output, COPY_BUF_SIZE);
057    }
058
059    /**
060     * Copies the content of a InputStream into an OutputStream
061     *
062     * @param input
063     *            the InputStream to copy
064     * @param output
065     *            the target Stream
066     * @param buffersize
067     *            the buffer size to use
068     * @throws IOException
069     *             if an error occurs
070     */
071    public static long copy(final InputStream input, final OutputStream output, int buffersize) throws IOException {
072        final byte[] buffer = new byte[buffersize];
073        int n = 0;
074        long count=0;
075        while (-1 != (n = input.read(buffer))) {
076            output.write(buffer, 0, n);
077            count += n;
078        }
079        return count;
080    }
081    
082    /**
083     * Skips the given number of bytes by repeatedly invoking skip on
084     * the given input stream if necessary.
085     *
086     * <p>In a case where the stream's skip() method returns 0 before
087     * the requested number of bytes has been skip this implementation
088     * will fall back to using the read() method.</p>
089     *
090     * <p>This method will only skip less than the requested number of
091     * bytes if the end of the input stream has been reached.</p>
092     *
093     * @param input stream to skip bytes in
094     * @param numToSkip the number of bytes to skip
095     * @return the number of bytes actually skipped
096     * @throws IOException
097     */
098    public static long skip(InputStream input, long numToSkip) throws IOException {
099        long available = numToSkip;
100        while (numToSkip > 0) {
101            long skipped = input.skip(numToSkip);
102            if (skipped == 0) {
103                break;
104            }
105            numToSkip -= skipped;
106        }
107            
108        while (numToSkip > 0) {
109            int read = readFully(input, SKIP_BUF, 0,
110                                 (int) Math.min(numToSkip, SKIP_BUF_SIZE));
111            if (read < 1) {
112                break;
113            }
114            numToSkip -= read;
115        }
116        return available - numToSkip;
117    }
118
119    /**
120     * Reads as much from input as possible to fill the given array.
121     *
122     * <p>This method may invoke read repeatedly to fill the array and
123     * only read less bytes than the length of the array if the end of
124     * the stream has been reached.</p>
125     *
126     * @param input stream to read from
127     * @param b buffer to fill
128     * @return the number of bytes actually read
129     * @throws IOException
130     */
131    public static int readFully(InputStream input, byte[] b) throws IOException {
132        return readFully(input, b, 0, b.length);
133    }
134
135    /**
136     * Reads as much from input as possible to fill the given array
137     * with the given amount of bytes.
138     *
139     * <p>This method may invoke read repeatedly to read the bytes and
140     * only read less bytes than the requested length if the end of
141     * the stream has been reached.</p>
142     *
143     * @param input stream to read from
144     * @param b buffer to fill
145     * @param offset offset into the buffer to start filling at
146     * @param len of bytes to read
147     * @return the number of bytes actually read
148     * @throws IOException
149     *             if an I/O error has occurred
150     */
151    public static int readFully(InputStream input, byte[] b, int offset, int len)
152        throws IOException {
153        if (len < 0 || offset < 0 || len + offset > b.length) {
154            throw new IndexOutOfBoundsException();
155        }
156        int count = 0, x = 0;
157        while (count != len) {
158            x = input.read(b, offset + count, len - count);
159            if (x == -1) {
160                break;
161            }
162            count += x;
163        }
164        return count;
165    }
166
167    // toByteArray(InputStream) copied from:
168    // commons/proper/io/trunk/src/main/java/org/apache/commons/io/IOUtils.java?revision=1428941
169    // January 8th, 2013
170    //
171    // Assuming our copy() works just as well as theirs!  :-)
172
173    /**
174     * Gets the contents of an <code>InputStream</code> as a <code>byte[]</code>.
175     * <p>
176     * This method buffers the input internally, so there is no need to use a
177     * <code>BufferedInputStream</code>.
178     *
179     * @param input  the <code>InputStream</code> to read from
180     * @return the requested byte array
181     * @throws NullPointerException if the input is null
182     * @throws IOException if an I/O error occurs
183     * @since 1.5
184     */
185    public static byte[] toByteArray(final InputStream input) throws IOException {
186        final ByteArrayOutputStream output = new ByteArrayOutputStream();
187        copy(input, output);
188        return output.toByteArray();
189    }
190
191    /**
192     * Closes the given Closeable and swallows any IOException that may occur.
193     * @param c Closeable to close, can be null
194     * @since 1.7
195     */
196    public static void closeQuietly(Closeable c) {
197        if (c != null) {
198            try {
199                c.close();
200            } catch (IOException ignored) { // NOPMD
201            }
202        }
203    }
204}