Skip to content

Package: SharedFileInputStream

SharedFileInputStream

nameinstructionbranchcomplexitylinemethod
SharedFileInputStream(File)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
SharedFileInputStream(File, int)
M: 24 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
SharedFileInputStream(SharedFileInputStream.SharedFile, long, long, int)
M: 36 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 12 C: 0
0%
M: 1 C: 0
0%
SharedFileInputStream(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
SharedFileInputStream(String, int)
M: 24 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
available()
M: 11 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
close()
M: 24 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
ensureOpen()
M: 9 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
fill()
M: 177 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 31 C: 0
0%
M: 1 C: 0
0%
finalize()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
getPosition()
M: 18 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
in_available()
M: 14 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
init(SharedFileInputStream.SharedFile, int)
M: 23 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
mark(int)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
markSupported()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
newStream(long, long)
M: 39 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
read()
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
read(byte[], int, int)
M: 58 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
read1(byte[], int, int)
M: 40 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
reset()
M: 15 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
skip(long)
M: 48 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 12 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package jakarta.mail.util;
18:
19: import jakarta.mail.internet.SharedInputStream;
20:
21: import java.io.BufferedInputStream;
22: import java.io.File;
23: import java.io.IOException;
24: import java.io.InputStream;
25: import java.io.RandomAccessFile;
26:
27: /**
28: * A <code>SharedFileInputStream</code> is a
29: * <code>BufferedInputStream</code> that buffers
30: * data from the file and supports the <code>mark</code>
31: * and <code>reset</code> methods. It also supports the
32: * <code>newStream</code> method that allows you to create
33: * other streams that represent subsets of the file.
34: * A <code>RandomAccessFile</code> object is used to
35: * access the file data. <p>
36: *
37: * Note that when the SharedFileInputStream is closed,
38: * all streams created with the <code>newStream</code>
39: * method are also closed. This allows the creator of the
40: * SharedFileInputStream object to control access to the
41: * underlying file and ensure that it is closed when
42: * needed, to avoid leaking file descriptors. Note also
43: * that this behavior contradicts the requirements of
44: * SharedInputStream and may change in a future release.
45: *
46: * @author Bill Shannon
47: * @since JavaMail 1.4
48: */
49: public class SharedFileInputStream extends BufferedInputStream
50: implements SharedInputStream {
51:
52: private static int defaultBufferSize = 2048;
53:
54: /**
55: * The file containing the data.
56: * Shared by all related SharedFileInputStreams.
57: */
58: protected RandomAccessFile in;
59:
60: /**
61: * The normal size of the read buffer.
62: */
63: protected int bufsize;
64:
65: /**
66: * The file offset that corresponds to the first byte in
67: * the read buffer.
68: */
69: protected long bufpos;
70:
71: /**
72: * The file offset of the start of data in this subset of the file.
73: */
74: protected long start = 0;
75:
76: /**
77: * The amount of data in this subset of the file.
78: */
79: protected long datalen;
80:
81: /**
82: * True if this is a top level stream created directly by "new".
83: * False if this is a derived stream created by newStream.
84: */
85: private boolean master = true;
86:
87: /**
88: * A shared class that keeps track of the references
89: * to a particular file so it can be closed when the
90: * last reference is gone.
91: */
92: static class SharedFile {
93: private int cnt;
94: private RandomAccessFile in;
95:
96: SharedFile(String file) throws IOException {
97: this.in = new RandomAccessFile(file, "r");
98: }
99:
100: SharedFile(File file) throws IOException {
101: this.in = new RandomAccessFile(file, "r");
102: }
103:
104: public synchronized RandomAccessFile open() {
105: cnt++;
106: return in;
107: }
108:
109: public synchronized void close() throws IOException {
110: if (cnt > 0 && --cnt <= 0)
111: in.close();
112: }
113:
114: public synchronized void forceClose() throws IOException {
115: if (cnt > 0) {
116: // normal case, close exceptions propagated
117: cnt = 0;
118: in.close();
119: } else {
120: // should already be closed, ignore exception
121: try {
122: in.close();
123: } catch (IOException ioex) {
124: }
125: }
126: }
127:
128: @Override
129: protected void finalize() throws Throwable {
130: try {
131: in.close();
132: } finally {
133: super.finalize();
134: }
135: }
136: }
137:
138: private SharedFile sf;
139:
140: /**
141: * Check to make sure that this stream has not been closed
142: */
143: private void ensureOpen() throws IOException {
144:• if (in == null)
145: throw new IOException("Stream closed");
146: }
147:
148: /**
149: * Creates a <code>SharedFileInputStream</code>
150: * for the file.
151: *
152: * @param file the file
153: * @throws IOException for errors opening the file
154: */
155: public SharedFileInputStream(File file) throws IOException {
156: this(file, defaultBufferSize);
157: }
158:
159: /**
160: * Creates a <code>SharedFileInputStream</code>
161: * for the named file
162: *
163: * @param file the file
164: * @throws IOException for errors opening the file
165: */
166: public SharedFileInputStream(String file) throws IOException {
167: this(file, defaultBufferSize);
168: }
169:
170: /**
171: * Creates a <code>SharedFileInputStream</code>
172: * with the specified buffer size.
173: *
174: * @param file the file
175: * @param size the buffer size.
176: * @throws IOException for errors opening the file
177: * @throws IllegalArgumentException if size ≤ 0.
178: */
179: public SharedFileInputStream(File file, int size) throws IOException {
180: super(null); // XXX - will it NPE?
181:• if (size <= 0)
182: throw new IllegalArgumentException("Buffer size <= 0");
183: init(new SharedFile(file), size);
184: }
185:
186: /**
187: * Creates a <code>SharedFileInputStream</code>
188: * with the specified buffer size.
189: *
190: * @param file the file
191: * @param size the buffer size.
192: * @throws IOException for errors opening the file
193: * @throws IllegalArgumentException if size ≤ 0.
194: */
195: public SharedFileInputStream(String file, int size) throws IOException {
196: super(null); // XXX - will it NPE?
197:• if (size <= 0)
198: throw new IllegalArgumentException("Buffer size <= 0");
199: init(new SharedFile(file), size);
200: }
201:
202: private void init(SharedFile sf, int size) throws IOException {
203: this.sf = sf;
204: this.in = sf.open();
205: this.start = 0;
206: this.datalen = in.length(); // XXX - file can't grow
207: this.bufsize = size;
208: buf = new byte[size];
209: }
210:
211: /**
212: * Used internally by the <code>newStream</code> method.
213: */
214: private SharedFileInputStream(SharedFile sf, long start, long len,
215: int bufsize) {
216: super(null);
217: this.master = false;
218: this.sf = sf;
219: this.in = sf.open();
220: this.start = start;
221: this.bufpos = start;
222: this.datalen = len;
223: this.bufsize = bufsize;
224: buf = new byte[bufsize];
225: }
226:
227: /**
228: * Fills the buffer with more data, taking into account
229: * shuffling and other tricks for dealing with marks.
230: * Assumes that it is being called by a synchronized method.
231: * This method also assumes that all data has already been read in,
232: * hence pos > count.
233: */
234: private void fill() throws IOException {
235:• if (markpos < 0) {
236: pos = 0; /* no mark: throw away the buffer */
237: bufpos += count;
238:• } else if (pos >= buf.length) /* no room left in buffer */
239:• if (markpos > 0) { /* can throw away early part of the buffer */
240: int sz = pos - markpos;
241: System.arraycopy(buf, markpos, buf, 0, sz);
242: pos = sz;
243: bufpos += markpos;
244: markpos = 0;
245:• } else if (buf.length >= marklimit) {
246: markpos = -1; /* buffer got too big, invalidate mark */
247: pos = 0; /* drop buffer contents */
248: bufpos += count;
249: } else { /* grow buffer */
250: int nsz = pos * 2;
251:• if (nsz > marklimit)
252: nsz = marklimit;
253: byte[] nbuf = new byte[nsz];
254: System.arraycopy(buf, 0, nbuf, 0, pos);
255: buf = nbuf;
256: }
257: count = pos;
258: // limit to datalen
259: int len = buf.length - pos;
260:• if (bufpos - start + pos + len > datalen)
261: len = (int) (datalen - (bufpos - start + pos));
262: synchronized (in) {
263: in.seek(bufpos + pos);
264: int n = in.read(buf, pos, len);
265:• if (n > 0)
266: count = n + pos;
267: }
268: }
269:
270: /**
271: * See the general contract of the <code>read</code>
272: * method of <code>InputStream</code>.
273: *
274: * @return the next byte of data, or <code>-1</code> if the end of the
275: * stream is reached.
276: * @throws IOException if an I/O error occurs.
277: */
278: @Override
279: public synchronized int read() throws IOException {
280: ensureOpen();
281:• if (pos >= count) {
282: fill();
283:• if (pos >= count)
284: return -1;
285: }
286: return buf[pos++] & 0xff;
287: }
288:
289: /**
290: * Read characters into a portion of an array, reading from the underlying
291: * stream at most once if necessary.
292: */
293: private int read1(byte[] b, int off, int len) throws IOException {
294: int avail = count - pos;
295:• if (avail <= 0) {
296: if (false) {
297:          /* If the requested length is at least as large as the buffer, and
298:          if there is no mark/reset activity, do not bother to copy the
299:          bytes into the local buffer. In this way buffered streams will
300:          cascade harmlessly. */
301: if (len >= buf.length && markpos < 0) {
302: // XXX - seek, update bufpos - how?
303: return in.read(b, off, len);
304: }
305: }
306: fill();
307: avail = count - pos;
308:• if (avail <= 0) return -1;
309: }
310: int cnt = Math.min(avail, len);
311: System.arraycopy(buf, pos, b, off, cnt);
312: pos += cnt;
313: return cnt;
314: }
315:
316: /**
317: * Reads bytes from this stream into the specified byte array,
318: * starting at the given offset.
319: *
320: * <p> This method implements the general contract of the corresponding
321: * <code>{@link java.io.InputStream#read(byte[], int, int) read}</code>
322: * method of the <code>{@link java.io.InputStream}</code> class.
323: *
324: * @param b destination buffer.
325: * @param off offset at which to start storing bytes.
326: * @param len maximum number of bytes to read.
327: * @return the number of bytes read, or <code>-1</code> if the end of
328: * the stream has been reached.
329: * @throws IOException if an I/O error occurs.
330: */
331: @Override
332: public synchronized int read(byte[] b, int off, int len)
333: throws IOException {
334: ensureOpen();
335:• if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
336: throw new IndexOutOfBoundsException();
337:• } else if (len == 0) {
338: return 0;
339: }
340:
341: int n = read1(b, off, len);
342:• if (n <= 0) return n;
343:• while ((n < len) /* && (in.available() > 0) */) {
344: int n1 = read1(b, off + n, len - n);
345:• if (n1 <= 0) break;
346: n += n1;
347: }
348: return n;
349: }
350:
351: /**
352: * See the general contract of the <code>skip</code>
353: * method of <code>InputStream</code>.
354: *
355: * @param n the number of bytes to be skipped.
356: * @return the actual number of bytes skipped.
357: * @throws IOException if an I/O error occurs.
358: */
359: @Override
360: public synchronized long skip(long n) throws IOException {
361: ensureOpen();
362:• if (n <= 0) {
363: return 0;
364: }
365: long avail = count - pos;
366:
367:• if (avail <= 0) {
368: // If no mark position set then don't keep in buffer
369:          /*
370: if (markpos <0)
371: return in.skip(n);
372:          */
373:
374: // Fill in buffer to save bytes for reset
375: fill();
376: avail = count - pos;
377:• if (avail <= 0)
378: return 0;
379: }
380:
381: long skipped = Math.min(avail, n);
382: pos += skipped;
383: return skipped;
384: }
385:
386: /**
387: * Returns the number of bytes that can be read from this input
388: * stream without blocking.
389: *
390: * @return the number of bytes that can be read from this input
391: * stream without blocking.
392: * @throws IOException if an I/O error occurs.
393: */
394: @Override
395: public synchronized int available() throws IOException {
396: ensureOpen();
397: return (count - pos) + in_available();
398: }
399:
400: private int in_available() throws IOException {
401: // XXX - overflow
402: return (int) ((start + datalen) - (bufpos + count));
403: }
404:
405: /**
406: * See the general contract of the <code>mark</code>
407: * method of <code>InputStream</code>.
408: *
409: * @param readlimit the maximum limit of bytes that can be read before
410: * the mark position becomes invalid.
411: * @see #reset()
412: */
413: @Override
414: public synchronized void mark(int readlimit) {
415: marklimit = readlimit;
416: markpos = pos;
417: }
418:
419: /**
420: * See the general contract of the <code>reset</code>
421: * method of <code>InputStream</code>.
422: * <p>
423: * If <code>markpos</code> is <code>-1</code>
424: * (no mark has been set or the mark has been
425: * invalidated), an <code>IOException</code>
426: * is thrown. Otherwise, <code>pos</code> is
427: * set equal to <code>markpos</code>.
428: *
429: * @throws IOException if this stream has not been marked or
430: * if the mark has been invalidated.
431: * @see #mark(int)
432: */
433: @Override
434: public synchronized void reset() throws IOException {
435: ensureOpen();
436:• if (markpos < 0)
437: throw new IOException("Resetting to invalid mark");
438: pos = markpos;
439: }
440:
441: /**
442: * Tests if this input stream supports the <code>mark</code>
443: * and <code>reset</code> methods. The <code>markSupported</code>
444: * method of <code>SharedFileInputStream</code> returns
445: * <code>true</code>.
446: *
447: * @return a <code>boolean</code> indicating if this stream type supports
448: * the <code>mark</code> and <code>reset</code> methods.
449: * @see java.io.InputStream#mark(int)
450: * @see java.io.InputStream#reset()
451: */
452: @Override
453: public boolean markSupported() {
454: return true;
455: }
456:
457: /**
458: * Closes this input stream and releases any system resources
459: * associated with the stream.
460: *
461: * @throws IOException if an I/O error occurs.
462: */
463: @Override
464: public void close() throws IOException {
465:• if (in == null)
466: return;
467: try {
468:• if (master)
469: sf.forceClose();
470: else
471: sf.close();
472: } finally {
473: sf = null;
474: in = null;
475: buf = null;
476: }
477: }
478:
479: /**
480: * Return the current position in the InputStream, as an
481: * offset from the beginning of the InputStream.
482: *
483: * @return the current position
484: */
485: @Override
486: public long getPosition() {
487: //System.out.println("getPosition: start " + start + " pos " + pos
488: //        + " bufpos " + bufpos + " = " + (bufpos + pos - start));
489:• if (in == null)
490: throw new RuntimeException("Stream closed");
491: return bufpos + pos - start;
492: }
493:
494: /**
495: * Return a new InputStream representing a subset of the data
496: * from this InputStream, starting at <code>start</code> (inclusive)
497: * up to <code>end</code> (exclusive). <code>start</code> must be
498: * non-negative. If <code>end</code> is -1, the new stream ends
499: * at the same place as this stream. The returned InputStream
500: * will also implement the SharedInputStream interface.
501: *
502: * @param start the starting position
503: * @param end the ending position + 1
504: * @return the new stream
505: */
506: @Override
507: public synchronized InputStream newStream(long start, long end) {
508:• if (in == null)
509: throw new RuntimeException("Stream closed");
510:• if (start < 0)
511: throw new IllegalArgumentException("start < 0");
512:• if (end == -1)
513: end = datalen;
514: return new SharedFileInputStream(sf,
515: this.start + start, end - start, bufsize);
516: }
517:
518: // for testing...
519: /*
520: public static void main(String[] argv) throws Exception {
521:         SharedFileInputStream is = new SharedFileInputStream(argv[0]);
522:         java.util.Random r = new java.util.Random();
523:         int b;
524:         while ((b = is.read()) >= 0) {
525:          System.out.write(b);
526:          if (r.nextDouble() < 0.3) {
527:                 InputStream is2 = is.newStream(is.getPosition(), -1);
528:                 int b2;
529:                 while ((b2 = is2.read()) >= 0)
530:                  ;
531:          }
532:         }
533: }
534: */
535:
536: /**
537: * Force this stream to close.
538: */
539: @Override
540: protected void finalize() throws Throwable {
541: super.finalize();
542: close();
543: }
544: }