Package: FileChannelImageInputStream
FileChannelImageInputStream
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
FileChannelImageInputStream(File) |
|
|
|
|
|
||||||||||||||||||||
close() |
|
|
|
|
|
||||||||||||||||||||
getMappedBuffer(int) |
|
|
|
|
|
||||||||||||||||||||
length() |
|
|
|
|
|
||||||||||||||||||||
read() |
|
|
|
|
|
||||||||||||||||||||
read(byte[], int, int) |
|
|
|
|
|
||||||||||||||||||||
readFully(char[], int, int) |
|
|
|
|
|
||||||||||||||||||||
readFully(double[], int, int) |
|
|
|
|
|
||||||||||||||||||||
readFully(float[], int, int) |
|
|
|
|
|
||||||||||||||||||||
readFully(int[], int, int) |
|
|
|
|
|
||||||||||||||||||||
readFully(long[], int, int) |
|
|
|
|
|
||||||||||||||||||||
readFully(short[], int, int) |
|
|
|
|
|
||||||||||||||||||||
seek(long) |
|
|
|
|
|
||||||||||||||||||||
setByteOrder(ByteOrder) |
|
|
|
|
|
||||||||||||||||||||
toString() |
|
|
|
|
|
Coverage
1: /*
2: * *********************************************************************************************************************
3: *
4: * Mistral: open source imaging engine
5: * http://tidalwave.it/projects/mistral
6: *
7: * Copyright (C) 2003 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/mistral-src
23: * git clone https://github.com/tidalwave-it/mistral-src
24: *
25: * *********************************************************************************************************************
26: */
27: /*
28: * To change this template, choose Tools | Templates
29: * and open the template in the editor.
30: */
31: package it.tidalwave.image.op.impl;
32:
33: import java.io.EOFException;
34: import java.io.File;
35: import java.io.IOException;
36: import java.io.RandomAccessFile;
37: import java.nio.ByteBuffer;
38: import java.nio.ByteOrder;
39: import java.nio.MappedByteBuffer;
40: import java.nio.channels.FileChannel;
41: import javax.imageio.stream.ImageInputStreamImpl;
42:
43: // ADAPTED from com.sun.media.imageio.stream.FileChannelImageInputStream, this also
44: // closes the RandomAccessFile and the channel.
45:
46: /**
47: * A class which implements <code>ImageInputStream</code> using a
48: * <code>FileChannel</code> as the eventual data source. The channel
49: * contents are assumed to be stable during the lifetime of the object.
50: *
51: * <p>Memory mapping and new I/O view <code>Buffer</code>s are used to
52: * read the data. Only methods which provide significant performance improvement with respect to the superclass
53: * implementation are overridden. Overridden methods are not commented individually unless some noteworthy aspect of the
54: * implementation must be described.</p>
55: *
56: * <p>The methods of this class are <b>not</b> synchronized.</p>
57: *
58: * @see javax.imageio.stream.ImageInputStream
59: * @see java.nio
60: * @see java.nio.channels.FileChannel
61: */
62: public class FileChannelImageInputStream extends ImageInputStreamImpl
63: {
64:
65: private final RandomAccessFile raf;
66:
67: /**
68: * The <code>FileChannel</code> data source.
69: */
70: private FileChannel channel;
71: /**
72: * A memory mapping of all or part of the channel.
73: */
74: private MappedByteBuffer mappedBuffer;
75: /**
76: * The stream position of the mapping.
77: */
78: private long mappedPos;
79: /**
80: * The stream position least upper bound of the mapping.
81: */
82: private long mappedUpperBound;
83:
84: /**
85: * Constructs a <code>FileChannelImageInputStream</code> from a
86: * <code>FileChannel</code>. The initial position of the stream
87: * stream is taken to be the position of the <code>FileChannel</code> parameter when this constructor is invoked.
88: * The
89: * stream and flushed positions are therefore both initialized to
90: * <code>channel.position()</code>.
91: *
92: * @param channel the source <code>FileChannel</code>.
93: * @throws IllegalArgumentException if <code>channel</code> is
94: * <code>null</code> or is not open.
95: * @throws IOException if a method invoked on <code>channel</code> throws an <code>IOException</code>.
96: */
97: public FileChannelImageInputStream (final File file)
98: throws IOException
99: {
100: raf = new RandomAccessFile(file, "r");
101: this.channel = raf.getChannel();
102: final var channelPosition = channel.position();
103: this.streamPos = this.flushedPos = channelPosition;
104: final var fullSize = channel.size() - channelPosition;
105: final var mappedSize = Math.min(fullSize, Integer.MAX_VALUE);
106: this.mappedPos = 0;
107: this.mappedUpperBound = mappedPos + mappedSize;
108: this.mappedBuffer = channel.map(FileChannel.MapMode.READ_ONLY, channelPosition, mappedSize);
109: }
110:
111: /**
112: * Returns a <code>MappedByteBuffer</code> which memory maps at least from the channel position corresponding to the
113: * current stream position to <code>len</code> bytes beyond. A new buffer is created only if necessary.
114: *
115: * @param len The number of bytes required beyond the current stream position.
116: */
117: private MappedByteBuffer getMappedBuffer (final int len)
118: throws IOException
119: {
120: // If request is outside mapped region, map a new region.
121:• if (streamPos < mappedPos || streamPos + len >= mappedUpperBound)
122: {
123:
124: // Set the map position.
125: mappedPos = streamPos;
126:
127: // Determine the map size.
128: final var mappedSize = Math.min(channel.size() - mappedPos,
129: Integer.MAX_VALUE);
130:
131: // Set the mapped upper bound.
132: mappedUpperBound = mappedPos + mappedSize;
133:
134: // Map the file.
135: mappedBuffer = channel.map(FileChannel.MapMode.READ_ONLY,
136: mappedPos, mappedSize);
137:
138: mappedBuffer.order(super.getByteOrder());
139:
140: }
141:
142: return mappedBuffer;
143: }
144:
145: // --- Implementation of superclass abstract methods. ---
146: @Override
147: public int read()
148: throws IOException
149: {
150: checkClosed();
151: bitOffset = 0;
152:
153: // Get the mapped buffer.
154: final ByteBuffer byteBuffer = getMappedBuffer(1);
155:
156: // Check number of bytes left.
157:• if (byteBuffer.remaining() < 1)
158: {
159: // Return EOF.
160: return -1;
161: }
162:
163: // Get the byte from the buffer.
164: final var value = byteBuffer.get() & 0xff;
165:
166: // Increment the stream position.
167: streamPos++;
168:
169: //System.out.println("value = "+value);
170:
171: return value;
172: }
173:
174: @Override
175: public int read (final byte[] b, final int off, int len)
176: throws IOException
177: {
178:• if (off < 0 || len < 0 || off + len > b.length)
179: {
180: // NullPointerException will be thrown before this if b is null.
181: throw new IndexOutOfBoundsException(
182: "off < 0 || len < 0 || off + len > b.length");
183: }
184:• else if (len == 0)
185: {
186: return 0;
187: }
188:
189: checkClosed();
190: bitOffset = 0;
191:
192: // Get the mapped buffer.
193: final ByteBuffer byteBuffer = getMappedBuffer(len);
194:
195: // Get the number of bytes remaining.
196: final var numBytesRemaining = byteBuffer.remaining();
197:
198: // Check number of bytes left.
199:• if (numBytesRemaining < 1)
200: {
201: // Return EOF.
202: return -1;
203: }
204:• else if (len > numBytesRemaining)
205: {
206: // Clamp 'len' to number of bytes in Buffer. Apparently some
207: // readers (JPEG) request data beyond the end of the file.
208: len = numBytesRemaining;
209: }
210:
211: // Read the data from the buffer.
212: byteBuffer.get(b, off, len);
213:
214: // Increment the stream position.
215: streamPos += len;
216:
217: return len;
218: }
219:
220: // --- Overriding of superclass methods. ---
221:
222: /**
223: * Invokes the superclass method and sets the internal reference to the source <code>FileChannel</code> to
224: * <code>null</code>. The source <code>FileChannel</code> is not closed.
225: *
226: * @throws IOException if an error occurs.
227: */
228: @Override
229: public void close()
230: throws IOException
231: {
232: super.close();
233: channel.close();
234: raf.close();
235: channel = null;
236: }
237:
238: @Override
239: public void readFully (final char[] c, final int off, final int len)
240: throws IOException
241: {
242:• if (off < 0 || len < 0 || off + len > c.length)
243: {
244: // NullPointerException will be thrown before this if c is null.
245: throw new IndexOutOfBoundsException(
246: "off < 0 || len < 0 || off + len > c.length");
247: }
248:• else if (len == 0)
249: {
250: return;
251: }
252:
253: // Determine the requested length in bytes.
254: final var byteLen = 2 * len;
255:
256: // Get the mapped buffer.
257: final ByteBuffer byteBuffer = getMappedBuffer(byteLen);
258:
259: // Ensure enough bytes remain.
260:• if (byteBuffer.remaining() < byteLen)
261: {
262: throw new EOFException();
263: }
264:
265: // Get the view Buffer.
266: final var viewBuffer = byteBuffer.asCharBuffer();
267:
268: // Get the chars.
269: viewBuffer.get(c, off, len);
270:
271: // Update the position.
272: seek(streamPos + byteLen);
273: }
274:
275: @Override
276: public void readFully (final short[] s, final int off, final int len)
277: throws IOException
278: {
279:• if (off < 0 || len < 0 || off + len > s.length)
280: {
281: // NullPointerException will be thrown before this if s is null.
282: throw new IndexOutOfBoundsException(
283: "off < 0 || len < 0 || off + len > s.length");
284: }
285:• else if (len == 0)
286: {
287: return;
288: }
289:
290: // Determine the requested length in bytes.
291: final var byteLen = 2 * len;
292:
293: // Get the mapped buffer.
294: final ByteBuffer byteBuffer = getMappedBuffer(byteLen);
295:
296: // Ensure enough bytes remain.
297:• if (byteBuffer.remaining() < byteLen)
298: {
299: throw new EOFException();
300: }
301:
302: // Get the view Buffer.
303: final var viewBuffer = byteBuffer.asShortBuffer();
304:
305: // Get the shorts.
306: viewBuffer.get(s, off, len);
307:
308: // Update the position.
309: seek(streamPos + byteLen);
310: }
311:
312: @Override
313: public void readFully (final int[] i, final int off, final int len)
314: throws IOException
315: {
316:• if (off < 0 || len < 0 || off + len > i.length)
317: {
318: // NullPointerException will be thrown before this if i is null.
319: throw new IndexOutOfBoundsException(
320: "off < 0 || len < 0 || off + len > i.length");
321: }
322:• else if (len == 0)
323: {
324: return;
325: }
326:
327: // Determine the requested length in bytes.
328: final var byteLen = 4 * len;
329:
330: // Get the mapped buffer.
331: final ByteBuffer byteBuffer = getMappedBuffer(byteLen);
332:
333: // Ensure enough bytes remain.
334:• if (byteBuffer.remaining() < byteLen)
335: {
336: throw new EOFException();
337: }
338:
339: // Get the view Buffer.
340: final var viewBuffer = byteBuffer.asIntBuffer();
341:
342: // Get the ints.
343: viewBuffer.get(i, off, len);
344:
345: // Update the position.
346: seek(streamPos + byteLen);
347: }
348:
349: @Override
350: public void readFully (final long[] l, final int off, final int len)
351: throws IOException
352: {
353:• if (off < 0 || len < 0 || off + len > l.length)
354: {
355: // NullPointerException will be thrown before this if l is null.
356: throw new IndexOutOfBoundsException(
357: "off < 0 || len < 0 || off + len > l.length");
358: }
359:• else if (len == 0)
360: {
361: return;
362: }
363:
364: // Determine the requested length in bytes.
365: final var byteLen = 8 * len;
366:
367: // Get the mapped buffer.
368: final ByteBuffer byteBuffer = getMappedBuffer(byteLen);
369:
370: // Ensure enough bytes remain.
371:• if (byteBuffer.remaining() < byteLen)
372: {
373: throw new EOFException();
374: }
375:
376: // Get the view Buffer.
377: final var viewBuffer = byteBuffer.asLongBuffer();
378:
379: // Get the longs.
380: viewBuffer.get(l, off, len);
381:
382: // Update the position.
383: seek(streamPos + byteLen);
384: }
385:
386: @Override
387: public void readFully (final float[] f, final int off, final int len)
388: throws IOException
389: {
390:• if (off < 0 || len < 0 || off + len > f.length)
391: {
392: // NullPointerException will be thrown before this if f is null.
393: throw new IndexOutOfBoundsException(
394: "off < 0 || len < 0 || off + len > f.length");
395: }
396:• else if (len == 0)
397: {
398: return;
399: }
400:
401: // Determine the requested length in bytes.
402: final var byteLen = 4 * len;
403:
404: // Get the mapped buffer.
405: final ByteBuffer byteBuffer = getMappedBuffer(byteLen);
406:
407: // Ensure enough bytes remain.
408:• if (byteBuffer.remaining() < byteLen)
409: {
410: throw new EOFException();
411: }
412:
413: // Get the view Buffer.
414: final var viewBuffer = byteBuffer.asFloatBuffer();
415:
416: // Get the floats.
417: viewBuffer.get(f, off, len);
418:
419: // Update the position.
420: seek(streamPos + byteLen);
421: }
422:
423: @Override
424: public void readFully (final double[] d, final int off, final int len)
425: throws IOException
426: {
427:• if (off < 0 || len < 0 || off + len > d.length)
428: {
429: // NullPointerException will be thrown before this if d is null.
430: throw new IndexOutOfBoundsException(
431: "off < 0 || len < 0 || off + len > d.length");
432: }
433:• else if (len == 0)
434: {
435: return;
436: }
437:
438: // Determine the requested length in bytes.
439: final var byteLen = 8 * len;
440:
441: // Get the mapped buffer.
442: final ByteBuffer byteBuffer = getMappedBuffer(byteLen);
443:
444: // Ensure enough bytes remain.
445:• if (byteBuffer.remaining() < byteLen)
446: {
447: throw new EOFException();
448: }
449:
450: // Get the view Buffer.
451: final var viewBuffer = byteBuffer.asDoubleBuffer();
452:
453: // Get the doubles.
454: viewBuffer.get(d, off, len);
455:
456: // Update the position.
457: seek(streamPos + byteLen);
458: }
459:
460: /**
461: * Returns the number of bytes currently in the <code>FileChannel</code>. If an <code>IOException</code> is
462: * encountered when querying the channel's size, -1L will be returned.
463: *
464: * @return The number of bytes in the channel -1L to indicate unknown length.
465: */
466: @Override
467: public long length()
468: {
469: // Initialize to value indicating unknown length.
470: var length = -1L;
471:
472: // Set length to current size with respect to initial position.
473: try
474: {
475: length = channel.size();
476: }
477: catch (IOException e)
478: {
479: // Default to unknown length.
480: }
481:
482: return length;
483: }
484:
485: /**
486: * Invokes the superclass method and sets the position within the memory mapped buffer. A new region is mapped if
487: * necessary. The position of the source <code>FileChannel</code> is not changed, i.e.,
488: * {@link java.nio.channels.FileChannel#position(long)} is not invoked.
489: */
490: @Override
491: public void seek (final long pos)
492: throws IOException
493: {
494: super.seek(pos);
495:
496:• if (pos >= mappedPos && pos < mappedUpperBound)
497: {
498: // Seeking to location within mapped buffer: set buffer position.
499: mappedBuffer.position((int)(pos - mappedPos));
500: }
501: else
502: {
503: // Seeking to location outside mapped buffer: get a new mapped
504: // buffer at current position with maximal size.
505: final var len = (int)Math.min(channel.size() - pos,
506: Integer.MAX_VALUE);
507: mappedBuffer = getMappedBuffer(len);
508: }
509: }
510:
511: @Override
512: public void setByteOrder (final ByteOrder networkByteOrder)
513: {
514: super.setByteOrder(networkByteOrder);
515: mappedBuffer.order(networkByteOrder);
516: }
517:
518: @Override
519: public String toString()
520: {
521: return String.format("FileChannelImageInputStream@%x", System.identityHashCode(this));
522: }
523: }