• 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.nano;
32 
33 import com.google.protobuf.nano.MapFactories.MapFactory;
34 
35 import java.io.IOException;
36 import java.nio.charset.Charset;
37 import java.util.Arrays;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 
41 /**
42  * The classes contained within are used internally by the Protocol Buffer
43  * library and generated message implementations. They are public only because
44  * those generated messages do not reside in the {@code protobuf} package.
45  * Others should not use this class directly.
46  *
47  * @author kenton@google.com (Kenton Varda)
48  */
49 public final class InternalNano {
50 
51   public static final int TYPE_DOUBLE   = 1;
52   public static final int TYPE_FLOAT    = 2;
53   public static final int TYPE_INT64    = 3;
54   public static final int TYPE_UINT64   = 4;
55   public static final int TYPE_INT32    = 5;
56   public static final int TYPE_FIXED64  = 6;
57   public static final int TYPE_FIXED32  = 7;
58   public static final int TYPE_BOOL     = 8;
59   public static final int TYPE_STRING   = 9;
60   public static final int TYPE_GROUP    = 10;
61   public static final int TYPE_MESSAGE  = 11;
62   public static final int TYPE_BYTES    = 12;
63   public static final int TYPE_UINT32   = 13;
64   public static final int TYPE_ENUM     = 14;
65   public static final int TYPE_SFIXED32 = 15;
66   public static final int TYPE_SFIXED64 = 16;
67   public static final int TYPE_SINT32   = 17;
68   public static final int TYPE_SINT64   = 18;
69 
70   protected static final Charset UTF_8 = Charset.forName("UTF-8");
71   protected static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
72 
InternalNano()73   private InternalNano() {}
74 
75   /**
76    * An object to provide synchronization when lazily initializing static fields
77    * of {@link MessageNano} subclasses.
78    * <p>
79    * To enable earlier versions of ProGuard to inline short methods from a
80    * generated MessageNano subclass to the call sites, that class must not have
81    * a class initializer, which will be created if there is any static variable
82    * initializers. To lazily initialize the static variables in a thread-safe
83    * manner, the initialization code will synchronize on this object.
84    */
85   public static final Object LAZY_INIT_LOCK = new Object();
86 
87   /**
88    * Helper called by generated code to construct default values for string
89    * fields.
90    * <p>
91    * The protocol compiler does not actually contain a UTF-8 decoder -- it
92    * just pushes UTF-8-encoded text around without touching it.  The one place
93    * where this presents a problem is when generating Java string literals.
94    * Unicode characters in the string literal would normally need to be encoded
95    * using a Unicode escape sequence, which would require decoding them.
96    * To get around this, protoc instead embeds the UTF-8 bytes into the
97    * generated code and leaves it to the runtime library to decode them.
98    * <p>
99    * It gets worse, though.  If protoc just generated a byte array, like:
100    *   new byte[] {0x12, 0x34, 0x56, 0x78}
101    * Java actually generates *code* which allocates an array and then fills
102    * in each value.  This is much less efficient than just embedding the bytes
103    * directly into the bytecode.  To get around this, we need another
104    * work-around.  String literals are embedded directly, so protoc actually
105    * generates a string literal corresponding to the bytes.  The easiest way
106    * to do this is to use the ISO-8859-1 character set, which corresponds to
107    * the first 256 characters of the Unicode range.  Protoc can then use
108    * good old CEscape to generate the string.
109    * <p>
110    * So we have a string literal which represents a set of bytes which
111    * represents another string.  This function -- stringDefaultValue --
112    * converts from the generated string to the string we actually want.  The
113    * generated code calls this automatically.
114    */
stringDefaultValue(String bytes)115   public static String stringDefaultValue(String bytes) {
116     return new String(bytes.getBytes(ISO_8859_1), InternalNano.UTF_8);
117   }
118 
119   /**
120    * Helper called by generated code to construct default values for bytes
121    * fields.
122    * <p>
123    * This is a lot like {@link #stringDefaultValue}, but for bytes fields.
124    * In this case we only need the second of the two hacks -- allowing us to
125    * embed raw bytes as a string literal with ISO-8859-1 encoding.
126    */
bytesDefaultValue(String bytes)127   public static byte[] bytesDefaultValue(String bytes) {
128     return bytes.getBytes(ISO_8859_1);
129   }
130 
131   /**
132    * Helper function to convert a string into UTF-8 while turning the
133    * UnsupportedEncodingException to a RuntimeException.
134    */
copyFromUtf8(final String text)135   public static byte[] copyFromUtf8(final String text) {
136     return text.getBytes(InternalNano.UTF_8);
137   }
138 
139   /**
140    * Checks repeated int field equality; null-value and 0-length fields are
141    * considered equal.
142    */
equals(int[] field1, int[] field2)143   public static boolean equals(int[] field1, int[] field2) {
144     if (field1 == null || field1.length == 0) {
145       return field2 == null || field2.length == 0;
146     } else {
147       return Arrays.equals(field1, field2);
148     }
149   }
150 
151   /**
152    * Checks repeated long field equality; null-value and 0-length fields are
153    * considered equal.
154    */
equals(long[] field1, long[] field2)155   public static boolean equals(long[] field1, long[] field2) {
156     if (field1 == null || field1.length == 0) {
157       return field2 == null || field2.length == 0;
158     } else {
159       return Arrays.equals(field1, field2);
160     }
161   }
162 
163   /**
164    * Checks repeated float field equality; null-value and 0-length fields are
165    * considered equal.
166    */
equals(float[] field1, float[] field2)167   public static boolean equals(float[] field1, float[] field2) {
168     if (field1 == null || field1.length == 0) {
169       return field2 == null || field2.length == 0;
170     } else {
171       return Arrays.equals(field1, field2);
172     }
173   }
174 
175   /**
176    * Checks repeated double field equality; null-value and 0-length fields are
177    * considered equal.
178    */
equals(double[] field1, double[] field2)179   public static boolean equals(double[] field1, double[] field2) {
180     if (field1 == null || field1.length == 0) {
181       return field2 == null || field2.length == 0;
182     } else {
183       return Arrays.equals(field1, field2);
184     }
185   }
186 
187   /**
188    * Checks repeated boolean field equality; null-value and 0-length fields are
189    * considered equal.
190    */
equals(boolean[] field1, boolean[] field2)191   public static boolean equals(boolean[] field1, boolean[] field2) {
192     if (field1 == null || field1.length == 0) {
193       return field2 == null || field2.length == 0;
194     } else {
195       return Arrays.equals(field1, field2);
196     }
197   }
198 
199   /**
200    * Checks repeated bytes field equality. Only non-null elements are tested.
201    * Returns true if the two fields have the same sequence of non-null
202    * elements. Null-value fields and fields of any length with only null
203    * elements are considered equal.
204    */
equals(byte[][] field1, byte[][] field2)205   public static boolean equals(byte[][] field1, byte[][] field2) {
206     int index1 = 0;
207     int length1 = field1 == null ? 0 : field1.length;
208     int index2 = 0;
209     int length2 = field2 == null ? 0 : field2.length;
210     while (true) {
211       while (index1 < length1 && field1[index1] == null) {
212         index1++;
213       }
214       while (index2 < length2 && field2[index2] == null) {
215         index2++;
216       }
217       boolean atEndOf1 = index1 >= length1;
218       boolean atEndOf2 = index2 >= length2;
219       if (atEndOf1 && atEndOf2) {
220         // no more non-null elements to test in both arrays
221         return true;
222       } else if (atEndOf1 != atEndOf2) {
223         // one of the arrays have extra non-null elements
224         return false;
225       } else if (!Arrays.equals(field1[index1], field2[index2])) {
226         // element mismatch
227         return false;
228       }
229       index1++;
230       index2++;
231     }
232   }
233 
234   /**
235    * Checks repeated string/message field equality. Only non-null elements are
236    * tested. Returns true if the two fields have the same sequence of non-null
237    * elements. Null-value fields and fields of any length with only null
238    * elements are considered equal.
239    */
equals(Object[] field1, Object[] field2)240   public static boolean equals(Object[] field1, Object[] field2) {
241     int index1 = 0;
242     int length1 = field1 == null ? 0 : field1.length;
243     int index2 = 0;
244     int length2 = field2 == null ? 0 : field2.length;
245     while (true) {
246       while (index1 < length1 && field1[index1] == null) {
247         index1++;
248       }
249       while (index2 < length2 && field2[index2] == null) {
250         index2++;
251       }
252       boolean atEndOf1 = index1 >= length1;
253       boolean atEndOf2 = index2 >= length2;
254       if (atEndOf1 && atEndOf2) {
255         // no more non-null elements to test in both arrays
256         return true;
257       } else if (atEndOf1 != atEndOf2) {
258         // one of the arrays have extra non-null elements
259         return false;
260       } else if (!field1[index1].equals(field2[index2])) {
261         // element mismatch
262         return false;
263       }
264       index1++;
265       index2++;
266     }
267   }
268 
269   /**
270    * Computes the hash code of a repeated int field. Null-value and 0-length
271    * fields have the same hash code.
272    */
hashCode(int[] field)273   public static int hashCode(int[] field) {
274     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
275   }
276 
277   /**
278    * Computes the hash code of a repeated long field. Null-value and 0-length
279    * fields have the same hash code.
280    */
hashCode(long[] field)281   public static int hashCode(long[] field) {
282     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
283   }
284 
285   /**
286    * Computes the hash code of a repeated float field. Null-value and 0-length
287    * fields have the same hash code.
288    */
hashCode(float[] field)289   public static int hashCode(float[] field) {
290     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
291   }
292 
293   /**
294    * Computes the hash code of a repeated double field. Null-value and 0-length
295    * fields have the same hash code.
296    */
hashCode(double[] field)297   public static int hashCode(double[] field) {
298     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
299   }
300 
301   /**
302    * Computes the hash code of a repeated boolean field. Null-value and 0-length
303    * fields have the same hash code.
304    */
hashCode(boolean[] field)305   public static int hashCode(boolean[] field) {
306     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
307   }
308 
309   /**
310    * Computes the hash code of a repeated bytes field. Only the sequence of all
311    * non-null elements are used in the computation. Null-value fields and fields
312    * of any length with only null elements have the same hash code.
313    */
hashCode(byte[][] field)314   public static int hashCode(byte[][] field) {
315     int result = 0;
316     for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) {
317       byte[] element = field[i];
318       if (element != null) {
319         result = 31 * result + Arrays.hashCode(element);
320       }
321     }
322     return result;
323   }
324 
325   /**
326    * Computes the hash code of a repeated string/message field. Only the
327    * sequence of all non-null elements are used in the computation. Null-value
328    * fields and fields of any length with only null elements have the same hash
329    * code.
330    */
hashCode(Object[] field)331   public static int hashCode(Object[] field) {
332     int result = 0;
333     for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) {
334       Object element = field[i];
335       if (element != null) {
336         result = 31 * result + element.hashCode();
337       }
338     }
339     return result;
340   }
primitiveDefaultValue(int type)341   private static Object primitiveDefaultValue(int type) {
342     switch (type) {
343       case TYPE_BOOL:
344         return Boolean.FALSE;
345       case TYPE_BYTES:
346         return WireFormatNano.EMPTY_BYTES;
347       case TYPE_STRING:
348         return "";
349       case TYPE_FLOAT:
350         return Float.valueOf(0);
351       case TYPE_DOUBLE:
352         return Double.valueOf(0);
353       case TYPE_ENUM:
354       case TYPE_FIXED32:
355       case TYPE_INT32:
356       case TYPE_UINT32:
357       case TYPE_SINT32:
358       case TYPE_SFIXED32:
359         return Integer.valueOf(0);
360       case TYPE_INT64:
361       case TYPE_UINT64:
362       case TYPE_SINT64:
363       case TYPE_FIXED64:
364       case TYPE_SFIXED64:
365         return Long.valueOf(0L);
366       case TYPE_MESSAGE:
367       case TYPE_GROUP:
368       default:
369         throw new IllegalArgumentException(
370             "Type: " + type + " is not a primitive type.");
371     }
372   }
373 
374   /**
375    * Merges the map entry into the map field. Note this is only supposed to
376    * be called by generated messages.
377    *
378    * @param map the map field; may be null, in which case a map will be
379    *        instantiated using the {@link MapFactories.MapFactory}
380    * @param input the input byte buffer
381    * @param keyType key type, as defined in InternalNano.TYPE_*
382    * @param valueType value type, as defined in InternalNano.TYPE_*
383    * @param value an new instance of the value, if the value is a TYPE_MESSAGE;
384    *        otherwise this parameter can be null and will be ignored.
385    * @param keyTag wire tag for the key
386    * @param valueTag wire tag for the value
387    * @return the map field
388    * @throws IOException
389    */
390   @SuppressWarnings("unchecked")
mergeMapEntry( CodedInputByteBufferNano input, Map<K, V> map, MapFactory mapFactory, int keyType, int valueType, V value, int keyTag, int valueTag)391   public static final <K, V> Map<K, V> mergeMapEntry(
392       CodedInputByteBufferNano input,
393       Map<K, V> map,
394       MapFactory mapFactory,
395       int keyType,
396       int valueType,
397       V value,
398       int keyTag,
399       int valueTag) throws IOException {
400     map = mapFactory.forMap(map);
401     final int length = input.readRawVarint32();
402     final int oldLimit = input.pushLimit(length);
403     K key = null;
404     while (true) {
405       int tag = input.readTag();
406       if (tag == 0) {
407         break;
408       }
409       if (tag == keyTag) {
410         key = (K) input.readPrimitiveField(keyType);
411       } else if (tag == valueTag) {
412         if (valueType == TYPE_MESSAGE) {
413           input.readMessage((MessageNano) value);
414         } else {
415           value = (V) input.readPrimitiveField(valueType);
416         }
417       } else {
418         if (!input.skipField(tag)) {
419           break;
420         }
421       }
422     }
423     input.checkLastTagWas(0);
424     input.popLimit(oldLimit);
425 
426     if (key == null) {
427       // key can only be primitive types.
428       key = (K) primitiveDefaultValue(keyType);
429     }
430 
431     if (value == null) {
432       // message type value will be initialized by code-gen.
433       value = (V) primitiveDefaultValue(valueType);
434     }
435 
436     map.put(key, value);
437     return map;
438   }
439 
serializeMapField( CodedOutputByteBufferNano output, Map<K, V> map, int number, int keyType, int valueType)440   public static <K, V> void serializeMapField(
441       CodedOutputByteBufferNano output,
442       Map<K, V> map, int number, int keyType, int valueType)
443           throws IOException {
444     for (Entry<K, V> entry: map.entrySet()) {
445       K key = entry.getKey();
446       V value = entry.getValue();
447       if (key == null || value == null) {
448         throw new IllegalStateException(
449             "keys and values in maps cannot be null");
450       }
451       int entrySize =
452           CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) +
453           CodedOutputByteBufferNano.computeFieldSize(2, valueType, value);
454       output.writeTag(number, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
455       output.writeRawVarint32(entrySize);
456       output.writeField(1, keyType, key);
457       output.writeField(2, valueType, value);
458     }
459   }
460 
computeMapFieldSize( Map<K, V> map, int number, int keyType, int valueType)461   public static <K, V> int computeMapFieldSize(
462       Map<K, V> map, int number, int keyType, int valueType) {
463     int size = 0;
464     int tagSize = CodedOutputByteBufferNano.computeTagSize(number);
465     for (Entry<K, V> entry: map.entrySet()) {
466       K key = entry.getKey();
467       V value = entry.getValue();
468       if (key == null || value == null) {
469         throw new IllegalStateException(
470             "keys and values in maps cannot be null");
471       }
472       int entrySize =
473           CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) +
474           CodedOutputByteBufferNano.computeFieldSize(2, valueType, value);
475       size += tagSize + entrySize
476           + CodedOutputByteBufferNano.computeRawVarint32Size(entrySize);
477     }
478     return size;
479   }
480 
481   /**
482    * Checks whether two {@link Map} are equal. We don't use the default equals
483    * method of {@link Map} because it compares by identity not by content for
484    * byte arrays.
485    */
equals(Map<K, V> a, Map<K, V> b)486   public static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) {
487     if (a == b) {
488       return true;
489     }
490     if (a == null) {
491       return b.size() == 0;
492     }
493     if (b == null) {
494       return a.size() == 0;
495     }
496     if (a.size() != b.size()) {
497       return false;
498     }
499     for (Entry<K, V> entry : a.entrySet()) {
500       if (!b.containsKey(entry.getKey())) {
501         return false;
502       }
503       if (!equalsMapValue(entry.getValue(), b.get(entry.getKey()))) {
504         return false;
505       }
506     }
507     return true;
508   }
509 
equalsMapValue(Object a, Object b)510   private static boolean equalsMapValue(Object a, Object b) {
511     if (a == null || b == null) {
512       throw new IllegalStateException(
513           "keys and values in maps cannot be null");
514     }
515     if (a instanceof byte[] && b instanceof byte[]) {
516       return Arrays.equals((byte[]) a, (byte[]) b);
517     }
518     return a.equals(b);
519   }
520 
hashCode(Map<K, V> map)521   public static <K, V> int hashCode(Map<K, V> map) {
522     if (map == null) {
523       return 0;
524     }
525     int result = 0;
526     for (Entry<K, V> entry : map.entrySet()) {
527       result += hashCodeForMap(entry.getKey())
528           ^ hashCodeForMap(entry.getValue());
529     }
530     return result;
531   }
532 
hashCodeForMap(Object o)533   private static int hashCodeForMap(Object o) {
534     if (o instanceof byte[]) {
535       return Arrays.hashCode((byte[]) o);
536     }
537     return o.hashCode();
538   }
539 
540   // This avoids having to make FieldArray public.
cloneUnknownFieldData(ExtendableMessageNano original, ExtendableMessageNano cloned)541   public static void cloneUnknownFieldData(ExtendableMessageNano original,
542       ExtendableMessageNano cloned) {
543     if (original.unknownFieldData != null) {
544       cloned.unknownFieldData = (FieldArray) original.unknownFieldData.clone();
545     }
546   }
547 }
548