1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.test.protoinputstream;
18 
19 import android.util.proto.ProtoInputStream;
20 import android.util.proto.ProtoStream;
21 import android.util.proto.WireTypeMismatchException;
22 
23 import com.android.test.protoinputstream.nano.Test;
24 
25 import com.google.protobuf.nano.MessageNano;
26 
27 import junit.framework.TestCase;
28 
29 import java.io.ByteArrayInputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 
33 public class ProtoInputStreamDoubleTest extends TestCase {
34 
35 
testRead()36     public void testRead() throws IOException {
37         testRead(0);
38         testRead(1);
39         testRead(5);
40     }
41 
testRead(int chunkSize)42     private void testRead(int chunkSize) throws IOException {
43         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
44 
45         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
46         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
47         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
48         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
49         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
50         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
51         final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
52         final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
53         final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
54         final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
55 
56         final byte[] protobuf = new byte[]{
57                 // 1 -> 0 - default value, not written
58                 // 2 -> 1
59                 (byte) 0x11,
60                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
61                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
62                 // 10 -> 1
63                 (byte) 0x51,
64                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
65                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
66                 // 3 -> -1234.432
67                 (byte) 0x19,
68                 (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
69                 (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
70                 // 4 -> 42.42
71                 (byte) 0x21,
72                 (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
73                 (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
74                 // 5 -> Double.MIN_NORMAL
75                 (byte) 0x29,
76                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
77                 (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
78                 // 6 -> DOUBLE.MIN_VALUE
79                 (byte) 0x31,
80                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
81                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
82                 // 7 -> Double.NEGATIVE_INFINITY
83                 (byte) 0x39,
84                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
85                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
86                 // 8 -> Double.NaN
87                 (byte) 0x41,
88                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
89                 (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
90                 // 9 -> Double.POSITIVE_INFINITY
91                 (byte) 0x49,
92                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
93                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
94         };
95 
96         InputStream stream = new ByteArrayInputStream(protobuf);
97         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
98         double[] results = new double[9];
99         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
100             switch (pi.getFieldNumber()) {
101                 case (int) fieldId1:
102                     fail("Should never reach this");
103                     break;
104                 case (int) fieldId2:
105                     results[1] = pi.readDouble(fieldId2);
106                     break;
107                 case (int) fieldId3:
108                     results[2] = pi.readDouble(fieldId3);
109                     break;
110                 case (int) fieldId4:
111                     results[3] = pi.readDouble(fieldId4);
112                     break;
113                 case (int) fieldId5:
114                     results[4] = pi.readDouble(fieldId5);
115                     break;
116                 case (int) fieldId6:
117                     results[5] = pi.readDouble(fieldId6);
118                     break;
119                 case (int) fieldId7:
120                     results[6] = pi.readDouble(fieldId7);
121                     break;
122                 case (int) fieldId8:
123                     results[7] = pi.readDouble(fieldId8);
124                     break;
125                 case (int) fieldId9:
126                     results[8] = pi.readDouble(fieldId9);
127                     break;
128                 case (int) fieldId10:
129                     // Intentionally don't read the data. Parse should continue normally
130                     break;
131                 default:
132                     fail("Unexpected field id " + pi.getFieldNumber());
133             }
134         }
135         stream.close();
136         assertEquals(0.0, results[0]);
137         assertEquals(1.0, results[1]);
138         assertEquals(-1234.432, results[2]);
139         assertEquals(42.42, results[3]);
140         assertEquals(Double.MIN_NORMAL, results[4]);
141         assertEquals(Double.MIN_VALUE, results[5]);
142         assertEquals(Double.NEGATIVE_INFINITY, results[6]);
143         assertEquals(Double.NaN, results[7]);
144         assertEquals(Double.POSITIVE_INFINITY, results[8]);
145     }
146 
147     /**
148      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
149      */
testReadCompat()150     public void testReadCompat() throws Exception {
151         testReadCompat(0);
152         testReadCompat(1);
153         testReadCompat(-1234.432);
154         testReadCompat(42.42);
155         testReadCompat(Double.MIN_NORMAL);
156         testReadCompat(Double.MIN_VALUE);
157         testReadCompat(Double.NEGATIVE_INFINITY);
158         testReadCompat(Double.NaN);
159         testReadCompat(Double.POSITIVE_INFINITY);
160     }
161 
162     /**
163      * Implementation of testReadCompat with a given value.
164      */
testReadCompat(double val)165     private void testReadCompat(double val) throws Exception {
166         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
167         final long fieldId = fieldFlags | ((long) 10 & 0x0ffffffffL);
168 
169         final Test.All all = new Test.All();
170         all.doubleField = val;
171 
172         final byte[] proto = MessageNano.toByteArray(all);
173 
174         final ProtoInputStream pi = new ProtoInputStream(proto);
175         final Test.All readback = Test.All.parseFrom(proto);
176 
177         double result = 0.0; // start off with default value
178         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
179             switch (pi.getFieldNumber()) {
180                 case (int) fieldId:
181                     result = pi.readDouble(fieldId);
182                     break;
183                 default:
184                     fail("Unexpected field id " + pi.getFieldNumber());
185             }
186         }
187 
188         assertEquals(readback.doubleField, result);
189     }
190 
191 
testRepeated()192     public void testRepeated() throws IOException {
193         testRepeated(0);
194         testRepeated(1);
195         testRepeated(5);
196     }
197 
testRepeated(int chunkSize)198     private void testRepeated(int chunkSize) throws IOException {
199         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
200 
201         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
202         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
203         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
204         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
205         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
206         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
207         final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
208         final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
209         final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
210         final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
211 
212         final byte[] protobuf = new byte[]{
213                 // 1 -> 0 - default value, written when repeated
214                 (byte) 0x09,
215                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
216                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
217                 // 2 -> 1
218                 (byte) 0x11,
219                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
220                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
221                 // 3 -> -1234.432
222                 (byte) 0x19,
223                 (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
224                 (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
225                 // 4 -> 42.42
226                 (byte) 0x21,
227                 (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
228                 (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
229                 // 5 -> Double.MIN_NORMAL
230                 (byte) 0x29,
231                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
232                 (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
233                 // 6 -> DOUBLE.MIN_VALUE
234                 (byte) 0x31,
235                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
236                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
237                 // 7 -> Double.NEGATIVE_INFINITY
238                 (byte) 0x39,
239                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
240                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
241                 // 8 -> Double.NaN
242                 (byte) 0x41,
243                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
244                 (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
245                 // 9 -> Double.POSITIVE_INFINITY
246                 (byte) 0x49,
247                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
248                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
249                 // 10 -> 1
250                 (byte) 0x51,
251                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
252                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
253 
254                 // 1 -> 0 - default value, written when repeated
255                 (byte) 0x09,
256                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
257                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
258                 // 2 -> 1
259                 (byte) 0x11,
260                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
261                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
262                 // 3 -> -1234.432
263                 (byte) 0x19,
264                 (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
265                 (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
266                 // 4 -> 42.42
267                 (byte) 0x21,
268                 (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
269                 (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
270                 // 5 -> Double.MIN_NORMAL
271                 (byte) 0x29,
272                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
273                 (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
274                 // 6 -> DOUBLE.MIN_VALUE
275                 (byte) 0x31,
276                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
277                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
278                 // 7 -> Double.NEGATIVE_INFINITY
279                 (byte) 0x39,
280                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
281                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
282                 // 8 -> Double.NaN
283                 (byte) 0x41,
284                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
285                 (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
286                 // 9 -> Double.POSITIVE_INFINITY
287                 (byte) 0x49,
288                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
289                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
290         };
291 
292         InputStream stream = new ByteArrayInputStream(protobuf);
293         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
294         double[][] results = new double[9][2];
295         int[] indices = new int[9];
296         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
297 
298             switch (pi.getFieldNumber()) {
299                 case (int) fieldId1:
300                     results[0][indices[0]++] = pi.readDouble(fieldId1);
301                     break;
302                 case (int) fieldId2:
303                     results[1][indices[1]++] = pi.readDouble(fieldId2);
304                     break;
305                 case (int) fieldId3:
306                     results[2][indices[2]++] = pi.readDouble(fieldId3);
307                     break;
308                 case (int) fieldId4:
309                     results[3][indices[3]++] = pi.readDouble(fieldId4);
310                     break;
311                 case (int) fieldId5:
312                     results[4][indices[4]++] = pi.readDouble(fieldId5);
313                     break;
314                 case (int) fieldId6:
315                     results[5][indices[5]++] = pi.readDouble(fieldId6);
316                     break;
317                 case (int) fieldId7:
318                     results[6][indices[6]++] = pi.readDouble(fieldId7);
319                     break;
320                 case (int) fieldId8:
321                     results[7][indices[7]++] = pi.readDouble(fieldId8);
322                     break;
323                 case (int) fieldId9:
324                     results[8][indices[8]++] = pi.readDouble(fieldId9);
325                     break;
326                 case (int) fieldId10:
327                     // Intentionally don't read the data. Parse should continue normally
328                     break;
329                 default:
330                     fail("Unexpected field id " + pi.getFieldNumber());
331             }
332         }
333         stream.close();
334         assertEquals(0.0, results[0][0]);
335         assertEquals(0.0, results[0][1]);
336         assertEquals(1.0, results[1][0]);
337         assertEquals(1.0, results[1][1]);
338         assertEquals(-1234.432, results[2][0]);
339         assertEquals(-1234.432, results[2][1]);
340         assertEquals(42.42, results[3][0]);
341         assertEquals(42.42, results[3][1]);
342         assertEquals(Double.MIN_NORMAL, results[4][0]);
343         assertEquals(Double.MIN_NORMAL, results[4][1]);
344         assertEquals(Double.MIN_VALUE, results[5][0]);
345         assertEquals(Double.MIN_VALUE, results[5][1]);
346         assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
347         assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
348         assertEquals(Double.NaN, results[7][0]);
349         assertEquals(Double.NaN, results[7][1]);
350         assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
351         assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
352     }
353 
354     /**
355      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
356      */
testRepeatedCompat()357     public void testRepeatedCompat() throws Exception {
358         testRepeatedCompat(new double[0]);
359         testRepeatedCompat(new double[]{0, 1, -1234.432, 42.42,
360                 Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
361                 Double.POSITIVE_INFINITY,
362         });
363     }
364 
365     /**
366      * Implementation of testRepeatedCompat with a given value.
367      */
testRepeatedCompat(double[] val)368     private void testRepeatedCompat(double[] val) throws Exception {
369         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
370         final long fieldId = fieldFlags | ((long) 11 & 0x0ffffffffL);
371 
372         final Test.All all = new Test.All();
373         all.doubleFieldRepeated = val;
374 
375         final byte[] proto = MessageNano.toByteArray(all);
376 
377         final ProtoInputStream pi = new ProtoInputStream(proto);
378         final Test.All readback = Test.All.parseFrom(proto);
379 
380         double[] result = new double[val.length];
381         int index = 0;
382         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
383             switch (pi.getFieldNumber()) {
384                 case (int) fieldId:
385                     result[index++] = pi.readDouble(fieldId);
386                     break;
387                 default:
388                     fail("Unexpected field id " + pi.getFieldNumber());
389             }
390         }
391 
392         assertEquals(readback.doubleFieldRepeated.length, result.length);
393         for (int i = 0; i < result.length; i++) {
394             assertEquals(readback.doubleFieldRepeated[i], result[i]);
395         }
396     }
397 
398 
testPacked()399     public void testPacked() throws IOException {
400         testPacked(0);
401         testPacked(1);
402         testPacked(5);
403     }
404 
testPacked(int chunkSize)405     private void testPacked(int chunkSize) throws IOException {
406         final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
407 
408         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
409         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
410         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
411         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
412         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
413         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
414         final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
415         final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
416         final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
417         final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
418 
419         final byte[] protobuf = new byte[]{
420                 // 1 -> 0 - default value, written when repeated
421                 (byte) 0x0a,
422                 (byte) 0x10,
423                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
424                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
425                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
426                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
427                 // 2 -> 1
428                 (byte) 0x12,
429                 (byte) 0x10,
430                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
431                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
432                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
433                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
434                 // 10 -> 1
435                 (byte) 0x52,
436                 (byte) 0x10,
437                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
438                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
439                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
440                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
441                 // 3 -> -1234.432
442                 (byte) 0x1a,
443                 (byte) 0x10,
444                 (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
445                 (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
446                 (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
447                 (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
448                 // 4 -> 42.42
449                 (byte) 0x22,
450                 (byte) 0x10,
451                 (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
452                 (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
453                 (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
454                 (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
455                 // 5 -> Double.MIN_NORMAL
456                 (byte) 0x2a,
457                 (byte) 0x10,
458                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
459                 (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
460                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
461                 (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
462                 // 6 -> DOUBLE.MIN_VALUE
463                 (byte) 0x32,
464                 (byte) 0x10,
465                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
466                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
467                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
468                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
469                 // 7 -> Double.NEGATIVE_INFINITY
470                 (byte) 0x3a,
471                 (byte) 0x10,
472                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
473                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
474                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
475                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
476                 // 8 -> Double.NaN
477                 (byte) 0x42,
478                 (byte) 0x10,
479                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
480                 (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
481                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
482                 (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
483                 // 9 -> Double.POSITIVE_INFINITY
484                 (byte) 0x4a,
485                 (byte) 0x10,
486                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
487                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
488                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
489                 (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
490         };
491 
492         InputStream stream = new ByteArrayInputStream(protobuf);
493         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
494         double[][] results = new double[9][2];
495         int[] indices = new int[9];
496         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
497 
498             switch (pi.getFieldNumber()) {
499                 case (int) fieldId1:
500                     results[0][indices[0]++] = pi.readDouble(fieldId1);
501                     break;
502                 case (int) fieldId2:
503                     results[1][indices[1]++] = pi.readDouble(fieldId2);
504                     break;
505                 case (int) fieldId3:
506                     results[2][indices[2]++] = pi.readDouble(fieldId3);
507                     break;
508                 case (int) fieldId4:
509                     results[3][indices[3]++] = pi.readDouble(fieldId4);
510                     break;
511                 case (int) fieldId5:
512                     results[4][indices[4]++] = pi.readDouble(fieldId5);
513                     break;
514                 case (int) fieldId6:
515                     results[5][indices[5]++] = pi.readDouble(fieldId6);
516                     break;
517                 case (int) fieldId7:
518                     results[6][indices[6]++] = pi.readDouble(fieldId7);
519                     break;
520                 case (int) fieldId8:
521                     results[7][indices[7]++] = pi.readDouble(fieldId8);
522                     break;
523                 case (int) fieldId9:
524                     results[8][indices[8]++] = pi.readDouble(fieldId9);
525                     break;
526                 case (int) fieldId10:
527                     // Intentionally don't read the data. Parse should continue normally
528                     break;
529                 default:
530                     fail("Unexpected field id " + pi.getFieldNumber());
531             }
532         }
533         stream.close();
534         assertEquals(0.0, results[0][0]);
535         assertEquals(0.0, results[0][1]);
536         assertEquals(1.0, results[1][0]);
537         assertEquals(1.0, results[1][1]);
538         assertEquals(-1234.432, results[2][0]);
539         assertEquals(-1234.432, results[2][1]);
540         assertEquals(42.42, results[3][0]);
541         assertEquals(42.42, results[3][1]);
542         assertEquals(Double.MIN_NORMAL, results[4][0]);
543         assertEquals(Double.MIN_NORMAL, results[4][1]);
544         assertEquals(Double.MIN_VALUE, results[5][0]);
545         assertEquals(Double.MIN_VALUE, results[5][1]);
546         assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
547         assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
548         assertEquals(Double.NaN, results[7][0]);
549         assertEquals(Double.NaN, results[7][1]);
550         assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
551         assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
552     }
553 
554     /**
555      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
556      */
testPackedCompat()557     public void testPackedCompat() throws Exception {
558         testPackedCompat(new double[0]);
559         testPackedCompat(new double[]{0, 1, -1234.432, 42.42,
560                 Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
561                 Double.POSITIVE_INFINITY,
562         });
563     }
564 
565     /**
566      * Implementation of testPackedCompat with a given value.
567      */
testPackedCompat(double[] val)568     private void testPackedCompat(double[] val) throws Exception {
569         final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
570         final long fieldId = fieldFlags | ((long) 12 & 0x0ffffffffL);
571 
572         final Test.All all = new Test.All();
573         all.doubleFieldPacked = val;
574 
575         final byte[] proto = MessageNano.toByteArray(all);
576 
577         final ProtoInputStream pi = new ProtoInputStream(proto);
578         final Test.All readback = Test.All.parseFrom(proto);
579 
580         double[] result = new double[val.length];
581         int index = 0;
582         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
583             switch (pi.getFieldNumber()) {
584                 case (int) fieldId:
585                     result[index++] = pi.readDouble(fieldId);
586                     break;
587                 default:
588                     fail("Unexpected field id " + pi.getFieldNumber());
589             }
590         }
591 
592         assertEquals(readback.doubleFieldPacked.length, result.length);
593         for (int i = 0; i < result.length; i++) {
594             assertEquals(readback.doubleFieldPacked[i], result[i]);
595         }
596     }
597 
598     /**
599      * Test that using the wrong read method throws an exception
600      */
testBadReadType()601     public void testBadReadType() throws IOException {
602         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
603 
604         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
605 
606         final byte[] protobuf = new byte[]{
607                 // 1 -> 1
608                 (byte) 0x09,
609                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
610                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
611         };
612 
613         ProtoInputStream pi = new ProtoInputStream(protobuf);
614         pi.nextField();
615         try {
616             pi.readFloat(fieldId1);
617             fail("Should have thrown IllegalArgumentException");
618         } catch (IllegalArgumentException iae) {
619             // good
620         }
621 
622         pi = new ProtoInputStream(protobuf);
623         pi.nextField();
624         try {
625             pi.readBoolean(fieldId1);
626             fail("Should have thrown IllegalArgumentException");
627         } catch (IllegalArgumentException iae) {
628             // good
629         }
630 
631         pi = new ProtoInputStream(protobuf);
632         pi.nextField();
633         try {
634             pi.readInt(fieldId1);
635             fail("Should have thrown IllegalArgumentException");
636         } catch (IllegalArgumentException iae) {
637             // good
638         }
639 
640         pi = new ProtoInputStream(protobuf);
641         pi.nextField();
642         try {
643             pi.readLong(fieldId1);
644             fail("Should have thrown IllegalArgumentException");
645         } catch (IllegalArgumentException iae) {
646             // good
647         }
648 
649         pi = new ProtoInputStream(protobuf);
650         pi.nextField();
651         try {
652             pi.readBytes(fieldId1);
653             fail("Should have thrown IllegalArgumentException");
654         } catch (IllegalArgumentException iae) {
655             // good
656         }
657 
658         pi = new ProtoInputStream(protobuf);
659         pi.nextField();
660         try {
661             pi.readString(fieldId1);
662             fail("Should have thrown IllegalArgumentException");
663         } catch (IllegalArgumentException iae) {
664             // good
665         }
666     }
667 
668     /**
669      * Test that unexpected wrong wire types will throw an exception
670      */
testBadWireType()671     public void testBadWireType() throws IOException {
672         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
673 
674         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
675         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
676         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
677         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
678 
679         final byte[] protobuf = new byte[]{
680                 // 1 : varint -> 1
681                 (byte) 0x08,
682                 (byte) 0x01,
683                 // 2 : fixed64 -> 0x1
684                 (byte) 0x11,
685                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
686                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
687                 // 3 : length delimited -> { 1 }
688                 (byte) 0x1a,
689                 (byte) 0x08,
690                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
691                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
692                 // 6 : fixed32
693                 (byte) 0x35,
694                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
695         };
696 
697         InputStream stream = new ByteArrayInputStream(protobuf);
698         final ProtoInputStream pi = new ProtoInputStream(stream);
699 
700         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
701             try {
702                 switch (pi.getFieldNumber()) {
703                     case (int) fieldId1:
704                         pi.readDouble(fieldId1);
705                         fail("Should have thrown a WireTypeMismatchException");
706                         break;
707                     case (int) fieldId2:
708                         pi.readDouble(fieldId2);
709                         // don't fail, fixed64 is ok
710                         break;
711                     case (int) fieldId3:
712                         pi.readDouble(fieldId3);
713                         // don't fail, length delimited is ok (represents packed doubles)
714                         break;
715                     case (int) fieldId6:
716                         pi.readDouble(fieldId6);
717                         fail("Should have thrown a WireTypeMismatchException");
718                         break;
719                     default:
720                         fail("Unexpected field id " + pi.getFieldNumber());
721                 }
722             } catch (WireTypeMismatchException wtme) {
723                 // good
724             }
725         }
726         stream.close();
727     }
728 }
729