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 * Tests reading 64-bit integers as split values. 412 */ 413 it('handles split 64 fields', function() { 414 var writer = new jspb.BinaryWriter(); 415 writer.writeInt64String(1, '4294967296'); 416 writer.writeSfixed64String(2, '4294967298'); 417 writer.writeInt64String(3, '3'); // 3 is the zig-zag encoding of -2. 418 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 419 420 function rejoin(lowBits, highBits) { 421 return highBits * 2 ** 32 + (lowBits >>> 0); 422 } 423 reader.nextField(); 424 expect(reader.getFieldNumber()).toEqual(1); 425 expect(reader.readSplitVarint64(rejoin)).toEqual(0x100000000); 426 427 reader.nextField(); 428 expect(reader.getFieldNumber()).toEqual(2); 429 expect(reader.readSplitFixed64(rejoin)).toEqual(0x100000002); 430 431 reader.nextField(); 432 expect(reader.getFieldNumber()).toEqual(3); 433 expect(reader.readSplitZigzagVarint64(rejoin)).toEqual(-2); 434 }); 435 436 /** 437 * Tests 64-bit fields that are handled as strings. 438 */ 439 it('testStringInt64Fields', function() { 440 var writer = new jspb.BinaryWriter(); 441 442 var testSignedData = [ 443 '2730538252207801776', 444 '-2688470994844604560', 445 '3398529779486536359', 446 '3568577411627971000', 447 '272477188847484900', 448 '-6649058714086158188', 449 '-7695254765712060806', 450 '-4525541438037104029', 451 '-4993706538836508568', 452 '4990160321893729138' 453 ]; 454 var testUnsignedData = [ 455 '7822732630241694882', 456 '6753602971916687352', 457 '2399935075244442116', 458 '8724292567325338867', 459 '16948784802625696584', 460 '4136275908516066934', 461 '3575388346793700364', 462 '5167142028379259461', 463 '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, 492 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); 493 494 doTestSignedField_( 495 jspb.BinaryReader.prototype.readSint64, 496 jspb.BinaryWriter.prototype.writeSint64, 497 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); 498 499 doTestSignedField_( 500 jspb.BinaryReader.prototype.readSintHash64, 501 jspb.BinaryWriter.prototype.writeSintHash64, 1, -Math.pow(2, 63), 502 Math.pow(2, 63) - 513, jspb.utils.numberToHash64); 503 }); 504 505 506 /** 507 * Tests fields that use fixed-length encoding. 508 */ 509 it('testFixedFields', function() { 510 doTestUnsignedField_( 511 jspb.BinaryReader.prototype.readFixed32, 512 jspb.BinaryWriter.prototype.writeFixed32, 513 1, Math.pow(2, 32) - 1, Math.round); 514 515 doTestUnsignedField_( 516 jspb.BinaryReader.prototype.readFixed64, 517 jspb.BinaryWriter.prototype.writeFixed64, 518 1, Math.pow(2, 64) - 1025, Math.round); 519 520 doTestSignedField_( 521 jspb.BinaryReader.prototype.readSfixed32, 522 jspb.BinaryWriter.prototype.writeSfixed32, 523 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); 524 525 doTestSignedField_( 526 jspb.BinaryReader.prototype.readSfixed64, 527 jspb.BinaryWriter.prototype.writeSfixed64, 528 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); 529 }); 530 531 532 /** 533 * Tests floating point fields. 534 */ 535 it('testFloatFields', function() { 536 doTestSignedField_( 537 jspb.BinaryReader.prototype.readFloat, 538 jspb.BinaryWriter.prototype.writeFloat, 539 jspb.BinaryConstants.FLOAT32_MIN, 540 -jspb.BinaryConstants.FLOAT32_MAX, 541 jspb.BinaryConstants.FLOAT32_MAX, 542 truncate); 543 544 doTestSignedField_( 545 jspb.BinaryReader.prototype.readDouble, 546 jspb.BinaryWriter.prototype.writeDouble, 547 jspb.BinaryConstants.FLOAT64_EPS * 10, 548 -jspb.BinaryConstants.FLOAT64_MIN, 549 jspb.BinaryConstants.FLOAT64_MIN, 550 function(x) { return x; }); 551 }); 552 553 554 /** 555 * Tests length-delimited string fields. 556 */ 557 it('testStringFields', function() { 558 var s1 = 'The quick brown fox jumps over the lazy dog.'; 559 var s2 = '人人生而自由,在尊嚴和權利上一律平等。'; 560 561 var writer = new jspb.BinaryWriter(); 562 563 writer.writeString(1, s1); 564 writer.writeString(2, s2); 565 566 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 567 568 reader.nextField(); 569 assertEquals(1, reader.getFieldNumber()); 570 assertEquals(s1, reader.readString()); 571 572 reader.nextField(); 573 assertEquals(2, reader.getFieldNumber()); 574 assertEquals(s2, reader.readString()); 575 }); 576 577 578 /** 579 * Tests length-delimited byte fields. 580 */ 581 it('testByteFields', function() { 582 var message = []; 583 var lowerLimit = 1; 584 var upperLimit = 256; 585 var scale = 1.1; 586 587 var writer = new jspb.BinaryWriter(); 588 589 for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) { 590 var len = Math.round(cursor); 591 var bytes = []; 592 for (var i = 0; i < len; i++) bytes.push(i % 256); 593 594 writer.writeBytes(len, bytes); 595 } 596 597 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 598 599 for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) { 600 var len = Math.round(cursor); 601 if (len != reader.getFieldNumber()) throw 'fail!'; 602 603 var bytes = reader.readBytes(); 604 if (len != bytes.length) throw 'fail!'; 605 for (var i = 0; i < bytes.length; i++) { 606 if (i % 256 != bytes[i]) throw 'fail!'; 607 } 608 } 609 }); 610 611 612 /** 613 * Tests nested messages. 614 */ 615 it('testNesting', function() { 616 var writer = new jspb.BinaryWriter(); 617 var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); 618 619 writer.writeInt32(1, 100); 620 621 // Add one message with 3 int fields. 622 writer.writeMessage(2, dummyMessage, function() { 623 writer.writeInt32(3, 300); 624 writer.writeInt32(4, 400); 625 writer.writeInt32(5, 500); 626 }); 627 628 // Add one empty message. 629 writer.writeMessage(6, dummyMessage, goog.nullFunction); 630 631 writer.writeInt32(7, 700); 632 633 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 634 635 // Validate outermost message. 636 637 reader.nextField(); 638 assertEquals(1, reader.getFieldNumber()); 639 assertEquals(100, reader.readInt32()); 640 641 reader.nextField(); 642 assertEquals(2, reader.getFieldNumber()); 643 reader.readMessage(dummyMessage, function() { 644 // Validate embedded message 1. 645 reader.nextField(); 646 assertEquals(3, reader.getFieldNumber()); 647 assertEquals(300, reader.readInt32()); 648 649 reader.nextField(); 650 assertEquals(4, reader.getFieldNumber()); 651 assertEquals(400, reader.readInt32()); 652 653 reader.nextField(); 654 assertEquals(5, reader.getFieldNumber()); 655 assertEquals(500, reader.readInt32()); 656 657 assertEquals(false, reader.nextField()); 658 }); 659 660 reader.nextField(); 661 assertEquals(6, reader.getFieldNumber()); 662 reader.readMessage(dummyMessage, function() { 663 // Validate embedded message 2. 664 665 assertEquals(false, reader.nextField()); 666 }); 667 668 reader.nextField(); 669 assertEquals(7, reader.getFieldNumber()); 670 assertEquals(700, reader.readInt32()); 671 672 assertEquals(false, reader.nextField()); 673 }); 674 675 /** 676 * Tests skipping fields of each type by interleaving them with sentinel 677 * values and skipping everything that's not a sentinel. 678 */ 679 it('testSkipField', function() { 680 var writer = new jspb.BinaryWriter(); 681 682 var sentinel = 123456789; 683 684 // Write varint fields of different sizes. 685 writer.writeInt32(1, sentinel); 686 writer.writeInt32(1, 1); 687 writer.writeInt32(1, 1000); 688 writer.writeInt32(1, 1000000); 689 writer.writeInt32(1, 1000000000); 690 691 // Write fixed 64-bit encoded fields. 692 writer.writeInt32(2, sentinel); 693 writer.writeDouble(2, 1); 694 writer.writeFixed64(2, 1); 695 writer.writeSfixed64(2, 1); 696 697 // Write fixed 32-bit encoded fields. 698 writer.writeInt32(3, sentinel); 699 writer.writeFloat(3, 1); 700 writer.writeFixed32(3, 1); 701 writer.writeSfixed32(3, 1); 702 703 // Write delimited fields. 704 writer.writeInt32(4, sentinel); 705 writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 706 writer.writeString(4, 'The quick brown fox jumps over the lazy dog'); 707 708 // Write a group with a nested group inside. 709 writer.writeInt32(5, sentinel); 710 var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); 711 writer.writeGroup(5, dummyMessage, function() { 712 // Previously the skipGroup implementation was wrong, which only consume 713 // the decoder by nextField. This case is for making the previous 714 // implementation failed in skipGroup by an early end group tag. 715 // The reason is 44 = 5 * 8 + 4, this will be translated in to a field 716 // with number 5 and with type 4 (end group) 717 writer.writeInt64(44, 44); 718 // This will make previous implementation failed by invalid tag (7). 719 writer.writeInt64(42, 47); 720 writer.writeInt64(42, 42); 721 // This is for making the previous implementation failed by an invalid 722 // varint. The bytes have at least 9 consecutive minus byte, which will 723 // fail in this.nextField for previous implementation. 724 writer.writeBytes(43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); 725 writer.writeGroup(6, dummyMessage, function() { 726 writer.writeInt64(84, 42); 727 writer.writeInt64(84, 44); 728 writer.writeBytes( 729 43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); 730 }); 731 }); 732 733 // Write final sentinel. 734 writer.writeInt32(6, sentinel); 735 736 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 737 738 function skip(field, count) { 739 for (var i = 0; i < count; i++) { 740 reader.nextField(); 741 if (field != reader.getFieldNumber()) throw 'fail!'; 742 reader.skipField(); 743 } 744 } 745 746 reader.nextField(); 747 assertEquals(1, reader.getFieldNumber()); 748 assertEquals(sentinel, reader.readInt32()); 749 skip(1, 4); 750 751 reader.nextField(); 752 assertEquals(2, reader.getFieldNumber()); 753 assertEquals(sentinel, reader.readInt32()); 754 skip(2, 3); 755 756 reader.nextField(); 757 assertEquals(3, reader.getFieldNumber()); 758 assertEquals(sentinel, reader.readInt32()); 759 skip(3, 3); 760 761 reader.nextField(); 762 assertEquals(4, reader.getFieldNumber()); 763 assertEquals(sentinel, reader.readInt32()); 764 skip(4, 2); 765 766 reader.nextField(); 767 assertEquals(5, reader.getFieldNumber()); 768 assertEquals(sentinel, reader.readInt32()); 769 skip(5, 1); 770 771 reader.nextField(); 772 assertEquals(6, reader.getFieldNumber()); 773 assertEquals(sentinel, reader.readInt32()); 774 }); 775 776 777 /** 778 * Tests packed fields. 779 */ 780 it('testPackedFields', function() { 781 var writer = new jspb.BinaryWriter(); 782 783 var sentinel = 123456789; 784 785 var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 786 var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]; 787 var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; 788 var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; 789 var boolData = [true, false, true, true, false, false, true, false]; 790 791 for (var i = 0; i < floatData.length; i++) { 792 floatData[i] = truncate(floatData[i]); 793 } 794 795 writer.writeInt32(1, sentinel); 796 797 writer.writePackedInt32(2, signedData); 798 writer.writePackedInt64(2, signedData); 799 writer.writePackedUint32(2, unsignedData); 800 writer.writePackedUint64(2, unsignedData); 801 writer.writePackedSint32(2, signedData); 802 writer.writePackedSint64(2, signedData); 803 writer.writePackedFixed32(2, unsignedData); 804 writer.writePackedFixed64(2, unsignedData); 805 writer.writePackedSfixed32(2, signedData); 806 writer.writePackedSfixed64(2, signedData); 807 writer.writePackedFloat(2, floatData); 808 writer.writePackedDouble(2, doubleData); 809 writer.writePackedBool(2, boolData); 810 writer.writePackedEnum(2, unsignedData); 811 812 writer.writeInt32(3, sentinel); 813 814 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 815 816 reader.nextField(); 817 assertEquals(sentinel, reader.readInt32()); 818 819 reader.nextField(); 820 assertElementsEquals(reader.readPackedInt32(), signedData); 821 822 reader.nextField(); 823 assertElementsEquals(reader.readPackedInt64(), signedData); 824 825 reader.nextField(); 826 assertElementsEquals(reader.readPackedUint32(), unsignedData); 827 828 reader.nextField(); 829 assertElementsEquals(reader.readPackedUint64(), unsignedData); 830 831 reader.nextField(); 832 assertElementsEquals(reader.readPackedSint32(), signedData); 833 834 reader.nextField(); 835 assertElementsEquals(reader.readPackedSint64(), signedData); 836 837 reader.nextField(); 838 assertElementsEquals(reader.readPackedFixed32(), unsignedData); 839 840 reader.nextField(); 841 assertElementsEquals(reader.readPackedFixed64(), unsignedData); 842 843 reader.nextField(); 844 assertElementsEquals(reader.readPackedSfixed32(), signedData); 845 846 reader.nextField(); 847 assertElementsEquals(reader.readPackedSfixed64(), signedData); 848 849 reader.nextField(); 850 assertElementsEquals(reader.readPackedFloat(), floatData); 851 852 reader.nextField(); 853 assertElementsEquals(reader.readPackedDouble(), doubleData); 854 855 reader.nextField(); 856 assertElementsEquals(reader.readPackedBool(), boolData); 857 858 reader.nextField(); 859 assertElementsEquals(reader.readPackedEnum(), unsignedData); 860 861 reader.nextField(); 862 assertEquals(sentinel, reader.readInt32()); 863 }); 864 865 866 /** 867 * Byte blobs inside nested messages should always have their byte offset set 868 * relative to the start of the outermost blob, not the start of their parent 869 * blob. 870 */ 871 it('testNestedBlobs', function() { 872 // Create a proto consisting of two nested messages, with the inner one 873 // containing a blob of bytes. 874 875 var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED; 876 var blob = [1, 2, 3, 4, 5]; 877 var writer = new jspb.BinaryWriter(); 878 var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); 879 880 writer.writeMessage(1, dummyMessage, function() { 881 writer.writeMessage(1, dummyMessage, function() { 882 writer.writeBytes(1, blob); 883 }); 884 }); 885 886 // Peel off the outer two message layers. Each layer should have two bytes 887 // of overhead, one for the field tag and one for the length of the inner 888 // blob. 889 890 var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer()); 891 assertEquals(fieldTag, decoder1.readUnsignedVarint32()); 892 assertEquals(blob.length + 4, decoder1.readUnsignedVarint32()); 893 894 var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4)); 895 assertEquals(fieldTag, decoder2.readUnsignedVarint32()); 896 assertEquals(blob.length + 2, decoder2.readUnsignedVarint32()); 897 898 assertEquals(fieldTag, decoder2.readUnsignedVarint32()); 899 assertEquals(blob.length, decoder2.readUnsignedVarint32()); 900 var bytes = decoder2.readBytes(blob.length); 901 902 assertElementsEquals(bytes, blob); 903 }); 904 905 906 /** 907 * Tests read callbacks. 908 */ 909 it('testReadCallbacks', function() { 910 var writer = new jspb.BinaryWriter(); 911 var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); 912 913 // Add an int, a submessage, and another int. 914 writer.writeInt32(1, 100); 915 916 writer.writeMessage(2, dummyMessage, function() { 917 writer.writeInt32(3, 300); 918 writer.writeInt32(4, 400); 919 writer.writeInt32(5, 500); 920 }); 921 922 writer.writeInt32(7, 700); 923 924 // Create the reader and register a custom read callback. 925 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 926 927 /** 928 * @param {!jspb.BinaryReader} reader 929 * @return {*} 930 */ 931 function readCallback(reader) { 932 reader.nextField(); 933 assertEquals(3, reader.getFieldNumber()); 934 assertEquals(300, reader.readInt32()); 935 936 reader.nextField(); 937 assertEquals(4, reader.getFieldNumber()); 938 assertEquals(400, reader.readInt32()); 939 940 reader.nextField(); 941 assertEquals(5, reader.getFieldNumber()); 942 assertEquals(500, reader.readInt32()); 943 944 assertEquals(false, reader.nextField()); 945 }; 946 947 reader.registerReadCallback('readCallback', readCallback); 948 949 // Read the container message. 950 reader.nextField(); 951 assertEquals(1, reader.getFieldNumber()); 952 assertEquals(100, reader.readInt32()); 953 954 reader.nextField(); 955 assertEquals(2, reader.getFieldNumber()); 956 reader.readMessage(dummyMessage, function() { 957 // Decode the embedded message using the registered callback. 958 reader.runReadCallback('readCallback'); 959 }); 960 961 reader.nextField(); 962 assertEquals(7, reader.getFieldNumber()); 963 assertEquals(700, reader.readInt32()); 964 965 assertEquals(false, reader.nextField()); 966 }); 967}); 968