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 (SilentFileInputStreamtStream.html#SilentFileInputStream">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 (SilentFileInputStreamtStream.html#SilentFileInputStream">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 try (TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(
207 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 /**
216 * Read the entire byte array into memory, or throw an exception.
217 *
218 * @param fd
219 * input stream to read the data from.
220 * @param dst
221 * buffer that must be fully populated, [off, off+len).
222 * @param off
223 * position within the buffer to start writing to.
224 * @param len
225 * number of bytes that must be read.
226 * @throws EOFException
227 * the stream ended before dst was fully populated.
228 * @throws java.io.IOException
229 * there was an error reading from the stream.
230 */
231 public static void readFully(final InputStream fd, final byte[] dst,
232 int off, int len) throws IOException {
233 while (len > 0) {
234 final int r = fd.read(dst, off, len);
235 if (r <= 0)
236 throw new EOFException(JGitText.get().shortReadOfBlock);
237 off += r;
238 len -= r;
239 }
240 }
241
242 /**
243 * Read as much of the array as possible from a channel.
244 *
245 * @param channel
246 * channel to read data from.
247 * @param dst
248 * buffer that must be fully populated, [off, off+len).
249 * @param off
250 * position within the buffer to start writing to.
251 * @param len
252 * number of bytes that should be read.
253 * @return number of bytes actually read.
254 * @throws java.io.IOException
255 * there was an error reading from the channel.
256 */
257 public static int read(ReadableByteChannel channel, byte[] dst, int off,
258 int len) throws IOException {
259 if (len == 0)
260 return 0;
261 int cnt = 0;
262 while (0 < len) {
263 int r = channel.read(ByteBuffer.wrap(dst, off, len));
264 if (r <= 0)
265 break;
266 off += r;
267 len -= r;
268 cnt += r;
269 }
270 return cnt != 0 ? cnt : -1;
271 }
272
273 /**
274 * Read the entire byte array into memory, unless input is shorter
275 *
276 * @param fd
277 * input stream to read the data from.
278 * @param dst
279 * buffer that must be fully populated, [off, off+len).
280 * @param off
281 * position within the buffer to start writing to.
282 * @return number of bytes in buffer or stream, whichever is shortest
283 * @throws java.io.IOException
284 * there was an error reading from the stream.
285 */
286 public static int readFully(InputStream fd, byte[] dst, int off)
287 throws IOException {
288 int r;
289 int len = 0;
290 while ((r = fd.read(dst, off, dst.length - off)) >= 0
291 && len < dst.length) {
292 off += r;
293 len += r;
294 }
295 return len;
296 }
297
298 /**
299 * Skip an entire region of an input stream.
300 * <p>
301 * The input stream's position is moved forward by the number of requested
302 * bytes, discarding them from the input. This method does not return until
303 * the exact number of bytes requested has been skipped.
304 *
305 * @param fd
306 * the stream to skip bytes from.
307 * @param toSkip
308 * total number of bytes to be discarded. Must be >= 0.
309 * @throws EOFException
310 * the stream ended before the requested number of bytes were
311 * skipped.
312 * @throws java.io.IOException
313 * there was an error reading from the stream.
314 */
315 public static void skipFully(InputStream fd, long toSkip)
316 throws IOException {
317 while (toSkip > 0) {
318 final long r = fd.skip(toSkip);
319 if (r <= 0)
320 throw new EOFException(JGitText.get().shortSkipOfBlock);
321 toSkip -= r;
322 }
323 }
324
325 /**
326 * Divides the given string into lines.
327 *
328 * @param s
329 * the string to read
330 * @return the string divided into lines
331 * @since 2.0
332 */
333 public static List<String> readLines(String s) {
334 List<String> l = new ArrayList<>();
335 StringBuilder sb = new StringBuilder();
336 for (int i = 0; i < s.length(); i++) {
337 char c = s.charAt(i);
338 if (c == '\n') {
339 l.add(sb.toString());
340 sb.setLength(0);
341 continue;
342 }
343 if (c == '\r') {
344 if (i + 1 < s.length()) {
345 c = s.charAt(++i);
346 l.add(sb.toString());
347 sb.setLength(0);
348 if (c != '\n')
349 sb.append(c);
350 continue;
351 } else { // EOF
352 l.add(sb.toString());
353 break;
354 }
355 }
356 sb.append(c);
357 }
358 l.add(sb.toString());
359 return l;
360 }
361
362 /**
363 * Read the next line from a reader.
364 * <p>
365 * Like {@link java.io.BufferedReader#readLine()}, but only treats
366 * {@code \n} as end-of-line, and includes the trailing newline.
367 *
368 * @param in
369 * the reader to read from.
370 * @param sizeHint
371 * hint for buffer sizing; 0 or negative for default.
372 * @return the next line from the input, always ending in {@code \n} unless
373 * EOF was reached.
374 * @throws java.io.IOException
375 * there was an error reading from the stream.
376 * @since 4.1
377 */
378 public static String readLine(Reader in, int sizeHint) throws IOException {
379 if (in.markSupported()) {
380 if (sizeHint <= 0) {
381 sizeHint = 1024;
382 }
383 StringBuilder sb = new StringBuilder(sizeHint);
384 char[] buf = new char[sizeHint];
385 while (true) {
386 in.mark(sizeHint);
387 int n = in.read(buf);
388 if (n < 0) {
389 in.reset();
390 return sb.toString();
391 }
392 for (int i = 0; i < n; i++) {
393 if (buf[i] == '\n') {
394 resetAndSkipFully(in, ++i);
395 sb.append(buf, 0, i);
396 return sb.toString();
397 }
398 }
399 if (n > 0) {
400 sb.append(buf, 0, n);
401 }
402 resetAndSkipFully(in, n);
403 }
404 } else {
405 StringBuilder buf = sizeHint > 0
406 ? new StringBuilder(sizeHint)
407 : new StringBuilder();
408 int i;
409 while ((i = in.read()) != -1) {
410 char c = (char) i;
411 buf.append(c);
412 if (c == '\n') {
413 break;
414 }
415 }
416 return buf.toString();
417 }
418 }
419
420 private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException {
421 fd.reset();
422 while (toSkip > 0) {
423 long r = fd.skip(toSkip);
424 if (r <= 0) {
425 throw new EOFException(JGitText.get().shortSkipOfBlock);
426 }
427 toSkip -= r;
428 }
429 }
430
431 private IO() {
432 // Don't create instances of a static only utility.
433 }
434 }