package com.liveramp.hank.storage.cueball;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import com.liveramp.hank.util.EncodingHelper;
final class Footer {
private final long[] hashIndex;
private final int maxUncompressedBufferSize;
private final int maxCompressedBufferSize;
private final long fileSize;
private final int footerLength;
public Footer(FileChannel channel, int hashIndexBits) throws IOException {
fileSize = channel.size();
final int hashIndexSize = 1 << hashIndexBits;
footerLength = hashIndexSize * 8 + 8;
byte[] footer = new byte[getFooterLength()];
int read = channel.read(ByteBuffer.wrap(footer), getFileSize() - getFooterLength());
if (read != getFooterLength()) {
throw new IOException("Tried to read " + getFooterLength() + " bytes of footer, but only got " + read + " bytes!");
}
hashIndex = new long[hashIndexSize];
for (int i = 0; i < getHashIndex().length; i++) {
final long offset = EncodingHelper.decodeLittleEndianFixedWidthLong(footer, i * 8, 8);
if (offset < -1) {
throw new IOException(String.format("Read an unexpectedly negative block offset (%d) at block position %d!", offset, i));
}
if (offset != -1 && i > 0 && hashIndex[i-1] != -1 && hashIndex[i-1] >= offset) {
throw new IOException(String.format("Discovered an offset inversion! block %d offset: %d, block %d offset: %d", i-1, hashIndex[i-1], i, offset));
}
getHashIndex()[i] = offset;
}
maxUncompressedBufferSize = (int) EncodingHelper.decodeLittleEndianFixedWidthLong(footer, footer.length - 8, 4);
if (maxUncompressedBufferSize < 0) {
throw new IOException(String.format("Read an invalid max uncompressed buffer size of %d!", maxUncompressedBufferSize));
}
maxCompressedBufferSize = (int) EncodingHelper.decodeLittleEndianFixedWidthLong(footer, footer.length - 4, 4);
if (maxCompressedBufferSize < 0) {
throw new IOException(String.format("Read an invalid max uncompressed buffer size of %d!", maxCompressedBufferSize));
}
}
public long[] getHashIndex() {
return hashIndex;
}
public int getMaxCompressedBufferSize() {
return maxCompressedBufferSize;
}
public int getMaxUncompressedBufferSize() {
return maxUncompressedBufferSize;
}
public long getFileSize() {
return fileSize;
}
public int getFooterLength() {
return footerLength;
}
public long getDataLength() {
return getFileSize() - getFooterLength();
}
}