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