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