Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
17 KiB
481 lines
17 KiB
// Protocol Buffers - Google's data interchange format |
|
// Copyright 2008 Google Inc. All rights reserved. |
|
// https://developers.google.com/protocol-buffers/ |
|
// |
|
// Redistribution and use in source and binary forms, with or without |
|
// modification, are permitted provided that the following conditions are |
|
// met: |
|
// |
|
// * Redistributions of source code must retain the above copyright |
|
// notice, this list of conditions and the following disclaimer. |
|
// * Redistributions in binary form must reproduce the above |
|
// copyright notice, this list of conditions and the following disclaimer |
|
// in the documentation and/or other materials provided with the |
|
// distribution. |
|
// * Neither the name of Google Inc. nor the names of its |
|
// contributors may be used to endorse or promote products derived from |
|
// this software without specific prior written permission. |
|
// |
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
/** |
|
* @fileoverview Test cases for jspb's binary protocol buffer decoder. |
|
* |
|
* There are two particular magic numbers that need to be pointed out - |
|
* 2^64-1025 is the largest number representable as both a double and an |
|
* unsigned 64-bit integer, and 2^63-513 is the largest number representable as |
|
* both a double and a signed 64-bit integer. |
|
* |
|
* Test suite is written using Jasmine -- see http://jasmine.github.io/ |
|
* |
|
* @author aappleby@google.com (Austin Appleby) |
|
*/ |
|
|
|
goog.require('goog.testing.asserts'); |
|
goog.require('jspb.BinaryConstants'); |
|
goog.require('jspb.BinaryDecoder'); |
|
goog.require('jspb.BinaryEncoder'); |
|
goog.require('jspb.utils'); |
|
|
|
|
|
/** |
|
* Tests encoding and decoding of unsigned types. |
|
* @param {Function} readValue |
|
* @param {Function} writeValue |
|
* @param {number} epsilon |
|
* @param {number} upperLimit |
|
* @param {Function} filter |
|
* @suppress {missingProperties|visibility} |
|
*/ |
|
function doTestUnsignedValue(readValue, |
|
writeValue, epsilon, upperLimit, filter) { |
|
var encoder = new jspb.BinaryEncoder(); |
|
|
|
// Encode zero and limits. |
|
writeValue.call(encoder, filter(0)); |
|
writeValue.call(encoder, filter(epsilon)); |
|
writeValue.call(encoder, filter(upperLimit)); |
|
|
|
// Encode positive values. |
|
for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { |
|
writeValue.call(encoder, filter(cursor)); |
|
} |
|
|
|
var decoder = jspb.BinaryDecoder.alloc(encoder.end()); |
|
|
|
// Check zero and limits. |
|
assertEquals(filter(0), readValue.call(decoder)); |
|
assertEquals(filter(epsilon), readValue.call(decoder)); |
|
assertEquals(filter(upperLimit), readValue.call(decoder)); |
|
|
|
// Check positive values. |
|
for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { |
|
if (filter(cursor) != readValue.call(decoder)) throw 'fail!'; |
|
} |
|
|
|
// Encoding values outside the valid range should assert. |
|
assertThrows(function() {writeValue.call(encoder, -1);}); |
|
assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); |
|
} |
|
|
|
|
|
/** |
|
* Tests encoding and decoding of signed types. |
|
* @param {Function} readValue |
|
* @param {Function} writeValue |
|
* @param {number} epsilon |
|
* @param {number} lowerLimit |
|
* @param {number} upperLimit |
|
* @param {Function} filter |
|
* @suppress {missingProperties} |
|
*/ |
|
function doTestSignedValue(readValue, |
|
writeValue, epsilon, lowerLimit, upperLimit, filter) { |
|
var encoder = new jspb.BinaryEncoder(); |
|
|
|
// Encode zero and limits. |
|
writeValue.call(encoder, filter(lowerLimit)); |
|
writeValue.call(encoder, filter(-epsilon)); |
|
writeValue.call(encoder, filter(0)); |
|
writeValue.call(encoder, filter(epsilon)); |
|
writeValue.call(encoder, filter(upperLimit)); |
|
|
|
var inputValues = []; |
|
|
|
// Encode negative values. |
|
for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { |
|
var val = filter(cursor); |
|
writeValue.call(encoder, val); |
|
inputValues.push(val); |
|
} |
|
|
|
// Encode positive values. |
|
for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { |
|
var val = filter(cursor); |
|
writeValue.call(encoder, val); |
|
inputValues.push(val); |
|
} |
|
|
|
var decoder = jspb.BinaryDecoder.alloc(encoder.end()); |
|
|
|
// Check zero and limits. |
|
assertEquals(filter(lowerLimit), readValue.call(decoder)); |
|
assertEquals(filter(-epsilon), readValue.call(decoder)); |
|
assertEquals(filter(0), readValue.call(decoder)); |
|
assertEquals(filter(epsilon), readValue.call(decoder)); |
|
assertEquals(filter(upperLimit), readValue.call(decoder)); |
|
|
|
// Verify decoded values. |
|
for (var i = 0; i < inputValues.length; i++) { |
|
assertEquals(inputValues[i], readValue.call(decoder)); |
|
} |
|
|
|
// Encoding values outside the valid range should assert. |
|
var pastLowerLimit = lowerLimit * 1.1; |
|
var pastUpperLimit = upperLimit * 1.1; |
|
if (pastLowerLimit !== -Infinity) { |
|
expect(() => void writeValue.call(encoder, pastLowerLimit)).toThrow(); |
|
} |
|
if (pastUpperLimit !== Infinity) { |
|
expect(() => void writeValue.call(encoder, pastUpperLimit)).toThrow(); |
|
} |
|
} |
|
|
|
describe('binaryDecoderTest', function() { |
|
/** |
|
* Tests the decoder instance cache. |
|
*/ |
|
it('testInstanceCache', /** @suppress {visibility} */ function() { |
|
// Empty the instance caches. |
|
jspb.BinaryDecoder.instanceCache_ = []; |
|
|
|
// Allocating and then freeing a decoder should put it in the instance |
|
// cache. |
|
jspb.BinaryDecoder.alloc().free(); |
|
|
|
assertEquals(1, jspb.BinaryDecoder.instanceCache_.length); |
|
|
|
// Allocating and then freeing three decoders should leave us with three in |
|
// the cache. |
|
|
|
var decoder1 = jspb.BinaryDecoder.alloc(); |
|
var decoder2 = jspb.BinaryDecoder.alloc(); |
|
var decoder3 = jspb.BinaryDecoder.alloc(); |
|
decoder1.free(); |
|
decoder2.free(); |
|
decoder3.free(); |
|
|
|
assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); |
|
}); |
|
|
|
|
|
describe('varint64', function() { |
|
var /** !jspb.BinaryEncoder */ encoder; |
|
var /** !jspb.BinaryDecoder */ decoder; |
|
|
|
var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00, |
|
0x00, 0x00, 0x00, 0x00); |
|
var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00, |
|
0x00, 0x00, 0x00, 0x00); |
|
var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78, |
|
0x87, 0x65, 0x43, 0x21); |
|
var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, |
|
0xFF, 0xFF, 0xFF, 0xFF); |
|
beforeEach(function() { |
|
encoder = new jspb.BinaryEncoder(); |
|
|
|
encoder.writeVarintHash64(hashA); |
|
encoder.writeVarintHash64(hashB); |
|
encoder.writeVarintHash64(hashC); |
|
encoder.writeVarintHash64(hashD); |
|
|
|
encoder.writeFixedHash64(hashA); |
|
encoder.writeFixedHash64(hashB); |
|
encoder.writeFixedHash64(hashC); |
|
encoder.writeFixedHash64(hashD); |
|
|
|
decoder = jspb.BinaryDecoder.alloc(encoder.end()); |
|
}); |
|
|
|
it('reads 64-bit integers as hash strings', function() { |
|
assertEquals(hashA, decoder.readVarintHash64()); |
|
assertEquals(hashB, decoder.readVarintHash64()); |
|
assertEquals(hashC, decoder.readVarintHash64()); |
|
assertEquals(hashD, decoder.readVarintHash64()); |
|
|
|
assertEquals(hashA, decoder.readFixedHash64()); |
|
assertEquals(hashB, decoder.readFixedHash64()); |
|
assertEquals(hashC, decoder.readFixedHash64()); |
|
assertEquals(hashD, decoder.readFixedHash64()); |
|
}); |
|
|
|
it('reads split 64 bit integers', function() { |
|
function hexJoin(bitsLow, bitsHigh) { |
|
return `0x${(bitsHigh >>> 0).toString(16)}:0x${ |
|
(bitsLow >>> 0).toString(16)}`; |
|
} |
|
function hexJoinHash(hash64) { |
|
jspb.utils.splitHash64(hash64); |
|
return hexJoin(jspb.utils.split64Low, jspb.utils.split64High); |
|
} |
|
|
|
expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashA)); |
|
expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashB)); |
|
expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashC)); |
|
expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashD)); |
|
|
|
expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashA)); |
|
expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashB)); |
|
expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashC)); |
|
expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashD)); |
|
}); |
|
}); |
|
|
|
describe('sint64', function() { |
|
var /** !jspb.BinaryDecoder */ decoder; |
|
|
|
var hashA = |
|
String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); |
|
var hashB = |
|
String.fromCharCode(0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); |
|
var hashC = |
|
String.fromCharCode(0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21); |
|
var hashD = |
|
String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); |
|
beforeEach(function() { |
|
var encoder = new jspb.BinaryEncoder(); |
|
|
|
encoder.writeZigzagVarintHash64(hashA); |
|
encoder.writeZigzagVarintHash64(hashB); |
|
encoder.writeZigzagVarintHash64(hashC); |
|
encoder.writeZigzagVarintHash64(hashD); |
|
|
|
decoder = jspb.BinaryDecoder.alloc(encoder.end()); |
|
}); |
|
|
|
it('reads 64-bit integers as decimal strings', function() { |
|
const signed = true; |
|
expect(decoder.readZigzagVarint64String()) |
|
.toEqual(jspb.utils.hash64ToDecimalString(hashA, signed)); |
|
expect(decoder.readZigzagVarint64String()) |
|
.toEqual(jspb.utils.hash64ToDecimalString(hashB, signed)); |
|
expect(decoder.readZigzagVarint64String()) |
|
.toEqual(jspb.utils.hash64ToDecimalString(hashC, signed)); |
|
expect(decoder.readZigzagVarint64String()) |
|
.toEqual(jspb.utils.hash64ToDecimalString(hashD, signed)); |
|
}); |
|
|
|
it('reads 64-bit integers as hash strings', function() { |
|
expect(decoder.readZigzagVarintHash64()).toEqual(hashA); |
|
expect(decoder.readZigzagVarintHash64()).toEqual(hashB); |
|
expect(decoder.readZigzagVarintHash64()).toEqual(hashC); |
|
expect(decoder.readZigzagVarintHash64()).toEqual(hashD); |
|
}); |
|
|
|
it('reads split 64 bit zigzag integers', function() { |
|
function hexJoin(bitsLow, bitsHigh) { |
|
return `0x${(bitsHigh >>> 0).toString(16)}:0x${ |
|
(bitsLow >>> 0).toString(16)}`; |
|
} |
|
function hexJoinHash(hash64) { |
|
jspb.utils.splitHash64(hash64); |
|
return hexJoin(jspb.utils.split64Low, jspb.utils.split64High); |
|
} |
|
|
|
expect(decoder.readSplitZigzagVarint64(hexJoin)) |
|
.toEqual(hexJoinHash(hashA)); |
|
expect(decoder.readSplitZigzagVarint64(hexJoin)) |
|
.toEqual(hexJoinHash(hashB)); |
|
expect(decoder.readSplitZigzagVarint64(hexJoin)) |
|
.toEqual(hexJoinHash(hashC)); |
|
expect(decoder.readSplitZigzagVarint64(hexJoin)) |
|
.toEqual(hexJoinHash(hashD)); |
|
}); |
|
|
|
it('does zigzag encoding properly', function() { |
|
// Test cases directly from the protobuf dev guide. |
|
// https://engdoc.corp.google.com/eng/howto/protocolbuffers/developerguide/encoding.shtml?cl=head#types |
|
var testCases = [ |
|
{original: '0', zigzag: '0'}, |
|
{original: '-1', zigzag: '1'}, |
|
{original: '1', zigzag: '2'}, |
|
{original: '-2', zigzag: '3'}, |
|
{original: '2147483647', zigzag: '4294967294'}, |
|
{original: '-2147483648', zigzag: '4294967295'}, |
|
// 64-bit extremes, not in dev guide. |
|
{original: '9223372036854775807', zigzag: '18446744073709551614'}, |
|
{original: '-9223372036854775808', zigzag: '18446744073709551615'}, |
|
// None of the above catch: bitsLow < 0 && bitsHigh > 0 && bitsHigh < |
|
// 0x1FFFFF. The following used to be broken. |
|
{original: '72000000000', zigzag: '144000000000'}, |
|
]; |
|
var encoder = new jspb.BinaryEncoder(); |
|
testCases.forEach(function(c) { |
|
encoder.writeZigzagVarint64String(c.original); |
|
}); |
|
var buffer = encoder.end(); |
|
var zigzagDecoder = jspb.BinaryDecoder.alloc(buffer); |
|
var varintDecoder = jspb.BinaryDecoder.alloc(buffer); |
|
testCases.forEach(function(c) { |
|
expect(zigzagDecoder.readZigzagVarint64String()).toEqual(c.original); |
|
expect(varintDecoder.readUnsignedVarint64String()).toEqual(c.zigzag); |
|
}); |
|
}); |
|
}); |
|
|
|
/** |
|
* Tests reading and writing large strings |
|
*/ |
|
it('testLargeStrings', function() { |
|
var encoder = new jspb.BinaryEncoder(); |
|
|
|
var len = 150000; |
|
var long_string = ''; |
|
for (var i = 0; i < len; i++) { |
|
long_string += 'a'; |
|
} |
|
|
|
encoder.writeString(long_string); |
|
|
|
var decoder = jspb.BinaryDecoder.alloc(encoder.end()); |
|
|
|
assertEquals(long_string, decoder.readString(len)); |
|
}); |
|
|
|
/** |
|
* Test encoding and decoding utf-8. |
|
*/ |
|
it('testUtf8', function() { |
|
var encoder = new jspb.BinaryEncoder(); |
|
|
|
var ascii = "ASCII should work in 3, 2, 1..."; |
|
var utf8_two_bytes = "©"; |
|
var utf8_three_bytes = "❄"; |
|
var utf8_four_bytes = "😁"; |
|
|
|
encoder.writeString(ascii); |
|
encoder.writeString(utf8_two_bytes); |
|
encoder.writeString(utf8_three_bytes); |
|
encoder.writeString(utf8_four_bytes); |
|
|
|
var decoder = jspb.BinaryDecoder.alloc(encoder.end()); |
|
|
|
assertEquals(ascii, decoder.readString(ascii.length)); |
|
assertEquals(utf8_two_bytes, decoder.readString(utf8_two_bytes.length)); |
|
assertEquals(utf8_three_bytes, decoder.readString(utf8_three_bytes.length)); |
|
assertEquals(utf8_four_bytes, decoder.readString(utf8_four_bytes.length)); |
|
}); |
|
|
|
/** |
|
* Verifies that misuse of the decoder class triggers assertions. |
|
*/ |
|
it('testDecodeErrors', function() { |
|
// Reading a value past the end of the stream should trigger an assertion. |
|
var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]); |
|
assertThrows(function() {decoder.readUint64()}); |
|
|
|
// Overlong varints should trigger assertions. |
|
decoder.setBlock([255, 255, 255, 255, 255, 255, |
|
255, 255, 255, 255, 255, 0]); |
|
assertThrows(function() {decoder.readUnsignedVarint64()}); |
|
decoder.reset(); |
|
assertThrows(function() {decoder.readSignedVarint64()}); |
|
decoder.reset(); |
|
assertThrows(function() {decoder.readZigzagVarint64()}); |
|
decoder.reset(); |
|
assertThrows(function() {decoder.readUnsignedVarint32()}); |
|
}); |
|
|
|
|
|
/** |
|
* Tests encoding and decoding of unsigned integers. |
|
*/ |
|
it('testUnsignedIntegers', function() { |
|
doTestUnsignedValue( |
|
jspb.BinaryDecoder.prototype.readUint8, |
|
jspb.BinaryEncoder.prototype.writeUint8, |
|
1, 0xFF, Math.round); |
|
|
|
doTestUnsignedValue( |
|
jspb.BinaryDecoder.prototype.readUint16, |
|
jspb.BinaryEncoder.prototype.writeUint16, |
|
1, 0xFFFF, Math.round); |
|
|
|
doTestUnsignedValue( |
|
jspb.BinaryDecoder.prototype.readUint32, |
|
jspb.BinaryEncoder.prototype.writeUint32, |
|
1, 0xFFFFFFFF, Math.round); |
|
|
|
doTestUnsignedValue( |
|
jspb.BinaryDecoder.prototype.readUint64, |
|
jspb.BinaryEncoder.prototype.writeUint64, |
|
1, Math.pow(2, 64) - 1025, Math.round); |
|
}); |
|
|
|
|
|
/** |
|
* Tests encoding and decoding of signed integers. |
|
*/ |
|
it('testSignedIntegers', function() { |
|
doTestSignedValue( |
|
jspb.BinaryDecoder.prototype.readInt8, |
|
jspb.BinaryEncoder.prototype.writeInt8, |
|
1, -0x80, 0x7F, Math.round); |
|
|
|
doTestSignedValue( |
|
jspb.BinaryDecoder.prototype.readInt16, |
|
jspb.BinaryEncoder.prototype.writeInt16, |
|
1, -0x8000, 0x7FFF, Math.round); |
|
|
|
doTestSignedValue( |
|
jspb.BinaryDecoder.prototype.readInt32, |
|
jspb.BinaryEncoder.prototype.writeInt32, |
|
1, -0x80000000, 0x7FFFFFFF, Math.round); |
|
|
|
doTestSignedValue( |
|
jspb.BinaryDecoder.prototype.readInt64, |
|
jspb.BinaryEncoder.prototype.writeInt64, |
|
1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); |
|
}); |
|
|
|
|
|
/** |
|
* Tests encoding and decoding of floats. |
|
*/ |
|
it('testFloats', function() { |
|
/** |
|
* @param {number} x |
|
* @return {number} |
|
*/ |
|
function truncate(x) { |
|
var temp = new Float32Array(1); |
|
temp[0] = x; |
|
return temp[0]; |
|
} |
|
doTestSignedValue( |
|
jspb.BinaryDecoder.prototype.readFloat, |
|
jspb.BinaryEncoder.prototype.writeFloat, |
|
jspb.BinaryConstants.FLOAT32_EPS, |
|
-jspb.BinaryConstants.FLOAT32_MAX, |
|
jspb.BinaryConstants.FLOAT32_MAX, |
|
truncate); |
|
|
|
doTestSignedValue( |
|
jspb.BinaryDecoder.prototype.readDouble, |
|
jspb.BinaryEncoder.prototype.writeDouble, |
|
jspb.BinaryConstants.FLOAT64_EPS * 10, |
|
-jspb.BinaryConstants.FLOAT64_MAX, |
|
jspb.BinaryConstants.FLOAT64_MAX, |
|
function(x) { return x; }); |
|
}); |
|
});
|
|
|