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 import java.util.Iterator; 35 import java.util.Map.Entry; 36 37 /** Schema used for proto2 messages using message_set_wireformat. */ 38 final class MessageSetSchema<T> implements Schema<T> { 39 private final MessageLite defaultInstance; 40 private final UnknownFieldSchema<?, ?> unknownFieldSchema; 41 private final boolean hasExtensions; 42 private final ExtensionSchema<?> extensionSchema; 43 MessageSetSchema( UnknownFieldSchema<?, ?> unknownFieldSchema, ExtensionSchema<?> extensionSchema, MessageLite defaultInstance)44 private MessageSetSchema( 45 UnknownFieldSchema<?, ?> unknownFieldSchema, 46 ExtensionSchema<?> extensionSchema, 47 MessageLite defaultInstance) { 48 this.unknownFieldSchema = unknownFieldSchema; 49 this.hasExtensions = extensionSchema.hasExtensions(defaultInstance); 50 this.extensionSchema = extensionSchema; 51 this.defaultInstance = defaultInstance; 52 } 53 newSchema( UnknownFieldSchema<?, ?> unknownFieldSchema, ExtensionSchema<?> extensionSchema, MessageLite defaultInstance)54 static <T> MessageSetSchema<T> newSchema( 55 UnknownFieldSchema<?, ?> unknownFieldSchema, 56 ExtensionSchema<?> extensionSchema, 57 MessageLite defaultInstance) { 58 return new MessageSetSchema<T>(unknownFieldSchema, extensionSchema, defaultInstance); 59 } 60 61 @SuppressWarnings("unchecked") 62 @Override newInstance()63 public T newInstance() { 64 // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this 65 // better. 66 if (defaultInstance instanceof GeneratedMessageLite) { 67 return (T) ((GeneratedMessageLite<?, ?>) defaultInstance).newMutableInstance(); 68 } else { 69 return (T) defaultInstance.newBuilderForType().buildPartial(); 70 } 71 } 72 73 @Override equals(T message, T other)74 public boolean equals(T message, T other) { 75 Object messageUnknown = unknownFieldSchema.getFromMessage(message); 76 Object otherUnknown = unknownFieldSchema.getFromMessage(other); 77 if (!messageUnknown.equals(otherUnknown)) { 78 return false; 79 } 80 if (hasExtensions) { 81 FieldSet<?> messageExtensions = extensionSchema.getExtensions(message); 82 FieldSet<?> otherExtensions = extensionSchema.getExtensions(other); 83 return messageExtensions.equals(otherExtensions); 84 } 85 return true; 86 } 87 88 @Override hashCode(T message)89 public int hashCode(T message) { 90 int hashCode = unknownFieldSchema.getFromMessage(message).hashCode(); 91 if (hasExtensions) { 92 FieldSet<?> extensions = extensionSchema.getExtensions(message); 93 hashCode = (hashCode * 53) + extensions.hashCode(); 94 } 95 return hashCode; 96 } 97 98 @Override mergeFrom(T message, T other)99 public void mergeFrom(T message, T other) { 100 SchemaUtil.mergeUnknownFields(unknownFieldSchema, message, other); 101 if (hasExtensions) { 102 SchemaUtil.mergeExtensions(extensionSchema, message, other); 103 } 104 } 105 106 @SuppressWarnings("unchecked") 107 @Override writeTo(T message, Writer writer)108 public void writeTo(T message, Writer writer) throws IOException { 109 FieldSet<?> extensions = extensionSchema.getExtensions(message); 110 Iterator<?> iterator = extensions.iterator(); 111 while (iterator.hasNext()) { 112 Entry<?, ?> extension = (Entry<?, ?>) iterator.next(); 113 FieldSet.FieldDescriptorLite<?> fd = (FieldSet.FieldDescriptorLite<?>) extension.getKey(); 114 if (fd.getLiteJavaType() != WireFormat.JavaType.MESSAGE || fd.isRepeated() || fd.isPacked()) { 115 throw new IllegalStateException("Found invalid MessageSet item."); 116 } 117 if (extension instanceof LazyField.LazyEntry) { 118 writer.writeMessageSetItem( 119 fd.getNumber(), ((LazyField.LazyEntry) extension).getField().toByteString()); 120 } else { 121 writer.writeMessageSetItem(fd.getNumber(), extension.getValue()); 122 } 123 } 124 writeUnknownFieldsHelper(unknownFieldSchema, message, writer); 125 } 126 127 /** 128 * A helper method for wildcard capture of {@code unknownFieldSchema}. See: 129 * https://docs.oracle.com/javase/tutorial/java/generics/capture.html 130 */ writeUnknownFieldsHelper( UnknownFieldSchema<UT, UB> unknownFieldSchema, T message, Writer writer)131 private <UT, UB> void writeUnknownFieldsHelper( 132 UnknownFieldSchema<UT, UB> unknownFieldSchema, T message, Writer writer) throws IOException { 133 unknownFieldSchema.writeAsMessageSetTo(unknownFieldSchema.getFromMessage(message), writer); 134 } 135 136 @SuppressWarnings("ReferenceEquality") 137 @Override mergeFrom( T message, byte[] data, int position, int limit, ArrayDecoders.Registers registers)138 public void mergeFrom( 139 T message, byte[] data, int position, int limit, ArrayDecoders.Registers registers) 140 throws IOException { 141 // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this 142 // better. 143 UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; 144 if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { 145 unknownFields = UnknownFieldSetLite.newInstance(); 146 ((GeneratedMessageLite) message).unknownFields = unknownFields; 147 } 148 final FieldSet<GeneratedMessageLite.ExtensionDescriptor> extensions = 149 ((GeneratedMessageLite.ExtendableMessage<?, ?>) message).ensureExtensionsAreMutable(); 150 GeneratedMessageLite.GeneratedExtension<?, ?> extension = null; 151 while (position < limit) { 152 position = ArrayDecoders.decodeVarint32(data, position, registers); 153 final int startTag = registers.int1; 154 if (startTag != WireFormat.MESSAGE_SET_ITEM_TAG) { 155 if (WireFormat.getTagWireType(startTag) == WireFormat.WIRETYPE_LENGTH_DELIMITED) { 156 extension = 157 (GeneratedMessageLite.GeneratedExtension<?, ?>) extensionSchema.findExtensionByNumber( 158 registers.extensionRegistry, defaultInstance, 159 WireFormat.getTagFieldNumber(startTag)); 160 if (extension != null) { 161 position = 162 ArrayDecoders.decodeMessageField( 163 Protobuf.getInstance().schemaFor( 164 extension.getMessageDefaultInstance().getClass()), 165 data, position, limit, registers); 166 extensions.setField(extension.descriptor, registers.object1); 167 } else { 168 position = 169 ArrayDecoders.decodeUnknownField( 170 startTag, data, position, limit, unknownFields, registers); 171 } 172 } else { 173 position = ArrayDecoders.skipField(startTag, data, position, limit, registers); 174 } 175 continue; 176 } 177 178 int typeId = 0; 179 ByteString rawBytes = null; 180 181 while (position < limit) { 182 position = ArrayDecoders.decodeVarint32(data, position, registers); 183 final int tag = registers.int1; 184 final int number = WireFormat.getTagFieldNumber(tag); 185 final int wireType = WireFormat.getTagWireType(tag); 186 switch (number) { 187 case WireFormat.MESSAGE_SET_TYPE_ID: 188 if (wireType == WireFormat.WIRETYPE_VARINT) { 189 position = ArrayDecoders.decodeVarint32(data, position, registers); 190 typeId = registers.int1; 191 // TODO(b/248560713) decide if we're keeping support for Full in schema classes and 192 // handle this better. 193 extension = 194 (GeneratedMessageLite.GeneratedExtension<?, ?>) 195 extensionSchema.findExtensionByNumber( 196 registers.extensionRegistry, defaultInstance, typeId); 197 continue; 198 } 199 break; 200 case WireFormat.MESSAGE_SET_MESSAGE: 201 if (extension != null) { 202 position = ArrayDecoders.decodeMessageField( 203 Protobuf.getInstance().schemaFor( 204 extension.getMessageDefaultInstance().getClass()), 205 data, position, limit, registers); 206 extensions.setField(extension.descriptor, registers.object1); 207 continue; 208 } else { 209 if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) { 210 position = ArrayDecoders.decodeBytes(data, position, registers); 211 rawBytes = (ByteString) registers.object1; 212 continue; 213 } 214 break; 215 } 216 default: 217 break; 218 } 219 if (tag == WireFormat.MESSAGE_SET_ITEM_END_TAG) { 220 break; 221 } 222 position = ArrayDecoders.skipField(tag, data, position, limit, registers); 223 } 224 225 if (rawBytes != null) { 226 unknownFields.storeField( 227 WireFormat.makeTag(typeId, WireFormat.WIRETYPE_LENGTH_DELIMITED), rawBytes); 228 } 229 } 230 if (position != limit) { 231 throw InvalidProtocolBufferException.parseFailure(); 232 } 233 } 234 235 @Override mergeFrom(T message, Reader reader, ExtensionRegistryLite extensionRegistry)236 public void mergeFrom(T message, Reader reader, ExtensionRegistryLite extensionRegistry) 237 throws IOException { 238 mergeFromHelper(unknownFieldSchema, extensionSchema, message, reader, extensionRegistry); 239 } 240 241 /** 242 * A helper method for wildcard capture of {@code unknownFieldSchema}. See: 243 * https://docs.oracle.com/javase/tutorial/java/generics/capture.html 244 */ 245 @SuppressWarnings("unchecked") mergeFromHelper( UnknownFieldSchema<UT, UB> unknownFieldSchema, ExtensionSchema<ET> extensionSchema, T message, Reader reader, ExtensionRegistryLite extensionRegistry)246 private <UT, UB, ET extends FieldSet.FieldDescriptorLite<ET>> void mergeFromHelper( 247 UnknownFieldSchema<UT, UB> unknownFieldSchema, 248 ExtensionSchema<ET> extensionSchema, 249 T message, 250 Reader reader, 251 ExtensionRegistryLite extensionRegistry) 252 throws IOException { 253 UB unknownFields = unknownFieldSchema.getBuilderFromMessage(message); 254 FieldSet<ET> extensions = extensionSchema.getMutableExtensions(message); 255 try { 256 while (true) { 257 final int number = reader.getFieldNumber(); 258 if (number == Reader.READ_DONE) { 259 return; 260 } 261 if (parseMessageSetItemOrUnknownField( 262 reader, 263 extensionRegistry, 264 extensionSchema, 265 extensions, 266 unknownFieldSchema, 267 unknownFields)) { 268 continue; 269 } 270 // Done reading. 271 return; 272 } 273 } finally { 274 unknownFieldSchema.setBuilderToMessage(message, unknownFields); 275 } 276 } 277 278 @Override makeImmutable(T message)279 public void makeImmutable(T message) { 280 unknownFieldSchema.makeImmutable(message); 281 extensionSchema.makeImmutable(message); 282 } 283 284 private <UT, UB, ET extends FieldSet.FieldDescriptorLite<ET>> parseMessageSetItemOrUnknownField( Reader reader, ExtensionRegistryLite extensionRegistry, ExtensionSchema<ET> extensionSchema, FieldSet<ET> extensions, UnknownFieldSchema<UT, UB> unknownFieldSchema, UB unknownFields)285 boolean parseMessageSetItemOrUnknownField( 286 Reader reader, 287 ExtensionRegistryLite extensionRegistry, 288 ExtensionSchema<ET> extensionSchema, 289 FieldSet<ET> extensions, 290 UnknownFieldSchema<UT, UB> unknownFieldSchema, 291 UB unknownFields) 292 throws IOException { 293 int startTag = reader.getTag(); 294 if (startTag != WireFormat.MESSAGE_SET_ITEM_TAG) { 295 if (WireFormat.getTagWireType(startTag) == WireFormat.WIRETYPE_LENGTH_DELIMITED) { 296 Object extension = 297 extensionSchema.findExtensionByNumber( 298 extensionRegistry, defaultInstance, WireFormat.getTagFieldNumber(startTag)); 299 if (extension != null) { 300 extensionSchema.parseLengthPrefixedMessageSetItem( 301 reader, extension, extensionRegistry, extensions); 302 return true; 303 } else { 304 return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader); 305 } 306 } else { 307 return reader.skipField(); 308 } 309 } 310 311 // The wire format for MessageSet is: 312 // message MessageSet { 313 // repeated group Item = 1 { 314 // required int32 typeId = 2; 315 // required bytes message = 3; 316 // } 317 // } 318 // "typeId" is the extension's field number. The extension can only be 319 // a message type, where "message" contains the encoded bytes of that 320 // message. 321 // 322 // In practice, we will probably never see a MessageSet item in which 323 // the message appears before the type ID, or where either field does not 324 // appear exactly once. However, in theory such cases are valid, so we 325 // should be prepared to accept them. 326 327 int typeId = 0; 328 ByteString rawBytes = null; // If we encounter "message" before "typeId" 329 Object extension = null; 330 331 // Read bytes from input, if we get it's type first then parse it eagerly, 332 // otherwise we store the raw bytes in a local variable. 333 loop: 334 while (true) { 335 final int number = reader.getFieldNumber(); 336 if (number == Reader.READ_DONE) { 337 break; 338 } 339 340 final int tag = reader.getTag(); 341 if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { 342 typeId = reader.readUInt32(); 343 extension = 344 extensionSchema.findExtensionByNumber(extensionRegistry, defaultInstance, typeId); 345 continue; 346 } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { 347 if (extension != null) { 348 extensionSchema.parseLengthPrefixedMessageSetItem( 349 reader, extension, extensionRegistry, extensions); 350 continue; 351 } 352 // We haven't seen a type ID yet or we want parse message lazily. 353 rawBytes = reader.readBytes(); 354 continue; 355 } else { 356 if (!reader.skipField()) { 357 break loop; // End of group 358 } 359 } 360 } 361 362 if (reader.getTag() != WireFormat.MESSAGE_SET_ITEM_END_TAG) { 363 throw InvalidProtocolBufferException.invalidEndTag(); 364 } 365 366 // If there are any rawBytes left, it means the message content appears before the type ID. 367 if (rawBytes != null) { 368 if (extension != null) { // We known the type 369 // TODO(xiaofeng): Instead of reading into a temporary ByteString, maybe there is a way 370 // to read directly from Reader to the submessage? 371 extensionSchema.parseMessageSetItem(rawBytes, extension, extensionRegistry, extensions); 372 } else { 373 unknownFieldSchema.addLengthDelimited(unknownFields, typeId, rawBytes); 374 } 375 } 376 return true; 377 } 378 379 @Override isInitialized(T message)380 public final boolean isInitialized(T message) { 381 FieldSet<?> extensions = extensionSchema.getExtensions(message); 382 return extensions.isInitialized(); 383 } 384 385 @Override getSerializedSize(T message)386 public int getSerializedSize(T message) { 387 int size = 0; 388 389 size += getUnknownFieldsSerializedSize(unknownFieldSchema, message); 390 391 if (hasExtensions) { 392 size += extensionSchema.getExtensions(message).getMessageSetSerializedSize(); 393 } 394 395 return size; 396 } 397 getUnknownFieldsSerializedSize( UnknownFieldSchema<UT, UB> schema, T message)398 private <UT, UB> int getUnknownFieldsSerializedSize( 399 UnknownFieldSchema<UT, UB> schema, T message) { 400 UT unknowns = schema.getFromMessage(message); 401 return schema.getSerializedSizeAsMessageSet(unknowns); 402 } 403 } 404