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