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