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 }