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>
5 * and other copyright owners as documented in the project's IP log.
6 *
7 * This program and the accompanying materials are made available
8 * under the terms of the Eclipse Distribution License v1.0 which
9 * accompanies this distribution, is reproduced below, and is
10 * available at http://www.eclipse.org/org/documents/edl-v10.php
11 *
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or
15 * without modification, are permitted provided that the following
16 * conditions are met:
17 *
18 * - Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * - Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials provided
24 * with the distribution.
25 *
26 * - Neither the name of the Eclipse Foundation, Inc. nor the
27 * names of its contributors may be used to endorse or promote
28 * products derived from this software without specific prior
29 * written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 */
45
46 package org.eclipse.jgit.util;
47
48 import java.io.EOFException;
49 import java.io.File;
50 import java.io.FileInputStream;
51 import java.io.FileNotFoundException;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.Reader;
55 import java.nio.ByteBuffer;
56 import java.nio.channels.ReadableByteChannel;
57 import java.text.MessageFormat;
58 import java.util.ArrayList;
59 import java.util.List;
60
61 import org.eclipse.jgit.internal.JGitText;
62
63 /**
64 * Input/Output utilities
65 */
66 public class IO {
67
68 /**
69 * Read an entire local file into memory as a byte array.
70 *
71 * @param path
72 * location of the file to read.
73 * @return complete contents of the requested local file.
74 * @throws FileNotFoundException
75 * the file does not exist.
76 * @throws IOException
77 * the file exists, but its contents cannot be read.
78 */
79 public static final byte[] readFully(final File path)
80 throws FileNotFoundException, IOException {
81 return IO.readFully(path, Integer.MAX_VALUE);
82 }
83
84 /**
85 * Read at most limit bytes from the local file into memory as a byte array.
86 *
87 * @param path
88 * location of the file to read.
89 * @param limit
90 * maximum number of bytes to read, if the file is larger than
91 * only the first limit number of bytes are returned
92 * @return complete contents of the requested local file. If the contents
93 * exceeds the limit, then only the limit is returned.
94 * @throws FileNotFoundException
95 * the file does not exist.
96 * @throws IOException
97 * the file exists, but its contents cannot be read.
98 */
99 public static final byte[] readSome(final File path, final int limit)
100 throws FileNotFoundException, IOException {
101 FileInputStream in = new FileInputStream(path);
102 try {
103 byte[] buf = new byte[limit];
104 int cnt = 0;
105 for (;;) {
106 int n = in.read(buf, cnt, buf.length - cnt);
107 if (n <= 0)
108 break;
109 cnt += n;
110 }
111 if (cnt == buf.length)
112 return buf;
113 byte[] res = new byte[cnt];
114 System.arraycopy(buf, 0, res, 0, cnt);
115 return res;
116 } finally {
117 try {
118 in.close();
119 } catch (IOException ignored) {
120 // do nothing
121 }
122 }
123 }
124
125 /**
126 * Read an entire local file into memory as a byte array.
127 *
128 * @param path
129 * location of the file to read.
130 * @param max
131 * maximum number of bytes to read, if the file is larger than
132 * this limit an IOException is thrown.
133 * @return complete contents of the requested local file.
134 * @throws FileNotFoundException
135 * the file does not exist.
136 * @throws IOException
137 * the file exists, but its contents cannot be read.
138 */
139 public static final byte[] readFully(final File path, final int max)
140 throws FileNotFoundException, IOException {
141 final FileInputStream in = new FileInputStream(path);
142 try {
143 long sz = Math.max(path.length(), 1);
144 if (sz > max)
145 throw new IOException(MessageFormat.format(
146 JGitText.get().fileIsTooLarge, path));
147
148 byte[] buf = new byte[(int) sz];
149 int valid = 0;
150 for (;;) {
151 if (buf.length == valid) {
152 if (buf.length == max) {
153 int next = in.read();
154 if (next < 0)
155 break;
156
157 throw new IOException(MessageFormat.format(
158 JGitText.get().fileIsTooLarge, path));
159 }
160
161 byte[] nb = new byte[Math.min(buf.length * 2, max)];
162 System.arraycopy(buf, 0, nb, 0, valid);
163 buf = nb;
164 }
165 int n = in.read(buf, valid, buf.length - valid);
166 if (n < 0)
167 break;
168 valid += n;
169 }
170 if (valid < buf.length) {
171 byte[] nb = new byte[valid];
172 System.arraycopy(buf, 0, nb, 0, valid);
173 buf = nb;
174 }
175 return buf;
176 } finally {
177 try {
178 in.close();
179 } catch (IOException ignored) {
180 // ignore any close errors, this was a read only stream
181 }
182 }
183 }
184
185 /**
186 * Read an entire input stream into memory as a ByteBuffer.
187 *
188 * Note: The stream is read to its end and is not usable after calling this
189 * method. The caller is responsible for closing the stream.
190 *
191 * @param in
192 * input stream to be read.
193 * @param sizeHint
194 * a hint on the approximate number of bytes contained in the
195 * stream, used to allocate temporary buffers more efficiently
196 * @return complete contents of the input stream. The ByteBuffer always has
197 * a writable backing array, with {@code position() == 0} and
198 * {@code limit()} equal to the actual length read. Callers may rely
199 * on obtaining the underlying array for efficient data access. If
200 * {@code sizeHint} was too large, the array may be over-allocated,
201 * resulting in {@code limit() < array().length}.
202 * @throws IOException
203 * there was an error reading from the stream.
204 */
205 public static ByteBuffer readWholeStream(InputStream in, int sizeHint)
206 throws IOException {
207 byte[] out = new byte[sizeHint];
208 int pos = 0;
209 while (pos < out.length) {
210 int read = in.read(out, pos, out.length - pos);
211 if (read < 0)
212 return ByteBuffer.wrap(out, 0, pos);
213 pos += read;
214 }
215
216 int last = in.read();
217 if (last < 0)
218 return ByteBuffer.wrap(out, 0, pos);
219
220 @SuppressWarnings("resource" /* java 7 */)
221 TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(Integer.MAX_VALUE);
222 tmp.write(out);
223 tmp.write(last);
224 tmp.copy(in);
225 return ByteBuffer.wrap(tmp.toByteArray());
226 }
227
228 /**
229 * Read the entire byte array into memory, or throw an exception.
230 *
231 * @param fd
232 * input stream to read the data from.
233 * @param dst
234 * buffer that must be fully populated, [off, off+len).
235 * @param off
236 * position within the buffer to start writing to.
237 * @param len
238 * number of bytes that must be read.
239 * @throws EOFException
240 * the stream ended before dst was fully populated.
241 * @throws IOException
242 * there was an error reading from the stream.
243 */
244 public static void readFully(final InputStream fd, final byte[] dst,
245 int off, int len) throws IOException {
246 while (len > 0) {
247 final int r = fd.read(dst, off, len);
248 if (r <= 0)
249 throw new EOFException(JGitText.get().shortReadOfBlock);
250 off += r;
251 len -= r;
252 }
253 }
254
255 /**
256 * Read as much of the array as possible from a channel.
257 *
258 * @param channel
259 * channel to read data from.
260 * @param dst
261 * buffer that must be fully populated, [off, off+len).
262 * @param off
263 * position within the buffer to start writing to.
264 * @param len
265 * number of bytes that should be read.
266 * @return number of bytes actually read.
267 * @throws IOException
268 * there was an error reading from the channel.
269 */
270 public static int read(ReadableByteChannel channel, byte[] dst, int off,
271 int len) throws IOException {
272 if (len == 0)
273 return 0;
274 int cnt = 0;
275 while (0 < len) {
276 int r = channel.read(ByteBuffer.wrap(dst, off, len));
277 if (r <= 0)
278 break;
279 off += r;
280 len -= r;
281 cnt += r;
282 }
283 return cnt != 0 ? cnt : -1;
284 }
285
286 /**
287 * Read the entire byte array into memory, unless input is shorter
288 *
289 * @param fd
290 * input stream to read the data from.
291 * @param dst
292 * buffer that must be fully populated, [off, off+len).
293 * @param off
294 * position within the buffer to start writing to.
295 * @return number of bytes in buffer or stream, whichever is shortest
296 * @throws IOException
297 * there was an error reading from the stream.
298 */
299 public static int readFully(InputStream fd, byte[] dst, int off)
300 throws IOException {
301 int r;
302 int len = 0;
303 while ((r = fd.read(dst, off, dst.length - off)) >= 0
304 && len < dst.length) {
305 off += r;
306 len += r;
307 }
308 return len;
309 }
310
311 /**
312 * Skip an entire region of an input stream.
313 * <p>
314 * The input stream's position is moved forward by the number of requested
315 * bytes, discarding them from the input. This method does not return until
316 * the exact number of bytes requested has been skipped.
317 *
318 * @param fd
319 * the stream to skip bytes from.
320 * @param toSkip
321 * total number of bytes to be discarded. Must be >= 0.
322 * @throws EOFException
323 * the stream ended before the requested number of bytes were
324 * skipped.
325 * @throws IOException
326 * there was an error reading from the stream.
327 */
328 public static void skipFully(final InputStream fd, long toSkip)
329 throws IOException {
330 while (toSkip > 0) {
331 final long r = fd.skip(toSkip);
332 if (r <= 0)
333 throw new EOFException(JGitText.get().shortSkipOfBlock);
334 toSkip -= r;
335 }
336 }
337
338 /**
339 * Divides the given string into lines.
340 *
341 * @param s
342 * the string to read
343 * @return the string divided into lines
344 * @since 2.0
345 */
346 public static List<String> readLines(final String s) {
347 List<String> l = new ArrayList<>();
348 StringBuilder sb = new StringBuilder();
349 for (int i = 0; i < s.length(); i++) {
350 char c = s.charAt(i);
351 if (c == '\n') {
352 l.add(sb.toString());
353 sb.setLength(0);
354 continue;
355 }
356 if (c == '\r') {
357 if (i + 1 < s.length()) {
358 c = s.charAt(++i);
359 l.add(sb.toString());
360 sb.setLength(0);
361 if (c != '\n')
362 sb.append(c);
363 continue;
364 } else { // EOF
365 l.add(sb.toString());
366 break;
367 }
368 }
369 sb.append(c);
370 }
371 l.add(sb.toString());
372 return l;
373 }
374
375 /**
376 * Read the next line from a reader.
377 * <p>
378 * Like {@link java.io.BufferedReader#readLine()}, but only treats
379 * {@code \n} as end-of-line, and includes the trailing newline.
380 *
381 * @param in
382 * the reader to read from.
383 * @param sizeHint
384 * hint for buffer sizing; 0 or negative for default.
385 * @return the next line from the input, always ending in {@code \n} unless
386 * EOF was reached.
387 * @throws IOException
388 * there was an error reading from the stream.
389 * @since 4.1
390 */
391 public static String readLine(Reader in, int sizeHint) throws IOException {
392 if (in.markSupported()) {
393 if (sizeHint <= 0) {
394 sizeHint = 1024;
395 }
396 StringBuilder sb = new StringBuilder(sizeHint);
397 char[] buf = new char[sizeHint];
398 while (true) {
399 in.mark(sizeHint);
400 int n = in.read(buf);
401 if (n < 0) {
402 in.reset();
403 return sb.toString();
404 }
405 for (int i = 0; i < n; i++) {
406 if (buf[i] == '\n') {
407 resetAndSkipFully(in, ++i);
408 sb.append(buf, 0, i);
409 return sb.toString();
410 }
411 }
412 if (n > 0) {
413 sb.append(buf, 0, n);
414 }
415 resetAndSkipFully(in, n);
416 }
417 } else {
418 StringBuilder buf = sizeHint > 0
419 ? new StringBuilder(sizeHint)
420 : new StringBuilder();
421 int i;
422 while ((i = in.read()) != -1) {
423 char c = (char) i;
424 buf.append(c);
425 if (c == '\n') {
426 break;
427 }
428 }
429 return buf.toString();
430 }
431 }
432
433 private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException {
434 fd.reset();
435 while (toSkip > 0) {
436 long r = fd.skip(toSkip);
437 if (r <= 0) {
438 throw new EOFException(JGitText.get().shortSkipOfBlock);
439 }
440 toSkip -= r;
441 }
442 }
443
444 private IO() {
445 // Don't create instances of a static only utility.
446 }
447 }