@Override
    public void sanityCheck() {
        TimestampOffset entry = lastEntry();
        long lastTimestamp = entry.timestamp;
        long lastOffset = entry.offset;
        if (entries() != 0 && lastTimestamp < timestamp(mmap(), 0))
            throw new CorruptIndexException("Corrupt time index found, time index file (" + file().getAbsolutePath() + ") has "
                + "non-zero size but the last timestamp is " + lastTimestamp + " which is less than the first timestamp "
                + timestamp(mmap(), 0));
        if (entries() != 0 && lastOffset < baseOffset())
            throw new CorruptIndexException("Corrupt time index found, time index file (" + file().getAbsolutePath() + ") has "
                + "non-zero size but the last offset is " + lastOffset + " which is less than the first offset " + baseOffset());
        if (length() % ENTRY_SIZE != 0)
            throw new CorruptIndexException("Time index file " + file().getAbsolutePath() + " is corrupt, found " + length()
                + " bytes which is neither positive nor a multiple of " + ENTRY_SIZE);
    }
@Test
    public void testSanityCheck() throws IOException {
        idx.sanityCheck();
        appendEntries(5);
        TimestampOffset firstEntry = idx.entry(0);
        idx.sanityCheck();
        idx.close();

        class MockTimeIndex extends TimeIndex {
            boolean shouldCorruptOffset = false;
            boolean shouldCorruptTimestamp = false;
            boolean shouldCorruptLength = false;

            MockTimeIndex(File file, long baseOffset, int maxEntries) throws IOException {
                super(file, baseOffset, maxEntries);
            }

            @Override
            public TimestampOffset lastEntry() {
                TimestampOffset superLastEntry = super.lastEntry();
                long offset = shouldCorruptOffset ? this.baseOffset() - 1 : superLastEntry.offset;
                long timestamp = shouldCorruptTimestamp ? firstEntry.timestamp - 1 : superLastEntry.timestamp;
                return new TimestampOffset(timestamp, offset);
            }
            @Override
            public long length() {
                long superLength = super.length();
                return shouldCorruptLength ? superLength - 1 : superLength;
            }

            public void setShouldCorruptOffset(boolean shouldCorruptOffset) {
                this.shouldCorruptOffset = shouldCorruptOffset;
            }

            public void setShouldCorruptTimestamp(boolean shouldCorruptTimestamp) {
                this.shouldCorruptTimestamp = shouldCorruptTimestamp;
            }

            public void setShouldCorruptLength(boolean shouldCorruptLength) {
                this.shouldCorruptLength = shouldCorruptLength;
            }
        }
        try (MockTimeIndex mockIdx = new MockTimeIndex(idx.file(), baseOffset, maxEntries * 12)) {
            mockIdx.setShouldCorruptOffset(true);
            assertThrows(CorruptIndexException.class, mockIdx::sanityCheck);
            mockIdx.setShouldCorruptOffset(false);

            mockIdx.setShouldCorruptTimestamp(true);
            assertThrows(CorruptIndexException.class, mockIdx::sanityCheck);
            mockIdx.setShouldCorruptTimestamp(false);

            mockIdx.setShouldCorruptLength(true);
            assertThrows(CorruptIndexException.class, mockIdx::sanityCheck);
            mockIdx.setShouldCorruptLength(false);

            mockIdx.sanityCheck();
        }
    }