1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.io.FileInputStream; 13 14 import hunt.Exceptions; 15 import hunt.io.Common; 16 17 import std.array; 18 import std.stdio; 19 20 21 /** 22 * A <code>FileInputStream</code> obtains input bytes 23 * from a file in a file system. What files 24 * are available depends on the host environment. 25 * 26 * <p><code>FileInputStream</code> is meant for reading streams of raw bytes 27 * such as image data. For reading streams of characters, consider using 28 * <code>FileReader</code>. 29 * 30 * @apiNote 31 * To release resources used by this stream {@link #close} should be called 32 * directly or by try-with-resources. Subclasses are responsible for the cleanup 33 * of resources acquired by the subclass. 34 * Subclasses that override {@link #finalize} in order to perform cleanup 35 * should be modified to use alternative cleanup mechanisms such as 36 * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method. 37 * 38 * @implSpec 39 * If this FileInputStream has been subclassed and the {@link #close} 40 * method has been overridden, the {@link #close} method will be 41 * called when the FileInputStream is unreachable. 42 * Otherwise, it is implementation specific how the resource cleanup described in 43 * {@link #close} is performed. 44 45 * 46 * @author Arthur van Hoff 47 * @see java.io.File 48 * @see java.io.FileDescriptor 49 * @see java.io.FileOutputStream 50 * @see java.nio.file.Files#newInputStream 51 * @since 1.0 52 */ 53 class FileInputStream : InputStream { 54 /* File Descriptor - handle to the open file */ 55 // private final FileDescriptor fd; 56 57 /** 58 * The path of the referenced file 59 * (null if the stream is created with a file descriptor) 60 */ 61 private string path; 62 private File file; 63 64 // private FileChannel channel; 65 66 private Object closeLock; 67 68 private bool closed; 69 70 // private final Object altFinalizer; 71 72 73 private void initialized() { 74 closeLock = new Object(); 75 } 76 77 /** 78 * Creates a <code>FileInputStream</code> by 79 * opening a connection to an actual file, 80 * the file named by the path name <code>name</code> 81 * in the file system. A new <code>FileDescriptor</code> 82 * object is created to represent this file 83 * connection. 84 * <p> 85 * First, if there is a security 86 * manager, its <code>checkRead</code> method 87 * is called with the <code>name</code> argument 88 * as its argument. 89 * <p> 90 * If the named file does not exist, is a directory rather than a regular 91 * file, or for some other reason cannot be opened for reading then a 92 * <code>FileNotFoundException</code> is thrown. 93 * 94 * @param name the system-dependent file name. 95 * @exception FileNotFoundException if the file does not exist, 96 * is a directory rather than a regular file, 97 * or for some other reason cannot be opened for 98 * reading. 99 * @exception SecurityException if a security manager exists and its 100 * <code>checkRead</code> method denies read access 101 * to the file. 102 * @see java.lang.SecurityManager#checkRead(java.lang.string) 103 */ 104 this(string name) { 105 if(name.empty) 106 throw new NullPointerException(); 107 this(File(name, "r")); 108 } 109 110 /** 111 * Creates a <code>FileInputStream</code> by 112 * opening a connection to an actual file, 113 * the file named by the <code>File</code> 114 * object <code>file</code> in the file system. 115 * A new <code>FileDescriptor</code> object 116 * is created to represent this file connection. 117 * <p> 118 * First, if there is a security manager, 119 * its <code>checkRead</code> method is called 120 * with the path represented by the <code>file</code> 121 * argument as its argument. 122 * <p> 123 * If the named file does not exist, is a directory rather than a regular 124 * file, or for some other reason cannot be opened for reading then a 125 * <code>FileNotFoundException</code> is thrown. 126 * 127 * @param file the file to be opened for reading. 128 * @exception FileNotFoundException if the file does not exist, 129 * is a directory rather than a regular file, 130 * or for some other reason cannot be opened for 131 * reading. 132 * @exception SecurityException if a security manager exists and its 133 * <code>checkRead</code> method denies read access to the file. 134 * @see java.io.File#getPath() 135 * @see java.lang.SecurityManager#checkRead(java.lang.string) 136 */ 137 this(File file) { 138 this.file = file; 139 // string name = (file != null ? file.getPath() : null); 140 // SecurityManager security = System.getSecurityManager(); 141 // if (security != null) { 142 // security.checkRead(name); 143 // } 144 // if (name == null) { 145 // throw new NullPointerException(); 146 // } 147 // if (file.isInvalid()) { 148 // throw new FileNotFoundException("Invalid file path"); 149 // } 150 // fd = new FileDescriptor(); 151 // fd.attach(this); 152 // path = name; 153 // open(name); 154 // altFinalizer = getFinalizer(this); 155 // if (altFinalizer == null) { 156 // FileCleanable.register(fd); // open set the fd, register the cleanup 157 // } 158 } 159 160 /** 161 * Creates a <code>FileInputStream</code> by using the file descriptor 162 * <code>fdObj</code>, which represents an existing connection to an 163 * actual file in the file system. 164 * <p> 165 * If there is a security manager, its <code>checkRead</code> method is 166 * called with the file descriptor <code>fdObj</code> as its argument to 167 * see if it's ok to read the file descriptor. If read access is denied 168 * to the file descriptor a <code>SecurityException</code> is thrown. 169 * <p> 170 * If <code>fdObj</code> is null then a <code>NullPointerException</code> 171 * is thrown. 172 * <p> 173 * This constructor does not throw an exception if <code>fdObj</code> 174 * is {@link java.io.FileDescriptor#valid() invalid}. 175 * However, if the methods are invoked on the resulting stream to attempt 176 * I/O on the stream, an <code>IOException</code> is thrown. 177 * 178 * @param fdObj the file descriptor to be opened for reading. 179 * @throws SecurityException if a security manager exists and its 180 * <code>checkRead</code> method denies read access to the 181 * file descriptor. 182 * @see SecurityManager#checkRead(java.io.FileDescriptor) 183 */ 184 // this(FileDescriptor fdObj) { 185 // SecurityManager security = System.getSecurityManager(); 186 // if (fdObj == null) { 187 // throw new NullPointerException(); 188 // } 189 // if (security != null) { 190 // security.checkRead(fdObj); 191 // } 192 // fd = fdObj; 193 // path = null; 194 // altFinalizer = null; 195 196 // /* 197 // * FileDescriptor is being shared by streams. 198 // * Register this stream with FileDescriptor tracker. 199 // */ 200 // fd.attach(this); 201 // } 202 203 /** 204 * Opens the specified file for reading. 205 * @param name the name of the file 206 */ 207 // private native void open0(string name); 208 209 // wrap native call to allow instrumentation 210 /** 211 * Opens the specified file for reading. 212 * @param name the name of the file 213 */ 214 // private void open(string name) { 215 // open0(name); 216 // } 217 218 /** 219 * Reads a byte of data from this input stream. This method blocks 220 * if no input is yet available. 221 * 222 * @return the next byte of data, or <code>-1</code> if the end of the 223 * file is reached. 224 * @exception IOException if an I/O error occurs. 225 */ 226 override int read() { 227 byte[] buf = file.rawRead(new byte[1]); 228 if(buf.length == 0) 229 return -1; 230 else 231 return buf[0]; 232 } 233 234 235 /** 236 * Reads a subarray as a sequence of bytes. 237 * @param b the data to be written 238 * @param off the start offset in the data 239 * @param len the number of bytes that are written 240 * @exception IOException If an I/O error has occurred. 241 */ 242 private int readBytes(byte[] b, int off, int len) { 243 byte[] buf = file.rawRead(b[off .. off+len]); 244 if(buf.length == 0) 245 return -1; 246 else 247 return cast(int)buf.length; 248 } 249 250 /** 251 * Reads up to <code>b.length</code> bytes of data from this input 252 * stream into an array of bytes. This method blocks until some input 253 * is available. 254 * 255 * @param b the buffer into which the data is read. 256 * @return the total number of bytes read into the buffer, or 257 * <code>-1</code> if there is no more data because the end of 258 * the file has been reached. 259 * @exception IOException if an I/O error occurs. 260 */ 261 override int read(byte[] b) { 262 return readBytes(b, 0, cast(int)b.length); 263 } 264 265 /** 266 * Reads up to <code>len</code> bytes of data from this input stream 267 * into an array of bytes. If <code>len</code> is not zero, the method 268 * blocks until some input is available; otherwise, no 269 * bytes are read and <code>0</code> is returned. 270 * 271 * @param b the buffer into which the data is read. 272 * @param off the start offset in the destination array <code>b</code> 273 * @param len the maximum number of bytes read. 274 * @return the total number of bytes read into the buffer, or 275 * <code>-1</code> if there is no more data because the end of 276 * the file has been reached. 277 * @exception NullPointerException If <code>b</code> is <code>null</code>. 278 * @exception IndexOutOfBoundsException If <code>off</code> is negative, 279 * <code>len</code> is negative, or <code>len</code> is greater than 280 * <code>b.length - off</code> 281 * @exception IOException if an I/O error occurs. 282 */ 283 override int read(byte[] b, int off, int len) { 284 return readBytes(b, off, len); 285 } 286 287 /** 288 * Skips over and discards <code>n</code> bytes of data from the 289 * input stream. 290 * 291 * <p>The <code>skip</code> method may, for a variety of 292 * reasons, end up skipping over some smaller number of bytes, 293 * possibly <code>0</code>. If <code>n</code> is negative, the method 294 * will try to skip backwards. In case the backing file does not support 295 * backward skip at its current position, an <code>IOException</code> is 296 * thrown. The actual number of bytes skipped is returned. If it skips 297 * forwards, it returns a positive value. If it skips backwards, it 298 * returns a negative value. 299 * 300 * <p>This method may skip more bytes than what are remaining in the 301 * backing file. This produces no exception and the number of bytes skipped 302 * may include some number of bytes that were beyond the EOF of the 303 * backing file. Attempting to read from the stream after skipping past 304 * the end will result in -1 indicating the end of the file. 305 * 306 * @param n the number of bytes to be skipped. 307 * @return the actual number of bytes skipped. 308 * @exception IOException if n is negative, if the stream does not 309 * support seek, or if an I/O error occurs. 310 */ 311 override long skip(long n) { 312 // auto last = file.tell(); 313 // file.seek(n); 314 // auto diff = file.tell() - last; 315 // return cast(long)diff; 316 throw new NotSupportedException(); 317 } 318 319 320 /** 321 * Returns an estimate of the number of remaining bytes that can be read (or 322 * skipped over) from this input stream without blocking by the next 323 * invocation of a method for this input stream. Returns 0 when the file 324 * position is beyond EOF. The next invocation might be the same thread 325 * or another thread. A single read or skip of this many bytes will not 326 * block, but may read or skip fewer bytes. 327 * 328 * <p> In some cases, a non-blocking read (or skip) may appear to be 329 * blocked when it is merely slow, for example when reading large 330 * files over slow networks. 331 * 332 * @return an estimate of the number of remaining bytes that can be read 333 * (or skipped over) from this input stream without blocking. 334 * @exception IOException if this file input stream has been closed by calling 335 * {@code close} or an I/O error occurs. 336 */ 337 override int available() { 338 // return available0(); 339 return cast(int)(file.size()); 340 } 341 342 // private native int available0(); 343 344 /** 345 * Closes this file input stream and releases any system resources 346 * associated with the stream. 347 * 348 * <p> If this stream has an associated channel then the channel is closed 349 * as well. 350 * 351 * @apiNote 352 * Overriding {@link #close} to perform cleanup actions is reliable 353 * only when called directly or when called by try-with-resources. 354 * Do not depend on finalization to invoke {@code close}; 355 * finalization is not reliable and is deprecated. 356 * If cleanup of native resources is needed, other mechanisms such as 357 * {@linkplain java.lang.ref.Cleaner} should be used. 358 * 359 * @exception IOException if an I/O error occurs. 360 * 361 * @revised 1.4 362 * @spec JSR-51 363 */ 364 override void close() { 365 if (closed) 366 return; 367 368 synchronized (closeLock) { 369 if (closed) return; 370 closed = true; 371 } 372 373 file.close(); 374 375 // FileChannel fc = channel; 376 // if (fc != null) { 377 // // possible race with getChannel(), benign since 378 // // FileChannel.close is final and idempotent 379 // fc.close(); 380 // } 381 382 // fd.closeAll(new Closeable() { 383 // void close() { 384 // fd.close(); 385 // } 386 // }); 387 } 388 389 /** 390 * Returns the <code>FileDescriptor</code> 391 * object that represents the connection to 392 * the actual file in the file system being 393 * used by this <code>FileInputStream</code>. 394 * 395 * @return the file descriptor object associated with this stream. 396 * @exception IOException if an I/O error occurs. 397 * @see java.io.FileDescriptor 398 */ 399 // final FileDescriptor getFD() { 400 // if (fd != null) { 401 // return fd; 402 // } 403 // throw new IOException(); 404 // } 405 406 /** 407 * Returns the unique {@link java.nio.channels.FileChannel FileChannel} 408 * object associated with this file input stream. 409 * 410 * <p> The initial {@link java.nio.channels.FileChannel#position() 411 * position} of the returned channel will be equal to the 412 * number of bytes read from the file so far. Reading bytes from this 413 * stream will increment the channel's position. Changing the channel's 414 * position, either explicitly or by reading, will change this stream's 415 * file position. 416 * 417 * @return the file channel associated with this file input stream 418 * 419 * @since 1.4 420 * @spec JSR-51 421 */ 422 // FileChannel getChannel() { 423 // FileChannel fc = this.channel; 424 // if (fc == null) { 425 // synchronized (this) { 426 // fc = this.channel; 427 // if (fc == null) { 428 // this.channel = fc = FileChannelImpl.open(fd, path, true, 429 // false, false, this); 430 // if (closed) { 431 // try { 432 // // possible race with close(), benign since 433 // // FileChannel.close is final and idempotent 434 // fc.close(); 435 // } catch (IOException ioe) { 436 // throw new InternalError(ioe); // should not happen 437 // } 438 // } 439 // } 440 // } 441 // } 442 // return fc; 443 // } 444 445 // private static native void initIDs(); 446 447 // static { 448 // initIDs(); 449 // } 450 451 /** 452 * Ensures that the {@link #close} method of this file input stream is 453 * called when there are no more references to it. 454 * The {@link #finalize} method does not call {@link #close} directly. 455 * 456 * @apiNote 457 * To release resources used by this stream {@link #close} should be called 458 * directly or by try-with-resources. 459 * 460 * @implSpec 461 * If this FileInputStream has been subclassed and the {@link #close} 462 * method has been overridden, the {@link #close} method will be 463 * called when the FileInputStream is unreachable. 464 * Otherwise, it is implementation specific how the resource cleanup described in 465 * {@link #close} is performed. 466 * 467 * @deprecated The {@code finalize} method has been deprecated and will be removed. 468 * Subclasses that override {@code finalize} in order to perform cleanup 469 * should be modified to use alternative cleanup mechanisms and 470 * to remove the overriding {@code finalize} method. 471 * When overriding the {@code finalize} method, its implementation must explicitly 472 * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}. 473 * See the specification for {@link Object#finalize()} for further 474 * information about migration options. 475 * 476 * @exception IOException if an I/O error occurs. 477 * @see java.io.FileInputStream#close() 478 */ 479 // @Deprecated(since="9", forRemoval = true) 480 // protected void finalize() { 481 // } 482 483 /* 484 * Returns a finalizer object if the FIS needs a finalizer; otherwise null. 485 * If the FIS has a close method; it needs an AltFinalizer. 486 */ 487 // private static Object getFinalizer(FileInputStream fis) { 488 // Class<?> clazz = fis.getClass(); 489 // while (clazz != FileInputStream.class) { 490 // try { 491 // clazz.getDeclaredMethod("close"); 492 // return new AltFinalizer(fis); 493 // } catch (NoSuchMethodException nsme) { 494 // // ignore 495 // } 496 // clazz = clazz.getSuperclass(); 497 // } 498 // return null; 499 // } 500 /** 501 * Class to call {@code FileInputStream.close} when finalized. 502 * If finalization of the stream is needed, an instance is created 503 * in its constructor(s). When the set of instances 504 * related to the stream is unreachable, the AltFinalizer performs 505 * the needed call to the stream's {@code close} method. 506 */ 507 // static class AltFinalizer { 508 // private final FileInputStream fis; 509 510 // AltFinalizer(FileInputStream fis) { 511 // this.fis = fis; 512 // } 513 514 // override 515 // @SuppressWarnings("deprecation") 516 // protected final void finalize() { 517 // try { 518 // if ((fis.fd != null) && (fis.fd != FileDescriptor.in)) { 519 // /* if fd is shared, the references in FileDescriptor 520 // * will ensure that finalizer is only called when 521 // * safe to do so. All references using the fd have 522 // * become unreachable. We can call close() 523 // */ 524 // fis.close(); 525 // } 526 // } catch (IOException ioe) { 527 // // ignore 528 // } 529 // } 530 // } 531 }