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.EOFException;
024import java.io.File;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.OutputStream;
028import java.nio.ByteBuffer;
029import java.nio.channels.ReadableByteChannel;
030import java.nio.file.Files;
031import java.nio.file.LinkOption;
032
033/**
034 * Utility functions
035 * @Immutable (has mutable data but it is write-only)
036 */
037public final class IOUtils {
038
039    private static final int COPY_BUF_SIZE = 8024;
040    private static final int SKIP_BUF_SIZE = 4096;
041
042    /**
043     * Empty array of type {@link LinkOption}.
044     *
045     * @since 1.21
046     */
047    public static final LinkOption[] EMPTY_LINK_OPTIONS = {};
048
049    // This buffer does not need to be synchronized because it is write only; the contents are ignored
050    // Does not affect Immutability
051    private static final byte[] SKIP_BUF = new byte[SKIP_BUF_SIZE];
052
053    /**
054     * Closes the given Closeable and swallows any IOException that may occur.
055     * @param c Closeable to close, can be null
056     * @since 1.7
057     */
058    public static void closeQuietly(final Closeable c) {
059        if (c != null) {
060            try {
061                c.close();
062            } catch (final IOException ignored) { // NOPMD NOSONAR
063            }
064        }
065    }
066
067    /**
068     * Copies the source file to the given output stream.
069     * @param sourceFile The file to read.
070     * @param outputStream The output stream to write.
071     * @throws IOException if an I/O error occurs when reading or writing.
072     * @since 1.21
073     */
074    public static void copy(final File sourceFile, final OutputStream outputStream) throws IOException {
075        Files.copy(sourceFile.toPath(), outputStream);
076    }
077
078    /**
079     * Copies the content of a InputStream into an OutputStream.
080     * Uses a default buffer size of 8024 bytes.
081     *
082     * @param input
083     *            the InputStream to copy
084     * @param output
085     *            the target, may be null to simulate output to dev/null on Linux and NUL on Windows
086     * @return the number of bytes copied
087     * @throws IOException
088     *             if an error occurs
089     */
090    public static long copy(final InputStream input, final OutputStream output) throws IOException {
091        return copy(input, output, COPY_BUF_SIZE);
092    }
093
094    /**
095     * Copies the content of a InputStream into an OutputStream
096     *
097     * @param input
098     *            the InputStream to copy
099     * @param output
100     *            the target, may be null to simulate output to dev/null on Linux and NUL on Windows
101     * @param buffersize
102     *            the buffer size to use, must be bigger than 0
103     * @return the number of bytes copied
104     * @throws IOException
105     *             if an error occurs
106     * @throws IllegalArgumentException
107     *             if buffersize is smaller than or equal to 0
108     */
109    public static long copy(final InputStream input, final OutputStream output, final int buffersize) throws IOException {
110        if (buffersize < 1) {
111            throw new IllegalArgumentException("buffersize must be bigger than 0");
112        }
113        final byte[] buffer = new byte[buffersize];
114        int n = 0;
115        long count = 0;
116        while (-1 != (n = input.read(buffer))) {
117            if (output != null) {
118                output.write(buffer, 0, n);
119            }
120            count += n;
121        }
122        return count;
123    }
124
125    /**
126     * Copies part of the content of a InputStream into an OutputStream.
127     * Uses a default buffer size of 8024 bytes.
128     *
129     * @param input
130     *            the InputStream to copy
131     * @param output
132     *            the target Stream
133     * @param len
134     *            maximum amount of bytes to copy
135     * @return the number of bytes copied
136     * @throws IOException
137     *             if an error occurs
138     * @since 1.21
139     */
140    public static long copyRange(final InputStream input, final long len, final OutputStream output)
141        throws IOException {
142        return copyRange(input, len, output, COPY_BUF_SIZE);
143    }
144
145    /**
146     * Copies part of the content of a InputStream into an OutputStream
147     *
148     * @param input
149     *            the InputStream to copy
150     * @param len
151     *            maximum amount of bytes to copy
152     * @param output
153     *            the target, may be null to simulate output to dev/null on Linux and NUL on Windows
154     * @param buffersize
155     *            the buffer size to use, must be bigger than 0
156     * @return the number of bytes copied
157     * @throws IOException
158     *             if an error occurs
159     * @throws IllegalArgumentException
160     *             if buffersize is smaller than or equal to 0
161     * @since 1.21
162     */
163    public static long copyRange(final InputStream input, final long len, final OutputStream output,
164        final int buffersize) throws IOException {
165        if (buffersize < 1) {
166            throw new IllegalArgumentException("buffersize must be bigger than 0");
167        }
168        final byte[] buffer = new byte[(int) Math.min(buffersize, len)];
169        int n = 0;
170        long count = 0;
171        while (count < len && -1 != (n = input.read(buffer, 0, (int) Math.min(len - count, buffer.length)))) {
172            if (output != null) {
173                output.write(buffer, 0, n);
174            }
175            count += n;
176        }
177        return count;
178    }
179
180    /**
181     * Reads as much from the file as possible to fill the given array.
182     *
183     * <p>This method may invoke read repeatedly to fill the array and
184     * only read less bytes than the length of the array if the end of
185     * the stream has been reached.</p>
186     *
187     * @param file file to read
188     * @param array buffer to fill
189     * @return the number of bytes actually read
190     * @throws IOException on error
191     * @since 1.20
192     */
193    public static int read(final File file, final byte[] array) throws IOException {
194        try (InputStream inputStream = Files.newInputStream(file.toPath())) {
195            return readFully(inputStream, array, 0, array.length);
196        }
197    }
198
199    /**
200     * Reads as much from input as possible to fill the given array.
201     *
202     * <p>This method may invoke read repeatedly to fill the array and
203     * only read less bytes than the length of the array if the end of
204     * the stream has been reached.</p>
205     *
206     * @param input stream to read from
207     * @param array buffer to fill
208     * @return the number of bytes actually read
209     * @throws IOException on error
210     */
211    public static int readFully(final InputStream input, final byte[] array) throws IOException {
212        return readFully(input, array, 0, array.length);
213    }
214
215    // toByteArray(InputStream) copied from:
216    // commons/proper/io/trunk/src/main/java/org/apache/commons/io/IOUtils.java?revision=1428941
217    // January 8th, 2013
218    //
219    // Assuming our copy() works just as well as theirs!  :-)
220
221    /**
222     * Reads as much from input as possible to fill the given array
223     * with the given amount of bytes.
224     *
225     * <p>This method may invoke read repeatedly to read the bytes and
226     * only read less bytes than the requested length if the end of
227     * the stream has been reached.</p>
228     *
229     * @param input stream to read from
230     * @param array buffer to fill
231     * @param offset offset into the buffer to start filling at
232     * @param len of bytes to read
233     * @return the number of bytes actually read
234     * @throws IOException
235     *             if an I/O error has occurred
236     */
237    public static int readFully(final InputStream input, final byte[] array, final int offset, final int len)
238        throws IOException {
239        if (len < 0 || offset < 0 || len + offset > array.length || len + offset < 0) {
240            throw new IndexOutOfBoundsException();
241        }
242        int count = 0, x = 0;
243        while (count != len) {
244            x = input.read(array, offset + count, len - count);
245            if (x == -1) {
246                break;
247            }
248            count += x;
249        }
250        return count;
251    }
252
253    /**
254     * Reads {@code b.remaining()} bytes from the given channel
255     * starting at the current channel's position.
256     *
257     * <p>This method reads repeatedly from the channel until the
258     * requested number of bytes are read. This method blocks until
259     * the requested number of bytes are read, the end of the channel
260     * is detected, or an exception is thrown.</p>
261     *
262     * @param channel the channel to read from
263     * @param byteBuffer the buffer into which the data is read.
264     * @throws IOException - if an I/O error occurs.
265     * @throws EOFException - if the channel reaches the end before reading all the bytes.
266     */
267    public static void readFully(final ReadableByteChannel channel, final ByteBuffer byteBuffer) throws IOException {
268        final int expectedLength = byteBuffer.remaining();
269        int read = 0;
270        while (read < expectedLength) {
271            final int readNow = channel.read(byteBuffer);
272            if (readNow <= 0) {
273                break;
274            }
275            read += readNow;
276        }
277        if (read < expectedLength) {
278            throw new EOFException();
279        }
280    }
281
282    /**
283     * Gets part of the contents of an {@code InputStream} as a {@code byte[]}.
284     *
285     * @param input  the {@code InputStream} to read from
286     * @param len
287     *            maximum amount of bytes to copy
288     * @return the requested byte array
289     * @throws NullPointerException if the input is null
290     * @throws IOException if an I/O error occurs
291     * @since 1.21
292     */
293    public static byte[] readRange(final InputStream input, final int len) throws IOException {
294        final ByteArrayOutputStream output = new ByteArrayOutputStream();
295        copyRange(input, len, output);
296        return output.toByteArray();
297    }
298
299    /**
300     * Gets part of the contents of an {@code ReadableByteChannel} as a {@code byte[]}.
301     *
302     * @param input  the {@code ReadableByteChannel} to read from
303     * @param len
304     *            maximum amount of bytes to copy
305     * @return the requested byte array
306     * @throws NullPointerException if the input is null
307     * @throws IOException if an I/O error occurs
308     * @since 1.21
309     */
310    public static byte[] readRange(final ReadableByteChannel input, final int len) throws IOException {
311        final ByteArrayOutputStream output = new ByteArrayOutputStream();
312        final ByteBuffer b = ByteBuffer.allocate(Math.min(len, COPY_BUF_SIZE));
313        int read = 0;
314        while (read < len) {
315            // Make sure we never read more than len bytes
316            b.limit(Math.min(len - read, b.capacity()));
317            final int readNow = input.read(b);
318            if (readNow <= 0) {
319                break;
320            }
321            output.write(b.array(), 0, readNow);
322            b.rewind();
323            read += readNow;
324        }
325        return output.toByteArray();
326    }
327
328    /**
329     * Skips the given number of bytes by repeatedly invoking skip on
330     * the given input stream if necessary.
331     *
332     * <p>In a case where the stream's skip() method returns 0 before
333     * the requested number of bytes has been skip this implementation
334     * will fall back to using the read() method.</p>
335     *
336     * <p>This method will only skip less than the requested number of
337     * bytes if the end of the input stream has been reached.</p>
338     *
339     * @param input stream to skip bytes in
340     * @param numToSkip the number of bytes to skip
341     * @return the number of bytes actually skipped
342     * @throws IOException on error
343     */
344    public static long skip(final InputStream input, long numToSkip) throws IOException {
345        final long available = numToSkip;
346        while (numToSkip > 0) {
347            final long skipped = input.skip(numToSkip);
348            if (skipped == 0) {
349                break;
350            }
351            numToSkip -= skipped;
352        }
353
354        while (numToSkip > 0) {
355            final int read = readFully(input, SKIP_BUF, 0,
356                                 (int) Math.min(numToSkip, SKIP_BUF_SIZE));
357            if (read < 1) {
358                break;
359            }
360            numToSkip -= read;
361        }
362        return available - numToSkip;
363    }
364
365    /**
366     * Gets the contents of an {@code InputStream} as a {@code byte[]}.
367     * <p>
368     * This method buffers the input internally, so there is no need to use a
369     * {@code BufferedInputStream}.
370     *
371     * @param input  the {@code InputStream} to read from
372     * @return the requested byte array
373     * @throws NullPointerException if the input is null
374     * @throws IOException if an I/O error occurs
375     * @since 1.5
376     */
377    public static byte[] toByteArray(final InputStream input) throws IOException {
378        final ByteArrayOutputStream output = new ByteArrayOutputStream();
379        copy(input, output);
380        return output.toByteArray();
381    }
382
383    /** Private constructor to prevent instantiation of this utility class. */
384    private IOUtils(){
385    }
386
387}