IO.java

  1. /*
  2.  * Copyright (C) 2008-2009, Google Inc.
  3.  * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  4.  * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.util;

  13. import java.io.EOFException;
  14. import java.io.File;
  15. import java.io.FileNotFoundException;
  16. import java.io.IOException;
  17. import java.io.InputStream;
  18. import java.io.Reader;
  19. import java.nio.ByteBuffer;
  20. import java.nio.channels.ReadableByteChannel;
  21. import java.text.MessageFormat;
  22. import java.util.ArrayList;
  23. import java.util.List;

  24. import org.eclipse.jgit.internal.JGitText;
  25. import org.eclipse.jgit.util.io.SilentFileInputStream;

  26. /**
  27.  * Input/Output utilities
  28.  */
  29. public class IO {

  30.     /**
  31.      * Read an entire local file into memory as a byte array.
  32.      *
  33.      * @param path
  34.      *            location of the file to read.
  35.      * @return complete contents of the requested local file.
  36.      * @throws java.io.FileNotFoundException
  37.      *             the file does not exist.
  38.      * @throws java.io.IOException
  39.      *             the file exists, but its contents cannot be read.
  40.      */
  41.     public static final byte[] readFully(File path)
  42.             throws FileNotFoundException, IOException {
  43.         return IO.readFully(path, Integer.MAX_VALUE);
  44.     }

  45.     /**
  46.      * Read at most limit bytes from the local file into memory as a byte array.
  47.      *
  48.      * @param path
  49.      *            location of the file to read.
  50.      * @param limit
  51.      *            maximum number of bytes to read, if the file is larger than
  52.      *            only the first limit number of bytes are returned
  53.      * @return complete contents of the requested local file. If the contents
  54.      *         exceeds the limit, then only the limit is returned.
  55.      * @throws java.io.FileNotFoundException
  56.      *             the file does not exist.
  57.      * @throws java.io.IOException
  58.      *             the file exists, but its contents cannot be read.
  59.      */
  60.     public static final byte[] readSome(File path, int limit)
  61.             throws FileNotFoundException, IOException {
  62.         try (SilentFileInputStream in = new SilentFileInputStream(path)) {
  63.             byte[] buf = new byte[limit];
  64.             int cnt = 0;
  65.             for (;;) {
  66.                 int n = in.read(buf, cnt, buf.length - cnt);
  67.                 if (n <= 0)
  68.                     break;
  69.                 cnt += n;
  70.             }
  71.             if (cnt == buf.length)
  72.                 return buf;
  73.             byte[] res = new byte[cnt];
  74.             System.arraycopy(buf, 0, res, 0, cnt);
  75.             return res;
  76.         }
  77.     }

  78.     /**
  79.      * Read an entire local file into memory as a byte array.
  80.      *
  81.      * @param path
  82.      *            location of the file to read.
  83.      * @param max
  84.      *            maximum number of bytes to read, if the file is larger than
  85.      *            this limit an IOException is thrown.
  86.      * @return complete contents of the requested local file.
  87.      * @throws java.io.FileNotFoundException
  88.      *             the file does not exist.
  89.      * @throws java.io.IOException
  90.      *             the file exists, but its contents cannot be read.
  91.      */
  92.     public static final byte[] readFully(File path, int max)
  93.             throws FileNotFoundException, IOException {
  94.         try (SilentFileInputStream in = new SilentFileInputStream(path)) {
  95.             long sz = Math.max(path.length(), 1);
  96.             if (sz > max)
  97.                 throw new IOException(MessageFormat.format(
  98.                         JGitText.get().fileIsTooLarge, path));

  99.             byte[] buf = new byte[(int) sz];
  100.             int valid = 0;
  101.             for (;;) {
  102.                 if (buf.length == valid) {
  103.                     if (buf.length == max) {
  104.                         int next = in.read();
  105.                         if (next < 0)
  106.                             break;

  107.                         throw new IOException(MessageFormat.format(
  108.                                 JGitText.get().fileIsTooLarge, path));
  109.                     }

  110.                     byte[] nb = new byte[Math.min(buf.length * 2, max)];
  111.                     System.arraycopy(buf, 0, nb, 0, valid);
  112.                     buf = nb;
  113.                 }
  114.                 int n = in.read(buf, valid, buf.length - valid);
  115.                 if (n < 0)
  116.                     break;
  117.                 valid += n;
  118.             }
  119.             if (valid < buf.length) {
  120.                 byte[] nb = new byte[valid];
  121.                 System.arraycopy(buf, 0, nb, 0, valid);
  122.                 buf = nb;
  123.             }
  124.             return buf;
  125.         }
  126.     }

  127.     /**
  128.      * Read an entire input stream into memory as a ByteBuffer.
  129.      *
  130.      * Note: The stream is read to its end and is not usable after calling this
  131.      * method. The caller is responsible for closing the stream.
  132.      *
  133.      * @param in
  134.      *            input stream to be read.
  135.      * @param sizeHint
  136.      *            a hint on the approximate number of bytes contained in the
  137.      *            stream, used to allocate temporary buffers more efficiently
  138.      * @return complete contents of the input stream. The ByteBuffer always has
  139.      *         a writable backing array, with {@code position() == 0} and
  140.      *         {@code limit()} equal to the actual length read. Callers may rely
  141.      *         on obtaining the underlying array for efficient data access. If
  142.      *         {@code sizeHint} was too large, the array may be over-allocated,
  143.      *         resulting in {@code limit() < array().length}.
  144.      * @throws java.io.IOException
  145.      *             there was an error reading from the stream.
  146.      */
  147.     public static ByteBuffer readWholeStream(InputStream in, int sizeHint)
  148.             throws IOException {
  149.         byte[] out = new byte[sizeHint];
  150.         int pos = 0;
  151.         while (pos < out.length) {
  152.             int read = in.read(out, pos, out.length - pos);
  153.             if (read < 0)
  154.                 return ByteBuffer.wrap(out, 0, pos);
  155.             pos += read;
  156.         }

  157.         int last = in.read();
  158.         if (last < 0)
  159.             return ByteBuffer.wrap(out, 0, pos);

  160.         try (TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(
  161.                 Integer.MAX_VALUE)) {
  162.             tmp.write(out);
  163.             tmp.write(last);
  164.             tmp.copy(in);
  165.             return ByteBuffer.wrap(tmp.toByteArray());
  166.         }
  167.     }

  168.     /**
  169.      * Read the entire byte array into memory, or throw an exception.
  170.      *
  171.      * @param fd
  172.      *            input stream to read the data from.
  173.      * @param dst
  174.      *            buffer that must be fully populated, [off, off+len).
  175.      * @param off
  176.      *            position within the buffer to start writing to.
  177.      * @param len
  178.      *            number of bytes that must be read.
  179.      * @throws EOFException
  180.      *             the stream ended before dst was fully populated.
  181.      * @throws java.io.IOException
  182.      *             there was an error reading from the stream.
  183.      */
  184.     public static void readFully(final InputStream fd, final byte[] dst,
  185.             int off, int len) throws IOException {
  186.         while (len > 0) {
  187.             final int r = fd.read(dst, off, len);
  188.             if (r <= 0)
  189.                 throw new EOFException(JGitText.get().shortReadOfBlock);
  190.             off += r;
  191.             len -= r;
  192.         }
  193.     }

  194.     /**
  195.      * Read as much of the array as possible from a channel.
  196.      *
  197.      * @param channel
  198.      *            channel to read data from.
  199.      * @param dst
  200.      *            buffer that must be fully populated, [off, off+len).
  201.      * @param off
  202.      *            position within the buffer to start writing to.
  203.      * @param len
  204.      *            number of bytes that should be read.
  205.      * @return number of bytes actually read.
  206.      * @throws java.io.IOException
  207.      *             there was an error reading from the channel.
  208.      */
  209.     public static int read(ReadableByteChannel channel, byte[] dst, int off,
  210.             int len) throws IOException {
  211.         if (len == 0)
  212.             return 0;
  213.         int cnt = 0;
  214.         while (0 < len) {
  215.             int r = channel.read(ByteBuffer.wrap(dst, off, len));
  216.             if (r <= 0)
  217.                 break;
  218.             off += r;
  219.             len -= r;
  220.             cnt += r;
  221.         }
  222.         return cnt != 0 ? cnt : -1;
  223.     }

  224.     /**
  225.      * Read the entire byte array into memory, unless input is shorter
  226.      *
  227.      * @param fd
  228.      *            input stream to read the data from.
  229.      * @param dst
  230.      *            buffer that must be fully populated, [off, off+len).
  231.      * @param off
  232.      *            position within the buffer to start writing to.
  233.      * @return number of bytes read
  234.      * @throws java.io.IOException
  235.      *             there was an error reading from the stream.
  236.      */
  237.     public static int readFully(InputStream fd, byte[] dst, int off)
  238.             throws IOException {
  239.         int r;
  240.         int len = 0;
  241.         while (off < dst.length
  242.                 && (r = fd.read(dst, off, dst.length - off)) >= 0) {
  243.             off += r;
  244.             len += r;
  245.         }
  246.         return len;
  247.     }

  248.     /**
  249.      * Skip an entire region of an input stream.
  250.      * <p>
  251.      * The input stream's position is moved forward by the number of requested
  252.      * bytes, discarding them from the input. This method does not return until
  253.      * the exact number of bytes requested has been skipped.
  254.      *
  255.      * @param fd
  256.      *            the stream to skip bytes from.
  257.      * @param toSkip
  258.      *            total number of bytes to be discarded. Must be &gt;= 0.
  259.      * @throws EOFException
  260.      *             the stream ended before the requested number of bytes were
  261.      *             skipped.
  262.      * @throws java.io.IOException
  263.      *             there was an error reading from the stream.
  264.      */
  265.     public static void skipFully(InputStream fd, long toSkip)
  266.             throws IOException {
  267.         while (toSkip > 0) {
  268.             final long r = fd.skip(toSkip);
  269.             if (r <= 0)
  270.                 throw new EOFException(JGitText.get().shortSkipOfBlock);
  271.             toSkip -= r;
  272.         }
  273.     }

  274.     /**
  275.      * Divides the given string into lines.
  276.      *
  277.      * @param s
  278.      *            the string to read
  279.      * @return the string divided into lines
  280.      * @since 2.0
  281.      */
  282.     public static List<String> readLines(String s) {
  283.         List<String> l = new ArrayList<>();
  284.         StringBuilder sb = new StringBuilder();
  285.         for (int i = 0; i < s.length(); i++) {
  286.             char c = s.charAt(i);
  287.             if (c == '\n') {
  288.                 l.add(sb.toString());
  289.                 sb.setLength(0);
  290.                 continue;
  291.             }
  292.             if (c == '\r') {
  293.                 if (i + 1 < s.length()) {
  294.                     c = s.charAt(++i);
  295.                     l.add(sb.toString());
  296.                     sb.setLength(0);
  297.                     if (c != '\n') {
  298.                         sb.append(c);
  299.                     }
  300.                     continue;
  301.                 }
  302.                 // EOF
  303.                 l.add(sb.toString());
  304.                 break;
  305.             }
  306.             sb.append(c);
  307.         }
  308.         l.add(sb.toString());
  309.         return l;
  310.     }

  311.     /**
  312.      * Read the next line from a reader.
  313.      * <p>
  314.      * Like {@link java.io.BufferedReader#readLine()}, but only treats
  315.      * {@code \n} as end-of-line, and includes the trailing newline.
  316.      *
  317.      * @param in
  318.      *            the reader to read from.
  319.      * @param sizeHint
  320.      *            hint for buffer sizing; 0 or negative for default.
  321.      * @return the next line from the input, always ending in {@code \n} unless
  322.      *         EOF was reached.
  323.      * @throws java.io.IOException
  324.      *             there was an error reading from the stream.
  325.      * @since 4.1
  326.      */
  327.     public static String readLine(Reader in, int sizeHint) throws IOException {
  328.         if (in.markSupported()) {
  329.             if (sizeHint <= 0) {
  330.                 sizeHint = 1024;
  331.             }
  332.             StringBuilder sb = new StringBuilder(sizeHint);
  333.             char[] buf = new char[sizeHint];
  334.             while (true) {
  335.                 in.mark(sizeHint);
  336.                 int n = in.read(buf);
  337.                 if (n < 0) {
  338.                     in.reset();
  339.                     return sb.toString();
  340.                 }
  341.                 for (int i = 0; i < n; i++) {
  342.                     if (buf[i] == '\n') {
  343.                         resetAndSkipFully(in, ++i);
  344.                         sb.append(buf, 0, i);
  345.                         return sb.toString();
  346.                     }
  347.                 }
  348.                 if (n > 0) {
  349.                     sb.append(buf, 0, n);
  350.                 }
  351.                 resetAndSkipFully(in, n);
  352.             }
  353.         }
  354.         StringBuilder buf = sizeHint > 0 ? new StringBuilder(sizeHint)
  355.                 : new StringBuilder();
  356.         int i;
  357.         while ((i = in.read()) != -1) {
  358.             char c = (char) i;
  359.             buf.append(c);
  360.             if (c == '\n') {
  361.                 break;
  362.             }
  363.         }
  364.         return buf.toString();
  365.     }

  366.     private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException {
  367.         fd.reset();
  368.         while (toSkip > 0) {
  369.             long r = fd.skip(toSkip);
  370.             if (r <= 0) {
  371.                 throw new EOFException(JGitText.get().shortSkipOfBlock);
  372.             }
  373.             toSkip -= r;
  374.         }
  375.     }

  376.     private IO() {
  377.         // Don't create instances of a static only utility.
  378.     }
  379. }