// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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. * * 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. * * 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. * * TODO(yatin,dweis): 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. * * 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(). * * 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. * * 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(yatin): 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. * *

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. * *

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. * *

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}. * *

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 serialze 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. * *

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; } } /** * 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"); } } }