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));
}
}