• 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
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