Index: third_party/protobuf/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java |
diff --git a/third_party/protobuf/java/src/main/java/com/google/protobuf/LazyFieldLite.java b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java |
similarity index 63% |
rename from third_party/protobuf/java/src/main/java/com/google/protobuf/LazyFieldLite.java |
rename to third_party/protobuf/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java |
index eea1fe3cdde1b3f395abb6664033f9207b2b13db..2febaacecbae0bc2182bf8debbbcf66fc30afcc4 100644 |
--- a/third_party/protobuf/java/src/main/java/com/google/protobuf/LazyFieldLite.java |
+++ b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java |
@@ -30,14 +30,26 @@ |
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 parse it on-demand. |
+ * 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. |
* |
- * LazyField is thread-compatible e.g. concurrent read are safe, however, |
- * synchronizations are 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, so you don't need to use it directly. |
+ * 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) |
*/ |
@@ -46,8 +58,34 @@ public class LazyFieldLite { |
ExtensionRegistryLite.getEmptyRegistry(); |
/** |
- * A delayed-parsed version of the bytes. When this is non-null then {@code extensionRegistry } is |
- * also non-null and {@code value} and {@code memoizedBytes} are null. |
+ * 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; |
@@ -60,12 +98,15 @@ public class LazyFieldLite { |
private ExtensionRegistryLite extensionRegistry; |
/** |
- * The parsed value. When this is non-null then {@code delayedBytes} will be null. |
+ * 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}. Will be null when {@code value} is null. |
+ * 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; |
@@ -94,6 +135,43 @@ public class LazyFieldLite { |
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. |
*/ |
@@ -230,6 +308,46 @@ public class LazyFieldLite { |
return; |
} |
} |
+ |
+ /** |
+ * 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) { |
@@ -259,10 +377,12 @@ public class LazyFieldLite { |
* parsed. Be careful when using this method. |
*/ |
public int getSerializedSize() { |
- if (delayedBytes != null) { |
- return delayedBytes.size(); |
- } else if (memoizedBytes != null) { |
+ // 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 { |
@@ -274,12 +394,14 @@ public class LazyFieldLite { |
* Returns a BytesString for this field in a thread-safe way. |
*/ |
public ByteString toByteString() { |
- if (delayedBytes != null) { |
- return delayedBytes; |
- } |
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; |
@@ -311,18 +433,15 @@ public class LazyFieldLite { |
.parseFrom(delayedBytes, extensionRegistry); |
this.value = parsedValue; |
this.memoizedBytes = delayedBytes; |
- this.delayedBytes = null; |
} else { |
this.value = defaultInstance; |
this.memoizedBytes = ByteString.EMPTY; |
- this.delayedBytes = null; |
} |
} 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; |
- this.delayedBytes = null; |
} |
} |
} |