1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
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 java.io.UnsupportedEncodingException;
34 import java.util.Arrays;
35 
36 /**
37  * The classes contained within are used internally by the Protocol Buffer
38  * library and generated message implementations. They are public only because
39  * those generated messages do not reside in the {@code protobuf} package.
40  * Others should not use this class directly.
41  *
42  * @author kenton@google.com (Kenton Varda)
43  */
44 public final class InternalNano {
45 
InternalNano()46   private InternalNano() {}
47 
48   /**
49    * An object to provide synchronization when lazily initializing static fields
50    * of {@link MessageNano} subclasses.
51    * <p>
52    * To enable earlier versions of ProGuard to inline short methods from a
53    * generated MessageNano subclass to the call sites, that class must not have
54    * a class initializer, which will be created if there is any static variable
55    * initializers. To lazily initialize the static variables in a thread-safe
56    * manner, the initialization code will synchronize on this object.
57    */
58   public static final Object LAZY_INIT_LOCK = new Object();
59 
60   /**
61    * Helper called by generated code to construct default values for string
62    * fields.
63    * <p>
64    * The protocol compiler does not actually contain a UTF-8 decoder -- it
65    * just pushes UTF-8-encoded text around without touching it.  The one place
66    * where this presents a problem is when generating Java string literals.
67    * Unicode characters in the string literal would normally need to be encoded
68    * using a Unicode escape sequence, which would require decoding them.
69    * To get around this, protoc instead embeds the UTF-8 bytes into the
70    * generated code and leaves it to the runtime library to decode them.
71    * <p>
72    * It gets worse, though.  If protoc just generated a byte array, like:
73    *   new byte[] {0x12, 0x34, 0x56, 0x78}
74    * Java actually generates *code* which allocates an array and then fills
75    * in each value.  This is much less efficient than just embedding the bytes
76    * directly into the bytecode.  To get around this, we need another
77    * work-around.  String literals are embedded directly, so protoc actually
78    * generates a string literal corresponding to the bytes.  The easiest way
79    * to do this is to use the ISO-8859-1 character set, which corresponds to
80    * the first 256 characters of the Unicode range.  Protoc can then use
81    * good old CEscape to generate the string.
82    * <p>
83    * So we have a string literal which represents a set of bytes which
84    * represents another string.  This function -- stringDefaultValue --
85    * converts from the generated string to the string we actually want.  The
86    * generated code calls this automatically.
87    */
stringDefaultValue(String bytes)88   public static String stringDefaultValue(String bytes) {
89     try {
90       return new String(bytes.getBytes("ISO-8859-1"), "UTF-8");
91     } catch (UnsupportedEncodingException e) {
92       // This should never happen since all JVMs are required to implement
93       // both of the above character sets.
94       throw new IllegalStateException(
95           "Java VM does not support a standard character set.", e);
96     }
97   }
98 
99   /**
100    * Helper called by generated code to construct default values for bytes
101    * fields.
102    * <p>
103    * This is a lot like {@link #stringDefaultValue}, but for bytes fields.
104    * In this case we only need the second of the two hacks -- allowing us to
105    * embed raw bytes as a string literal with ISO-8859-1 encoding.
106    */
bytesDefaultValue(String bytes)107   public static byte[] bytesDefaultValue(String bytes) {
108     try {
109       return bytes.getBytes("ISO-8859-1");
110     } catch (UnsupportedEncodingException e) {
111       // This should never happen since all JVMs are required to implement
112       // ISO-8859-1.
113       throw new IllegalStateException(
114           "Java VM does not support a standard character set.", e);
115     }
116   }
117 
118   /**
119    * Helper function to convert a string into UTF-8 while turning the
120    * UnsupportedEncodingException to a RuntimeException.
121    */
copyFromUtf8(final String text)122   public static byte[] copyFromUtf8(final String text) {
123     try {
124       return text.getBytes("UTF-8");
125     } catch (UnsupportedEncodingException e) {
126       throw new RuntimeException("UTF-8 not supported?");
127     }
128   }
129 
130   /**
131    * Checks repeated int field equality; null-value and 0-length fields are
132    * considered equal.
133    */
equals(int[] field1, int[] field2)134   public static boolean equals(int[] field1, int[] field2) {
135     if (field1 == null || field1.length == 0) {
136       return field2 == null || field2.length == 0;
137     } else {
138       return Arrays.equals(field1, field2);
139     }
140   }
141 
142   /**
143    * Checks repeated long field equality; null-value and 0-length fields are
144    * considered equal.
145    */
equals(long[] field1, long[] field2)146   public static boolean equals(long[] field1, long[] field2) {
147     if (field1 == null || field1.length == 0) {
148       return field2 == null || field2.length == 0;
149     } else {
150       return Arrays.equals(field1, field2);
151     }
152   }
153 
154   /**
155    * Checks repeated float field equality; null-value and 0-length fields are
156    * considered equal.
157    */
equals(float[] field1, float[] field2)158   public static boolean equals(float[] field1, float[] field2) {
159     if (field1 == null || field1.length == 0) {
160       return field2 == null || field2.length == 0;
161     } else {
162       return Arrays.equals(field1, field2);
163     }
164   }
165 
166   /**
167    * Checks repeated double field equality; null-value and 0-length fields are
168    * considered equal.
169    */
equals(double[] field1, double[] field2)170   public static boolean equals(double[] field1, double[] field2) {
171     if (field1 == null || field1.length == 0) {
172       return field2 == null || field2.length == 0;
173     } else {
174       return Arrays.equals(field1, field2);
175     }
176   }
177 
178   /**
179    * Checks repeated boolean field equality; null-value and 0-length fields are
180    * considered equal.
181    */
equals(boolean[] field1, boolean[] field2)182   public static boolean equals(boolean[] field1, boolean[] field2) {
183     if (field1 == null || field1.length == 0) {
184       return field2 == null || field2.length == 0;
185     } else {
186       return Arrays.equals(field1, field2);
187     }
188   }
189 
190   /**
191    * Checks repeated bytes field equality. Only non-null elements are tested.
192    * Returns true if the two fields have the same sequence of non-null
193    * elements. Null-value fields and fields of any length with only null
194    * elements are considered equal.
195    */
equals(byte[][] field1, byte[][] field2)196   public static boolean equals(byte[][] field1, byte[][] field2) {
197     int index1 = 0;
198     int length1 = field1 == null ? 0 : field1.length;
199     int index2 = 0;
200     int length2 = field2 == null ? 0 : field2.length;
201     while (true) {
202       while (index1 < length1 && field1[index1] == null) {
203         index1++;
204       }
205       while (index2 < length2 && field2[index2] == null) {
206         index2++;
207       }
208       boolean atEndOf1 = index1 >= length1;
209       boolean atEndOf2 = index2 >= length2;
210       if (atEndOf1 && atEndOf2) {
211         // no more non-null elements to test in both arrays
212         return true;
213       } else if (atEndOf1 != atEndOf2) {
214         // one of the arrays have extra non-null elements
215         return false;
216       } else if (!Arrays.equals(field1[index1], field2[index2])) {
217         // element mismatch
218         return false;
219       }
220       index1++;
221       index2++;
222     }
223   }
224 
225   /**
226    * Checks repeated string/message field equality. Only non-null elements are
227    * tested. Returns true if the two fields have the same sequence of non-null
228    * elements. Null-value fields and fields of any length with only null
229    * elements are considered equal.
230    */
equals(Object[] field1, Object[] field2)231   public static boolean equals(Object[] field1, Object[] field2) {
232     int index1 = 0;
233     int length1 = field1 == null ? 0 : field1.length;
234     int index2 = 0;
235     int length2 = field2 == null ? 0 : field2.length;
236     while (true) {
237       while (index1 < length1 && field1[index1] == null) {
238         index1++;
239       }
240       while (index2 < length2 && field2[index2] == null) {
241         index2++;
242       }
243       boolean atEndOf1 = index1 >= length1;
244       boolean atEndOf2 = index2 >= length2;
245       if (atEndOf1 && atEndOf2) {
246         // no more non-null elements to test in both arrays
247         return true;
248       } else if (atEndOf1 != atEndOf2) {
249         // one of the arrays have extra non-null elements
250         return false;
251       } else if (!field1[index1].equals(field2[index2])) {
252         // element mismatch
253         return false;
254       }
255       index1++;
256       index2++;
257     }
258   }
259 
260   /**
261    * Computes the hash code of a repeated int field. Null-value and 0-length
262    * fields have the same hash code.
263    */
hashCode(int[] field)264   public static int hashCode(int[] field) {
265     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
266   }
267 
268   /**
269    * Computes the hash code of a repeated long field. Null-value and 0-length
270    * fields have the same hash code.
271    */
hashCode(long[] field)272   public static int hashCode(long[] field) {
273     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
274   }
275 
276   /**
277    * Computes the hash code of a repeated float field. Null-value and 0-length
278    * fields have the same hash code.
279    */
hashCode(float[] field)280   public static int hashCode(float[] field) {
281     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
282   }
283 
284   /**
285    * Computes the hash code of a repeated double field. Null-value and 0-length
286    * fields have the same hash code.
287    */
hashCode(double[] field)288   public static int hashCode(double[] field) {
289     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
290   }
291 
292   /**
293    * Computes the hash code of a repeated boolean field. Null-value and 0-length
294    * fields have the same hash code.
295    */
hashCode(boolean[] field)296   public static int hashCode(boolean[] field) {
297     return field == null || field.length == 0 ? 0 : Arrays.hashCode(field);
298   }
299 
300   /**
301    * Computes the hash code of a repeated bytes field. Only the sequence of all
302    * non-null elements are used in the computation. Null-value fields and fields
303    * of any length with only null elements have the same hash code.
304    */
hashCode(byte[][] field)305   public static int hashCode(byte[][] field) {
306     int result = 0;
307     for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) {
308       byte[] element = field[i];
309       if (element != null) {
310         result = 31 * result + Arrays.hashCode(element);
311       }
312     }
313     return result;
314   }
315 
316   /**
317    * Computes the hash code of a repeated string/message field. Only the
318    * sequence of all non-null elements are used in the computation. Null-value
319    * fields and fields of any length with only null elements have the same hash
320    * code.
321    */
hashCode(Object[] field)322   public static int hashCode(Object[] field) {
323     int result = 0;
324     for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) {
325       Object element = field[i];
326       if (element != null) {
327         result = 31 * result + element.hashCode();
328       }
329     }
330     return result;
331   }
332 
333   // This avoids having to make FieldArray public.
cloneUnknownFieldData(ExtendableMessageNano original, ExtendableMessageNano cloned)334   public static void cloneUnknownFieldData(ExtendableMessageNano original,
335       ExtendableMessageNano cloned) {
336     if (original.unknownFieldData != null) {
337       cloned.unknownFieldData = (FieldArray) original.unknownFieldData.clone();
338     }
339   }
340 
341 }
342