package com.beijunyi.parallelgit.filesystem.io;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.OpenOption;
import java.util.Collection;
import static java.nio.file.StandardOpenOption.*;
/**
* For read-only access to a git object, we don't need (or want) to read the entire thing into
* memory.
*/
public class GfsSeekableReadOnlyByteChannel implements SeekableByteChannel {
private final FileNode file;
private InputStream stream = null;
private long position = 0;
boolean isOpen = true;
// see skip() for details
private long manualSkip;
GfsSeekableReadOnlyByteChannel(FileNode file, Collection<? extends OpenOption> options) throws IOException {
this.file = file;
if(options.contains(APPEND)) {
position = file.getSize();
} else {
stream = file.getInputStream();
}
}
@Override
public int read(ByteBuffer dst) throws IOException {
if (!isOpen)
throw new ClosedChannelException();
if (stream == null)
return -1;
if (manualSkip > 0) {
//see skip() for details
byte[] junk = new byte[(int)Math.min(65536, manualSkip)];
while (manualSkip > 0) {
manualSkip -= stream.read(junk, 0, (int)Math.min(junk.length, manualSkip));
}
}
int result = stream.read(dst.array(), dst.arrayOffset() + dst.position(), dst.remaining());
position += result;
return result;
}
@Override
public int write(ByteBuffer byteBuffer) throws IOException {
throw new NonWritableChannelException();
}
@Override
public long position() throws IOException {
return position;
}
@Override
public SeekableByteChannel position(long newPosition) throws IOException {
if (newPosition < position) {
if (stream != null)
stream.close();
stream = file.getInputStream();
position = 0;
manualSkip = 0;
}
if (newPosition >= size()) {
position = newPosition;
stream = null;
}
while (newPosition > position) {
long skipped = stream.skip(newPosition - position);
if (skipped == 0) {
// Incomprehensibly, InputStream.skip() is allowed to return
// zero for no reason at all. We could keep looping in that
// case, but for all we know, we'regoing to just keep getting
// zeros. So instead, we'll set this variable and handle
// it in read()
manualSkip = newPosition - position;
break;
}
position += skipped;
}
return this;
}
@Override
public long size() throws IOException {
return file.getSize();
}
@Override
public SeekableByteChannel truncate(long l) throws IOException {
throw new NonWritableChannelException();
}
@Override
public boolean isOpen() {
return isOpen;
}
@Override
public void close() throws IOException {
if (stream != null)
stream.close();
isOpen = false;
}
}