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