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