package io.fathom.cloud.storage.api.os.models;
import io.fathom.cloud.blobs.BlobData;
import io.fathom.cloud.blobs.BlobStore;
import io.fathom.cloud.protobuf.FileModel.FileData;
import io.fathom.cloud.protobuf.FileModel.FileRange;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import com.fathomdb.utils.Hex;
import com.google.common.collect.Lists;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
public class CloudObject {
protected static final ByteSource EMPTY = ByteStreams.asByteSource(new byte[0]);
final FileData data;
public CloudObject(FileData data) {
this.data = data;
}
public InputStream getInputStream(final BlobStore blobStore) throws IOException {
return getInputStream(blobStore, null, null);
}
public InputStream getInputStream(final BlobStore blobStore, final Long from, final Long to) throws IOException {
// if (data.getRangesCount() == 1) {
// return getInputStream(keyValueStore, data.getRanges(0)
// .getContentKey());
// } else {
// TODO: This can be made much more efficient
List<FileRange> ranges = data.getRangesList();
if (from != null || to != null) {
List<FileRange> matching = Lists.newArrayList();
for (FileRange range : ranges) {
if (from != null) {
if (from > range.getEnd()) {
continue;
}
}
if (to != null) {
if (to <= range.getStart()) {
continue;
}
}
matching.add(range);
}
ranges = matching;
}
final Iterator<FileRange> it = data.getRangesList().iterator();
SequenceInputStream sis = new SequenceInputStream(new Enumeration<InputStream>() {
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public InputStream nextElement() {
ByteSource is;
try {
FileRange range = it.next();
is = getBlob(blobStore, range.getContentKey());
if (to != null || from != null) {
long start = 0;
if (from != null) {
start = Math.max(0, from - range.getStart());
// Enforced by the skipping logic
assert start <= is.size();
}
long end = is.size();
if (to != null) {
end = Math.min(to - range.getStart(), is.size());
// Enforced by the skipping logic
assert end >= 0;
}
long length = end - start;
if (length <= 0) {
assert length == 0;
// Easier than worrying about hasNext
is = EMPTY;
} else {
is = is.slice(start, length);
}
}
// TODO: Open buffered stream??
return is.openStream();
} catch (IOException e) {
throw new IllegalStateException("Error reading data", e);
}
}
});
return sis;
// }
}
BlobData getBlob(BlobStore blobStore, ByteString key) throws IOException {
final BlobData is = blobStore.find(key);
if (is == null) {
throw new IOException("Unable to open storage for range: " + Hex.toHex(key.toByteArray()));
}
return is;
}
public ByteSource asByteSource(final BlobStore blobStore, final Long from, final Long to) {
return new ByteSource() {
@Override
public InputStream openStream() throws IOException {
return getInputStream(blobStore, from, to);
}
};
}
}