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