static StoreFindToken fromBytes(DataInputStream stream, StoreKeyFactory factory) throws IOException {
    StoreFindToken storeFindToken;
    // read version
    short version = stream.readShort();
    switch (version) {
      case VERSION_0:
        // backwards compatibility
        LogSegmentName logSegmentName = LogSegmentName.generateFirstSegmentName(false);
        // read sessionId
        String sessionId = Utils.readIntString(stream);
        UUID sessionIdUUID = null;
        if (!sessionId.isEmpty()) {
          sessionIdUUID = UUID.fromString(sessionId);
        }
        // read offset
        long offset = stream.readLong();
        // read index start offset
        long indexStartOffset = stream.readLong();
        if (indexStartOffset != UNINITIALIZED_OFFSET) {
          // read store key if needed
          storeFindToken = new StoreFindToken(FindTokenType.IndexBased, new Offset(logSegmentName, indexStartOffset),
              factory.getStoreKey(stream), sessionIdUUID, null, false, VERSION_0, null, null,
              UNINITIALIZED_RESET_KEY_VERSION);
        } else if (offset != UNINITIALIZED_OFFSET) {
          storeFindToken =
              new StoreFindToken(FindTokenType.JournalBased, new Offset(logSegmentName, offset), null, sessionIdUUID,
                  null, false, VERSION_0, null, null, UNINITIALIZED_RESET_KEY_VERSION);
        } else {
          storeFindToken =
              new StoreFindToken(FindTokenType.Uninitialized, null, null, null, null, true, VERSION_0, null, null,
                  UNINITIALIZED_RESET_KEY_VERSION);
        }
        break;
      case VERSION_1:
        // read sessionId
        sessionId = Utils.readIntString(stream);
        sessionIdUUID = null;
        if (!sessionId.isEmpty()) {
          sessionIdUUID = UUID.fromString(sessionId);
        }
        // read type
        FindTokenType type = FindTokenType.values()[stream.readShort()];
        switch (type) {
          case Uninitialized:
            storeFindToken =
                new StoreFindToken(FindTokenType.Uninitialized, null, null, null, null, true, VERSION_1, null, null,
                    UNINITIALIZED_RESET_KEY_VERSION);
            break;
          case JournalBased:
            Offset logOffset = Offset.fromBytes(stream);
            storeFindToken =
                new StoreFindToken(FindTokenType.JournalBased, logOffset, null, sessionIdUUID, null, false, VERSION_1,
                    null, null, UNINITIALIZED_RESET_KEY_VERSION);
            break;
          case IndexBased:
            Offset indexSegmentStartOffset = Offset.fromBytes(stream);
            storeFindToken =
                new StoreFindToken(FindTokenType.IndexBased, indexSegmentStartOffset, factory.getStoreKey(stream),
                    sessionIdUUID, null, false, VERSION_1, null, null, UNINITIALIZED_RESET_KEY_VERSION);
            break;
          default:
            throw new IllegalStateException("Unknown store find token type: " + type);
        }
        break;
      case VERSION_2:
        // read type
        type = FindTokenType.values()[stream.readShort()];
        switch (type) {
          case Uninitialized:
            storeFindToken =
                new StoreFindToken(FindTokenType.Uninitialized, null, null, null, null, true, VERSION_2, null, null,
                    UNINITIALIZED_RESET_KEY_VERSION);
            break;
          case JournalBased:
            // read incarnationId
            String incarnationId = Utils.readIntString(stream);
            UUID incarnationIdUUID = UUID.fromString(incarnationId);
            // read sessionId
            sessionId = Utils.readIntString(stream);
            sessionIdUUID = UUID.fromString(sessionId);
            Offset logOffset = Offset.fromBytes(stream);
            byte inclusive = stream.readByte();
            storeFindToken =
                new StoreFindToken(FindTokenType.JournalBased, logOffset, null, sessionIdUUID, incarnationIdUUID,
                    inclusive == (byte) 1, VERSION_2, null, null, UNINITIALIZED_RESET_KEY_VERSION);
            break;
          case IndexBased:
            // read incarnationId
            incarnationId = Utils.readIntString(stream);
            incarnationIdUUID = UUID.fromString(incarnationId);
            // read sessionId
            sessionId = Utils.readIntString(stream);
            sessionIdUUID = UUID.fromString(sessionId);
            Offset indexSegmentStartOffset = Offset.fromBytes(stream);
            StoreKey storeKey = factory.getStoreKey(stream);
            storeFindToken =
                new StoreFindToken(FindTokenType.IndexBased, indexSegmentStartOffset, storeKey, sessionIdUUID,
                    incarnationIdUUID, false, VERSION_2, null, null, UNINITIALIZED_RESET_KEY_VERSION);
            break;
          default:
            throw new IllegalStateException("Unknown store find token type: " + type);
        }
        break;
      case VERSION_3:
        // read type
        type = FindTokenType.values()[stream.readShort()];
        switch (type) {
          case Uninitialized:
            storeFindToken = new StoreFindToken();
            break;
          case JournalBased:
            // read incarnationId
            String incarnationId = Utils.readIntString(stream);
            UUID incarnationIdUUID = UUID.fromString(incarnationId);
            // read sessionId
            sessionId = Utils.readIntString(stream);
            sessionIdUUID = UUID.fromString(sessionId);
            Offset logOffset = Offset.fromBytes(stream);
            byte inclusive = stream.readByte();
            byte hasRestKeyInfo = stream.readByte();
            // read reset key (if present)
            StoreKey journalTokenResetKey = hasRestKeyInfo == (byte) 1 ? factory.getStoreKey(stream) : null;
            // read reset key type (if present)
            PersistentIndex.IndexEntryType journalResetKeyType = hasRestKeyInfo == (byte) 1 ?
                PersistentIndex.IndexEntryType.values()[stream.readShort()] : null;
            // read reset key life version (if present)
            short journalResetKeyVersion =
                hasRestKeyInfo == (byte) 1 ? stream.readShort() : UNINITIALIZED_RESET_KEY_VERSION;
            storeFindToken = new StoreFindToken(logOffset, sessionIdUUID, incarnationIdUUID, inclusive == (byte) 1,
                journalTokenResetKey, journalResetKeyType, journalResetKeyVersion);
            break;
          case IndexBased:
            // read incarnationId
            incarnationId = Utils.readIntString(stream);
            incarnationIdUUID = UUID.fromString(incarnationId);
            // read sessionId
            sessionId = Utils.readIntString(stream);
            sessionIdUUID = UUID.fromString(sessionId);
            Offset indexSegmentStartOffset = Offset.fromBytes(stream);
            StoreKey storeKey = factory.getStoreKey(stream);
            hasRestKeyInfo = stream.readByte();
            // read reset key (if present)
            StoreKey indexTokenResetKey = hasRestKeyInfo == (short) 1 ? factory.getStoreKey(stream) : null;
            // read reset key type (if present)
            PersistentIndex.IndexEntryType indexResetKeyType =
                hasRestKeyInfo == (short) 1 ? PersistentIndex.IndexEntryType.values()[stream.readShort()] : null;
            // read reset key life version (if present)
            short indexResetKeyVersion =
                hasRestKeyInfo == (short) 1 ? stream.readShort() : UNINITIALIZED_RESET_KEY_VERSION;
            storeFindToken = new StoreFindToken(storeKey, indexSegmentStartOffset, sessionIdUUID, incarnationIdUUID,
                indexTokenResetKey, indexResetKeyType, indexResetKeyVersion);
            break;
          default:
            throw new IllegalStateException("Unknown store find token type: " + type);
        }
        break;
      default:
        throw new IllegalArgumentException("Unrecognized version in StoreFindToken: " + version);
    }
    return storeFindToken;
  }
Instantiating {@link StoreFindToken}
@param type the {@link FindTokenType} of the token
@param offset the offset that this token refers to
@param key The {@link StoreKey} that the token refers to
@param sessionId the sessionId of the store that this token refers to
@param incarnationId the incarnationId of the store that this token refers to
@param inclusive {@code true} if the offset is inclusive or in other words the blob at the given offset is inclusive.
                 {@code false} otherwise
@param version refers to the version of the token
@param resetKey The {@link StoreKey} at start offset of current index segment. When log segment on source node is
                compacted (and token becomes invalid), this key is used to find current valid log segment where
                {@link PersistentIndex#findEntriesSince(FindToken, long)} can start from.
@param resetKeyType The {@link PersistentIndex.IndexEntryType} associated with this reset key.
@param resetKeyVersion The life version of reset key.
ambry-store/src/main/java/com/github/ambry/store/StoreFindToken.java