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

se.wfh.libs.common.utils.cfr.ChainedFileReaderImpl Maven / Gradle / Ivy

There is a newer version: 0.17
Show newest version
package se.wfh.libs.common.utils.cfr;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

class ChainedFileReaderImpl {
	private CachedInputStream cachedStream = null;

	private final boolean cacheStream;

	private final List files = Collections
			.synchronizedList(new ArrayList<>());

	private long nextStart = 0;

	private long seqPos = 0;

	public ChainedFileReaderImpl(final boolean cacheStream) {
		this.cacheStream = cacheStream;
	}

	public void add(final File next) throws FileNotFoundException {
		// add the file to the end of the list
		files.add(new FileInfo(next, nextStart));

		// set the new start index
		nextStart += next.length();
	}

	public int available() {
		return (int) (nextStart - seqPos);
	}

	public void close() throws IOException {
		// if a cached stream is open, close it
		if (cachedStream != null) {
			cachedStream.getStream().close();
			cachedStream = null;
		}
	}

	private void closeStream(final Closeable stream) throws IOException {
		if (!cacheStream && stream != null) {
			stream.close();
		}
	}

	private void incCached(final int amount) {
		if (cachedStream != null) {
			cachedStream.incRead(amount);
		}
	}

	public long length() {
		return nextStart;
	}

	public int read(final byte buff[], final int off, final int len)
			throws IOException {
		int read = read(buff, off, seqPos, len);

		if (read > 0) {
			seqPos += read;
		}

		return read;
	}

	public int read(final byte buff[], final int off, final long pos,
			final int len) throws IOException {
		int read = 0;

		InputStream istream = null;
		try {
			while (read < len) {
				istream = seek(pos + read);
				if (istream == null) {
					if (read == 0) {
						read = -1;
					}

					break;
				}

				int cur = istream.read(buff, read + off, len - read);

				if (cur == -1) {
					continue;
				}

				read += cur;
				incCached(cur);
			}
		} finally {
			closeStream(istream);
		}

		return read;
	}

	public void reset() {
		seqPos = 0;
	}

	private InputStream seek(final long pos) throws IOException {
		if (pos >= nextStart) {
			return null;
		}

		/* search for cached stream */
		InputStream result = seekCachedStream(pos);
		if (result != null) {
			return result;
		}

		/*
		 * just allow one call to this function. as we iterate over the list of
		 * files, lock on them.
		 */
		Optional info = files
				.stream()
				.parallel()
				.filter(
						elem -> pos >= elem.getStart()
								&& pos < elem.getStart() + elem.getLength()).findFirst();

		if (info.isPresent()) {
			try {
				FileInfo match = info.get();

				/* open the stream */
				result = new FileInputStream(match.getFile());

				/* how many bytes we have to skip */
				long toSkip = pos - match.getStart();
				skipStream(result, toSkip);

				/* cache stream */
				cachedStream = new CachedInputStream(result, pos - match.getStart(),
						match.getStart() + match.getLength());
			} finally {
				if (cachedStream == null && result != null) {
					result.close();
				}
			}
		}

		return result;
	}

	private InputStream seekCachedStream(final long pos) throws IOException {
		InputStream result = null;

		/* check if the stream we seek is cached */
		if (cachedStream == null || !cacheStream) {
			return null;
		}

		/* is the cached stream the right one for the seeked position? */
		if (cachedStream.getPos() + seqPos <= pos && cachedStream.getEnd() > pos) {
			/* fetch the stream */
			InputStream stream = cachedStream.getStream();

			/* skip forward the the seeked position */
			skipStream(stream, pos - cachedStream.getPos());

			/* return the stream */
			result = stream;
		} else {
			/*
			 * the cached stream is not the right one for the seeked position. Close
			 * the old one.
			 */
			cachedStream.getStream().close();
			cachedStream = null;
		}

		return result;
	}

	public long skip(final long nbr) throws IOException {
		/* increment the position index */
		seqPos += nbr;

		/* return skipped count */
		return nbr;
	}

	private void skipStream(final InputStream result, final long count)
			throws IOException {
		/* counter for the skipped bytes */
		long skipped = 0;

		/* number of bytes to skip */
		long toSkip = count;

		/* as long as there are bytes to skip */
		while (toSkip > 0) {
			/* try to skip all bytes at once */
			skipped = result.skip(toSkip);

			/* if skipped return -1, the end of stream has been reached. */
			if (skipped < 0) {
				continue;
			}

			/*
			 * recalculate the bytes we need to skip according to the just
			 * skipped bytes.
			 */
			toSkip -= skipped;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy