Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java
41153 views
1
/*
2
* Copyright (c) 2000, 2017, 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} that gets its
40
* input from a regular {@code InputStream}. 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} that will read
72
* from a given {@code InputStream}.
73
*
74
* <p> A temporary file is used as a cache. If
75
* {@code cacheDir} is non-{@code null} and is a
76
* directory, the file will be created there. If it is
77
* {@code null}, the system-dependent default temporary-file
78
* directory will be used (see the documentation for
79
* {@code File.createTempFile} for details).
80
*
81
* @param stream an {@code InputStream} to read from.
82
* @param cacheDir a {@code File} indicating where the
83
* cache file should be created, or {@code null} to use the
84
* system directory.
85
*
86
* @exception IllegalArgumentException if {@code stream} is
87
* {@code null}.
88
* @exception IllegalArgumentException if {@code cacheDir} is
89
* non-{@code null} but is not a directory.
90
* @throws 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} bytes are cached,
122
* or the end of the source is reached. The return value
123
* is equal to the smaller of {@code pos} and the
124
* length of the source file.
125
*
126
* @throws IOException if an I/O error occurs while reading from the
127
* source file
128
*/
129
private long readUntil(long pos) throws IOException {
130
// We've already got enough data cached
131
if (pos < length) {
132
return pos;
133
}
134
// pos >= length but length isn't getting any bigger, so return it
135
if (foundEOF) {
136
return length;
137
}
138
139
long len = pos - length;
140
cache.seek(length);
141
while (len > 0) {
142
// Copy a buffer's worth of data from the source to the cache
143
// BUFFER_LENGTH will always fit into an int so this is safe
144
int nbytes =
145
stream.read(buf, 0, (int)Math.min(len, (long)BUFFER_LENGTH));
146
if (nbytes == -1) {
147
foundEOF = true;
148
return length;
149
}
150
151
cache.write(buf, 0, nbytes);
152
len -= nbytes;
153
length += nbytes;
154
}
155
156
return pos;
157
}
158
159
public int read() throws IOException {
160
checkClosed();
161
bitOffset = 0;
162
long next = streamPos + 1;
163
long pos = readUntil(next);
164
if (pos >= next) {
165
cache.seek(streamPos++);
166
return cache.read();
167
} else {
168
return -1;
169
}
170
}
171
172
public int read(byte[] b, int off, int len) throws IOException {
173
checkClosed();
174
175
if (b == null) {
176
throw new NullPointerException("b == null!");
177
}
178
// Fix 4430357 - if off + len < 0, overflow occurred
179
if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
180
throw new IndexOutOfBoundsException
181
("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
182
}
183
184
bitOffset = 0;
185
186
if (len == 0) {
187
return 0;
188
}
189
190
long pos = readUntil(streamPos + len);
191
192
// len will always fit into an int so this is safe
193
len = (int)Math.min((long)len, pos - streamPos);
194
if (len > 0) {
195
cache.seek(streamPos);
196
cache.readFully(b, off, len);
197
streamPos += len;
198
return len;
199
} else {
200
return -1;
201
}
202
}
203
204
/**
205
* Returns {@code true} since this
206
* {@code ImageInputStream} caches data in order to allow
207
* seeking backwards.
208
*
209
* @return {@code true}.
210
*
211
* @see #isCachedMemory
212
* @see #isCachedFile
213
*/
214
public boolean isCached() {
215
return true;
216
}
217
218
/**
219
* Returns {@code true} since this
220
* {@code ImageInputStream} maintains a file cache.
221
*
222
* @return {@code true}.
223
*
224
* @see #isCached
225
* @see #isCachedMemory
226
*/
227
public boolean isCachedFile() {
228
return true;
229
}
230
231
/**
232
* Returns {@code false} since this
233
* {@code ImageInputStream} does not maintain a main memory
234
* cache.
235
*
236
* @return {@code false}.
237
*
238
* @see #isCached
239
* @see #isCachedFile
240
*/
241
public boolean isCachedMemory() {
242
return false;
243
}
244
245
/**
246
* Closes this {@code FileCacheImageInputStream}, closing
247
* and removing the cache file. The source {@code InputStream}
248
* is not closed.
249
*
250
* @throws IOException if an error occurs.
251
*/
252
public void close() throws IOException {
253
super.close();
254
disposerRecord.dispose(); // this will close/delete the cache file
255
stream = null;
256
cache = null;
257
cacheFile = null;
258
StreamCloser.removeFromQueue(closeAction);
259
}
260
261
/**
262
* {@inheritDoc}
263
*
264
* @deprecated The {@code finalize} method has been deprecated.
265
* Subclasses that override {@code finalize} in order to perform cleanup
266
* should be modified to use alternative cleanup mechanisms and
267
* to remove the overriding {@code finalize} method.
268
* When overriding the {@code finalize} method, its implementation must explicitly
269
* ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
270
* See the specification for {@link Object#finalize()} for further
271
* information about migration options.
272
*/
273
@Deprecated(since="9")
274
protected void finalize() throws Throwable {
275
// Empty finalizer: for performance reasons we instead use the
276
// Disposer mechanism for ensuring that the underlying
277
// RandomAccessFile is closed/deleted prior to garbage collection
278
}
279
280
private static class StreamDisposerRecord implements DisposerRecord {
281
private File cacheFile;
282
private RandomAccessFile cache;
283
284
public StreamDisposerRecord(File cacheFile, RandomAccessFile cache) {
285
this.cacheFile = cacheFile;
286
this.cache = cache;
287
}
288
289
public synchronized void dispose() {
290
if (cache != null) {
291
try {
292
cache.close();
293
} catch (IOException e) {
294
} finally {
295
cache = null;
296
}
297
}
298
if (cacheFile != null) {
299
cacheFile.delete();
300
cacheFile = null;
301
}
302
// Note: Explicit removal of the stream from the StreamCloser
303
// queue is not mandatory in this case, as it will be removed
304
// automatically by GC shortly after this method is called.
305
}
306
}
307
}
308
309