aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
blob: 49ecfc0be69b27f79ba3ad86b5bb510587b1e3ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
// 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.
   *
   * <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 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.
   *
   * <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;
    }
  }


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