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 38 * when parsing a protocol message but whose field numbers or types are 39 * unrecognized. This most frequently occurs when new fields are added to a 40 * message type and then messages containing those fields are read by old 41 * software that was compiled before the new types were added. 42 * 43 * <p>For use by generated code only. 44 * 45 * @author dweis@google.com (Daniel Weis) 46 */ 47 public final class UnknownFieldSetLite { 48 49 // Arbitrarily chosen. 50 // TODO(dweis): Tune this number? 51 private static final int MIN_CAPACITY = 8; 52 53 private static final UnknownFieldSetLite DEFAULT_INSTANCE = 54 new UnknownFieldSetLite(0, new int[0], new Object[0], false /* isMutable */); 55 56 /** 57 * Get an empty {@code UnknownFieldSetLite}. 58 * 59 * <p>For use by generated code only. 60 */ getDefaultInstance()61 public static UnknownFieldSetLite getDefaultInstance() { 62 return DEFAULT_INSTANCE; 63 } 64 65 /** 66 * Returns a new mutable instance. 67 */ newInstance()68 static UnknownFieldSetLite newInstance() { 69 return new UnknownFieldSetLite(); 70 } 71 72 /** 73 * Returns a mutable {@code UnknownFieldSetLite} that is the composite of {@code first} and 74 * {@code second}. 75 */ mutableCopyOf(UnknownFieldSetLite first, UnknownFieldSetLite second)76 static UnknownFieldSetLite mutableCopyOf(UnknownFieldSetLite first, UnknownFieldSetLite second) { 77 int count = first.count + second.count; 78 int[] tags = Arrays.copyOf(first.tags, count); 79 System.arraycopy(second.tags, 0, tags, first.count, second.count); 80 Object[] objects = Arrays.copyOf(first.objects, count); 81 System.arraycopy(second.objects, 0, objects, first.count, second.count); 82 return new UnknownFieldSetLite(count, tags, objects, true /* isMutable */); 83 } 84 85 /** 86 * The number of elements in the set. 87 */ 88 private int count; 89 90 /** 91 * The tag numbers for the elements in the set. 92 */ 93 private int[] tags; 94 95 /** 96 * The boxed values of the elements in the set. 97 */ 98 private Object[] objects; 99 100 /** 101 * The lazily computed serialized size of the set. 102 */ 103 private int memoizedSerializedSize = -1; 104 105 /** 106 * Indicates that this object is mutable. 107 */ 108 private boolean isMutable; 109 110 /** 111 * Constructs a mutable {@code UnknownFieldSetLite}. 112 */ UnknownFieldSetLite()113 private UnknownFieldSetLite() { 114 this(0, new int[MIN_CAPACITY], new Object[MIN_CAPACITY], true /* isMutable */); 115 } 116 117 /** 118 * Constructs the {@code UnknownFieldSetLite}. 119 */ UnknownFieldSetLite(int count, int[] tags, Object[] objects, boolean isMutable)120 private UnknownFieldSetLite(int count, int[] tags, Object[] objects, boolean isMutable) { 121 this.count = count; 122 this.tags = tags; 123 this.objects = objects; 124 this.isMutable = isMutable; 125 } 126 127 /** 128 * Marks this object as immutable. 129 * 130 * <p>Future calls to methods that attempt to modify this object will throw. 131 */ makeImmutable()132 public void makeImmutable() { 133 this.isMutable = false; 134 } 135 136 /** 137 * Throws an {@link UnsupportedOperationException} if immutable. 138 */ checkMutable()139 void checkMutable() { 140 if (!isMutable) { 141 throw new UnsupportedOperationException(); 142 } 143 } 144 145 /** 146 * Serializes the set and writes it to {@code output}. 147 * 148 * <p>For use by generated code only. 149 */ writeTo(CodedOutputStream output)150 public void writeTo(CodedOutputStream output) throws IOException { 151 for (int i = 0; i < count; i++) { 152 int tag = tags[i]; 153 int fieldNumber = WireFormat.getTagFieldNumber(tag); 154 switch (WireFormat.getTagWireType(tag)) { 155 case WireFormat.WIRETYPE_VARINT: 156 output.writeUInt64(fieldNumber, (Long) objects[i]); 157 break; 158 case WireFormat.WIRETYPE_FIXED32: 159 output.writeFixed32(fieldNumber, (Integer) objects[i]); 160 break; 161 case WireFormat.WIRETYPE_FIXED64: 162 output.writeFixed64(fieldNumber, (Long) objects[i]); 163 break; 164 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 165 output.writeBytes(fieldNumber, (ByteString) objects[i]); 166 break; 167 case WireFormat.WIRETYPE_START_GROUP: 168 output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); 169 ((UnknownFieldSetLite) objects[i]).writeTo(output); 170 output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); 171 break; 172 default: 173 throw InvalidProtocolBufferException.invalidWireType(); 174 } 175 } 176 } 177 178 /** 179 * Get the number of bytes required to encode this set. 180 * 181 * <p>For use by generated code only. 182 */ getSerializedSize()183 public int getSerializedSize() { 184 int size = memoizedSerializedSize; 185 if (size != -1) { 186 return size; 187 } 188 189 size = 0; 190 for (int i = 0; i < count; i++) { 191 int tag = tags[i]; 192 int fieldNumber = WireFormat.getTagFieldNumber(tag); 193 switch (WireFormat.getTagWireType(tag)) { 194 case WireFormat.WIRETYPE_VARINT: 195 size += CodedOutputStream.computeUInt64Size(fieldNumber, (Long) objects[i]); 196 break; 197 case WireFormat.WIRETYPE_FIXED32: 198 size += CodedOutputStream.computeFixed32Size(fieldNumber, (Integer) objects[i]); 199 break; 200 case WireFormat.WIRETYPE_FIXED64: 201 size += CodedOutputStream.computeFixed64Size(fieldNumber, (Long) objects[i]); 202 break; 203 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 204 size += CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) objects[i]); 205 break; 206 case WireFormat.WIRETYPE_START_GROUP: 207 size += CodedOutputStream.computeTagSize(fieldNumber) * 2 208 + ((UnknownFieldSetLite) objects[i]).getSerializedSize(); 209 break; 210 default: 211 throw new IllegalStateException(InvalidProtocolBufferException.invalidWireType()); 212 } 213 } 214 215 memoizedSerializedSize = size; 216 217 return size; 218 } 219 220 @Override equals(Object obj)221 public boolean equals(Object obj) { 222 if (this == obj) { 223 return true; 224 } 225 226 if (obj == null) { 227 return false; 228 } 229 230 if (!(obj instanceof UnknownFieldSetLite)) { 231 return false; 232 } 233 234 UnknownFieldSetLite other = (UnknownFieldSetLite) obj; 235 if (count != other.count 236 // TODO(dweis): Only have to compare up to count but at worst 2x worse than we need to do. 237 || !Arrays.equals(tags, other.tags) 238 || !Arrays.deepEquals(objects, other.objects)) { 239 return false; 240 } 241 242 return true; 243 } 244 245 @Override hashCode()246 public int hashCode() { 247 int hashCode = 17; 248 249 hashCode = 31 * hashCode + count; 250 hashCode = 31 * hashCode + Arrays.hashCode(tags); 251 hashCode = 31 * hashCode + Arrays.deepHashCode(objects); 252 253 return hashCode; 254 } 255 256 /** 257 * Prints a String representation of the unknown field set. 258 * 259 * <p>For use by generated code only. 260 * 261 * @param buffer the buffer to write to 262 * @param indent the number of spaces the fields should be indented by 263 */ printWithIndent(StringBuilder buffer, int indent)264 final void printWithIndent(StringBuilder buffer, int indent) { 265 for (int i = 0; i < count; i++) { 266 int fieldNumber = WireFormat.getTagFieldNumber(tags[i]); 267 MessageLiteToString.printField(buffer, indent, String.valueOf(fieldNumber), objects[i]); 268 } 269 } 270 storeField(int tag, Object value)271 private void storeField(int tag, Object value) { 272 ensureCapacity(); 273 274 tags[count] = tag; 275 objects[count] = value; 276 count++; 277 } 278 279 /** 280 * Ensures that our arrays are long enough to store more metadata. 281 */ ensureCapacity()282 private void ensureCapacity() { 283 if (count == tags.length) { 284 int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1; 285 int newLength = count + increment; 286 287 tags = Arrays.copyOf(tags, newLength); 288 objects = Arrays.copyOf(objects, newLength); 289 } 290 } 291 292 /** 293 * Parse a single field from {@code input} and merge it into this set. 294 * 295 * <p>For use by generated code only. 296 * 297 * @param tag The field's tag number, which was already parsed. 298 * @return {@code false} if the tag is an end group tag. 299 */ mergeFieldFrom(final int tag, final CodedInputStream input)300 boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { 301 checkMutable(); 302 final int fieldNumber = WireFormat.getTagFieldNumber(tag); 303 switch (WireFormat.getTagWireType(tag)) { 304 case WireFormat.WIRETYPE_VARINT: 305 storeField(tag, input.readInt64()); 306 return true; 307 case WireFormat.WIRETYPE_FIXED32: 308 storeField(tag, input.readFixed32()); 309 return true; 310 case WireFormat.WIRETYPE_FIXED64: 311 storeField(tag, input.readFixed64()); 312 return true; 313 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 314 storeField(tag, input.readBytes()); 315 return true; 316 case WireFormat.WIRETYPE_START_GROUP: 317 final UnknownFieldSetLite subFieldSet = new UnknownFieldSetLite(); 318 subFieldSet.mergeFrom(input); 319 input.checkLastTagWas( 320 WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); 321 storeField(tag, subFieldSet); 322 return true; 323 case WireFormat.WIRETYPE_END_GROUP: 324 return false; 325 default: 326 throw InvalidProtocolBufferException.invalidWireType(); 327 } 328 } 329 330 /** 331 * Convenience method for merging a new field containing a single varint 332 * value. This is used in particular when an unknown enum value is 333 * encountered. 334 * 335 * <p>For use by generated code only. 336 */ mergeVarintField(int fieldNumber, int value)337 UnknownFieldSetLite mergeVarintField(int fieldNumber, int value) { 338 checkMutable(); 339 if (fieldNumber == 0) { 340 throw new IllegalArgumentException("Zero is not a valid field number."); 341 } 342 343 storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_VARINT), (long) value); 344 345 return this; 346 } 347 348 /** 349 * Convenience method for merging a length-delimited field. 350 * 351 * <p>For use by generated code only. 352 */ mergeLengthDelimitedField(final int fieldNumber, final ByteString value)353 UnknownFieldSetLite mergeLengthDelimitedField(final int fieldNumber, final ByteString value) { 354 checkMutable(); 355 if (fieldNumber == 0) { 356 throw new IllegalArgumentException("Zero is not a valid field number."); 357 } 358 359 storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED), value); 360 361 return this; 362 } 363 364 /** 365 * Parse an entire message from {@code input} and merge its fields into 366 * this set. 367 */ mergeFrom(final CodedInputStream input)368 private UnknownFieldSetLite mergeFrom(final CodedInputStream input) throws IOException { 369 // Ensures initialization in mergeFieldFrom. 370 while (true) { 371 final int tag = input.readTag(); 372 if (tag == 0 || !mergeFieldFrom(tag, input)) { 373 break; 374 } 375 } 376 return this; 377 } 378 } 379