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.Arrays; 35 36 /** 37 * {@code UnknownFieldSetLite} is used to keep track of fields which were seen when parsing a 38 * protocol message but whose field numbers or types are unrecognized. This most frequently occurs 39 * when new fields are added to a message type and then messages containing those fields are read by 40 * old software that was compiled before the new types were added. 41 * 42 * <p>For use by generated code only. 43 * 44 * @author dweis@google.com (Daniel Weis) 45 */ 46 public final class UnknownFieldSetLite { 47 48 // Arbitrarily chosen. 49 // TODO(dweis): Tune this number? 50 private static final int MIN_CAPACITY = 8; 51 52 private static final UnknownFieldSetLite DEFAULT_INSTANCE = 53 new UnknownFieldSetLite(0, new int[0], new Object[0], /* isMutable= */ false); 54 55 /** 56 * Get an empty {@code UnknownFieldSetLite}. 57 * 58 * <p>For use by generated code only. 59 */ getDefaultInstance()60 public static UnknownFieldSetLite getDefaultInstance() { 61 return DEFAULT_INSTANCE; 62 } 63 64 /** Returns a new mutable instance. */ newInstance()65 static UnknownFieldSetLite newInstance() { 66 return new UnknownFieldSetLite(); 67 } 68 69 /** 70 * Returns a mutable {@code UnknownFieldSetLite} that is the composite of {@code first} and {@code 71 * second}. 72 */ mutableCopyOf(UnknownFieldSetLite first, UnknownFieldSetLite second)73 static UnknownFieldSetLite mutableCopyOf(UnknownFieldSetLite first, UnknownFieldSetLite second) { 74 int count = first.count + second.count; 75 int[] tags = Arrays.copyOf(first.tags, count); 76 System.arraycopy(second.tags, 0, tags, first.count, second.count); 77 Object[] objects = Arrays.copyOf(first.objects, count); 78 System.arraycopy(second.objects, 0, objects, first.count, second.count); 79 return new UnknownFieldSetLite(count, tags, objects, /* isMutable= */ true); 80 } 81 82 /** The number of elements in the set. */ 83 private int count; 84 85 /** The tag numbers for the elements in the set. */ 86 private int[] tags; 87 88 /** The boxed values of the elements in the set. */ 89 private Object[] objects; 90 91 /** The lazily computed serialized size of the set. */ 92 private int memoizedSerializedSize = -1; 93 94 /** Indicates that this object is mutable. */ 95 private boolean isMutable; 96 97 /** Constructs a mutable {@code UnknownFieldSetLite}. */ UnknownFieldSetLite()98 private UnknownFieldSetLite() { 99 this(0, new int[MIN_CAPACITY], new Object[MIN_CAPACITY], /* isMutable= */ true); 100 } 101 102 /** Constructs the {@code UnknownFieldSetLite}. */ UnknownFieldSetLite(int count, int[] tags, Object[] objects, boolean isMutable)103 private UnknownFieldSetLite(int count, int[] tags, Object[] objects, boolean isMutable) { 104 this.count = count; 105 this.tags = tags; 106 this.objects = objects; 107 this.isMutable = isMutable; 108 } 109 110 /** 111 * Marks this object as immutable. 112 * 113 * <p>Future calls to methods that attempt to modify this object will throw. 114 */ makeImmutable()115 public void makeImmutable() { 116 this.isMutable = false; 117 } 118 119 /** Throws an {@link UnsupportedOperationException} if immutable. */ checkMutable()120 void checkMutable() { 121 if (!isMutable) { 122 throw new UnsupportedOperationException(); 123 } 124 } 125 126 /** 127 * Serializes the set and writes it to {@code output}. 128 * 129 * <p>For use by generated code only. 130 */ writeTo(CodedOutputStream output)131 public void writeTo(CodedOutputStream output) throws IOException { 132 for (int i = 0; i < count; i++) { 133 int tag = tags[i]; 134 int fieldNumber = WireFormat.getTagFieldNumber(tag); 135 switch (WireFormat.getTagWireType(tag)) { 136 case WireFormat.WIRETYPE_VARINT: 137 output.writeUInt64(fieldNumber, (Long) objects[i]); 138 break; 139 case WireFormat.WIRETYPE_FIXED32: 140 output.writeFixed32(fieldNumber, (Integer) objects[i]); 141 break; 142 case WireFormat.WIRETYPE_FIXED64: 143 output.writeFixed64(fieldNumber, (Long) objects[i]); 144 break; 145 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 146 output.writeBytes(fieldNumber, (ByteString) objects[i]); 147 break; 148 case WireFormat.WIRETYPE_START_GROUP: 149 output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); 150 ((UnknownFieldSetLite) objects[i]).writeTo(output); 151 output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); 152 break; 153 default: 154 throw InvalidProtocolBufferException.invalidWireType(); 155 } 156 } 157 } 158 159 /** 160 * Serializes the set and writes it to {@code output} using {@code MessageSet} wire format. 161 * 162 * <p>For use by generated code only. 163 */ writeAsMessageSetTo(CodedOutputStream output)164 public void writeAsMessageSetTo(CodedOutputStream output) throws IOException { 165 for (int i = 0; i < count; i++) { 166 int fieldNumber = WireFormat.getTagFieldNumber(tags[i]); 167 output.writeRawMessageSetExtension(fieldNumber, (ByteString) objects[i]); 168 } 169 } 170 171 /** Serializes the set and writes it to {@code writer} using {@code MessageSet} wire format. */ writeAsMessageSetTo(Writer writer)172 void writeAsMessageSetTo(Writer writer) throws IOException { 173 if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { 174 // Write fields in descending order. 175 for (int i = count - 1; i >= 0; i--) { 176 int fieldNumber = WireFormat.getTagFieldNumber(tags[i]); 177 writer.writeMessageSetItem(fieldNumber, objects[i]); 178 } 179 } else { 180 // Write fields in ascending order. 181 for (int i = 0; i < count; i++) { 182 int fieldNumber = WireFormat.getTagFieldNumber(tags[i]); 183 writer.writeMessageSetItem(fieldNumber, objects[i]); 184 } 185 } 186 } 187 188 /** Serializes the set and writes it to {@code writer}. */ writeTo(Writer writer)189 public void writeTo(Writer writer) throws IOException { 190 if (count == 0) { 191 return; 192 } 193 194 // TODO: tags are not sorted, so there's no write order guarantees 195 if (writer.fieldOrder() == Writer.FieldOrder.ASCENDING) { 196 for (int i = 0; i < count; ++i) { 197 writeField(tags[i], objects[i], writer); 198 } 199 } else { 200 for (int i = count - 1; i >= 0; --i) { 201 writeField(tags[i], objects[i], writer); 202 } 203 } 204 } 205 writeField(int tag, Object object, Writer writer)206 private static void writeField(int tag, Object object, Writer writer) throws IOException { 207 int fieldNumber = WireFormat.getTagFieldNumber(tag); 208 switch (WireFormat.getTagWireType(tag)) { 209 case WireFormat.WIRETYPE_VARINT: 210 writer.writeInt64(fieldNumber, (Long) object); 211 break; 212 case WireFormat.WIRETYPE_FIXED32: 213 writer.writeFixed32(fieldNumber, (Integer) object); 214 break; 215 case WireFormat.WIRETYPE_FIXED64: 216 writer.writeFixed64(fieldNumber, (Long) object); 217 break; 218 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 219 writer.writeBytes(fieldNumber, (ByteString) object); 220 break; 221 case WireFormat.WIRETYPE_START_GROUP: 222 if (writer.fieldOrder() == Writer.FieldOrder.ASCENDING) { 223 writer.writeStartGroup(fieldNumber); 224 ((UnknownFieldSetLite) object).writeTo(writer); 225 writer.writeEndGroup(fieldNumber); 226 } else { 227 writer.writeEndGroup(fieldNumber); 228 ((UnknownFieldSetLite) object).writeTo(writer); 229 writer.writeStartGroup(fieldNumber); 230 } 231 break; 232 default: 233 // TODO(liujisi): Change writeTo to throw IOException? 234 throw new RuntimeException(InvalidProtocolBufferException.invalidWireType()); 235 } 236 } 237 238 /** 239 * Get the number of bytes required to encode this field, including field number, using {@code 240 * MessageSet} wire format. 241 */ getSerializedSizeAsMessageSet()242 public int getSerializedSizeAsMessageSet() { 243 int size = memoizedSerializedSize; 244 if (size != -1) { 245 return size; 246 } 247 248 size = 0; 249 for (int i = 0; i < count; i++) { 250 int tag = tags[i]; 251 int fieldNumber = WireFormat.getTagFieldNumber(tag); 252 size += 253 CodedOutputStream.computeRawMessageSetExtensionSize(fieldNumber, (ByteString) objects[i]); 254 } 255 256 memoizedSerializedSize = size; 257 258 return size; 259 } 260 261 /** 262 * Get the number of bytes required to encode this set. 263 * 264 * <p>For use by generated code only. 265 */ getSerializedSize()266 public int getSerializedSize() { 267 int size = memoizedSerializedSize; 268 if (size != -1) { 269 return size; 270 } 271 272 size = 0; 273 for (int i = 0; i < count; i++) { 274 int tag = tags[i]; 275 int fieldNumber = WireFormat.getTagFieldNumber(tag); 276 switch (WireFormat.getTagWireType(tag)) { 277 case WireFormat.WIRETYPE_VARINT: 278 size += CodedOutputStream.computeUInt64Size(fieldNumber, (Long) objects[i]); 279 break; 280 case WireFormat.WIRETYPE_FIXED32: 281 size += CodedOutputStream.computeFixed32Size(fieldNumber, (Integer) objects[i]); 282 break; 283 case WireFormat.WIRETYPE_FIXED64: 284 size += CodedOutputStream.computeFixed64Size(fieldNumber, (Long) objects[i]); 285 break; 286 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 287 size += CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) objects[i]); 288 break; 289 case WireFormat.WIRETYPE_START_GROUP: 290 size += 291 CodedOutputStream.computeTagSize(fieldNumber) * 2 292 + ((UnknownFieldSetLite) objects[i]).getSerializedSize(); 293 break; 294 default: 295 throw new IllegalStateException(InvalidProtocolBufferException.invalidWireType()); 296 } 297 } 298 299 memoizedSerializedSize = size; 300 301 return size; 302 } 303 equals(int[] tags1, int[] tags2, int count)304 private static boolean equals(int[] tags1, int[] tags2, int count) { 305 for (int i = 0; i < count; ++i) { 306 if (tags1[i] != tags2[i]) { 307 return false; 308 } 309 } 310 return true; 311 } 312 equals(Object[] objects1, Object[] objects2, int count)313 private static boolean equals(Object[] objects1, Object[] objects2, int count) { 314 for (int i = 0; i < count; ++i) { 315 if (!objects1[i].equals(objects2[i])) { 316 return false; 317 } 318 } 319 return true; 320 } 321 322 @Override equals(Object obj)323 public boolean equals(Object obj) { 324 if (this == obj) { 325 return true; 326 } 327 328 if (obj == null) { 329 return false; 330 } 331 332 if (!(obj instanceof UnknownFieldSetLite)) { 333 return false; 334 } 335 336 UnknownFieldSetLite other = (UnknownFieldSetLite) obj; 337 if (count != other.count 338 || !equals(tags, other.tags, count) 339 || !equals(objects, other.objects, count)) { 340 return false; 341 } 342 343 return true; 344 } 345 hashCode(int[] tags, int count)346 private static int hashCode(int[] tags, int count) { 347 int hashCode = 17; 348 for (int i = 0; i < count; ++i) { 349 hashCode = 31 * hashCode + tags[i]; 350 } 351 return hashCode; 352 } 353 hashCode(Object[] objects, int count)354 private static int hashCode(Object[] objects, int count) { 355 int hashCode = 17; 356 for (int i = 0; i < count; ++i) { 357 hashCode = 31 * hashCode + objects[i].hashCode(); 358 } 359 return hashCode; 360 } 361 362 @Override hashCode()363 public int hashCode() { 364 int hashCode = 17; 365 366 hashCode = 31 * hashCode + count; 367 hashCode = 31 * hashCode + hashCode(tags, count); 368 hashCode = 31 * hashCode + hashCode(objects, count); 369 370 return hashCode; 371 } 372 373 /** 374 * Prints a String representation of the unknown field set. 375 * 376 * <p>For use by generated code only. 377 * 378 * @param buffer the buffer to write to 379 * @param indent the number of spaces the fields should be indented by 380 */ printWithIndent(StringBuilder buffer, int indent)381 final void printWithIndent(StringBuilder buffer, int indent) { 382 for (int i = 0; i < count; i++) { 383 int fieldNumber = WireFormat.getTagFieldNumber(tags[i]); 384 MessageLiteToString.printField(buffer, indent, String.valueOf(fieldNumber), objects[i]); 385 } 386 } 387 388 // Package private for unsafe experimental runtime. storeField(int tag, Object value)389 void storeField(int tag, Object value) { 390 checkMutable(); 391 ensureCapacity(); 392 393 tags[count] = tag; 394 objects[count] = value; 395 count++; 396 } 397 398 /** Ensures that our arrays are long enough to store more metadata. */ ensureCapacity()399 private void ensureCapacity() { 400 if (count == tags.length) { 401 int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1; 402 int newLength = count + increment; 403 404 tags = Arrays.copyOf(tags, newLength); 405 objects = Arrays.copyOf(objects, newLength); 406 } 407 } 408 409 /** 410 * Parse a single field from {@code input} and merge it into this set. 411 * 412 * <p>For use by generated code only. 413 * 414 * @param tag The field's tag number, which was already parsed. 415 * @return {@code false} if the tag is an end group tag. 416 */ mergeFieldFrom(final int tag, final CodedInputStream input)417 boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { 418 checkMutable(); 419 final int fieldNumber = WireFormat.getTagFieldNumber(tag); 420 switch (WireFormat.getTagWireType(tag)) { 421 case WireFormat.WIRETYPE_VARINT: 422 storeField(tag, input.readInt64()); 423 return true; 424 case WireFormat.WIRETYPE_FIXED32: 425 storeField(tag, input.readFixed32()); 426 return true; 427 case WireFormat.WIRETYPE_FIXED64: 428 storeField(tag, input.readFixed64()); 429 return true; 430 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 431 storeField(tag, input.readBytes()); 432 return true; 433 case WireFormat.WIRETYPE_START_GROUP: 434 final UnknownFieldSetLite subFieldSet = new UnknownFieldSetLite(); 435 subFieldSet.mergeFrom(input); 436 input.checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); 437 storeField(tag, subFieldSet); 438 return true; 439 case WireFormat.WIRETYPE_END_GROUP: 440 return false; 441 default: 442 throw InvalidProtocolBufferException.invalidWireType(); 443 } 444 } 445 446 /** 447 * Convenience method for merging a new field containing a single varint value. This is used in 448 * particular when an unknown enum value is encountered. 449 * 450 * <p>For use by generated code only. 451 */ mergeVarintField(int fieldNumber, int value)452 UnknownFieldSetLite mergeVarintField(int fieldNumber, int value) { 453 checkMutable(); 454 if (fieldNumber == 0) { 455 throw new IllegalArgumentException("Zero is not a valid field number."); 456 } 457 458 storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_VARINT), (long) value); 459 460 return this; 461 } 462 463 /** 464 * Convenience method for merging a length-delimited field. 465 * 466 * <p>For use by generated code only. 467 */ mergeLengthDelimitedField(final int fieldNumber, final ByteString value)468 UnknownFieldSetLite mergeLengthDelimitedField(final int fieldNumber, final ByteString value) { 469 checkMutable(); 470 if (fieldNumber == 0) { 471 throw new IllegalArgumentException("Zero is not a valid field number."); 472 } 473 474 storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED), value); 475 476 return this; 477 } 478 479 /** Parse an entire message from {@code input} and merge its fields into this set. */ mergeFrom(final CodedInputStream input)480 private UnknownFieldSetLite mergeFrom(final CodedInputStream input) throws IOException { 481 // Ensures initialization in mergeFieldFrom. 482 while (true) { 483 final int tag = input.readTag(); 484 if (tag == 0 || !mergeFieldFrom(tag, input)) { 485 break; 486 } 487 } 488 return this; 489 } 490 } 491