From d1fde361a738aa86625bfd7fa7442ef28a6b3011 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Sun, 8 Jan 2017 12:53:46 -0800 Subject: Fix int64 decoding on 32-bit machines. --- php/src/Google/Protobuf/Internal/GPBUtil.php | 9 ++- php/src/Google/Protobuf/Internal/GPBWire.php | 85 ++++++++++++++++-------- php/src/Google/Protobuf/Internal/InputStream.php | 9 ++- php/src/Google/Protobuf/Internal/Message.php | 15 +++-- php/tests/encode_decode_test.php | 35 ++++++++++ php/tests/php_implementation_test.php | 52 +++++++-------- 6 files changed, 142 insertions(+), 63 deletions(-) diff --git a/php/src/Google/Protobuf/Internal/GPBUtil.php b/php/src/Google/Protobuf/Internal/GPBUtil.php index 30d7350f..ba1d2eb3 100644 --- a/php/src/Google/Protobuf/Internal/GPBUtil.php +++ b/php/src/Google/Protobuf/Internal/GPBUtil.php @@ -43,8 +43,15 @@ class GPBUtil if ($isNeg) { $value = bcsub(0, $value); } + $high = (int) bcdiv(bcadd($value, 1), 4294967296); - $low = (int) bcmod($value, 4294967296); + $low = bcmod($value, 4294967296); + if (bccomp($low, 2147483647) > 0) { + $low = (int) bcsub($low, 4294967296); + } else { + $low = (int) $low; + } + if ($isNeg) { $high = ~$high; $low = ~$low; diff --git a/php/src/Google/Protobuf/Internal/GPBWire.php b/php/src/Google/Protobuf/Internal/GPBWire.php index 7e2c124f..f75e0861 100644 --- a/php/src/Google/Protobuf/Internal/GPBWire.php +++ b/php/src/Google/Protobuf/Internal/GPBWire.php @@ -437,34 +437,65 @@ class GPBWire public static function varint64Size($value) { - if ($value < 0) { - return 10; - } - if ($value < (1 << 7)) { - return 1; - } - if ($value < (1 << 14)) { - return 2; - } - if ($value < (1 << 21)) { - return 3; - } - if ($value < (1 << 28)) { - return 4; - } - if ($value < (1 << 35)) { - return 5; - } - if ($value < (1 << 42)) { - return 6; - } - if ($value < (1 << 49)) { - return 7; - } - if ($value < (1 << 56)) { - return 8; + if (PHP_INT_SIZE == 4) { + if (bccomp($value, 0) < 0) { + return 10; + } + if (bccomp($value, 1 << 7) < 0) { + return 1; + } + if (bccomp($value, 1 << 14) < 0) { + return 2; + } + if (bccomp($value, 1 << 21) < 0) { + return 3; + } + if (bccomp($value, 1 << 28) < 0) { + return 4; + } + if (bccomp($value, '34359738368') < 0) { + return 5; + } + if (bccomp($value, '4398046511104') < 0) { + return 6; + } + if (bccomp($value, '562949953421312') < 0) { + return 7; + } + if (bccomp($value, '72057594037927936') < 0) { + return 8; + } + return 9; + } else { + if ($value < 0) { + return 10; + } + if ($value < (1 << 7)) { + return 1; + } + if ($value < (1 << 14)) { + return 2; + } + if ($value < (1 << 21)) { + return 3; + } + if ($value < (1 << 28)) { + return 4; + } + if ($value < (1 << 35)) { + return 5; + } + if ($value < (1 << 42)) { + return 6; + } + if ($value < (1 << 49)) { + return 7; + } + if ($value < (1 << 56)) { + return 8; + } + return 9; } - return 9; } public static function serializeFieldToStream( diff --git a/php/src/Google/Protobuf/Internal/InputStream.php b/php/src/Google/Protobuf/Internal/InputStream.php index c5a76d5d..bf052c2f 100644 --- a/php/src/Google/Protobuf/Internal/InputStream.php +++ b/php/src/Google/Protobuf/Internal/InputStream.php @@ -46,6 +46,9 @@ function combineInt32ToInt64($high, $low) } } $result = bcadd(bcmul($high, 4294967296), $low); + if ($low < 0) { + $result = bcadd($result, 4294967296); + } if ($isNeg) { $result = bcsub(0, $result); } @@ -179,9 +182,9 @@ class InputStream if ($bits >= 32) { $high |= (($b & 0x7F) << ($bits - 32)); } else if ($bits > 25){ - $high_bits = $bits - 25; - $low = ($low | (($b & 0x7F) << $bits)) & (int) 0xFFFFFFFF; - $high = $b & ((0x1 << $high_bits) -1); + // $bits is 28 in this case. + $low |= (($b & 0x7F) << 28); + $high = ($b & 0x7F) >> 4; } else { $low |= (($b & 0x7F) << $bits); } diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php index 3d1f1598..7d1e51e8 100644 --- a/php/src/Google/Protobuf/Internal/Message.php +++ b/php/src/Google/Protobuf/Internal/Message.php @@ -175,17 +175,22 @@ class Message case GPBType::FLOAT: return 0.0; case GPBType::UINT32: - case GPBType::UINT64: case GPBType::INT32: - case GPBType::INT64: case GPBType::FIXED32: - case GPBType::FIXED64: case GPBType::SFIXED32: - case GPBType::SFIXED64: case GPBType::SINT32: - case GPBType::SINT64: case GPBType::ENUM: return 0; + case GPBType::INT64: + case GPBType::UINT64: + case GPBType::FIXED64: + case GPBType::SFIXED64: + case GPBType::SINT64: + if (PHP_INT_SIZE === 4) { + return '0'; + } else { + return 0; + } case GPBType::BOOL: return false; case GPBType::STRING: diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php index af9c0415..7bb75336 100644 --- a/php/tests/encode_decode_test.php +++ b/php/tests/encode_decode_test.php @@ -132,4 +132,39 @@ class EncodeDecodeTest extends TestBase $to->decode(TestUtil::getGoldenTestUnpackedMessage()); TestUtil::assertTestPackedMessage($to); } + + public function testDecodeInt64() + { + // Read 64 testing + $testVals = array( + '10' => '100a', + '100' => '1064', + '800' => '10a006', + '6400' => '108032', + '70400' => '1080a604', + '774400' => '1080a22f', + '9292800' => '108098b704', + '74342400' => '1080c0b923', + '743424000' => '108080bfe202', + '8177664000' => '108080b5bb1e', + '65421312000' => '108080a8dbf301', + '785055744000' => '108080e0c7ec16', + '9420668928000' => '10808080dd969202', + '103627358208000' => '10808080fff9c717', + '1139900940288000' => '10808080f5bd978302', + '13678811283456000' => '10808080fce699a618', + '109430490267648000' => '10808080e0b7ceb1c201', + '984874412408832000' => '10808080e0f5c1bed50d', + ); + + $msg = new TestMessage(); + foreach ($testVals as $original => $encoded) { + $msg->setOptionalInt64($original); + $data = $msg->encode(); + $this->assertSame($encoded, bin2hex($data)); + $msg->setOptionalInt64(0); + $msg->decode($data); + $this->assertEquals($original, $msg->getOptionalInt64()); + } + } } diff --git a/php/tests/php_implementation_test.php b/php/tests/php_implementation_test.php index 6b922a9b..ac7c13dc 100644 --- a/php/tests/php_implementation_test.php +++ b/php/tests/php_implementation_test.php @@ -368,33 +368,31 @@ class ImplementationTest extends TestBase $this->assertFalse($input->readVarint64($var)); // Read 64 testing - if (PHP_INT_SIZE > 4) { - $testVals = array( - '10' => '0a000000000000000000', - '100' => '64000000000000000000', - '800' => 'a0060000000000000000', - '6400' => '80320000000000000000', - '70400' => '80a60400000000000000', - '774400' => '80a22f00000000000000', - '9292800' => '8098b704000000000000', - '74342400' => '80c0b923000000000000', - '743424000' => '8080bfe2020000000000', - '8177664000' => '8080b5bb1e0000000000', - '65421312000' => '8080a8dbf30100000000', - '785055744000' => '8080e0c7ec1600000000', - '9420668928000' => '808080dd969202000000', - '103627358208000' => '808080fff9c717000000', - '1139900940288000' => '808080f5bd9783020000', - '13678811283456000' => '808080fce699a6180000', - '109430490267648000' => '808080e0b7ceb1c20100', - '984874412408832000' => '808080e0f5c1bed50d00', - ); - - foreach ($testVals as $original => $encoded) { - $input = new InputStream(hex2bin($encoded)); - $this->assertTrue($input->readVarint64($var)); - $this->assertSame($original, $var); - } + $testVals = array( + '10' => '0a000000000000000000', + '100' => '64000000000000000000', + '800' => 'a0060000000000000000', + '6400' => '80320000000000000000', + '70400' => '80a60400000000000000', + '774400' => '80a22f00000000000000', + '9292800' => '8098b704000000000000', + '74342400' => '80c0b923000000000000', + '743424000' => '8080bfe2020000000000', + '8177664000' => '8080b5bb1e0000000000', + '65421312000' => '8080a8dbf30100000000', + '785055744000' => '8080e0c7ec1600000000', + '9420668928000' => '808080dd969202000000', + '103627358208000' => '808080fff9c717000000', + '1139900940288000' => '808080f5bd9783020000', + '13678811283456000' => '808080fce699a6180000', + '109430490267648000' => '808080e0b7ceb1c20100', + '984874412408832000' => '808080e0f5c1bed50d00', + ); + + foreach ($testVals as $original => $encoded) { + $input = new InputStream(hex2bin($encoded)); + $this->assertTrue($input->readVarint64($var)); + $this->assertEquals($original, $var); } } -- cgit v1.2.3