• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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