private long readAndPopulateIndex(long offsetUpto) throws Exception {
    final String Index_File_Name_Suffix = "index";
    long lastEligibleSegmentEndOffset = -1;
    File indexDir = new File(dataDir);
    File[] indexFiles = indexDir.listFiles(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return name.endsWith(Index_File_Name_Suffix);
      }
    });

    Arrays.sort(indexFiles, new Comparator<File>() {
      @Override
      public int compare(File o1, File o2) {
        if (o1 == null || o2 == null) {
          throw new NullPointerException("arguments to compare two files is null");
        }
        // File name pattern for index is offset_name. We extract the offset from
        // name to compare
        int o1Index = o1.getName().indexOf("_", 0);
        long o1Offset = Long.parseLong(o1.getName().substring(0, o1Index));
        int o2Index = o2.getName().indexOf("_", 0);
        long o2Offset = Long.parseLong(o2.getName().substring(0, o2Index));
        if (o1Offset == o2Offset) {
          return 0;
        } else if (o1Offset < o2Offset) {
          return -1;
        } else {
          return 1;
        }
      }
    });

    /* The idea is to populate everything till the offsetUpto in the rangeMap, rest in offRangeMap */

    HashMap<BlobId, IndexValue> blobMap = rangeMap;
    int numberOfKeysProcessed = 0;
    for (File indexFile : indexFiles) {
      DataInputStream stream = null;
      try {
        Offset segmentStartOffset = IndexSegment.getIndexSegmentStartOffset(indexFile.getName());
        /* Read each index file as long as it is within the endToken and populate a map with the status of the blob.*/
        stream = new DataInputStream(new FileInputStream(indexFile));
        short version = stream.readShort();
        if (version != 0) {
          throw new IllegalStateException("Unknown index file version: " + version);
        }
        int keysize = stream.readInt();
        int valueSize = stream.readInt();
        long segmentEndOffset = stream.readLong();
        if (segmentStartOffset.getOffset() > offsetUpto) {
          if (!blobMap.equals(offRangeMap)) {
            System.out.println(
                "Reached the last segment with segment start offset " + segmentStartOffset + " greater than offsetUpto "
                    + offsetUpto);
            //switch to offRangeMap for subsequent entries.
            blobMap = offRangeMap;
          }
        } else {
          lastEligibleSegmentEndOffset = segmentEndOffset;
        }
        int Crc_Size = 8;
        StoreKeyFactory storeKeyFactory = Utils.getObj("com.github.ambry.commons.BlobIdFactory", map);
        while (stream.available() > Crc_Size) {
          BlobId key = (BlobId) storeKeyFactory.getStoreKey(stream);
          int valueByteSize;
          switch (version) {
            case PersistentIndex.VERSION_0:
              valueByteSize = IndexValue.INDEX_VALUE_SIZE_IN_BYTES_V0;
              break;
            case PersistentIndex.VERSION_1:
            case PersistentIndex.VERSION_2:
              valueByteSize = IndexValue.INDEX_VALUE_SIZE_IN_BYTES_V1_V2;
              break;
            case PersistentIndex.VERSION_3:
            case PersistentIndex.VERSION_4:
              valueByteSize = IndexValue.INDEX_VALUE_SIZE_IN_BYTES_V3_V4;
              break;
            default:
              throw new IllegalArgumentException("Unknown PersistentIndex Version.");
          }
          byte[] value = new byte[valueByteSize];
          stream.read(value);
          IndexValue blobValue = new IndexValue(segmentStartOffset.getName(), ByteBuffer.wrap(value), version);
          boolean deleted = blobValue.isFlagSet(IndexValue.Flags.Delete_Index);
          numberOfKeysProcessed++;
          IndexValue oldValue = blobMap.get(key);
          if (oldValue != null) {
            // If there was an old entry for the same key, then ensure that the old value was not a delete
            // and the new value *is* a delete.
            if (oldValue.isFlagSet(IndexValue.Flags.Delete_Index)) {
              System.err.println("Old value was a delete and is getting replaced!");
            }
            if (!blobValue.isFlagSet(IndexValue.Flags.Delete_Index)) {
              System.err.println("New value is not a delete!");
            }
          }
          blobMap.put(key, blobValue);
        }
      } finally {
        if (stream != null) {
          stream.close();
        }
      }
    }

    System.out.println("Total number of keys processed " + numberOfKeysProcessed);
    return lastEligibleSegmentEndOffset;
  }
@param offsetUpto - the entries upto this offset are put into rangeMap, rest are put into offRangeMap
@return the last offset in the rangeMap.
@throws Exception
ambry-tools/src/main/java/com/github/ambry/store/HardDeleteVerifier.java