private void readFromFile(File fileToRead, Journal journal) throws StoreException {
    logger.info("IndexSegment : {} reading index from file", indexFile.getAbsolutePath());

    ByteBuffer byteBuffer = checkFileDataIntegrity(fileToRead);
    byteBuffer.position(0);
    byteBuffer.limit(byteBuffer.capacity() - CRC_FIELD_LENGTH);
    // We've checked the CRC and it matches, which means this index segment file is intact. All the errors from here
    // on are logical errors, not format errors.

    // tmpEntrySize is a local variable. If the readFromfile is successful, will set maxEntrySize with it.
    // tmpValueSize is a local variable. If the readFromfile is successful, will set valueSize with it.
    int tmpEntrySize;
    int tmpValueSize;
    index.clear();
    try (DataInputStream stream = new ByteBufferDataInputStream(byteBuffer)) {
      setVersion(stream.readShort());
      switch (getVersion()) {
        case PersistentIndex.VERSION_0:
        case PersistentIndex.VERSION_1:
          int keySize = stream.readInt();
          tmpValueSize = stream.readInt();
          tmpEntrySize = keySize + tmpValueSize;
          indexSizeExcludingEntries = VERSION_FIELD_LENGTH + KEY_OR_ENTRY_SIZE_FIELD_LENGTH + VALUE_SIZE_FIELD_LENGTH
              + LOG_END_OFFSET_FIELD_LENGTH + CRC_FIELD_LENGTH;
          break;
        case PersistentIndex.VERSION_2:
        case PersistentIndex.VERSION_3:
        case PersistentIndex.VERSION_4:
          tmpEntrySize = stream.readInt();
          tmpValueSize = stream.readInt();
          indexSizeExcludingEntries = VERSION_FIELD_LENGTH + KEY_OR_ENTRY_SIZE_FIELD_LENGTH + VALUE_SIZE_FIELD_LENGTH
              + LOG_END_OFFSET_FIELD_LENGTH + CRC_FIELD_LENGTH;
          break;
        default:
          throw new StoreException("IndexSegment : " + indexFile.getAbsolutePath() + " invalid version in index file ",
              StoreErrorCodes.IndexVersionError);
      }
      long logEndOffset = stream.readLong();
      if (getVersion() == PersistentIndex.VERSION_0) {
        lastModifiedTimeSec.set(indexFile.lastModified() / 1000);
      } else {
        lastModifiedTimeSec.set(stream.readLong());
        StoreKey storeKey = factory.getStoreKey(stream);
        PersistentIndex.IndexEntryType resetKeyType = PersistentIndex.IndexEntryType.values()[stream.readShort()];
        short resetKeyLifeVersion = UNINITIALIZED_RESET_KEY_VERSION;
        indexSizeExcludingEntries +=
            LAST_MODIFIED_TIME_FIELD_LENGTH + storeKey.sizeInBytes() + RESET_KEY_TYPE_FIELD_LENGTH;
        if (getVersion() >= PersistentIndex.VERSION_4) {
          resetKeyLifeVersion = stream.readShort();
          indexSizeExcludingEntries += RESET_KEY_VERSION_FIELD_LENGTH;
        }
        resetKeyInfo = new ResetKeyInfo(storeKey, resetKeyType, resetKeyLifeVersion);
      }
      firstKeyRelativeOffset = indexSizeExcludingEntries - CRC_FIELD_LENGTH;
      logger.trace("IndexSegment : {} reading log end offset {} from file", indexFile.getAbsolutePath(), logEndOffset);
      long maxEndOffset = Long.MIN_VALUE;
      byte[] padding = new byte[tmpEntrySize - tmpValueSize];
      while (stream.available() > CRC_FIELD_LENGTH) {
        StoreKey key = factory.getStoreKey(stream);
        byte[] value = new byte[tmpValueSize];
        stream.readFully(value);
        if (getVersion() >= PersistentIndex.VERSION_2) {
          stream.readFully(padding, 0, tmpEntrySize - (key.sizeInBytes() + tmpValueSize));
        }
        IndexValue blobValue = new IndexValue(startOffset.getName(), ByteBuffer.wrap(value), getVersion());
        long offsetInLogSegment = blobValue.getOffset().getOffset();
        // ignore entries that have offsets outside the log end offset that this index represents
        if (offsetInLogSegment + blobValue.getSize() <= logEndOffset) {
          boolean isPresent = index.containsKey(key);
          index.computeIfAbsent(key, k -> new ConcurrentSkipListSet<>()).add(blobValue);
          logger.trace("IndexSegment : {} putting key {} in index offset {} size {}", indexFile.getAbsolutePath(), key,
              blobValue.getOffset(), blobValue.getSize());
          // regenerate the bloom filter for index segments that are not sealed
          if (!isPresent) {
            bloomFilter.add(getStoreKeyBytes(key));
          }
          // add to the journal
          long oMsgOff = blobValue.getOriginalMessageOffset();
          if (oMsgOff != IndexValue.UNKNOWN_ORIGINAL_MESSAGE_OFFSET && offsetInLogSegment != oMsgOff
              && oMsgOff >= startOffset.getOffset()
              && journal.getKeyAtOffset(new Offset(startOffset.getName(), oMsgOff)) == null) {
            // we add an entry for the original message offset if it is within the same index segment and
            // an entry is not already in the journal
            journal.addEntry(new Offset(startOffset.getName(), blobValue.getOriginalMessageOffset()), key);
          }
          journal.addEntry(blobValue.getOffset(), key);
          // sizeWritten is only used for in-memory segments, and for those the padding does not come into picture.
          sizeWritten.addAndGet(key.sizeInBytes() + tmpValueSize);
          numberOfItems.incrementAndGet();
          if (offsetInLogSegment + blobValue.getSize() > maxEndOffset) {
            maxEndOffset = offsetInLogSegment + blobValue.getSize();
          }
        } else {
          logger.info(
              "IndexSegment : {} ignoring index entry outside the log end offset that was not synced logEndOffset "
                  + "{} key {} entryOffset {} entrySize {} entryDeleteState {}", indexFile.getAbsolutePath(),
              logEndOffset, key, blobValue.getOffset(), blobValue.getSize(), blobValue.isDelete());
        }
      }
      endOffset.set(new Offset(startOffset.getName(), maxEndOffset));
      logger.trace("IndexSegment : {} setting end offset for index {}", indexFile.getAbsolutePath(), maxEndOffset);
      maxEntrySize = tmpEntrySize;
      valueSize = tmpValueSize;
    } catch (IOException e) {
      StoreErrorCodes errorCode = StoreException.resolveErrorCode(e);
      throw new StoreException("IndexSegment : " + indexFile.getAbsolutePath() + " encountered " + errorCode.toString()
          + " while reading from file ", e, errorCode);
    }
  }
Reads the index segment from file into an in memory representation
@param fileToRead The file to read the index segment from
@param journal The journal to use.
@throws StoreException
@throws FileNotFoundException
ambry-store/src/main/java/com/github/ambry/store/IndexSegment.java