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