All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.archive.streamcontext.AbstractBufferingStream Maven / Gradle / Ivy

There is a newer version: 1.1.9
Show newest version
package org.archive.streamcontext;

import java.io.IOException;

/**
 * Class which implements the bulk of the functionality needed for Stream
 * behavior, leaving concrete subclasses to implement only 3 simple methods
 * beyond their constructors:
 * 
 *   doSeek()
 *   doRead()
 *   doClose()
 *   
 * This class maintains a buffer of bytes read, and attempts to be efficient
 * about seeks within that buffer.
 * 
 * @author brad
 *
 */
public abstract class AbstractBufferingStream implements Stream {
	protected static int DEFAULT_READ_SIZE = 4096;

	protected long offset = 0L;
	protected boolean closed = false;
	protected boolean atEof = false;
	protected byte buffer[] = null;
	protected int bufferRemaining = 0;
	protected int bufferCursor = 0;
	
	public AbstractBufferingStream() {
		this(0L,DEFAULT_READ_SIZE);
	}
	public AbstractBufferingStream(long offset) {
		this(offset,DEFAULT_READ_SIZE);
	}
	public AbstractBufferingStream(long offset, int readSize) {
		if(offset < 0) {
			throw new IndexOutOfBoundsException();
		}
		this.offset = offset;
		buffer = new byte[readSize];
		closed = false;
		atEof = false;
		bufferRemaining = 0;
		bufferCursor = 0;
	}

	public abstract int doRead(byte[] b, int off, int len) throws IOException;
	public abstract void doSeek(long offset) throws IOException;
	public abstract void doClose() throws IOException;

	public boolean atEof() {
		return atEof;
	}

	public long getOffset() {
		return offset;
	}

	public int read(byte[] b, int off, int len) throws IOException {
		if(closed) {
			throw new IOException("Read after close()");
		}
		if(atEof) {
			return -1;
		}
		int amtRead = 0;
		while(len > 0) {
			if(bufferRemaining > 0) {
				int amtToCopy = Math.min(bufferRemaining, len);
				System.arraycopy(buffer,bufferCursor,b,off,amtToCopy);
				bufferCursor += amtToCopy;
				bufferRemaining -= amtToCopy;
				off += amtToCopy;
				len -= amtToCopy;
				amtRead += amtToCopy;
			}
			// either we satisfied the read request, or the buffer is empty:
			if(len > 0) {
				// our buffer is empty at this point, fill it up:
				int amtReadNow = doRead(buffer,0,buffer.length);
				if(amtReadNow == -1) {
					atEof = true;
					break;
				}
				bufferCursor = 0;
				bufferRemaining = amtReadNow;
			}
		}
		if(amtRead == 0) {
			// must be at EOF:
			amtRead = -1;
		} else {
			// got some data: advance the offset:
			offset += amtRead;
		}
		return amtRead;
	}

	
	public long setOffset(long newOffset) throws IOException {
		if(offset < newOffset) {
			// we're scanning ahead:
			long amtToSkip = newOffset - offset;
			if(amtToSkip < bufferRemaining) {
				// skipping to somewhere in our current buffer:
				bufferRemaining -= amtToSkip;
				bufferCursor += amtToSkip;
			} else {
				// seeking forward beyond buffer:
				// OPTIMIZ: are few read()s a bit rather than seek()?
				doSeek(newOffset);
				bufferRemaining = 0;
			}
			atEof = false;
		} else if(offset > newOffset) {
			long amtToReverse = offset - newOffset;
			if(amtToReverse < bufferCursor) {
				// within our buffer:
				bufferCursor -= amtToReverse;
				bufferRemaining += amtToReverse;
			} else {
				// seeking backwards beyond buffer:
				doSeek(newOffset);
				bufferRemaining = 0;
			}
			atEof = false;
		}
		offset = newOffset;
		return newOffset;		
	}
	
	public void close() throws IOException {
		if(!closed) {
			doClose();
			closed = true;
		}
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy