// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

package com.google.protobuf;

import java.io.IOException;

/**
 * LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores the message in a
 * ByteString initially and then parses it on-demand.
 *
 * <p>LazyFieldLite is thread-compatible: concurrent reads are safe once the proto that this
 * LazyFieldLite is a part of is no longer being mutated by its Builder. However, explicit
 * synchronization is needed under read/write situations.
 *
 * <p>When a LazyFieldLite is used in the context of a MessageLite object, its behavior is
 * considered to be immutable and none of the setter methods in its API are expected to be invoked.
 * All of the getters are expected to be thread-safe. When used in the context of a
 * MessageLite.Builder, setters can be invoked, but there is no guarantee of thread safety.
 *
 * <p>TODO: Consider splitting this class's functionality and put the mutable methods
 * into a separate builder class to allow us to give stronger compile-time guarantees.
 *
 * <p>This class is internal implementation detail of the protobuf library, so you don't need to use
 * it directly.
 *
 * @author xiangl@google.com (Xiang Li)
 */
public class LazyFieldLite {
  private static final ExtensionRegistryLite EMPTY_REGISTRY =
      ExtensionRegistryLite.getEmptyRegistry();

  /*
   * The value associated with the LazyFieldLite object is stored in one or more of the following
   * three fields (delayedBytes, value, memoizedBytes). They should together be interpreted as
   * follows.
   *
   * 1) delayedBytes can be non-null, while value and memoizedBytes is null. The object will be in
   * this state while the value for the object has not yet been parsed.
   *
   * 2) Both delayedBytes and value are non-null. The object transitions to this state as soon as
   * some caller needs to access the value (by invoking getValue()).
   *
   * 3) memoizedBytes is merely an optimization for calls to LazyFieldLite.toByteString() to avoid
   * recomputing the ByteString representation on each call. Instead, when the value is parsed from
   * delayedBytes, we will also assign the contents of delayedBytes to memoizedBytes (since that is
   * the ByteString representation of value).
   *
   * 4) Finally, if the LazyFieldLite was created directly with a parsed MessageLite value, then
   * delayedBytes will be null, and memoizedBytes will be initialized only upon the first call to
   * LazyFieldLite.toByteString().
   *
   * <p>Given the above conditions, any caller that needs a serialized representation of this object
   * must first check if the memoizedBytes or delayedBytes ByteString is non-null and use it
   * directly; if both of those are null, it can look at the parsed value field. Similarly, any
   * caller that needs a parsed value must first check if the value field is already non-null, if
   * not it must parse the value from delayedBytes.
   */

  /**
   * A delayed-parsed version of the contents of this field. When this field is non-null, then the
   * "value" field is allowed to be null until the time that the value needs to be read.
   *
   * <p>When delayedBytes is non-null then {@code extensionRegistry} is required to also be
   * non-null. {@code value} and {@code memoizedBytes} will be initialized lazily.
   */
  private ByteString delayedBytes;

  /**
   * An {@code ExtensionRegistryLite} for parsing bytes. It is non-null on a best-effort basis. It
   * is only guaranteed to be non-null if this message was initialized using bytes and an {@code
   * ExtensionRegistry}. If it directly had a value set then it will be null, unless it has been
   * merged with another {@code LazyFieldLite} that had an {@code ExtensionRegistry}.
   */
  private ExtensionRegistryLite extensionRegistry;

  /**
   * The parsed value. When this is null and a caller needs access to the MessageLite value, then
   * {@code delayedBytes} will be parsed lazily at that time.
   */
  protected volatile MessageLite value;

  /**
   * The memoized bytes for {@code value}. This is an optimization for the toByteString() method to
   * not have to recompute its return-value on each invocation. TODO: Figure out whether this
   * optimization is actually necessary.
   */
  private volatile ByteString memoizedBytes;

  /** Constructs a LazyFieldLite with bytes that will be parsed lazily. */
  public LazyFieldLite(ExtensionRegistryLite extensionRegistry, ByteString bytes) {
    checkArguments(extensionRegistry, bytes);
    this.extensionRegistry = extensionRegistry;
    this.delayedBytes = bytes;
  }

  /** Constructs a LazyFieldLite with no contents, and no ability to parse extensions. */
  public LazyFieldLite() {}

  /**
   * Constructs a LazyFieldLite instance with a value. The LazyFieldLite may not be able to parse
   * the extensions in the value as it has no ExtensionRegistry.
   */
  public static LazyFieldLite fromValue(MessageLite value) {
    LazyFieldLite lf = new LazyFieldLite();
    lf.setValue(value);
    return lf;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (!(o instanceof LazyFieldLite)) {
      return false;
    }

    LazyFieldLite other = (LazyFieldLite) o;

    // Lazy fields do not work well with equals... If both are delayedBytes, we do not have a
    // mechanism to deserialize them so we rely on bytes equality. Otherwise we coerce into an
    // actual message (if necessary) and call equals on the message itself. This implies that two
    // messages can by unequal but then be turned equal simply be invoking a getter on a lazy field.
    MessageLite value1 = value;
    MessageLite value2 = other.value;
    if (value1 == null && value2 == null) {
      return toByteString().equals(other.toByteString());
    } else if (value1 != null && value2 != null) {
      return value1.equals(value2);
    } else if (value1 != null) {
      return value1.equals(other.getValue(value1.getDefaultInstanceForType()));
    } else {
      return getValue(value2.getDefaultInstanceForType()).equals(value2);
    }
  }

  @Override
  public int hashCode() {
    // We can't provide a memoizable hash code for lazy fields. The byte strings may have different
    // hash codes but evaluate to equivalent messages. And we have no facility for constructing
    // a message here if we were not already holding a value.
    return 1;
  }

  /**
   * Determines whether this LazyFieldLite instance represents the default instance of this type.
   */
  public boolean containsDefaultInstance() {
    return memoizedBytes == ByteString.EMPTY
        || value == null && (delayedBytes == null || delayedBytes == ByteString.EMPTY);
  }

  /**
   * Clears the value state of this instance.
   *
   * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
   * situations.
   */
  public void clear() {
    // Don't clear the ExtensionRegistry. It might prove useful later on when merging in another
    // value, but there is no guarantee that it will contain all extensions that were directly set
    // on the values that need to be merged.
    delayedBytes = null;
    value = null;
    memoizedBytes = null;
  }

  /**
   * Overrides the contents of this LazyField.
   *
   * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
   * situations.
   */
  public void set(LazyFieldLite other) {
    this.delayedBytes = other.delayedBytes;
    this.value = other.value;
    this.memoizedBytes = other.memoizedBytes;
    // If the other LazyFieldLite was created by directly setting the value rather than first by
    // parsing, then it will not have an extensionRegistry. In this case we hold on to the existing
    // extensionRegistry, which has no guarantees that it has all the extensions that will be
    // directly set on the value.
    if (other.extensionRegistry != null) {
      this.extensionRegistry = other.extensionRegistry;
    }
  }

  /**
   * Returns message instance. It may do some thread-safe delayed parsing of bytes.
   *
   * @param defaultInstance its message's default instance. It's also used to get parser for the
   *     message type.
   */
  public MessageLite getValue(MessageLite defaultInstance) {
    ensureInitialized(defaultInstance);
    return value;
  }

  /**
   * Sets the value of the instance and returns the old value without delay parsing anything.
   *
   * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
   * situations.
   */
  public MessageLite setValue(MessageLite value) {
    MessageLite originalValue = this.value;
    this.delayedBytes = null;
    this.memoizedBytes = null;
    this.value = value;
    return originalValue;
  }

  /**
   * Merges another instance's contents. In some cases may drop some extensions if both fields
   * contain data. If the other field has an {@code ExtensionRegistry} but this does not, then this
   * field will copy over that {@code ExtensionRegistry}.
   *
   * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
   * situations.
   */
  public void merge(LazyFieldLite other) {
    if (other.containsDefaultInstance()) {
      return;
    }

    if (this.containsDefaultInstance()) {
      set(other);
      return;
    }

    // If the other field has an extension registry but this does not, copy over the other extension
    // registry.
    if (this.extensionRegistry == null) {
      this.extensionRegistry = other.extensionRegistry;
    }

    // In the case that both of them are not parsed we simply concatenate the bytes to save time. In
    // the (probably rare) case that they have different extension registries there is a chance that
    // some of the extensions may be dropped, but the tradeoff of making this operation fast seems
    // to outway the benefits of combining the extension registries, which is not normally done for
    // lite protos anyways.
    if (this.delayedBytes != null && other.delayedBytes != null) {
      this.delayedBytes = this.delayedBytes.concat(other.delayedBytes);
      return;
    }

    // At least one is parsed and both contain data. We won't drop any extensions here directly, but
    // in the case that the extension registries are not the same then we might in the future if we
    // need to serialize and parse a message again.
    if (this.value == null && other.value != null) {
      setValue(mergeValueAndBytes(other.value, this.delayedBytes, this.extensionRegistry));
      return;
    } else if (this.value != null && other.value == null) {
      setValue(mergeValueAndBytes(this.value, other.delayedBytes, other.extensionRegistry));
      return;
    }

    // At this point we have two fully parsed messages.
    setValue(this.value.toBuilder().mergeFrom(other.value).build());
  }

  /**
   * Merges another instance's contents from a stream.
   *
   * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
   * situations.
   */
  public void mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
      throws IOException {
    if (this.containsDefaultInstance()) {
      setByteString(input.readBytes(), extensionRegistry);
      return;
    }

    // If the other field has an extension registry but this does not, copy over the other extension
    // registry.
    if (this.extensionRegistry == null) {
      this.extensionRegistry = extensionRegistry;
    }

    // In the case that both of them are not parsed we simply concatenate the bytes to save time. In
    // the (probably rare) case that they have different extension registries there is a chance that
    // some of the extensions may be dropped, but the tradeoff of making this operation fast seems
    // to outway the benefits of combining the extension registries, which is not normally done for
    // lite protos anyways.
    if (this.delayedBytes != null) {
      setByteString(this.delayedBytes.concat(input.readBytes()), this.extensionRegistry);
      return;
    }

    // We are parsed and both contain data. We won't drop any extensions here directly, but in the
    // case that the extension registries are not the same then we might in the future if we
    // need to serialize and parse a message again.
    try {
      setValue(value.toBuilder().mergeFrom(input, extensionRegistry).build());
    } catch (InvalidProtocolBufferException e) {
      // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
      // was invalid.
    }
  }

  private static MessageLite mergeValueAndBytes(
      MessageLite value, ByteString otherBytes, ExtensionRegistryLite extensionRegistry) {
    try {
      return value.toBuilder().mergeFrom(otherBytes, extensionRegistry).build();
    } catch (InvalidProtocolBufferException e) {
      // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
      // was invalid.
      return value;
    }
  }

  /** Sets this field with bytes to delay-parse. */
  public void setByteString(ByteString bytes, ExtensionRegistryLite extensionRegistry) {
    checkArguments(extensionRegistry, bytes);
    this.delayedBytes = bytes;
    this.extensionRegistry = extensionRegistry;
    this.value = null;
    this.memoizedBytes = null;
  }

  /**
   * Due to the optional field can be duplicated at the end of serialized bytes, which will make the
   * serialized size changed after LazyField parsed. Be careful when using this method.
   */
  public int getSerializedSize() {
    // We *must* return delayed bytes size if it was ever set because the dependent messages may
    // have memoized serialized size based off of it.
    if (memoizedBytes != null) {
      return memoizedBytes.size();
    } else if (delayedBytes != null) {
      return delayedBytes.size();
    } else if (value != null) {
      return value.getSerializedSize();
    } else {
      return 0;
    }
  }

  /** Returns a BytesString for this field in a thread-safe way. */
  public ByteString toByteString() {
    if (memoizedBytes != null) {
      return memoizedBytes;
    }
    // We *must* return delayed bytes if it was set because the dependent messages may have
    // memoized serialized size based off of it.
    if (delayedBytes != null) {
      return delayedBytes;
    }
    synchronized (this) {
      if (memoizedBytes != null) {
        return memoizedBytes;
      }
      if (value == null) {
        memoizedBytes = ByteString.EMPTY;
      } else {
        memoizedBytes = value.toByteString();
      }
      return memoizedBytes;
    }
  }

  /** Writes this lazy field into a {@link Writer}. */
  void writeTo(Writer writer, int fieldNumber) throws IOException {
    if (memoizedBytes != null) {
      writer.writeBytes(fieldNumber, memoizedBytes);
    } else if (delayedBytes != null) {
      writer.writeBytes(fieldNumber, delayedBytes);
    } else if (value != null) {
      writer.writeMessage(fieldNumber, value);
    } else {
      writer.writeBytes(fieldNumber, ByteString.EMPTY);
    }
  }

  /** Might lazily parse the bytes that were previously passed in. Is thread-safe. */
  protected void ensureInitialized(MessageLite defaultInstance) {
    if (value != null) {
      return;
    }
    synchronized (this) {
      if (value != null) {
        return;
      }
      try {
        if (delayedBytes != null) {
          // The extensionRegistry shouldn't be null here since we have delayedBytes.
          MessageLite parsedValue =
              defaultInstance.getParserForType().parseFrom(delayedBytes, extensionRegistry);
          this.value = parsedValue;
          this.memoizedBytes = delayedBytes;
        } else {
          this.value = defaultInstance;
          this.memoizedBytes = ByteString.EMPTY;
        }
      } catch (InvalidProtocolBufferException e) {
        // Nothing is logged and no exceptions are thrown. Clients will be unaware that this proto
        // was invalid.
        this.value = defaultInstance;
        this.memoizedBytes = ByteString.EMPTY;
      }
    }
  }

  private static void checkArguments(ExtensionRegistryLite extensionRegistry, ByteString bytes) {
    if (extensionRegistry == null) {
      throw new NullPointerException("found null ExtensionRegistry");
    }
    if (bytes == null) {
      throw new NullPointerException("found null ByteString");
    }
  }
}
