Browse Source

Fix int64 decoding on 32-bit machines.

Bo Yang 8 years ago
parent
commit
d1fde361a7

+ 8 - 1
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;

+ 58 - 27
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(

+ 6 - 3
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);
                 }

+ 10 - 5
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:

+ 35 - 0
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());
+        }
+    }
 }

+ 25 - 27
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);
         }
     }