| /* |
| * Copyright (C) 2008-2009, Google Inc. |
| * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com> |
| * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package org.eclipse.jgit.util; |
| |
| import java.io.EOFException; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.ReadableByteChannel; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.jgit.internal.JGitText; |
| import org.eclipse.jgit.util.io.SilentFileInputStream; |
| |
| /** |
| * Input/Output utilities |
| */ |
| public class IO { |
| |
| /** |
| * Read an entire local file into memory as a byte array. |
| * |
| * @param path |
| * location of the file to read. |
| * @return complete contents of the requested local file. |
| * @throws java.io.FileNotFoundException |
| * the file does not exist. |
| * @throws java.io.IOException |
| * the file exists, but its contents cannot be read. |
| */ |
| public static final byte[] readFully(File path) |
| throws FileNotFoundException, IOException { |
| return IO.readFully(path, Integer.MAX_VALUE); |
| } |
| |
| /** |
| * Read at most limit bytes from the local file into memory as a byte array. |
| * |
| * @param path |
| * location of the file to read. |
| * @param limit |
| * maximum number of bytes to read, if the file is larger than |
| * only the first limit number of bytes are returned |
| * @return complete contents of the requested local file. If the contents |
| * exceeds the limit, then only the limit is returned. |
| * @throws java.io.FileNotFoundException |
| * the file does not exist. |
| * @throws java.io.IOException |
| * the file exists, but its contents cannot be read. |
| */ |
| public static final byte[] readSome(File path, int limit) |
| throws FileNotFoundException, IOException { |
| try (SilentFileInputStream in = new SilentFileInputStream(path)) { |
| return in.readNBytes(limit); |
| } |
| } |
| |
| /** |
| * Read an entire local file into memory as a byte array. |
| * |
| * @param path |
| * location of the file to read. |
| * @param max |
| * maximum number of bytes to read, if the file is larger than |
| * this limit an IOException is thrown. |
| * @return complete contents of the requested local file. |
| * @throws java.io.FileNotFoundException |
| * the file does not exist. |
| * @throws java.io.IOException |
| * the file exists, but its contents cannot be read. |
| */ |
| public static final byte[] readFully(File path, int max) |
| throws FileNotFoundException, IOException { |
| try (SilentFileInputStream in = new SilentFileInputStream(path)) { |
| byte[] buf = in.readNBytes(max); |
| if (in.read() != -1) { |
| throw new IOException(MessageFormat.format( |
| JGitText.get().fileIsTooLarge, path)); |
| } |
| return buf; |
| } |
| } |
| |
| /** |
| * Read an entire input stream into memory as a ByteBuffer. |
| * |
| * Note: The stream is read to its end and is not usable after calling this |
| * method. The caller is responsible for closing the stream. |
| * |
| * @param in |
| * input stream to be read. |
| * @param sizeHint |
| * a hint on the approximate number of bytes contained in the |
| * stream, used to allocate temporary buffers more efficiently |
| * @return complete contents of the input stream. The ByteBuffer always has |
| * a writable backing array, with {@code position() == 0} and |
| * {@code limit()} equal to the actual length read. Callers may rely |
| * on obtaining the underlying array for efficient data access. If |
| * {@code sizeHint} was too large, the array may be over-allocated, |
| * resulting in {@code limit() < array().length}. |
| * @throws java.io.IOException |
| * there was an error reading from the stream. |
| */ |
| public static ByteBuffer readWholeStream(InputStream in, int sizeHint) |
| throws IOException { |
| return ByteBuffer.wrap(in.readAllBytes()); |
| } |
| |
| /** |
| * Read the entire byte array into memory, or throw an exception. |
| * |
| * @param fd |
| * input stream to read the data from. |
| * @param dst |
| * buffer that must be fully populated, [off, off+len). |
| * @param off |
| * position within the buffer to start writing to. |
| * @param len |
| * number of bytes that must be read. |
| * @throws EOFException |
| * the stream ended before dst was fully populated. |
| * @throws java.io.IOException |
| * there was an error reading from the stream. |
| */ |
| public static void readFully(final InputStream fd, final byte[] dst, |
| int off, int len) throws IOException { |
| int read = fd.readNBytes(dst, off, len); |
| if (read != len) |
| throw new EOFException(JGitText.get().shortReadOfBlock); |
| } |
| |
| /** |
| * Read from input until the entire byte array filled, or throw an exception |
| * if stream ends first. |
| * |
| * @param fd |
| * input stream to read the data from. |
| * @param dst |
| * buffer that must be fully populated |
| * @throws EOFException |
| * the stream ended before dst was fully populated. |
| * @throws java.io.IOException |
| * there was an error reading from the stream. |
| * @since 6.5 |
| */ |
| public static void readFully(InputStream fd, byte[] dst) |
| throws IOException { |
| readFully(fd, dst, 0, dst.length); |
| } |
| |
| /** |
| * Read as much of the array as possible from a channel. |
| * |
| * @param channel |
| * channel to read data from. |
| * @param dst |
| * buffer that must be fully populated, [off, off+len). |
| * @param off |
| * position within the buffer to start writing to. |
| * @param len |
| * number of bytes that should be read. |
| * @return number of bytes actually read. |
| * @throws java.io.IOException |
| * there was an error reading from the channel. |
| */ |
| public static int read(ReadableByteChannel channel, byte[] dst, int off, |
| int len) throws IOException { |
| if (len == 0) |
| return 0; |
| int cnt = 0; |
| while (0 < len) { |
| int r = channel.read(ByteBuffer.wrap(dst, off, len)); |
| if (r <= 0) |
| break; |
| off += r; |
| len -= r; |
| cnt += r; |
| } |
| return cnt != 0 ? cnt : -1; |
| } |
| |
| /** |
| * Read the entire byte array into memory, unless input is shorter |
| * |
| * @param fd |
| * input stream to read the data from. |
| * @param dst |
| * buffer that must be fully populated, [off, off+len). |
| * @param off |
| * position within the buffer to start writing to. |
| * @return number of bytes read |
| * @throws java.io.IOException |
| * there was an error reading from the stream. |
| */ |
| public static int readFully(InputStream fd, byte[] dst, int off) |
| throws IOException { |
| return fd.readNBytes(dst, off, dst.length - off); |
| } |
| |
| /** |
| * Skip an entire region of an input stream. |
| * <p> |
| * The input stream's position is moved forward by the number of requested |
| * bytes, discarding them from the input. This method does not return until |
| * the exact number of bytes requested has been skipped. |
| * |
| * @param fd |
| * the stream to skip bytes from. |
| * @param toSkip |
| * total number of bytes to be discarded. Must be >= 0. |
| * @throws EOFException |
| * the stream ended before the requested number of bytes were |
| * skipped. |
| * @throws java.io.IOException |
| * there was an error reading from the stream. |
| */ |
| public static void skipFully(InputStream fd, long toSkip) |
| throws IOException { |
| // same as fd.skipNBytes(toSkip) of JDK 12; |
| while (toSkip > 0) { |
| final long r = fd.skip(toSkip); |
| if (r <= 0) |
| throw new EOFException(JGitText.get().shortSkipOfBlock); |
| toSkip -= r; |
| } |
| } |
| |
| /** |
| * Divides the given string into lines. |
| * |
| * @param s |
| * the string to read |
| * @return the string divided into lines |
| * @since 2.0 |
| */ |
| public static List<String> readLines(String s) { |
| List<String> l = new ArrayList<>(); |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < s.length(); i++) { |
| char c = s.charAt(i); |
| if (c == '\n') { |
| l.add(sb.toString()); |
| sb.setLength(0); |
| continue; |
| } |
| if (c == '\r') { |
| if (i + 1 < s.length()) { |
| c = s.charAt(++i); |
| l.add(sb.toString()); |
| sb.setLength(0); |
| if (c != '\n') { |
| sb.append(c); |
| } |
| continue; |
| } |
| // EOF |
| l.add(sb.toString()); |
| break; |
| } |
| sb.append(c); |
| } |
| l.add(sb.toString()); |
| return l; |
| } |
| |
| /** |
| * Read the next line from a reader. |
| * <p> |
| * Like {@link java.io.BufferedReader#readLine()}, but only treats |
| * {@code \n} as end-of-line, and includes the trailing newline. |
| * |
| * @param in |
| * the reader to read from. |
| * @param sizeHint |
| * hint for buffer sizing; 0 or negative for default. |
| * @return the next line from the input, always ending in {@code \n} unless |
| * EOF was reached. |
| * @throws java.io.IOException |
| * there was an error reading from the stream. |
| * @since 4.1 |
| */ |
| public static String readLine(Reader in, int sizeHint) throws IOException { |
| if (in.markSupported()) { |
| if (sizeHint <= 0) { |
| sizeHint = 1024; |
| } |
| StringBuilder sb = new StringBuilder(sizeHint); |
| char[] buf = new char[sizeHint]; |
| while (true) { |
| in.mark(sizeHint); |
| int n = in.read(buf); |
| if (n < 0) { |
| in.reset(); |
| return sb.toString(); |
| } |
| for (int i = 0; i < n; i++) { |
| if (buf[i] == '\n') { |
| resetAndSkipFully(in, ++i); |
| sb.append(buf, 0, i); |
| return sb.toString(); |
| } |
| } |
| if (n > 0) { |
| sb.append(buf, 0, n); |
| } |
| resetAndSkipFully(in, n); |
| } |
| } |
| StringBuilder buf = sizeHint > 0 ? new StringBuilder(sizeHint) |
| : new StringBuilder(); |
| int i; |
| while ((i = in.read()) != -1) { |
| char c = (char) i; |
| buf.append(c); |
| if (c == '\n') { |
| break; |
| } |
| } |
| return buf.toString(); |
| } |
| |
| private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException { |
| fd.reset(); |
| while (toSkip > 0) { |
| long r = fd.skip(toSkip); |
| if (r <= 0) { |
| throw new EOFException(JGitText.get().shortSkipOfBlock); |
| } |
| toSkip -= r; |
| } |
| } |
| |
| private IO() { |
| // Don't create instances of a static only utility. |
| } |
| } |