001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.x.imageio.stream;
019:
020: import java.util.ArrayList;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024:
025: public final class RandomAccessMemoryCache {
026: private static final int BLOCK_SHIFT = 9;
027: private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT;
028: private static final int BLOCK_MASK = BLOCK_SIZE - 1;
029:
030: private long length;
031:
032: private int firstUndisposed = 0;
033:
034: private ArrayList<byte[]> blocks = new ArrayList<byte[]>();
035:
036: public RandomAccessMemoryCache() {
037: }
038:
039: public long length() {
040: return length;
041: }
042:
043: public void close() {
044: blocks.clear();
045: length = 0;
046: }
047:
048: private void grow(long pos) {
049: int blocksNeeded = (int) (pos >> BLOCK_SHIFT) - blocks.size()
050: + 1;
051: for (int i = 0; i < blocksNeeded; i++) {
052: blocks.add(new byte[BLOCK_SIZE]);
053: }
054:
055: length = pos + 1;
056: }
057:
058: public void putData(int oneByte, long pos) {
059: if (pos >= length) {
060: grow(pos);
061: }
062:
063: byte[] block = blocks.get((int) (pos >> BLOCK_SHIFT));
064: block[(int) (pos & BLOCK_MASK)] = (byte) oneByte;
065: }
066:
067: public void putData(byte[] buffer, int offset, int count, long pos) {
068: if (count > buffer.length - offset || count < 0 || offset < 0) {
069: throw new IndexOutOfBoundsException();
070: }
071: if (count == 0) {
072: return;
073: }
074:
075: long lastPos = pos + count - 1;
076: if (lastPos >= length) {
077: grow(lastPos);
078: }
079:
080: while (count > 0) {
081: byte[] block = blocks.get((int) (pos >> BLOCK_SHIFT));
082: int blockOffset = (int) (pos & BLOCK_MASK);
083: int toCopy = Math.min(BLOCK_SIZE - blockOffset, count);
084: System
085: .arraycopy(buffer, offset, block, blockOffset,
086: toCopy);
087: pos += toCopy;
088: count -= toCopy;
089: offset += toCopy;
090: }
091: }
092:
093: public int getData(long pos) {
094: if (pos >= length) {
095: return -1;
096: }
097:
098: byte[] block = blocks.get((int) (pos >> BLOCK_SHIFT));
099: return block[(int) (pos & BLOCK_MASK)] & 0xFF;
100: }
101:
102: public int getData(byte[] buffer, int offset, int count, long pos) {
103: if (count > buffer.length - offset || count < 0 || offset < 0) {
104: throw new IndexOutOfBoundsException();
105: }
106: if (count == 0) {
107: return 0;
108: }
109: if (pos >= length) {
110: return -1;
111: }
112:
113: if (count + pos > length) {
114: count = (int) (length - pos);
115: }
116:
117: byte[] block = blocks.get((int) (pos >> BLOCK_SHIFT));
118: int nbytes = Math.min(count, BLOCK_SIZE
119: - (int) (pos & BLOCK_MASK));
120: System.arraycopy(block, (int) (pos & BLOCK_MASK), buffer,
121: offset, nbytes);
122:
123: return nbytes;
124: }
125:
126: /*
127: public void seek(long pos) throws IOException {
128: if (pos < 0) {
129: throw new IOException("seek position is negative");
130: }
131: this.pos = pos;
132: }
133:
134: public void readFully(byte[] buffer) throws IOException {
135: readFully(buffer, 0, buffer.length);
136: }
137:
138: public void readFully(byte[] buffer, int offset, int count) throws IOException {
139: if (0 <= offset && offset <= buffer.length && 0 <= count && count <= buffer.length - offset) {
140: while (count > 0) {
141: int result = read(buffer, offset, count);
142: if (result >= 0) {
143: offset += result;
144: count -= result;
145: } else {
146: throw new EOFException();
147: }
148: }
149: } else {
150: throw new IndexOutOfBoundsException();
151: }
152: }
153:
154: public long getFilePointer() {
155: return pos;
156: }
157: */
158:
159: public void freeBefore(long pos) {
160: int blockIdx = (int) (pos >> BLOCK_SHIFT);
161: if (blockIdx <= firstUndisposed) { // Nothing to do
162: return;
163: }
164:
165: for (int i = firstUndisposed; i < blockIdx; i++) {
166: blocks.set(i, null);
167: }
168:
169: firstUndisposed = blockIdx;
170: }
171:
172: public int appendData(InputStream is, int count) throws IOException {
173: if (count <= 0) {
174: return 0;
175: }
176:
177: long startPos = length;
178: long lastPos = length + count - 1;
179: grow(lastPos); // Changes length
180:
181: int blockIdx = (int) (startPos >> BLOCK_SHIFT);
182: int offset = (int) (startPos & BLOCK_MASK);
183:
184: int bytesAppended = 0;
185:
186: while (count > 0) {
187: byte[] block = blocks.get(blockIdx);
188: int toCopy = Math.min(BLOCK_SIZE - offset, count);
189: count -= toCopy;
190:
191: while (toCopy > 0) {
192: int bytesRead = is.read(block, offset, toCopy);
193:
194: if (bytesRead < 0) {
195: length -= (count - bytesAppended);
196: return bytesAppended;
197: }
198:
199: toCopy -= bytesRead;
200: offset += bytesRead;
201: }
202:
203: blockIdx++;
204: offset = 0;
205: }
206:
207: return count;
208: }
209:
210: public void getData(OutputStream os, int count, long pos)
211: throws IOException {
212: if (pos + count > length) {
213: throw new IndexOutOfBoundsException("Argument out of cache");
214: }
215:
216: int blockIdx = (int) (pos >> BLOCK_SHIFT);
217: int offset = (int) (pos & BLOCK_MASK);
218: if (blockIdx < firstUndisposed) {
219: throw new IndexOutOfBoundsException(
220: "The requested data are already disposed");
221: }
222:
223: while (count > 0) {
224: byte[] block = blocks.get(blockIdx);
225: int toWrite = Math.min(BLOCK_SIZE - offset, count);
226: os.write(block, offset, toWrite);
227:
228: blockIdx++;
229: offset = 0;
230: count -= toWrite;
231: }
232: }
233: }
|