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/** 32 * @fileoverview Test cases for jspb's binary protocol buffer reader. 33 * 34 * There are two particular magic numbers that need to be pointed out - 35 * 2^64-1025 is the largest number representable as both a double and an 36 * unsigned 64-bit integer, and 2^63-513 is the largest number representable as 37 * both a double and a signed 64-bit integer. 38 * 39 * Test suite is written using Jasmine -- see http://jasmine.github.io/ 40 * 41 * @author aappleby@google.com (Austin Appleby) 42 */ 43 44goog.require('goog.testing.asserts'); 45goog.require('jspb.BinaryConstants'); 46goog.require('jspb.BinaryDecoder'); 47goog.require('jspb.BinaryReader'); 48goog.require('jspb.BinaryWriter'); 49goog.requireType('jspb.BinaryMessage'); 50 51 52describe('binaryReaderTest', function() { 53 /** 54 * Tests the reader instance cache. 55 */ 56 it('testInstanceCaches', /** @suppress {visibility} */ function() { 57 var writer = new jspb.BinaryWriter(); 58 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 59 writer.writeMessage(1, dummyMessage, goog.nullFunction); 60 writer.writeMessage(2, dummyMessage, goog.nullFunction); 61 62 var buffer = writer.getResultBuffer(); 63 64 // Empty the instance caches. 65 jspb.BinaryReader.instanceCache_ = []; 66 67 // Allocating and then freeing three decoders should leave us with three in 68 // the cache. 69 70 var decoder1 = jspb.BinaryDecoder.alloc(); 71 var decoder2 = jspb.BinaryDecoder.alloc(); 72 var decoder3 = jspb.BinaryDecoder.alloc(); 73 decoder1.free(); 74 decoder2.free(); 75 decoder3.free(); 76 77 assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); 78 assertEquals(0, jspb.BinaryReader.instanceCache_.length); 79 80 // Allocating and then freeing a reader should remove one decoder from its 81 // cache, but it should stay stuck to the reader afterwards since we can't 82 // have a reader without a decoder. 83 jspb.BinaryReader.alloc().free(); 84 85 assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); 86 assertEquals(1, jspb.BinaryReader.instanceCache_.length); 87 88 // Allocating a reader should remove a reader from the cache. 89 var reader = jspb.BinaryReader.alloc(buffer); 90 91 assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); 92 assertEquals(0, jspb.BinaryReader.instanceCache_.length); 93 94 // Processing the message reuses the current reader. 95 reader.nextField(); 96 assertEquals(1, reader.getFieldNumber()); 97 reader.readMessage(dummyMessage, function() { 98 assertEquals(0, jspb.BinaryReader.instanceCache_.length); 99 }); 100 101 reader.nextField(); 102 assertEquals(2, reader.getFieldNumber()); 103 reader.readMessage(dummyMessage, function() { 104 assertEquals(0, jspb.BinaryReader.instanceCache_.length); 105 }); 106 107 assertEquals(false, reader.nextField()); 108 109 assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); 110 assertEquals(0, jspb.BinaryReader.instanceCache_.length); 111 112 // Freeing the reader should put it back into the cache. 113 reader.free(); 114 115 assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); 116 assertEquals(1, jspb.BinaryReader.instanceCache_.length); 117 }); 118 119 120 /** 121 * @param {number} x 122 * @return {number} 123 */ 124 function truncate(x) { 125 var temp = new Float32Array(1); 126 temp[0] = x; 127 return temp[0]; 128 } 129 130 131 /** 132 * Verifies that misuse of the reader class triggers assertions. 133 */ 134 it('testReadErrors', /** @suppress {checkTypes|visibility} */ function() { 135 // Calling readMessage on a non-delimited field should trigger an 136 // assertion. 137 var reader = jspb.BinaryReader.alloc([8, 1]); 138 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 139 reader.nextField(); 140 assertThrows(function() { 141 reader.readMessage(dummyMessage, goog.nullFunction); 142 }); 143 144 // Reading past the end of the stream should trigger an assertion. 145 reader = jspb.BinaryReader.alloc([9, 1]); 146 reader.nextField(); 147 assertThrows(function() { 148 reader.readFixed64() 149 }); 150 151 // Reading past the end of a submessage should trigger an assertion. 152 reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]); 153 reader.nextField(); 154 reader.readMessage(dummyMessage, function() { 155 reader.nextField(); 156 assertThrows(function() { 157 reader.readFixed32() 158 }); 159 }); 160 161 // Skipping an invalid field should trigger an assertion. 162 reader = jspb.BinaryReader.alloc([12, 1]); 163 reader.nextWireType_ = 1000; 164 assertThrows(function() { 165 reader.skipField() 166 }); 167 168 // Reading fields with the wrong wire type should assert. 169 reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]); 170 reader.nextField(); 171 assertThrows(function() { 172 reader.readInt32() 173 }); 174 assertThrows(function() { 175 reader.readInt32String() 176 }); 177 assertThrows(function() { 178 reader.readInt64() 179 }); 180 assertThrows(function() { 181 reader.readInt64String() 182 }); 183 assertThrows(function() { 184 reader.readUint32() 185 }); 186 assertThrows(function() { 187 reader.readUint32String() 188 }); 189 assertThrows(function() { 190 reader.readUint64() 191 }); 192 assertThrows(function() { 193 reader.readUint64String() 194 }); 195 assertThrows(function() { 196 reader.readSint32() 197 }); 198 assertThrows(function() { 199 reader.readBool() 200 }); 201 assertThrows(function() { 202 reader.readEnum() 203 }); 204 205 reader = jspb.BinaryReader.alloc([8, 1]); 206 reader.nextField(); 207 assertThrows(function() { 208 reader.readFixed32() 209 }); 210 assertThrows(function() { 211 reader.readFixed64() 212 }); 213 assertThrows(function() { 214 reader.readSfixed32() 215 }); 216 assertThrows(function() { 217 reader.readSfixed64() 218 }); 219 assertThrows(function() { 220 reader.readFloat() 221 }); 222 assertThrows(function() { 223 reader.readDouble() 224 }); 225 226 assertThrows(function() { 227 reader.readString() 228 }); 229 assertThrows(function() { 230 reader.readBytes() 231 }); 232 }); 233 234 235 /** 236 * Tests encoding and decoding of unsigned field types. 237 * @param {Function} readField 238 * @param {Function} writeField 239 * @param {number} epsilon 240 * @param {number} upperLimit 241 * @param {Function} filter 242 * @private 243 * @suppress {missingProperties} 244 */ 245 var doTestUnsignedField_ = function( 246 readField, writeField, epsilon, upperLimit, filter) { 247 assertNotNull(readField); 248 assertNotNull(writeField); 249 250 var writer = new jspb.BinaryWriter(); 251 252 // Encode zero and limits. 253 writeField.call(writer, 1, filter(0)); 254 writeField.call(writer, 2, filter(epsilon)); 255 writeField.call(writer, 3, filter(upperLimit)); 256 257 // Encode positive values. 258 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 259 writeField.call(writer, 4, filter(cursor)); 260 } 261 262 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 263 264 // Check zero and limits. 265 reader.nextField(); 266 assertEquals(1, reader.getFieldNumber()); 267 assertEquals(filter(0), readField.call(reader)); 268 269 reader.nextField(); 270 assertEquals(2, reader.getFieldNumber()); 271 assertEquals(filter(epsilon), readField.call(reader)); 272 273 reader.nextField(); 274 assertEquals(3, reader.getFieldNumber()); 275 assertEquals(filter(upperLimit), readField.call(reader)); 276 277 // Check positive values. 278 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 279 reader.nextField(); 280 if (4 != reader.getFieldNumber()) throw 'fail!'; 281 if (filter(cursor) != readField.call(reader)) throw 'fail!'; 282 } 283 }; 284 285 286 /** 287 * Tests encoding and decoding of signed field types. 288 * @param {Function} readField 289 * @param {Function} writeField 290 * @param {number} epsilon 291 * @param {number} lowerLimit 292 * @param {number} upperLimit 293 * @param {Function} filter 294 * @private 295 * @suppress {missingProperties} 296 */ 297 var doTestSignedField_ = function( 298 readField, writeField, epsilon, lowerLimit, upperLimit, filter) { 299 var writer = new jspb.BinaryWriter(); 300 301 // Encode zero and limits. 302 writeField.call(writer, 1, filter(lowerLimit)); 303 writeField.call(writer, 2, filter(-epsilon)); 304 writeField.call(writer, 3, filter(0)); 305 writeField.call(writer, 4, filter(epsilon)); 306 writeField.call(writer, 5, filter(upperLimit)); 307 308 var inputValues = []; 309 310 // Encode negative values. 311 for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { 312 var val = filter(cursor); 313 writeField.call(writer, 6, val); 314 inputValues.push({fieldNumber: 6, value: val}); 315 } 316 317 // Encode positive values. 318 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 319 var val = filter(cursor); 320 writeField.call(writer, 7, val); 321 inputValues.push({fieldNumber: 7, value: val}); 322 } 323 324 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 325 326 // Check zero and limits. 327 reader.nextField(); 328 assertEquals(1, reader.getFieldNumber()); 329 assertEquals(filter(lowerLimit), readField.call(reader)); 330 331 reader.nextField(); 332 assertEquals(2, reader.getFieldNumber()); 333 assertEquals(filter(-epsilon), readField.call(reader)); 334 335 reader.nextField(); 336 assertEquals(3, reader.getFieldNumber()); 337 assertEquals(filter(0), readField.call(reader)); 338 339 reader.nextField(); 340 assertEquals(4, reader.getFieldNumber()); 341 assertEquals(filter(epsilon), readField.call(reader)); 342 343 reader.nextField(); 344 assertEquals(5, reader.getFieldNumber()); 345 assertEquals(filter(upperLimit), readField.call(reader)); 346 347 for (var i = 0; i < inputValues.length; i++) { 348 var expected = inputValues[i]; 349 reader.nextField(); 350 assertEquals(expected.fieldNumber, reader.getFieldNumber()); 351 assertEquals(expected.value, readField.call(reader)); 352 } 353 }; 354 355 356 /** 357 * Tests fields that use varint encoding. 358 */ 359 it('testVarintFields', function() { 360 assertNotUndefined(jspb.BinaryReader.prototype.readUint32); 361 assertNotUndefined(jspb.BinaryWriter.prototype.writeUint32); 362 assertNotUndefined(jspb.BinaryReader.prototype.readUint64); 363 assertNotUndefined(jspb.BinaryWriter.prototype.writeUint64); 364 assertNotUndefined(jspb.BinaryReader.prototype.readBool); 365 assertNotUndefined(jspb.BinaryWriter.prototype.writeBool); 366 doTestUnsignedField_( 367 jspb.BinaryReader.prototype.readUint32, 368 jspb.BinaryWriter.prototype.writeUint32, 1, Math.pow(2, 32) - 1, 369 Math.round); 370 371 doTestUnsignedField_( 372 jspb.BinaryReader.prototype.readUint64, 373 jspb.BinaryWriter.prototype.writeUint64, 1, Math.pow(2, 64) - 1025, 374 Math.round); 375 376 doTestSignedField_( 377 jspb.BinaryReader.prototype.readInt32, 378 jspb.BinaryWriter.prototype.writeInt32, 1, -Math.pow(2, 31), 379 Math.pow(2, 31) - 1, Math.round); 380 381 doTestSignedField_( 382 jspb.BinaryReader.prototype.readInt64, 383 jspb.BinaryWriter.prototype.writeInt64, 1, -Math.pow(2, 63), 384 Math.pow(2, 63) - 513, Math.round); 385 386 doTestSignedField_( 387 jspb.BinaryReader.prototype.readEnum, 388 jspb.BinaryWriter.prototype.writeEnum, 1, -Math.pow(2, 31), 389 Math.pow(2, 31) - 1, Math.round); 390 391 doTestUnsignedField_( 392 jspb.BinaryReader.prototype.readBool, 393 jspb.BinaryWriter.prototype.writeBool, 1, 1, function(x) { 394 return !!x; 395 }); 396 }); 397 398 399 /** 400 * Tests reading a field from hexadecimal string (format: '08 BE EF'). 401 * @param {Function} readField 402 * @param {number} expected 403 * @param {string} hexString 404 */ 405 function doTestHexStringVarint_(readField, expected, hexString) { 406 var bytesCount = (hexString.length + 1) / 3; 407 var bytes = new Uint8Array(bytesCount); 408 for (var i = 0; i < bytesCount; i++) { 409 bytes[i] = parseInt(hexString.substring(i * 3, i * 3 + 2), 16); 410 } 411 var reader = jspb.BinaryReader.alloc(bytes); 412 reader.nextField(); 413 assertEquals(expected, readField.call(reader)); 414 } 415 416 417 /** 418 * Tests non-canonical redundant varint decoding. 419 */ 420 it('testRedundantVarintFields', function() { 421 assertNotNull(jspb.BinaryReader.prototype.readUint32); 422 assertNotNull(jspb.BinaryReader.prototype.readUint64); 423 assertNotNull(jspb.BinaryReader.prototype.readSint32); 424 assertNotNull(jspb.BinaryReader.prototype.readSint64); 425 426 // uint32 and sint32 take no more than 5 bytes 427 // 08 - field prefix (type = 0 means varint) 428 doTestHexStringVarint_( 429 jspb.BinaryReader.prototype.readUint32, 12, '08 8C 80 80 80 00'); 430 431 // 11 stands for -6 in zigzag encoding 432 doTestHexStringVarint_( 433 jspb.BinaryReader.prototype.readSint32, -6, '08 8B 80 80 80 00'); 434 435 // uint64 and sint64 take no more than 10 bytes 436 // 08 - field prefix (type = 0 means varint) 437 doTestHexStringVarint_( 438 jspb.BinaryReader.prototype.readUint64, 12, 439 '08 8C 80 80 80 80 80 80 80 80 00'); 440 441 // 11 stands for -6 in zigzag encoding 442 doTestHexStringVarint_( 443 jspb.BinaryReader.prototype.readSint64, -6, 444 '08 8B 80 80 80 80 80 80 80 80 00'); 445 }); 446 447 448 /** 449 * Tests 64-bit fields that are handled as strings. 450 */ 451 it('testStringInt64Fields', function() { 452 var writer = new jspb.BinaryWriter(); 453 454 var testSignedData = [ 455 '2730538252207801776', '-2688470994844604560', '3398529779486536359', 456 '3568577411627971000', '272477188847484900', '-6649058714086158188', 457 '-7695254765712060806', '-4525541438037104029', '-4993706538836508568', 458 '4990160321893729138' 459 ]; 460 var testUnsignedData = [ 461 '7822732630241694882', '6753602971916687352', '2399935075244442116', 462 '8724292567325338867', '16948784802625696584', '4136275908516066934', 463 '3575388346793700364', '5167142028379259461', '1557573948689737699', 464 '17100725280812548567' 465 ]; 466 467 for (var i = 0; i < testSignedData.length; i++) { 468 writer.writeInt64String(2 * i + 1, testSignedData[i]); 469 writer.writeUint64String(2 * i + 2, testUnsignedData[i]); 470 } 471 472 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 473 474 for (var i = 0; i < testSignedData.length; i++) { 475 reader.nextField(); 476 assertEquals(2 * i + 1, reader.getFieldNumber()); 477 assertEquals(testSignedData[i], reader.readInt64String()); 478 reader.nextField(); 479 assertEquals(2 * i + 2, reader.getFieldNumber()); 480 assertEquals(testUnsignedData[i], reader.readUint64String()); 481 } 482 }); 483 484 485 /** 486 * Tests fields that use zigzag encoding. 487 */ 488 it('testZigzagFields', function() { 489 doTestSignedField_( 490 jspb.BinaryReader.prototype.readSint32, 491 jspb.BinaryWriter.prototype.writeSint32, 1, -Math.pow(2, 31), 492 Math.pow(2, 31) - 1, Math.round); 493 494 doTestSignedField_( 495 jspb.BinaryReader.prototype.readSint64, 496 jspb.BinaryWriter.prototype.writeSint64, 1, -Math.pow(2, 63), 497 Math.pow(2, 63) - 513, Math.round); 498 }); 499 500 501 /** 502 * Tests fields that use fixed-length encoding. 503 */ 504 it('testFixedFields', function() { 505 doTestUnsignedField_( 506 jspb.BinaryReader.prototype.readFixed32, 507 jspb.BinaryWriter.prototype.writeFixed32, 1, Math.pow(2, 32) - 1, 508 Math.round); 509 510 doTestUnsignedField_( 511 jspb.BinaryReader.prototype.readFixed64, 512 jspb.BinaryWriter.prototype.writeFixed64, 1, Math.pow(2, 64) - 1025, 513 Math.round); 514 515 doTestSignedField_( 516 jspb.BinaryReader.prototype.readSfixed32, 517 jspb.BinaryWriter.prototype.writeSfixed32, 1, -Math.pow(2, 31), 518 Math.pow(2, 31) - 1, Math.round); 519 520 doTestSignedField_( 521 jspb.BinaryReader.prototype.readSfixed64, 522 jspb.BinaryWriter.prototype.writeSfixed64, 1, -Math.pow(2, 63), 523 Math.pow(2, 63) - 513, Math.round); 524 }); 525 526 527 /** 528 * Tests floating point fields. 529 */ 530 it('testFloatFields', function() { 531 doTestSignedField_( 532 jspb.BinaryReader.prototype.readFloat, 533 jspb.BinaryWriter.prototype.writeFloat, 534 jspb.BinaryConstants.FLOAT32_MIN, -jspb.BinaryConstants.FLOAT32_MAX, 535 jspb.BinaryConstants.FLOAT32_MAX, truncate); 536 537 doTestSignedField_( 538 jspb.BinaryReader.prototype.readDouble, 539 jspb.BinaryWriter.prototype.writeDouble, 540 jspb.BinaryConstants.FLOAT64_EPS * 10, 541 -jspb.BinaryConstants.FLOAT64_MIN, jspb.BinaryConstants.FLOAT64_MIN, 542 function(x) { 543 return x; 544 }); 545 }); 546 547 548 /** 549 * Tests length-delimited string fields. 550 */ 551 it('testStringFields', function() { 552 var s1 = 'The quick brown fox jumps over the lazy dog.'; 553 var s2 = '人人生而自由,在尊嚴和權利上一律平等。'; 554 555 var writer = new jspb.BinaryWriter(); 556 557 writer.writeString(1, s1); 558 writer.writeString(2, s2); 559 560 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 561 562 reader.nextField(); 563 assertEquals(1, reader.getFieldNumber()); 564 assertEquals(s1, reader.readString()); 565 566 reader.nextField(); 567 assertEquals(2, reader.getFieldNumber()); 568 assertEquals(s2, reader.readString()); 569 }); 570 571 572 /** 573 * Tests length-delimited byte fields. 574 */ 575 it('testByteFields', function() { 576 var message = []; 577 var lowerLimit = 1; 578 var upperLimit = 256; 579 var scale = 1.1; 580 581 var writer = new jspb.BinaryWriter(); 582 583 for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) { 584 var len = Math.round(cursor); 585 var bytes = []; 586 for (var i = 0; i < len; i++) bytes.push(i % 256); 587 588 writer.writeBytes(len, bytes); 589 } 590 591 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 592 593 for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) { 594 var len = Math.round(cursor); 595 if (len != reader.getFieldNumber()) throw 'fail!'; 596 597 var bytes = reader.readBytes(); 598 if (len != bytes.length) throw 'fail!'; 599 for (var i = 0; i < bytes.length; i++) { 600 if (i % 256 != bytes[i]) throw 'fail!'; 601 } 602 } 603 }); 604 605 606 /** 607 * Tests nested messages. 608 */ 609 it('testNesting', function() { 610 var writer = new jspb.BinaryWriter(); 611 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 612 613 writer.writeInt32(1, 100); 614 615 // Add one message with 3 int fields. 616 writer.writeMessage(2, dummyMessage, function() { 617 writer.writeInt32(3, 300); 618 writer.writeInt32(4, 400); 619 writer.writeInt32(5, 500); 620 }); 621 622 // Add one empty message. 623 writer.writeMessage(6, dummyMessage, goog.nullFunction); 624 625 writer.writeInt32(7, 700); 626 627 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 628 629 // Validate outermost message. 630 631 reader.nextField(); 632 assertEquals(1, reader.getFieldNumber()); 633 assertEquals(100, reader.readInt32()); 634 635 reader.nextField(); 636 assertEquals(2, reader.getFieldNumber()); 637 reader.readMessage(dummyMessage, function() { 638 // Validate embedded message 1. 639 reader.nextField(); 640 assertEquals(3, reader.getFieldNumber()); 641 assertEquals(300, reader.readInt32()); 642 643 reader.nextField(); 644 assertEquals(4, reader.getFieldNumber()); 645 assertEquals(400, reader.readInt32()); 646 647 reader.nextField(); 648 assertEquals(5, reader.getFieldNumber()); 649 assertEquals(500, reader.readInt32()); 650 651 assertEquals(false, reader.nextField()); 652 }); 653 654 reader.nextField(); 655 assertEquals(6, reader.getFieldNumber()); 656 reader.readMessage(dummyMessage, function() { 657 // Validate embedded message 2. 658 659 assertEquals(false, reader.nextField()); 660 }); 661 662 reader.nextField(); 663 assertEquals(7, reader.getFieldNumber()); 664 assertEquals(700, reader.readInt32()); 665 666 assertEquals(false, reader.nextField()); 667 }); 668 669 /** 670 * Tests skipping fields of each type by interleaving them with sentinel 671 * values and skipping everything that's not a sentinel. 672 */ 673 it('testSkipField', function() { 674 var writer = new jspb.BinaryWriter(); 675 676 var sentinel = 123456789; 677 678 // Write varint fields of different sizes. 679 writer.writeInt32(1, sentinel); 680 writer.writeInt32(1, 1); 681 writer.writeInt32(1, 1000); 682 writer.writeInt32(1, 1000000); 683 writer.writeInt32(1, 1000000000); 684 685 // Write fixed 64-bit encoded fields. 686 writer.writeInt32(2, sentinel); 687 writer.writeDouble(2, 1); 688 writer.writeFixed64(2, 1); 689 writer.writeSfixed64(2, 1); 690 691 // Write fixed 32-bit encoded fields. 692 writer.writeInt32(3, sentinel); 693 writer.writeFloat(3, 1); 694 writer.writeFixed32(3, 1); 695 writer.writeSfixed32(3, 1); 696 697 // Write delimited fields. 698 writer.writeInt32(4, sentinel); 699 writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 700 writer.writeString(4, 'The quick brown fox jumps over the lazy dog'); 701 702 // Write a group with a nested group inside. 703 writer.writeInt32(5, sentinel); 704 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 705 writer.writeGroup(5, dummyMessage, function() { 706 writer.writeInt64(42, 42); 707 writer.writeGroup(6, dummyMessage, function() { 708 writer.writeInt64(84, 42); 709 }); 710 }); 711 712 // Write final sentinel. 713 writer.writeInt32(6, sentinel); 714 715 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 716 717 function skip(field, count) { 718 for (var i = 0; i < count; i++) { 719 reader.nextField(); 720 if (field != reader.getFieldNumber()) throw 'fail!'; 721 reader.skipField(); 722 } 723 } 724 725 reader.nextField(); 726 assertEquals(1, reader.getFieldNumber()); 727 assertEquals(sentinel, reader.readInt32()); 728 skip(1, 4); 729 730 reader.nextField(); 731 assertEquals(2, reader.getFieldNumber()); 732 assertEquals(sentinel, reader.readInt32()); 733 skip(2, 3); 734 735 reader.nextField(); 736 assertEquals(3, reader.getFieldNumber()); 737 assertEquals(sentinel, reader.readInt32()); 738 skip(3, 3); 739 740 reader.nextField(); 741 assertEquals(4, reader.getFieldNumber()); 742 assertEquals(sentinel, reader.readInt32()); 743 skip(4, 2); 744 745 reader.nextField(); 746 assertEquals(5, reader.getFieldNumber()); 747 assertEquals(sentinel, reader.readInt32()); 748 skip(5, 1); 749 750 reader.nextField(); 751 assertEquals(6, reader.getFieldNumber()); 752 assertEquals(sentinel, reader.readInt32()); 753 }); 754 755 756 /** 757 * Tests packed fields. 758 */ 759 it('testPackedFields', function() { 760 var writer = new jspb.BinaryWriter(); 761 762 var sentinel = 123456789; 763 764 var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 765 var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]; 766 var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; 767 var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; 768 var boolData = [true, false, true, true, false, false, true, false]; 769 770 for (var i = 0; i < floatData.length; i++) { 771 floatData[i] = truncate(floatData[i]); 772 } 773 774 writer.writeInt32(1, sentinel); 775 776 writer.writePackedInt32(2, signedData); 777 writer.writePackedInt64(2, signedData); 778 writer.writePackedUint32(2, unsignedData); 779 writer.writePackedUint64(2, unsignedData); 780 writer.writePackedSint32(2, signedData); 781 writer.writePackedSint64(2, signedData); 782 writer.writePackedFixed32(2, unsignedData); 783 writer.writePackedFixed64(2, unsignedData); 784 writer.writePackedSfixed32(2, signedData); 785 writer.writePackedSfixed64(2, signedData); 786 writer.writePackedFloat(2, floatData); 787 writer.writePackedDouble(2, doubleData); 788 writer.writePackedBool(2, boolData); 789 writer.writePackedEnum(2, unsignedData); 790 791 writer.writeInt32(3, sentinel); 792 793 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 794 795 reader.nextField(); 796 assertEquals(sentinel, reader.readInt32()); 797 798 reader.nextField(); 799 assertElementsEquals(reader.readPackedInt32(), signedData); 800 801 reader.nextField(); 802 assertElementsEquals(reader.readPackedInt64(), signedData); 803 804 reader.nextField(); 805 assertElementsEquals(reader.readPackedUint32(), unsignedData); 806 807 reader.nextField(); 808 assertElementsEquals(reader.readPackedUint64(), unsignedData); 809 810 reader.nextField(); 811 assertElementsEquals(reader.readPackedSint32(), signedData); 812 813 reader.nextField(); 814 assertElementsEquals(reader.readPackedSint64(), signedData); 815 816 reader.nextField(); 817 assertElementsEquals(reader.readPackedFixed32(), unsignedData); 818 819 reader.nextField(); 820 assertElementsEquals(reader.readPackedFixed64(), unsignedData); 821 822 reader.nextField(); 823 assertElementsEquals(reader.readPackedSfixed32(), signedData); 824 825 reader.nextField(); 826 assertElementsEquals(reader.readPackedSfixed64(), signedData); 827 828 reader.nextField(); 829 assertElementsEquals(reader.readPackedFloat(), floatData); 830 831 reader.nextField(); 832 assertElementsEquals(reader.readPackedDouble(), doubleData); 833 834 reader.nextField(); 835 assertElementsEquals(reader.readPackedBool(), boolData); 836 837 reader.nextField(); 838 assertElementsEquals(reader.readPackedEnum(), unsignedData); 839 840 reader.nextField(); 841 assertEquals(sentinel, reader.readInt32()); 842 }); 843 844 845 /** 846 * Byte blobs inside nested messages should always have their byte offset set 847 * relative to the start of the outermost blob, not the start of their parent 848 * blob. 849 */ 850 it('testNestedBlobs', function() { 851 // Create a proto consisting of two nested messages, with the inner one 852 // containing a blob of bytes. 853 854 var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED; 855 var blob = [1, 2, 3, 4, 5]; 856 var writer = new jspb.BinaryWriter(); 857 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 858 859 writer.writeMessage(1, dummyMessage, function() { 860 writer.writeMessage(1, dummyMessage, function() { 861 writer.writeBytes(1, blob); 862 }); 863 }); 864 865 // Peel off the outer two message layers. Each layer should have two bytes 866 // of overhead, one for the field tag and one for the length of the inner 867 // blob. 868 869 var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer()); 870 assertEquals(fieldTag, decoder1.readUnsignedVarint32()); 871 assertEquals(blob.length + 4, decoder1.readUnsignedVarint32()); 872 873 var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4)); 874 assertEquals(fieldTag, decoder2.readUnsignedVarint32()); 875 assertEquals(blob.length + 2, decoder2.readUnsignedVarint32()); 876 877 assertEquals(fieldTag, decoder2.readUnsignedVarint32()); 878 assertEquals(blob.length, decoder2.readUnsignedVarint32()); 879 var bytes = decoder2.readBytes(blob.length); 880 881 assertElementsEquals(bytes, blob); 882 }); 883 884 885 /** 886 * Tests read callbacks. 887 */ 888 it('testReadCallbacks', function() { 889 var writer = new jspb.BinaryWriter(); 890 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 891 892 // Add an int, a submessage, and another int. 893 writer.writeInt32(1, 100); 894 895 writer.writeMessage(2, dummyMessage, function() { 896 writer.writeInt32(3, 300); 897 writer.writeInt32(4, 400); 898 writer.writeInt32(5, 500); 899 }); 900 901 writer.writeInt32(7, 700); 902 903 // Create the reader and register a custom read callback. 904 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 905 906 /** 907 * @param {!jspb.BinaryReader} reader 908 * @return {*} 909 */ 910 function readCallback(reader) { 911 reader.nextField(); 912 assertEquals(3, reader.getFieldNumber()); 913 assertEquals(300, reader.readInt32()); 914 915 reader.nextField(); 916 assertEquals(4, reader.getFieldNumber()); 917 assertEquals(400, reader.readInt32()); 918 919 reader.nextField(); 920 assertEquals(5, reader.getFieldNumber()); 921 assertEquals(500, reader.readInt32()); 922 923 assertEquals(false, reader.nextField()); 924 }; 925 926 reader.registerReadCallback('readCallback', readCallback); 927 928 // Read the container message. 929 reader.nextField(); 930 assertEquals(1, reader.getFieldNumber()); 931 assertEquals(100, reader.readInt32()); 932 933 reader.nextField(); 934 assertEquals(2, reader.getFieldNumber()); 935 reader.readMessage(dummyMessage, function() { 936 // Decode the embedded message using the registered callback. 937 reader.runReadCallback('readCallback'); 938 }); 939 940 reader.nextField(); 941 assertEquals(7, reader.getFieldNumber()); 942 assertEquals(700, reader.readInt32()); 943 944 assertEquals(false, reader.nextField()); 945 }); 946}); 947