History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/imageio/stream/MemoryCache.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/imageio/stream/MemoryCache.java+364−0
@@ -0,0 +1,364 @@
1+/*
2+ * Copyright (c) 2000, 2003, 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.util.ArrayList;
29+import java.io.InputStream;
30+import java.io.OutputStream;
31+import java.io.IOException;
32+
33+/**
34+ * Package-visible class consolidating common code for
35+ * <code>MemoryCacheImageInputStream</code> and
36+ * <code>MemoryCacheImageOutputStream</code>.
37+ * This class keeps an <code>ArrayList</code> of 8K blocks,
38+ * loaded sequentially. Blocks may only be disposed of
39+ * from the index 0 forward. As blocks are freed, the
40+ * corresponding entries in the array list are set to
41+ * <code>null</code>, but no compacting is performed.
42+ * This allows the index for each block to never change,
43+ * and the length of the cache is always the same as the
44+ * total amount of data ever cached. Cached data is
45+ * therefore always contiguous from the point of last
46+ * disposal to the current length.
47+ *
48+ * <p> The total number of blocks resident in the cache must not
49+ * exceed <code>Integer.MAX_VALUE</code>. In practice, the limit of
50+ * available memory will be exceeded long before this becomes an
51+ * issue, since a full cache would contain 8192*2^31 = 16 terabytes of
52+ * data.
53+ *
54+ * A <code>MemoryCache</code> may be reused after a call
55+ * to <code>reset()</code>.
56+ */
57+class MemoryCache {
58+
59+ private static final int BUFFER_LENGTH = 8192;
60+
61+ private ArrayList cache = new ArrayList();
62+
63+ private long cacheStart = 0L;
64+
65+ /**
66+ * The largest position ever written to the cache.
67+ */
68+ private long length = 0L;
69+
70+ private byte[] getCacheBlock(long blockNum) throws IOException {
71+ long blockOffset = blockNum - cacheStart;
72+ if (blockOffset > Integer.MAX_VALUE) {
73+ // This can only happen when the cache hits 16 terabytes of
74+ // contiguous data...
75+ throw new IOException("Cache addressing limit exceeded!");
76+ }
77+ return (byte[])cache.get((int)blockOffset);
78+ }
79+
80+ /**
81+ * Ensures that at least <code>pos</code> bytes are cached,
82+ * or the end of the source is reached. The return value
83+ * is equal to the smaller of <code>pos</code> and the
84+ * length of the source.
85+ */
86+ public long loadFromStream(InputStream stream, long pos)
87+ throws IOException {
88+ // We've already got enough data cached
89+ if (pos < length) {
90+ return pos;
91+ }
92+
93+ int offset = (int)(length % BUFFER_LENGTH);
94+ byte [] buf = null;
95+
96+ long len = pos - length;
97+ if (offset != 0) {
98+ buf = getCacheBlock(length/BUFFER_LENGTH);
99+ }
100+
101+ while (len > 0) {
102+ if (buf == null) {
103+ try {
104+ buf = new byte[BUFFER_LENGTH];
105+ } catch (OutOfMemoryError e) {
106+ throw new IOException("No memory left for cache!");
107+ }
108+ offset = 0;
109+ }
110+
111+ int left = BUFFER_LENGTH - offset;
112+ int nbytes = (int)Math.min(len, (long)left);
113+ nbytes = stream.read(buf, offset, nbytes);
114+ if (nbytes == -1) {
115+ return length; // EOF
116+ }
117+
118+ if (offset == 0) {
119+ cache.add(buf);
120+ }
121+
122+ len -= nbytes;
123+ length += nbytes;
124+ offset += nbytes;
125+
126+ if (offset >= BUFFER_LENGTH) {
127+ // we've filled the current buffer, so a new one will be
128+ // allocated next time around (and offset will be reset to 0)
129+ buf = null;
130+ }
131+ }
132+
133+ return pos;
134+ }
135+
136+ /**
137+ * Writes out a portion of the cache to an <code>OutputStream</code>.
138+ * This method preserves no state about the output stream, and does
139+ * not dispose of any blocks containing bytes written. To dispose
140+ * blocks, use {@link #disposeBefore <code>disposeBefore()</code>}.
141+ *
142+ * @exception IndexOutOfBoundsException if any portion of
143+ * the requested data is not in the cache (including if <code>pos</code>
144+ * is in a block already disposed), or if either <code>pos</code> or
145+ * <code>len</code> is < 0.
146+ */
147+ public void writeToStream(OutputStream stream, long pos, long len)
148+ throws IOException {
149+ if (pos + len > length) {
150+ throw new IndexOutOfBoundsException("Argument out of cache");
151+ }
152+ if ((pos < 0) || (len < 0)) {
153+ throw new IndexOutOfBoundsException("Negative pos or len");
154+ }
155+ if (len == 0) {
156+ return;
157+ }
158+
159+ long bufIndex = pos/BUFFER_LENGTH;
160+ if (bufIndex < cacheStart) {
161+ throw new IndexOutOfBoundsException("pos already disposed");
162+ }
163+ int offset = (int)(pos % BUFFER_LENGTH);
164+
165+ byte[] buf = getCacheBlock(bufIndex++);
166+ while (len > 0) {
167+ if (buf == null) {
168+ buf = getCacheBlock(bufIndex++);
169+ offset = 0;
170+ }
171+ int nbytes = (int)Math.min(len, (long)(BUFFER_LENGTH - offset));
172+ stream.write(buf, offset, nbytes);
173+ buf = null;
174+ len -= nbytes;
175+ }
176+ }
177+
178+ /**
179+ * Ensure that there is space to write a byte at the given position.
180+ */
181+ private void pad(long pos) throws IOException {
182+ long currIndex = cacheStart + cache.size() - 1;
183+ long lastIndex = pos/BUFFER_LENGTH;
184+ long numNewBuffers = lastIndex - currIndex;
185+ for (long i = 0; i < numNewBuffers; i++) {
186+ try {
187+ cache.add(new byte[BUFFER_LENGTH]);
188+ } catch (OutOfMemoryError e) {
189+ throw new IOException("No memory left for cache!");
190+ }
191+ }
192+ }
193+
194+ /**
195+ * Overwrites and/or appends the cache from a byte array.
196+ * The length of the cache will be extended as needed to hold
197+ * the incoming data.
198+ *
199+ * @param b an array of bytes containing data to be written.
200+ * @param off the starting offset withing the data array.
201+ * @param len the number of bytes to be written.
202+ * @param pos the cache position at which to begin writing.
203+ *
204+ * @exception NullPointerException if <code>b</code> is <code>null</code>.
205+ * @exception IndexOutOfBoundsException if <code>off</code>,
206+ * <code>len</code>, or <code>pos</code> are negative,
207+ * or if <code>off+len > b.length</code>.
208+ */
209+ public void write(byte[] b, int off, int len, long pos)
210+ throws IOException {
211+ if (b == null) {
212+ throw new NullPointerException("b == null!");
213+ }
214+ // Fix 4430357 - if off + len < 0, overflow occurred
215+ if ((off < 0) || (len < 0) || (pos < 0) ||
216+ (off + len > b.length) || (off + len < 0)) {
217+ throw new IndexOutOfBoundsException();
218+ }
219+
220+ // Ensure there is space for the incoming data
221+ long lastPos = pos + len - 1;
222+ if (lastPos >= length) {
223+ pad(lastPos);
224+ length = lastPos + 1;
225+ }
226+
227+ // Copy the data into the cache, block by block
228+ int offset = (int)(pos % BUFFER_LENGTH);
229+ while (len > 0) {
230+ byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
231+ int nbytes = Math.min(len, BUFFER_LENGTH - offset);
232+ System.arraycopy(b, off, buf, offset, nbytes);
233+
234+ pos += nbytes;
235+ off += nbytes;
236+ len -= nbytes;
237+ offset = 0; // Always after the first time
238+ }
239+ }
240+
241+ /**
242+ * Overwrites or appends a single byte to the cache.
243+ * The length of the cache will be extended as needed to hold
244+ * the incoming data.
245+ *
246+ * @param b an <code>int</code> whose 8 least significant bits
247+ * will be written.
248+ * @param pos the cache position at which to begin writing.
249+ *
250+ * @exception IndexOutOfBoundsException if <code>pos</code> is negative.
251+ */
252+ public void write(int b, long pos) throws IOException {
253+ if (pos < 0) {
254+ throw new ArrayIndexOutOfBoundsException("pos < 0");
255+ }
256+
257+ // Ensure there is space for the incoming data
258+ if (pos >= length) {
259+ pad(pos);
260+ length = pos + 1;
261+ }
262+
263+ // Insert the data.
264+ byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
265+ int offset = (int)(pos % BUFFER_LENGTH);
266+ buf[offset] = (byte)b;
267+ }
268+
269+ /**
270+ * Returns the total length of data that has been cached,
271+ * regardless of whether any early blocks have been disposed.
272+ * This value will only ever increase.
273+ */
274+ public long getLength() {
275+ return length;
276+ }
277+
278+ /**
279+ * Returns the single byte at the given position, as an
280+ * <code>int</code>. Returns -1 if this position has
281+ * not been cached or has been disposed.
282+ */
283+ public int read(long pos) throws IOException {
284+ if (pos >= length) {
285+ return -1;
286+ }
287+
288+ byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
289+ if (buf == null) {
290+ return -1;
291+ }
292+
293+ return buf[(int)(pos % BUFFER_LENGTH)] & 0xff;
294+ }
295+
296+ /**
297+ * Copy <code>len</code> bytes from the cache, starting
298+ * at cache position <code>pos</code>, into the array
299+ * <code>b</code> at offset <code>off</code>.
300+ *
301+ * @exception NullPointerException if b is <code>null</code>
302+ * @exception IndexOutOfBoundsException if <code>off</code>,
303+ * <code>len</code> or <code>pos</code> are negative or if
304+ * <code>off + len > b.length</code> or if any portion of the
305+ * requested data is not in the cache (including if
306+ * <code>pos</code> is in a block that has already been disposed).
307+ */
308+ public void read(byte[] b, int off, int len, long pos)
309+ throws IOException {
310+ if (b == null) {
311+ throw new NullPointerException("b == null!");
312+ }
313+ // Fix 4430357 - if off + len < 0, overflow occurred
314+ if ((off < 0) || (len < 0) || (pos < 0) ||
315+ (off + len > b.length) || (off + len < 0)) {
316+ throw new IndexOutOfBoundsException();
317+ }
318+ if (pos + len > length) {
319+ throw new IndexOutOfBoundsException();
320+ }
321+
322+ long index = pos/BUFFER_LENGTH;
323+ int offset = (int)pos % BUFFER_LENGTH;
324+ while (len > 0) {
325+ int nbytes = Math.min(len, BUFFER_LENGTH - offset);
326+ byte[] buf = getCacheBlock(index++);
327+ System.arraycopy(buf, offset, b, off, nbytes);
328+
329+ len -= nbytes;
330+ off += nbytes;
331+ offset = 0; // Always after the first time
332+ }
333+ }
334+
335+ /**
336+ * Free the blocks up to the position <code>pos</code>.
337+ * The byte at <code>pos</code> remains available.
338+ *
339+ * @exception IndexOutOfBoundsException if <code>pos</code>
340+ * is in a block that has already been disposed.
341+ */
342+ public void disposeBefore(long pos) {
343+ long index = pos/BUFFER_LENGTH;
344+ if (index < cacheStart) {
345+ throw new IndexOutOfBoundsException("pos already disposed");
346+ }
347+ long numBlocks = Math.min(index - cacheStart, cache.size());
348+ for (long i = 0; i < numBlocks; i++) {
349+ cache.remove(0);
350+ }
351+ this.cacheStart = index;
352+ }
353+
354+ /**
355+ * Erase the entire cache contents and reset the length to 0.
356+ * The cache object may subsequently be reused as though it had just
357+ * been allocated.
358+ */
359+ public void reset() {
360+ cache.clear();
361+ cacheStart = 0;
362+ length = 0L;
363+ }
364+ }