Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/imageio/stream/FileCacheImageInputStream.java
38918 views
1
/*
2
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package javax.imageio.stream;
27
28
import java.io.File;
29
import java.io.InputStream;
30
import java.io.IOException;
31
import java.io.RandomAccessFile;
32
import java.nio.file.Files;
33
import com.sun.imageio.stream.StreamCloser;
34
import com.sun.imageio.stream.StreamFinalizer;
35
import sun.java2d.Disposer;
36
import sun.java2d.DisposerRecord;
37
38
/**
39
* An implementation of <code>ImageInputStream</code> that gets its
40
* input from a regular <code>InputStream</code>. A file is used to
41
* cache previously read data.
42
*
43
*/
44
public class FileCacheImageInputStream extends ImageInputStreamImpl {
45
46
private InputStream stream;
47
48
private File cacheFile;
49
50
private RandomAccessFile cache;
51
52
private static final int BUFFER_LENGTH = 1024;
53
54
private byte[] buf = new byte[BUFFER_LENGTH];
55
56
private long length = 0L;
57
58
private boolean foundEOF = false;
59
60
/** The referent to be registered with the Disposer. */
61
private final Object disposerReferent;
62
63
/** The DisposerRecord that closes the underlying cache. */
64
private final DisposerRecord disposerRecord;
65
66
/** The CloseAction that closes the stream in
67
* the StreamCloser's shutdown hook */
68
private final StreamCloser.CloseAction closeAction;
69
70
/**
71
* Constructs a <code>FileCacheImageInputStream</code> that will read
72
* from a given <code>InputStream</code>.
73
*
74
* <p> A temporary file is used as a cache. If
75
* <code>cacheDir</code>is non-<code>null</code> and is a
76
* directory, the file will be created there. If it is
77
* <code>null</code>, the system-dependent default temporary-file
78
* directory will be used (see the documentation for
79
* <code>File.createTempFile</code> for details).
80
*
81
* @param stream an <code>InputStream</code> to read from.
82
* @param cacheDir a <code>File</code> indicating where the
83
* cache file should be created, or <code>null</code> to use the
84
* system directory.
85
*
86
* @exception IllegalArgumentException if <code>stream</code> is
87
* <code>null</code>.
88
* @exception IllegalArgumentException if <code>cacheDir</code> is
89
* non-<code>null</code> but is not a directory.
90
* @exception IOException if a cache file cannot be created.
91
*/
92
public FileCacheImageInputStream(InputStream stream, File cacheDir)
93
throws IOException {
94
if (stream == null) {
95
throw new IllegalArgumentException("stream == null!");
96
}
97
if ((cacheDir != null) && !(cacheDir.isDirectory())) {
98
throw new IllegalArgumentException("Not a directory!");
99
}
100
this.stream = stream;
101
if (cacheDir == null)
102
this.cacheFile = Files.createTempFile("imageio", ".tmp").toFile();
103
else
104
this.cacheFile = Files.createTempFile(cacheDir.toPath(), "imageio", ".tmp")
105
.toFile();
106
this.cache = new RandomAccessFile(cacheFile, "rw");
107
108
this.closeAction = StreamCloser.createCloseAction(this);
109
StreamCloser.addToQueue(closeAction);
110
111
disposerRecord = new StreamDisposerRecord(cacheFile, cache);
112
if (getClass() == FileCacheImageInputStream.class) {
113
disposerReferent = new Object();
114
Disposer.addRecord(disposerReferent, disposerRecord);
115
} else {
116
disposerReferent = new StreamFinalizer(this);
117
}
118
}
119
120
/**
121
* Ensures that at least <code>pos</code> bytes are cached,
122
* or the end of the source is reached. The return value
123
* is equal to the smaller of <code>pos</code> and the
124
* length of the source file.
125
*/
126
private long readUntil(long pos) throws IOException {
127
// We've already got enough data cached
128
if (pos < length) {
129
return pos;
130
}
131
// pos >= length but length isn't getting any bigger, so return it
132
if (foundEOF) {
133
return length;
134
}
135
136
long len = pos - length;
137
cache.seek(length);
138
while (len > 0) {
139
// Copy a buffer's worth of data from the source to the cache
140
// BUFFER_LENGTH will always fit into an int so this is safe
141
int nbytes =
142
stream.read(buf, 0, (int)Math.min(len, (long)BUFFER_LENGTH));
143
if (nbytes == -1) {
144
foundEOF = true;
145
return length;
146
}
147
148
cache.write(buf, 0, nbytes);
149
len -= nbytes;
150
length += nbytes;
151
}
152
153
return pos;
154
}
155
156
public int read() throws IOException {
157
checkClosed();
158
bitOffset = 0;
159
long next = streamPos + 1;
160
long pos = readUntil(next);
161
if (pos >= next) {
162
cache.seek(streamPos++);
163
return cache.read();
164
} else {
165
return -1;
166
}
167
}
168
169
public int read(byte[] b, int off, int len) throws IOException {
170
checkClosed();
171
172
if (b == null) {
173
throw new NullPointerException("b == null!");
174
}
175
// Fix 4430357 - if off + len < 0, overflow occurred
176
if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
177
throw new IndexOutOfBoundsException
178
("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
179
}
180
181
bitOffset = 0;
182
183
if (len == 0) {
184
return 0;
185
}
186
187
long pos = readUntil(streamPos + len);
188
189
// len will always fit into an int so this is safe
190
len = (int)Math.min((long)len, pos - streamPos);
191
if (len > 0) {
192
cache.seek(streamPos);
193
cache.readFully(b, off, len);
194
streamPos += len;
195
return len;
196
} else {
197
return -1;
198
}
199
}
200
201
/**
202
* Returns <code>true</code> since this
203
* <code>ImageInputStream</code> caches data in order to allow
204
* seeking backwards.
205
*
206
* @return <code>true</code>.
207
*
208
* @see #isCachedMemory
209
* @see #isCachedFile
210
*/
211
public boolean isCached() {
212
return true;
213
}
214
215
/**
216
* Returns <code>true</code> since this
217
* <code>ImageInputStream</code> maintains a file cache.
218
*
219
* @return <code>true</code>.
220
*
221
* @see #isCached
222
* @see #isCachedMemory
223
*/
224
public boolean isCachedFile() {
225
return true;
226
}
227
228
/**
229
* Returns <code>false</code> since this
230
* <code>ImageInputStream</code> does not maintain a main memory
231
* cache.
232
*
233
* @return <code>false</code>.
234
*
235
* @see #isCached
236
* @see #isCachedFile
237
*/
238
public boolean isCachedMemory() {
239
return false;
240
}
241
242
/**
243
* Closes this <code>FileCacheImageInputStream</code>, closing
244
* and removing the cache file. The source <code>InputStream</code>
245
* is not closed.
246
*
247
* @exception IOException if an error occurs.
248
*/
249
public void close() throws IOException {
250
super.close();
251
disposerRecord.dispose(); // this will close/delete the cache file
252
stream = null;
253
cache = null;
254
cacheFile = null;
255
StreamCloser.removeFromQueue(closeAction);
256
}
257
258
/**
259
* {@inheritDoc}
260
*/
261
protected void finalize() throws Throwable {
262
// Empty finalizer: for performance reasons we instead use the
263
// Disposer mechanism for ensuring that the underlying
264
// RandomAccessFile is closed/deleted prior to garbage collection
265
}
266
267
private static class StreamDisposerRecord implements DisposerRecord {
268
private File cacheFile;
269
private RandomAccessFile cache;
270
271
public StreamDisposerRecord(File cacheFile, RandomAccessFile cache) {
272
this.cacheFile = cacheFile;
273
this.cache = cache;
274
}
275
276
public synchronized void dispose() {
277
if (cache != null) {
278
try {
279
cache.close();
280
} catch (IOException e) {
281
} finally {
282
cache = null;
283
}
284
}
285
if (cacheFile != null) {
286
cacheFile.delete();
287
cacheFile = null;
288
}
289
// Note: Explicit removal of the stream from the StreamCloser
290
// queue is not mandatory in this case, as it will be removed
291
// automatically by GC shortly after this method is called.
292
}
293
}
294
}
295
296