View Javadoc
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  
13  package org.eclipse.jgit.util;
14  
15  import java.io.EOFException;
16  import java.io.File;
17  import java.io.FileNotFoundException;
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.Reader;
21  import java.nio.ByteBuffer;
22  import java.nio.channels.ReadableByteChannel;
23  import java.text.MessageFormat;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.eclipse.jgit.internal.JGitText;
28  import org.eclipse.jgit.util.io.SilentFileInputStream;
29  
30  /**
31   * Input/Output utilities
32   */
33  public class IO {
34  
35  	/**
36  	 * Read an entire local file into memory as a byte array.
37  	 *
38  	 * @param path
39  	 *            location of the file to read.
40  	 * @return complete contents of the requested local file.
41  	 * @throws java.io.FileNotFoundException
42  	 *             the file does not exist.
43  	 * @throws java.io.IOException
44  	 *             the file exists, but its contents cannot be read.
45  	 */
46  	public static final byte[] readFully(File path)
47  			throws FileNotFoundException, IOException {
48  		return IO.readFully(path, Integer.MAX_VALUE);
49  	}
50  
51  	/**
52  	 * Read at most limit bytes from the local file into memory as a byte array.
53  	 *
54  	 * @param path
55  	 *            location of the file to read.
56  	 * @param limit
57  	 *            maximum number of bytes to read, if the file is larger than
58  	 *            only the first limit number of bytes are returned
59  	 * @return complete contents of the requested local file. If the contents
60  	 *         exceeds the limit, then only the limit is returned.
61  	 * @throws java.io.FileNotFoundException
62  	 *             the file does not exist.
63  	 * @throws java.io.IOException
64  	 *             the file exists, but its contents cannot be read.
65  	 */
66  	public static final byte[] readSome(File path, int limit)
67  			throws FileNotFoundException, IOException {
68  		try (SilentFileInputStreamtStream.html#SilentFileInputStream">SilentFileInputStream in = new SilentFileInputStream(path)) {
69  			byte[] buf = new byte[limit];
70  			int cnt = 0;
71  			for (;;) {
72  				int n = in.read(buf, cnt, buf.length - cnt);
73  				if (n <= 0)
74  					break;
75  				cnt += n;
76  			}
77  			if (cnt == buf.length)
78  				return buf;
79  			byte[] res = new byte[cnt];
80  			System.arraycopy(buf, 0, res, 0, cnt);
81  			return res;
82  		}
83  	}
84  
85  	/**
86  	 * Read an entire local file into memory as a byte array.
87  	 *
88  	 * @param path
89  	 *            location of the file to read.
90  	 * @param max
91  	 *            maximum number of bytes to read, if the file is larger than
92  	 *            this limit an IOException is thrown.
93  	 * @return complete contents of the requested local file.
94  	 * @throws java.io.FileNotFoundException
95  	 *             the file does not exist.
96  	 * @throws java.io.IOException
97  	 *             the file exists, but its contents cannot be read.
98  	 */
99  	public static final byte[] readFully(File path, int max)
100 			throws FileNotFoundException, IOException {
101 		try (SilentFileInputStreamtStream.html#SilentFileInputStream">SilentFileInputStream in = new SilentFileInputStream(path)) {
102 			long sz = Math.max(path.length(), 1);
103 			if (sz > max)
104 				throw new IOException(MessageFormat.format(
105 						JGitText.get().fileIsTooLarge, path));
106 
107 			byte[] buf = new byte[(int) sz];
108 			int valid = 0;
109 			for (;;) {
110 				if (buf.length == valid) {
111 					if (buf.length == max) {
112 						int next = in.read();
113 						if (next < 0)
114 							break;
115 
116 						throw new IOException(MessageFormat.format(
117 								JGitText.get().fileIsTooLarge, path));
118 					}
119 
120 					byte[] nb = new byte[Math.min(buf.length * 2, max)];
121 					System.arraycopy(buf, 0, nb, 0, valid);
122 					buf = nb;
123 				}
124 				int n = in.read(buf, valid, buf.length - valid);
125 				if (n < 0)
126 					break;
127 				valid += n;
128 			}
129 			if (valid < buf.length) {
130 				byte[] nb = new byte[valid];
131 				System.arraycopy(buf, 0, nb, 0, valid);
132 				buf = nb;
133 			}
134 			return buf;
135 		}
136 	}
137 
138 	/**
139 	 * Read an entire input stream into memory as a ByteBuffer.
140 	 *
141 	 * Note: The stream is read to its end and is not usable after calling this
142 	 * method. The caller is responsible for closing the stream.
143 	 *
144 	 * @param in
145 	 *            input stream to be read.
146 	 * @param sizeHint
147 	 *            a hint on the approximate number of bytes contained in the
148 	 *            stream, used to allocate temporary buffers more efficiently
149 	 * @return complete contents of the input stream. The ByteBuffer always has
150 	 *         a writable backing array, with {@code position() == 0} and
151 	 *         {@code limit()} equal to the actual length read. Callers may rely
152 	 *         on obtaining the underlying array for efficient data access. If
153 	 *         {@code sizeHint} was too large, the array may be over-allocated,
154 	 *         resulting in {@code limit() < array().length}.
155 	 * @throws java.io.IOException
156 	 *             there was an error reading from the stream.
157 	 */
158 	public static ByteBuffer readWholeStream(InputStream in, int sizeHint)
159 			throws IOException {
160 		byte[] out = new byte[sizeHint];
161 		int pos = 0;
162 		while (pos < out.length) {
163 			int read = in.read(out, pos, out.length - pos);
164 			if (read < 0)
165 				return ByteBuffer.wrap(out, 0, pos);
166 			pos += read;
167 		}
168 
169 		int last = in.read();
170 		if (last < 0)
171 			return ByteBuffer.wrap(out, 0, pos);
172 
173 		try (TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(
174 				Integer.MAX_VALUE)) {
175 			tmp.write(out);
176 			tmp.write(last);
177 			tmp.copy(in);
178 			return ByteBuffer.wrap(tmp.toByteArray());
179 		}
180 	}
181 
182 	/**
183 	 * Read the entire byte array into memory, or throw an exception.
184 	 *
185 	 * @param fd
186 	 *            input stream to read the data from.
187 	 * @param dst
188 	 *            buffer that must be fully populated, [off, off+len).
189 	 * @param off
190 	 *            position within the buffer to start writing to.
191 	 * @param len
192 	 *            number of bytes that must be read.
193 	 * @throws EOFException
194 	 *             the stream ended before dst was fully populated.
195 	 * @throws java.io.IOException
196 	 *             there was an error reading from the stream.
197 	 */
198 	public static void readFully(final InputStream fd, final byte[] dst,
199 			int off, int len) throws IOException {
200 		while (len > 0) {
201 			final int r = fd.read(dst, off, len);
202 			if (r <= 0)
203 				throw new EOFException(JGitText.get().shortReadOfBlock);
204 			off += r;
205 			len -= r;
206 		}
207 	}
208 
209 	/**
210 	 * Read as much of the array as possible from a channel.
211 	 *
212 	 * @param channel
213 	 *            channel to read data from.
214 	 * @param dst
215 	 *            buffer that must be fully populated, [off, off+len).
216 	 * @param off
217 	 *            position within the buffer to start writing to.
218 	 * @param len
219 	 *            number of bytes that should be read.
220 	 * @return number of bytes actually read.
221 	 * @throws java.io.IOException
222 	 *             there was an error reading from the channel.
223 	 */
224 	public static int read(ReadableByteChannel channel, byte[] dst, int off,
225 			int len) throws IOException {
226 		if (len == 0)
227 			return 0;
228 		int cnt = 0;
229 		while (0 < len) {
230 			int r = channel.read(ByteBuffer.wrap(dst, off, len));
231 			if (r <= 0)
232 				break;
233 			off += r;
234 			len -= r;
235 			cnt += r;
236 		}
237 		return cnt != 0 ? cnt : -1;
238 	}
239 
240 	/**
241 	 * Read the entire byte array into memory, unless input is shorter
242 	 *
243 	 * @param fd
244 	 *            input stream to read the data from.
245 	 * @param dst
246 	 *            buffer that must be fully populated, [off, off+len).
247 	 * @param off
248 	 *            position within the buffer to start writing to.
249 	 * @return number of bytes in buffer or stream, whichever is shortest
250 	 * @throws java.io.IOException
251 	 *             there was an error reading from the stream.
252 	 */
253 	public static int readFully(InputStream fd, byte[] dst, int off)
254 			throws IOException {
255 		int r;
256 		int len = 0;
257 		while ((r = fd.read(dst, off, dst.length - off)) >= 0
258 				&& len < dst.length) {
259 			off += r;
260 			len += r;
261 		}
262 		return len;
263 	}
264 
265 	/**
266 	 * Skip an entire region of an input stream.
267 	 * <p>
268 	 * The input stream's position is moved forward by the number of requested
269 	 * bytes, discarding them from the input. This method does not return until
270 	 * the exact number of bytes requested has been skipped.
271 	 *
272 	 * @param fd
273 	 *            the stream to skip bytes from.
274 	 * @param toSkip
275 	 *            total number of bytes to be discarded. Must be &gt;= 0.
276 	 * @throws EOFException
277 	 *             the stream ended before the requested number of bytes were
278 	 *             skipped.
279 	 * @throws java.io.IOException
280 	 *             there was an error reading from the stream.
281 	 */
282 	public static void skipFully(InputStream fd, long toSkip)
283 			throws IOException {
284 		while (toSkip > 0) {
285 			final long r = fd.skip(toSkip);
286 			if (r <= 0)
287 				throw new EOFException(JGitText.get().shortSkipOfBlock);
288 			toSkip -= r;
289 		}
290 	}
291 
292 	/**
293 	 * Divides the given string into lines.
294 	 *
295 	 * @param s
296 	 *            the string to read
297 	 * @return the string divided into lines
298 	 * @since 2.0
299 	 */
300 	public static List<String> readLines(String s) {
301 		List<String> l = new ArrayList<>();
302 		StringBuilder sb = new StringBuilder();
303 		for (int i = 0; i < s.length(); i++) {
304 			char c = s.charAt(i);
305 			if (c == '\n') {
306 				l.add(sb.toString());
307 				sb.setLength(0);
308 				continue;
309 			}
310 			if (c == '\r') {
311 				if (i + 1 < s.length()) {
312 					c = s.charAt(++i);
313 					l.add(sb.toString());
314 					sb.setLength(0);
315 					if (c != '\n') {
316 						sb.append(c);
317 					}
318 					continue;
319 				}
320 				// EOF
321 				l.add(sb.toString());
322 				break;
323 			}
324 			sb.append(c);
325 		}
326 		l.add(sb.toString());
327 		return l;
328 	}
329 
330 	/**
331 	 * Read the next line from a reader.
332 	 * <p>
333 	 * Like {@link java.io.BufferedReader#readLine()}, but only treats
334 	 * {@code \n} as end-of-line, and includes the trailing newline.
335 	 *
336 	 * @param in
337 	 *            the reader to read from.
338 	 * @param sizeHint
339 	 *            hint for buffer sizing; 0 or negative for default.
340 	 * @return the next line from the input, always ending in {@code \n} unless
341 	 *         EOF was reached.
342 	 * @throws java.io.IOException
343 	 *             there was an error reading from the stream.
344 	 * @since 4.1
345 	 */
346 	public static String readLine(Reader in, int sizeHint) throws IOException {
347 		if (in.markSupported()) {
348 			if (sizeHint <= 0) {
349 				sizeHint = 1024;
350 			}
351 			StringBuilder sb = new StringBuilder(sizeHint);
352 			char[] buf = new char[sizeHint];
353 			while (true) {
354 				in.mark(sizeHint);
355 				int n = in.read(buf);
356 				if (n < 0) {
357 					in.reset();
358 					return sb.toString();
359 				}
360 				for (int i = 0; i < n; i++) {
361 					if (buf[i] == '\n') {
362 						resetAndSkipFully(in, ++i);
363 						sb.append(buf, 0, i);
364 						return sb.toString();
365 					}
366 				}
367 				if (n > 0) {
368 					sb.append(buf, 0, n);
369 				}
370 				resetAndSkipFully(in, n);
371 			}
372 		}
373 		StringBuilder buf = sizeHint > 0 ? new StringBuilder(sizeHint)
374 				: new StringBuilder();
375 		int i;
376 		while ((i = in.read()) != -1) {
377 			char c = (char) i;
378 			buf.append(c);
379 			if (c == '\n') {
380 				break;
381 			}
382 		}
383 		return buf.toString();
384 	}
385 
386 	private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException {
387 		fd.reset();
388 		while (toSkip > 0) {
389 			long r = fd.skip(toSkip);
390 			if (r <= 0) {
391 				throw new EOFException(JGitText.get().shortSkipOfBlock);
392 			}
393 			toSkip -= r;
394 		}
395 	}
396 
397 	private IO() {
398 		// Don't create instances of a static only utility.
399 	}
400 }