• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import java.io.IOException;
11 
12 /**
13  * LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores the message in a
14  * ByteString initially and then parses it on-demand.
15  *
16  * <p>LazyFieldLite is thread-compatible: concurrent reads are safe once the proto that this
17  * LazyFieldLite is a part of is no longer being mutated by its Builder. However, explicit
18  * synchronization is needed under read/write situations.
19  *
20  * <p>When a LazyFieldLite is used in the context of a MessageLite object, its behavior is
21  * considered to be immutable and none of the setter methods in its API are expected to be invoked.
22  * All of the getters are expected to be thread-safe. When used in the context of a
23  * MessageLite.Builder, setters can be invoked, but there is no guarantee of thread safety.
24  *
25  * <p>TODO: Consider splitting this class's functionality and put the mutable methods
26  * into a separate builder class to allow us to give stronger compile-time guarantees.
27  *
28  * <p>This class is internal implementation detail of the protobuf library, so you don't need to use
29  * it directly.
30  *
31  * @author xiangl@google.com (Xiang Li)
32  */
33 public class LazyFieldLite {
34 
35   /*
36    * The value associated with the LazyFieldLite object is stored in one or more of the following
37    * three fields (delayedBytes, value, memoizedBytes). They should together be interpreted as
38    * follows.
39    *
40    * 1) delayedBytes can be non-null, while value and memoizedBytes is null. The object will be in
41    * this state while the value for the object has not yet been parsed.
42    *
43    * 2) Both delayedBytes and value are non-null. The object transitions to this state as soon as
44    * some caller needs to access the value (by invoking getValue()).
45    *
46    * 3) memoizedBytes is merely an optimization for calls to LazyFieldLite.toByteString() to avoid
47    * recomputing the ByteString representation on each call. Instead, when the value is parsed from
48    * delayedBytes, we will also assign the contents of delayedBytes to memoizedBytes (since that is
49    * the ByteString representation of value).
50    *
51    * 4) Finally, if the LazyFieldLite was created directly with a parsed MessageLite value, then
52    * delayedBytes will be null, and memoizedBytes will be initialized only upon the first call to
53    * LazyFieldLite.toByteString().
54    *
55    * <p>Given the above conditions, any caller that needs a serialized representation of this object
56    * must first check if the memoizedBytes or delayedBytes ByteString is non-null and use it
57    * directly; if both of those are null, it can look at the parsed value field. Similarly, any
58    * caller that needs a parsed value must first check if the value field is already non-null, if
59    * not it must parse the value from delayedBytes.
60    */
61 
62   /**
63    * A delayed-parsed version of the contents of this field. When this field is non-null, then the
64    * "value" field is allowed to be null until the time that the value needs to be read.
65    *
66    * <p>When delayedBytes is non-null then {@code extensionRegistry} is required to also be
67    * non-null. {@code value} and {@code memoizedBytes} will be initialized lazily.
68    */
69   private ByteString delayedBytes;
70 
71   /**
72    * An {@code ExtensionRegistryLite} for parsing bytes. It is non-null on a best-effort basis. It
73    * is only guaranteed to be non-null if this message was initialized using bytes and an {@code
74    * ExtensionRegistry}. If it directly had a value set then it will be null, unless it has been
75    * merged with another {@code LazyFieldLite} that had an {@code ExtensionRegistry}.
76    */
77   private ExtensionRegistryLite extensionRegistry;
78 
79   /**
80    * The parsed value. When this is null and a caller needs access to the MessageLite value, then
81    * {@code delayedBytes} will be parsed lazily at that time.
82    */
83   protected volatile MessageLite value;
84 
85   /**
86    * The memoized bytes for {@code value}. This is an optimization for the toByteString() method to
87    * not have to recompute its return-value on each invocation. TODO: Figure out whether this
88    * optimization is actually necessary.
89    */
90   private volatile ByteString memoizedBytes;
91 
92   /** Constructs a LazyFieldLite with bytes that will be parsed lazily. */
LazyFieldLite(ExtensionRegistryLite extensionRegistry, ByteString bytes)93   public LazyFieldLite(ExtensionRegistryLite extensionRegistry, ByteString bytes) {
94     checkArguments(extensionRegistry, bytes);
95     this.extensionRegistry = extensionRegistry;
96     this.delayedBytes = bytes;
97   }
98 
99   /** Constructs a LazyFieldLite with no contents, and no ability to parse extensions. */
LazyFieldLite()100   public LazyFieldLite() {}
101 
102   /**
103    * Constructs a LazyFieldLite instance with a value. The LazyFieldLite may not be able to parse
104    * the extensions in the value as it has no ExtensionRegistry.
105    */
fromValue(MessageLite value)106   public static LazyFieldLite fromValue(MessageLite value) {
107     LazyFieldLite lf = new LazyFieldLite();
108     lf.setValue(value);
109     return lf;
110   }
111 
112   @Override
equals(Object o)113   public boolean equals(Object o) {
114     if (this == o) {
115       return true;
116     }
117 
118     if (!(o instanceof LazyFieldLite)) {
119       return false;
120     }
121 
122     LazyFieldLite other = (LazyFieldLite) o;
123 
124     // Lazy fields do not work well with equals... If both are delayedBytes, we do not have a
125     // mechanism to deserialize them so we rely on bytes equality. Otherwise we coerce into an
126     // actual message (if necessary) and call equals on the message itself. This implies that two
127     // messages can by unequal but then be turned equal simply be invoking a getter on a lazy field.
128     MessageLite value1 = value;
129     MessageLite value2 = other.value;
130     if (value1 == null && value2 == null) {
131       return toByteString().equals(other.toByteString());
132     } else if (value1 != null && value2 != null) {
133       return value1.equals(value2);
134     } else if (value1 != null) {
135       return value1.equals(other.getValue(value1.getDefaultInstanceForType()));
136     } else {
137       return getValue(value2.getDefaultInstanceForType()).equals(value2);
138     }
139   }
140 
141   @Override
hashCode()142   public int hashCode() {
143     // We can't provide a memoizable hash code for lazy fields. The byte strings may have different
144     // hash codes but evaluate to equivalent messages. And we have no facility for constructing
145     // a message here if we were not already holding a value.
146     return 1;
147   }
148 
149   /**
150    * Determines whether this LazyFieldLite instance represents the default instance of this type.
151    */
containsDefaultInstance()152   public boolean containsDefaultInstance() {
153     return memoizedBytes == ByteString.EMPTY
154         || value == null && (delayedBytes == null || delayedBytes == ByteString.EMPTY);
155   }
156 
157   /**
158    * Clears the value state of this instance.
159    *
160    * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
161    * situations.
162    */
clear()163   public void clear() {
164     // Don't clear the ExtensionRegistry. It might prove useful later on when merging in another
165     // value, but there is no guarantee that it will contain all extensions that were directly set
166     // on the values that need to be merged.
167     delayedBytes = null;
168     value = null;
169     memoizedBytes = null;
170   }
171 
172   /**
173    * Overrides the contents of this LazyField.
174    *
175    * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
176    * situations.
177    */
set(LazyFieldLite other)178   public void set(LazyFieldLite other) {
179     this.delayedBytes = other.delayedBytes;
180     this.value = other.value;
181     this.memoizedBytes = other.memoizedBytes;
182     // If the other LazyFieldLite was created by directly setting the value rather than first by
183     // parsing, then it will not have an extensionRegistry. In this case we hold on to the existing
184     // extensionRegistry, which has no guarantees that it has all the extensions that will be
185     // directly set on the value.
186     if (other.extensionRegistry != null) {
187       this.extensionRegistry = other.extensionRegistry;
188     }
189   }
190 
191   /**
192    * Returns message instance. It may do some thread-safe delayed parsing of bytes.
193    *
194    * @param defaultInstance its message's default instance. It's also used to get parser for the
195    *     message type.
196    */
getValue(MessageLite defaultInstance)197   public MessageLite getValue(MessageLite defaultInstance) {
198     ensureInitialized(defaultInstance);
199     return value;
200   }
201 
202   /**
203    * Sets the value of the instance and returns the old value without delay parsing anything.
204    *
205    * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
206    * situations.
207    */
setValue(MessageLite value)208   public MessageLite setValue(MessageLite value) {
209     MessageLite originalValue = this.value;
210     this.delayedBytes = null;
211     this.memoizedBytes = null;
212     this.value = value;
213     return originalValue;
214   }
215 
216   /**
217    * Merges another instance's contents. In some cases may drop some extensions if both fields
218    * contain data. If the other field has an {@code ExtensionRegistry} but this does not, then this
219    * field will copy over that {@code ExtensionRegistry}.
220    *
221    * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
222    * situations.
223    */
merge(LazyFieldLite other)224   public void merge(LazyFieldLite other) {
225     if (other.containsDefaultInstance()) {
226       return;
227     }
228 
229     if (this.containsDefaultInstance()) {
230       set(other);
231       return;
232     }
233 
234     // If the other field has an extension registry but this does not, copy over the other extension
235     // registry.
236     if (this.extensionRegistry == null) {
237       this.extensionRegistry = other.extensionRegistry;
238     }
239 
240     // In the case that both of them are not parsed we simply concatenate the bytes to save time. In
241     // the (probably rare) case that they have different extension registries there is a chance that
242     // some of the extensions may be dropped, but the tradeoff of making this operation fast seems
243     // to outway the benefits of combining the extension registries, which is not normally done for
244     // lite protos anyways.
245     if (this.delayedBytes != null && other.delayedBytes != null) {
246       this.delayedBytes = this.delayedBytes.concat(other.delayedBytes);
247       return;
248     }
249 
250     // At least one is parsed and both contain data. We won't drop any extensions here directly, but
251     // in the case that the extension registries are not the same then we might in the future if we
252     // need to serialize and parse a message again.
253     if (this.value == null && other.value != null) {
254       setValue(mergeValueAndBytes(other.value, this.delayedBytes, this.extensionRegistry));
255       return;
256     } else if (this.value != null && other.value == null) {
257       setValue(mergeValueAndBytes(this.value, other.delayedBytes, other.extensionRegistry));
258       return;
259     }
260 
261     // At this point we have two fully parsed messages.
262     setValue(this.value.toBuilder().mergeFrom(other.value).build());
263   }
264 
265   /**
266    * Merges another instance's contents from a stream.
267    *
268    * <p>LazyField is not thread-safe for write access. Synchronizations are needed under read/write
269    * situations.
270    */
mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)271   public void mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
272       throws IOException {
273     if (this.containsDefaultInstance()) {
274       setByteString(input.readBytes(), extensionRegistry);
275       return;
276     }
277 
278     // If the other field has an extension registry but this does not, copy over the other extension
279     // registry.
280     if (this.extensionRegistry == null) {
281       this.extensionRegistry = extensionRegistry;
282     }
283 
284     // In the case that both of them are not parsed we simply concatenate the bytes to save time. In
285     // the (probably rare) case that they have different extension registries there is a chance that
286     // some of the extensions may be dropped, but the tradeoff of making this operation fast seems
287     // to outway the benefits of combining the extension registries, which is not normally done for
288     // lite protos anyways.
289     if (this.delayedBytes != null) {
290       setByteString(this.delayedBytes.concat(input.readBytes()), this.extensionRegistry);
291       return;
292     }
293 
294     // We are parsed and both contain data. We won't drop any extensions here directly, but in the
295     // case that the extension registries are not the same then we might in the future if we
296     // need to serialize and parse a message again.
297     try {
298       setValue(value.toBuilder().mergeFrom(input, extensionRegistry).build());
299     } catch (InvalidProtocolBufferException e) {
300       // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
301       // was invalid.
302     }
303   }
304 
mergeValueAndBytes( MessageLite value, ByteString otherBytes, ExtensionRegistryLite extensionRegistry)305   private static MessageLite mergeValueAndBytes(
306       MessageLite value, ByteString otherBytes, ExtensionRegistryLite extensionRegistry) {
307     try {
308       return value.toBuilder().mergeFrom(otherBytes, extensionRegistry).build();
309     } catch (InvalidProtocolBufferException e) {
310       // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
311       // was invalid.
312       return value;
313     }
314   }
315 
316   /** Sets this field with bytes to delay-parse. */
setByteString(ByteString bytes, ExtensionRegistryLite extensionRegistry)317   public void setByteString(ByteString bytes, ExtensionRegistryLite extensionRegistry) {
318     checkArguments(extensionRegistry, bytes);
319     this.delayedBytes = bytes;
320     this.extensionRegistry = extensionRegistry;
321     this.value = null;
322     this.memoizedBytes = null;
323   }
324 
325   /**
326    * Due to the optional field can be duplicated at the end of serialized bytes, which will make the
327    * serialized size changed after LazyField parsed. Be careful when using this method.
328    */
getSerializedSize()329   public int getSerializedSize() {
330     // We *must* return delayed bytes size if it was ever set because the dependent messages may
331     // have memoized serialized size based off of it.
332     if (memoizedBytes != null) {
333       return memoizedBytes.size();
334     } else if (delayedBytes != null) {
335       return delayedBytes.size();
336     } else if (value != null) {
337       return value.getSerializedSize();
338     } else {
339       return 0;
340     }
341   }
342 
343   /** Returns a BytesString for this field in a thread-safe way. */
toByteString()344   public ByteString toByteString() {
345     if (memoizedBytes != null) {
346       return memoizedBytes;
347     }
348     // We *must* return delayed bytes if it was set because the dependent messages may have
349     // memoized serialized size based off of it.
350     if (delayedBytes != null) {
351       return delayedBytes;
352     }
353     synchronized (this) {
354       if (memoizedBytes != null) {
355         return memoizedBytes;
356       }
357       if (value == null) {
358         memoizedBytes = ByteString.EMPTY;
359       } else {
360         memoizedBytes = value.toByteString();
361       }
362       return memoizedBytes;
363     }
364   }
365 
366   /** Writes this lazy field into a {@link Writer}. */
writeTo(Writer writer, int fieldNumber)367   void writeTo(Writer writer, int fieldNumber) throws IOException {
368     if (memoizedBytes != null) {
369       writer.writeBytes(fieldNumber, memoizedBytes);
370     } else if (delayedBytes != null) {
371       writer.writeBytes(fieldNumber, delayedBytes);
372     } else if (value != null) {
373       writer.writeMessage(fieldNumber, value);
374     } else {
375       writer.writeBytes(fieldNumber, ByteString.EMPTY);
376     }
377   }
378 
379   /** Might lazily parse the bytes that were previously passed in. Is thread-safe. */
ensureInitialized(MessageLite defaultInstance)380   protected void ensureInitialized(MessageLite defaultInstance) {
381     if (value != null) {
382       return;
383     }
384     synchronized (this) {
385       if (value != null) {
386         return;
387       }
388       try {
389         if (delayedBytes != null) {
390           // The extensionRegistry shouldn't be null here since we have delayedBytes.
391           MessageLite parsedValue =
392               defaultInstance.getParserForType().parseFrom(delayedBytes, extensionRegistry);
393           this.value = parsedValue;
394           this.memoizedBytes = delayedBytes;
395         } else {
396           this.value = defaultInstance;
397           this.memoizedBytes = ByteString.EMPTY;
398         }
399       } catch (InvalidProtocolBufferException e) {
400         // Nothing is logged and no exceptions are thrown. Clients will be unaware that this proto
401         // was invalid.
402         this.value = defaultInstance;
403         this.memoizedBytes = ByteString.EMPTY;
404       }
405     }
406   }
407 
checkArguments(ExtensionRegistryLite extensionRegistry, ByteString bytes)408   private static void checkArguments(ExtensionRegistryLite extensionRegistry, ByteString bytes) {
409     if (extensionRegistry == null) {
410       throw new NullPointerException("found null ExtensionRegistry");
411     }
412     if (bytes == null) {
413       throw new NullPointerException("found null ByteString");
414     }
415   }
416 }
417