001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032 package com.imagero.uio.bio.content;
033
034 import com.imagero.uio.io.IOutils;
035 import com.imagero.uio.io.UnexpectedEOFException;
036
037 import java.io.InputStream;
038 import java.io.IOException;
039 import java.io.EOFException;
040 import java.util.Hashtable;
041
042 /**
043 * Date: 05.01.2008
044 *
045 * @author Andrey Kuznetsov
046 */
047 public class MemoryCachedInputStreamContent extends Content {
048 InputStream in;
049 int chunkSize;
050 boolean finished;
051
052 int overflow = 5;
053
054 Hashtable ht = new Hashtable();
055
056 int lastChunkSize;
057
058 public MemoryCachedInputStreamContent(InputStream in, int chunkSize) {
059 this.in = in;
060 this.chunkSize = chunkSize;
061 }
062
063 public int load(long offset, int destOffset, byte[] dest) throws IOException {
064 long index = offset / chunkSize;
065 if (finished && index >= readCount) {
066 throw new EOFException();
067 }
068 if (!finished) {
069 try {
070 for (long i = readCount; i <= index; i++) {
071 byte[] b = new byte[chunkSize];
072 Chunk chunk = addChunk(b, i); //first add then read
073 int length = b.length;
074 lastChunkSize = chunkSize;
075 try {
076 IOutils.readFully(in, b);
077 } catch (UnexpectedEOFException ex) {
078 length = ex.getCount();
079 }
080 if (chunk.src.length != length) {
081 if (length > 0) {
082 b = new byte[length];
083 System.arraycopy(chunk.src, 0, b, 0, length);
084 chunk.src = b;
085 lastChunkSize = length;
086 } else {
087 ht.remove(new Long(i));
088 }
089 finished = true;
090 break;
091 }
092 }
093 } catch (IOException ex) {
094 finished = true;
095 }
096 }
097 if (index <= readCount) {
098 return copyData(dest, destOffset, offset);
099 }
100 return 0;
101 }
102
103 protected void prepare() {
104 try {
105 for (long i = readCount; !finished; i++) {
106 byte[] b = new byte[chunkSize];
107 Chunk chunk = addChunk(b, i); //first add then read
108 int length = b.length;
109 lastChunkSize = chunkSize;
110 try {
111 IOutils.readFully(in, b);
112 } catch (UnexpectedEOFException ex) {
113 length = ex.getCount();
114 }
115 if (chunk.src.length != length) {
116 if (length > 0) {
117 b = new byte[length];
118 System.arraycopy(chunk.src, 0, b, 0, length);
119 chunk.src = b;
120 lastChunkSize = length;
121 } else {
122 ht.remove(new Long(i));
123 }
124 finished = true;
125 break;
126 }
127 }
128 } catch (IOException ex) {
129 finished = true;
130 }
131 }
132
133 private int copyData(byte[] dest, int destOffset, long streamOffset) {
134 long index = streamOffset / chunkSize;
135 Chunk chunk = (Chunk) ht.get(new Long(index));
136 if (chunk != null) {
137 return chunk.copyInterval(dest, destOffset, streamOffset);
138 }
139 return 0;
140 }
141
142 public void close() {
143 }
144
145 long readCount;
146
147 private Chunk addChunk(byte[] buf, long index) {
148 long start = index * chunkSize;
149 Chunk helper = new Chunk(buf, index, start);
150 readCount++;
151 ht.put(new Long(index), helper);
152 return helper;
153 }
154
155 class Chunk {
156 byte[] src;
157 long index;
158
159 long start;
160
161 private Chunk parent;
162
163 private Chunk left;
164 private Chunk right;
165
166 public Chunk(byte[] buf, long index, long start) {
167 this.src = buf;
168 this.index = index;
169 this.start = start;
170 }
171
172 /**
173 *
174 * @param dest destination array
175 * @param destOffset start offset in destination array
176 * @param absOffset absolute offset in stream
177 * @return how much bytes was copied
178 */
179 int copyInterval(byte[] dest, int destOffset, long absOffset) {
180 if (src != null) {
181 if ((start > absOffset) || (absOffset > start + src.length)) {
182 throw new IndexOutOfBoundsException("Given offset is out of chunk bounds");
183 }
184 if (destOffset < 0 || destOffset > dest.length) {
185 throw new IndexOutOfBoundsException("Illegal destination offset: " + destOffset);
186 }
187 int srcOffset = (int) (absOffset - start);
188 int length = Math.min(dest.length - destOffset, src.length - srcOffset);
189 System.arraycopy(src, srcOffset, dest, destOffset, length);
190 if (srcOffset == 0 && length == src.length) {
191 free();
192 } else {
193 if (srcOffset == 0) {
194 //right part leftover
195 byte[] buf = new byte[src.length - length];
196 System.arraycopy(src, length, buf, 0, buf.length);
197 src = buf;
198 } else if (srcOffset + length == src.length) {
199 //left part leftover
200 byte[] buf = new byte[src.length - length];
201 System.arraycopy(src, 0, buf, 0, buf.length);
202 src = buf;
203 } else {
204 byte[] leftBuf = new byte[srcOffset];
205 System.arraycopy(src, 0, leftBuf, 0, leftBuf.length);
206 left = new Chunk(leftBuf, -1, start);
207 left.parent = this;
208
209 byte[] rightBuf = new byte[src.length - (srcOffset + length)];
210 System.arraycopy(src, srcOffset + length, rightBuf, 0, rightBuf.length);
211 right = new Chunk(rightBuf, -1, start + srcOffset + length);
212 right.parent = this;
213
214 src = null;
215 }
216 }
217 return length;
218 } else {
219 Chunk chunk = getChild(absOffset);
220 if (chunk != null) {
221 return chunk.copyInterval(dest, destOffset, absOffset);
222 }
223 }
224 return 0;
225 }
226
227 private Chunk getChild(long absOffset) {
228 if (right.start <= absOffset) {
229 if (right.src != null) {
230 return right;
231 } else {
232 return right.getChild(absOffset);
233 }
234 } else if (left.start <= absOffset) {
235 if (left.src != null) {
236 return left;
237 } else {
238 return left.getChild(absOffset);
239 }
240 }
241 return null;
242 }
243
244 private void free() {
245 if (parent != null) {
246 parent.removeChild(this);
247 } else {
248 ht.remove(new Long(index));
249 }
250 }
251
252 private void removeChild(Chunk c) {
253 if (c == null && c.parent != this) {
254 return;
255 }
256 if (c == left) {
257 left = null;
258 } else if (c == right) {
259 right = null;
260 } else {
261 return;
262 }
263
264 if (left == null && right == null) {
265 free();
266 } else {
267 if (left != null) {
268 connectChild(left);
269 } else if (right != null) {
270 connectChild(right);
271 }
272 }
273 }
274
275 private void connectChild(Chunk c) {
276 src = c.src;
277 left = c.left;
278 right = c.right;
279 }
280 }
281
282
283 public boolean canReload() {
284 return false;
285 }
286
287 public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
288 }
289
290 public long length() throws IOException {
291 if (!finished) {
292 prepare();
293 }
294 return (readCount - 1) * chunkSize + lastChunkSize;
295 }
296
297 public boolean writable() {
298 return false;
299 }
300 }