X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Futil%2FCachedInputStream.java;h=ac3ab9771321f5c33d07d36800707fec7bd7fdf5;hb=HEAD;hp=a712f84c78658d773e3bba4cc6511f8957501579;hpb=3a85dab61cef1346315ca40d3004f8772815127f;p=org.ibex.util.git diff --git a/src/org/ibex/util/CachedInputStream.java b/src/org/ibex/util/CachedInputStream.java index a712f84..ac3ab97 100644 --- a/src/org/ibex/util/CachedInputStream.java +++ b/src/org/ibex/util/CachedInputStream.java @@ -1,87 +1,109 @@ -// Copyright (C) 2003 Adam Megacz all rights reserved. -// -// You may modify, copy, and redistribute this code under the terms of -// the GNU Library Public License version 2.1, with the exception of -// the portion of clause 6a after the semicolon (aka the "obnoxious -// relink clause") +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. package org.ibex.util; + import java.io.*; -// FEATURE: don't use a byte[] if we have a diskCache file -/** - * Wraps around an InputStream, caching the stream in a byte[] as it - * is read and permitting multiple simultaneous readers +/** Wraps around an InputStream, caching the stream in a byte[] as it + * is read and permitting multiple simultaneous readers. + * + * @author adam@ibex.org, crawshaw@ibex.org */ public class CachedInputStream { - boolean filling = false; ///< true iff some thread is blocked on us waiting for input - boolean eof = false; ///< true iff end of stream has been reached - byte[] cache = new byte[1024 * 128]; - int size = 0; - final InputStream is; - File diskCache; + private final InputStream is; + + private byte[] cache = new byte[128]; + private File cacheFile; + private FileOutputStream out; + + /** True iff end of stream has been reached. */ + private boolean eof = false; - public CachedInputStream(InputStream is) { this(is, null); } - public CachedInputStream(InputStream is, File diskCache) { + /** Number of bytes currently cached. */ + private long size = 0; + + public CachedInputStream(InputStream is) { this.is = is; - this.diskCache = diskCache; + this.cacheFile = null; + this.out = null; } - public InputStream getInputStream() throws IOException { - if (diskCache != null && diskCache.exists()) return new FileInputStream(diskCache); - return new SubStream(); + public CachedInputStream(InputStream is, File cacheFile) throws IOException { + this.is = is; + this.cacheFile = cacheFile; + this.out = new FileOutputStream(cacheFile); } - public void grow(int newLength) { - if (newLength < cache.length) return; - byte[] newCache = new byte[cache.length + 2 * (newLength - cache.length)]; - System.arraycopy(cache, 0, newCache, 0, size); - cache = newCache; - } + public InputStream getInputStream() throws IOException { return new SubStream(); } + + private synchronized int fill(int need) throws IOException { + int ret; - synchronized void fillCache(int howMuch) throws IOException { - if (filling) { try { wait(); } catch (InterruptedException e) { }; return; } - filling = true; - grow(size + howMuch); - int ret = is.read(cache, size, howMuch); - if (ret == -1) { - eof = true; - // FIXME: probably a race here - if (diskCache != null && !diskCache.exists()) - try { - File cacheFile = new File(diskCache + ".incomplete"); - FileOutputStream cacheFileStream = new FileOutputStream(cacheFile); - cacheFileStream.write(cache, 0, size); - cacheFileStream.close(); - cacheFile.renameTo(diskCache); - } catch (IOException e) { - Log.info(this, "exception thrown while writing disk cache"); - Log.info(this, e); - } + if (out == null) { + if (size + need > cache.length) { + byte[] newCache = new byte[Math.max(cache.length * 2, (int)(size + need))]; + System.arraycopy(cache, 0, newCache, 0, (int)size); + cache = newCache; + } + ret = is.read(cache, (int)size, need); + } else { + ret = is.read(cache, 0, Math.min(cache.length, need)); + if (ret > 0) out.write(cache, 0, ret); } + + if (ret == -1) { eof = true; if (out != null) { out.close(); out = null; } } else size += ret; - filling = false; - notifyAll(); + + return ret; } - private class SubStream extends InputStream implements KnownLength { - int pos = 0; - public int available() { return Math.max(0, size - pos); } - public long skip(long n) throws IOException { pos += (int)n; return n; } // FEATURE: don't skip past EOF - public int getLength() { return eof ? size : is instanceof KnownLength ? ((KnownLength)is).getLength() : 0; } - public int read() throws IOException { // FEATURE: be smarter here - byte[] b = new byte[1]; - int ret = read(b, 0, 1); - return ret == -1 ? -1 : b[0]&0xff; + private final class SubStream extends InputStream { + private final InputStream fis; + private long pos = 0; + + private SubStream() throws IOException { + fis = cacheFile != null ? new FileInputStream(cacheFile) : null; } + + public int available() { synchronized(CachedInputStream.this) { return (int)(size - pos); } } + public long skip(long n) throws IOException { + synchronized(CachedInputStream.this) { + int need = (int)(pos + n - size); + while (need > 0 && !eof) need -= fill(need); + n -= need; + if (fis != null) { long fisn = n; while(fisn > 0) fisn -= fis.skip(fisn); } + pos += n; + return n; + } + } + public long length() { synchronized(CachedInputStream.this) { + return eof ? size : is instanceof SubStream ? ((SubStream)is).length() : 0; + } } + + private final byte[] single = new byte[1]; + public int read() throws IOException { + int ret = read(single, 0, 1); + return ret == -1 ? -1 : single[0]&0xff; + } + public int read(byte[] b, int off, int len) throws IOException { synchronized(CachedInputStream.this) { - while (pos >= size && !eof) fillCache(pos + len - size); - if (eof && pos == size) return -1; - int count = Math.min(size - pos, len); - System.arraycopy(cache, pos, b, off, count); - pos += count; - return count; + int i = (int)(pos - size) + len; while (i > 0 && !eof) i -= (int)fill(i); + if (i > 0) len -= i; + + if (fis != null) { + int fislen = len; + while (fislen > 0) { + int j = fis.read(b, off, fislen); + if (j == -1) throw new IOException("less data retrieved from disk than written to it"); + off += j; fislen -= j; + } + } else System.arraycopy(cache, (int)pos, b, off, len); + + pos += len; + return len; } } }