From 51c5ff889ccd3836c25f40baafb350f92c9ee103 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Tue, 25 Oct 2016 17:27:05 -0700 Subject: Fix pure php implementation for 32-bit machine. (#2282) --- php/src/Google/Protobuf/Internal/GPBUtil.php | 41 ++++- php/src/Google/Protobuf/Internal/GPBWire.php | 47 +++--- php/src/Google/Protobuf/Internal/InputStream.php | 66 +++++++- php/src/Google/Protobuf/Internal/Message.php | 5 - php/src/Google/Protobuf/Internal/OutputStream.php | 51 +++++-- php/src/Google/Protobuf/Internal/Type.php | 175 ---------------------- 6 files changed, 155 insertions(+), 230 deletions(-) delete mode 100644 php/src/Google/Protobuf/Internal/Type.php (limited to 'php/src') diff --git a/php/src/Google/Protobuf/Internal/GPBUtil.php b/php/src/Google/Protobuf/Internal/GPBUtil.php index 417a9729..30d7350f 100644 --- a/php/src/Google/Protobuf/Internal/GPBUtil.php +++ b/php/src/Google/Protobuf/Internal/GPBUtil.php @@ -37,6 +37,28 @@ use Google\Protobuf\Internal\RepeatedField; class GPBUtil { + public function divideInt64ToInt32($value, &$high, &$low, $trim = false) + { + $isNeg = (bccomp($value, 0) < 0); + if ($isNeg) { + $value = bcsub(0, $value); + } + $high = (int) bcdiv(bcadd($value, 1), 4294967296); + $low = (int) bcmod($value, 4294967296); + if ($isNeg) { + $high = ~$high; + $low = ~$low; + $low++; + if (!$low) { + $high++; + } + } + + if ($trim) { + $high = 0; + } + } + public static function checkString(&$var, $check_utf8) { @@ -70,9 +92,14 @@ class GPBUtil public static function checkUint32(&$var) { if (is_numeric($var)) { - $var = intval($var); if (PHP_INT_SIZE === 8) { + $var = intval($var); $var |= ((-(($var >> 31) & 0x1)) & ~0xFFFFFFFF); + } else { + if (bccomp($var, 0x7FFFFFFF) > 0) { + $var = bcsub($var, "4294967296"); + } + $var = (int) $var; } } else { trigger_error("Expect integer.", E_USER_ERROR); @@ -82,7 +109,11 @@ class GPBUtil public static function checkInt64(&$var) { if (is_numeric($var)) { - $var = intval($var); + if (PHP_INT_SIZE == 8) { + $var = intval($var); + } else { + $var = bcdiv($var, 1, 0); + } } else { trigger_error("Expect integer.", E_USER_ERROR); } @@ -91,7 +122,11 @@ class GPBUtil public static function checkUint64(&$var) { if (is_numeric($var)) { - $var = intval($var); + if (PHP_INT_SIZE == 8) { + $var = intval($var); + } else { + $var = bcdiv($var, 1, 0); + } } else { trigger_error("Expect integer.", E_USER_ERROR); } diff --git a/php/src/Google/Protobuf/Internal/GPBWire.php b/php/src/Google/Protobuf/Internal/GPBWire.php index 0e741e15..7e2c124f 100644 --- a/php/src/Google/Protobuf/Internal/GPBWire.php +++ b/php/src/Google/Protobuf/Internal/GPBWire.php @@ -32,10 +32,6 @@ namespace Google\Protobuf\Internal; -use Google\Protobuf\Internal\GPBUtil; -use Google\Protobuf\Internal\Int64; -use Google\Protobuf\Internal\Uint64; - class GPBWire { @@ -150,20 +146,28 @@ class GPBWire public static function zigZagEncode64($int64) { - $a = $int64->copy()->leftShift(1); - $b = $int64->copy()->rightShift(63); - $result = $a->bitXor($b); - $uint64 = Uint64::newValue($result->high, $result->low); - return $uint64; + if (PHP_INT_SIZE == 4) { + if (bccomp($int64, 0) >= 0) { + return bcmul($int64, 2); + } else { + return bcsub(bcmul(bcsub(0, $int64), 2), 1); + } + } else { + return ($int64 << 1) ^ ($int64 >> 63); + } } public static function zigZagDecode64($uint64) { - $a = $uint64->copy()->rightShift(1); - $b = $uint64->oddMask(); - $result = $a->bitXor($b); - $int64 = Int64::newValue($result->high, $result->low); - return $int64; + if (PHP_INT_SIZE == 4) { + if (bcmod($uint64, 2) == 0) { + return bcdiv($uint64, 2, 0); + } else { + return bcsub(0, bcdiv(bcadd($uint64, 1), 2, 0)); + } + } else { + return (($uint64 >> 1) & 0x7FFFFFFFFFFFFFFF) ^ (-($uint64 & 1)); + } } public static function readInt32(&$input, &$value) @@ -227,11 +231,7 @@ class GPBWire public static function readSfixed64(&$input, &$value) { - if (!self::readFixed64($input, $value)) { - return false; - } - $value = Int64::newValue($value->high, $value->low); - return true; + return $input->readLittleEndian64($value); } public static function readFloat(&$input, &$value) @@ -259,7 +259,7 @@ class GPBWire if (!$input->readVarint64($value)) { return false; } - if ($value->high === 0 && $value->low === 0) { + if ($value == 0) { $value = false; } else { $value = true; @@ -324,8 +324,8 @@ class GPBWire public static function writeSint64(&$output, $value) { - $value = GPBWire::zigZagEncode64(GPBUtil::Int64($value)); - return $output->writeVarint64($value->toInteger()); + $value = GPBWire::zigZagEncode64($value); + return $output->writeVarint64($value); } public static function writeFixed32(&$output, $value) @@ -431,9 +431,8 @@ class GPBWire public static function sint64Size($value) { - $value = GPBUtil::Int64($value); $value = self::zigZagEncode64($value); - return self::varint64Size($value->toInteger()); + return self::varint64Size($value); } public static function varint64Size($value) diff --git a/php/src/Google/Protobuf/Internal/InputStream.php b/php/src/Google/Protobuf/Internal/InputStream.php index 18d07075..6d6c74e9 100644 --- a/php/src/Google/Protobuf/Internal/InputStream.php +++ b/php/src/Google/Protobuf/Internal/InputStream.php @@ -34,6 +34,24 @@ namespace Google\Protobuf\Internal; use Google\Protobuf\Internal\Uint64; +function combineInt32ToInt64($high, $low) +{ + $isNeg = $high < 0; + if ($isNeg) { + $high = ~$high; + $low = ~$low; + $low++; + if (!$low) { + $high++; + } + } + $result = bcadd(bcmul($high, 4294967296), $low); + if ($isNeg) { + $result = bcsub(0, $result); + } + return $result; +} + class InputStream { @@ -116,11 +134,23 @@ class InputStream if (!$this->readVarint64($var)) { return false; } - $var = $var->toInteger() & 0xFFFFFFFF; + + if (PHP_INT_SIZE == 4) { + $var = bcmod($var, 4294967296); + } else { + $var &= 0xFFFFFFFF; + } + // Convert large uint32 to int32. - if (PHP_INT_SIZE === 8 && ($var > 0x7FFFFFFF)) { - $var = $var | (0xFFFFFFFF << 32); + if ($var > 0x7FFFFFFF) { + if (PHP_INT_SIZE === 8) { + $var = $var | (0xFFFFFFFF << 32); + } else { + $var = bcsub($var, 4294967296); + } } + + $var = intval($var); return true; } @@ -130,7 +160,8 @@ class InputStream */ public function readVarint64(&$var) { - $result = new Uint64(0); + $high = 0; + $low = 0; $count = 0; $b = 0; @@ -142,12 +173,27 @@ class InputStream return false; } $b = ord($this->buffer[$this->current]); - $result->bitOr((new Uint64($b & 0x7F))->leftShift(7 * $count)); + $bits = 7 * $count; + 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); + } else { + $low |= (($b & 0x7F) << $bits); + } + $this->advance(1); $count += 1; } while ($b & 0x80); - $var = $result; + if (PHP_INT_SIZE == 4) { + $var = combineInt32ToInt64($high, $low); + } else { + $var = ($high & 0xFFFFFFFF) << 32 | + ($low & 0xFFFFFFFF); + } return true; } @@ -161,7 +207,7 @@ class InputStream if (!$this->readVarint64($var)) { return false; } - $var = $var->toInteger(); + $var = (int)$var; return true; } @@ -197,7 +243,11 @@ class InputStream return false; } $high = unpack('V', $data)[1]; - $var = Uint64::newValue($high, $low); + if (PHP_INT_SIZE == 4) { + $var = combineInt32ToInt64($high, $low); + } else { + $var = ($high << 32) | $low; + } return true; } diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php index 7bdc6a8c..38513e91 100644 --- a/php/src/Google/Protobuf/Internal/Message.php +++ b/php/src/Google/Protobuf/Internal/Message.php @@ -210,13 +210,11 @@ class Message if (!GPBWire::readInt64($input, $value)) { return false; } - $value = $value->toInteger(); break; case GPBType::UINT64: if (!GPBWire::readUint64($input, $value)) { return false; } - $value = $value->toInteger(); break; case GPBType::INT32: if (!GPBWire::readInt32($input, $value)) { @@ -227,7 +225,6 @@ class Message if (!GPBWire::readFixed64($input, $value)) { return false; } - $value = $value->toInteger(); break; case GPBType::FIXED32: if (!GPBWire::readFixed32($input, $value)) { @@ -285,7 +282,6 @@ class Message if (!GPBWire::readSfixed64($input, $value)) { return false; } - $value = $value->toInteger(); break; case GPBType::SINT32: if (!GPBWire::readSint32($input, $value)) { @@ -296,7 +292,6 @@ class Message if (!GPBWire::readSint64($input, $value)) { return false; } - $value = $value->toInteger(); break; default: user_error("Unsupported type."); diff --git a/php/src/Google/Protobuf/Internal/OutputStream.php b/php/src/Google/Protobuf/Internal/OutputStream.php index fcc5ce6d..587ac352 100644 --- a/php/src/Google/Protobuf/Internal/OutputStream.php +++ b/php/src/Google/Protobuf/Internal/OutputStream.php @@ -90,8 +90,6 @@ class OutputStream public function writeRaw($data, $size) { if ($this->buffer_size < $size) { - var_dump($this->buffer_size); - var_dump($size); trigger_error("Output stream doesn't have enough buffer."); return false; } @@ -107,15 +105,28 @@ class OutputStream private static function writeVarintToArray($value, &$buffer, $trim = false) { $current = 0; - if ($trim) { - $value &= 0xFFFFFFFF; + + $high = 0; + $low = 0; + if (PHP_INT_SIZE == 4) { + GPBUtil::divideInt64ToInt32($value, $high, $low, $trim); + } else { + if ($trim) { + $low = $value & 0xFFFFFFFF; + } else { + $low = $value; + } } - while ($value >= 0x80 || $value < 0) { - $buffer[$current] = chr($value | 0x80); + + while ($low >= 0x80 || $low < 0) { + $buffer[$current] = chr($low | 0x80); $value = ($value >> 7) & ~(0x7F << ((PHP_INT_SIZE << 3) - 7)); + $carry = ($high & 0x7F) << ((PHP_INT_SIZE << 3) - 7); + $high = ($high >> 7) & ~(0x7F << ((PHP_INT_SIZE << 3) - 7)); + $low = (($low >> 7) & ~(0x7F << ((PHP_INT_SIZE << 3) - 7)) | $carry); $current++; } - $buffer[$current] = chr($value); + $buffer[$current] = chr($low); return $current + 1; } @@ -130,14 +141,24 @@ class OutputStream private static function writeLittleEndian64ToArray($value, &$buffer) { - $buffer[0] = chr($value & 0x000000FF); - $buffer[1] = chr(($value >> 8) & 0x000000FF); - $buffer[2] = chr(($value >> 16) & 0x000000FF); - $buffer[3] = chr(($value >> 24) & 0x000000FF); - $buffer[4] = chr(($value >> 32) & 0x000000FF); - $buffer[5] = chr(($value >> 40) & 0x000000FF); - $buffer[6] = chr(($value >> 48) & 0x000000FF); - $buffer[7] = chr(($value >> 56) & 0x000000FF); + $high = 0; + $low = 0; + if (PHP_INT_SIZE == 4) { + GPBUtil::divideInt64ToInt32($value, $high, $low); + } else { + $low = $value & 0xFFFFFFFF; + $high = ($value >> 32) & 0xFFFFFFFF; + } + + $buffer[0] = chr($low & 0x000000FF); + $buffer[1] = chr(($low >> 8) & 0x000000FF); + $buffer[2] = chr(($low >> 16) & 0x000000FF); + $buffer[3] = chr(($low >> 24) & 0x000000FF); + $buffer[4] = chr($high & 0x000000FF); + $buffer[5] = chr(($high >> 8) & 0x000000FF); + $buffer[6] = chr(($high >> 16) & 0x000000FF); + $buffer[7] = chr(($high >> 24) & 0x000000FF); return 8; } + } diff --git a/php/src/Google/Protobuf/Internal/Type.php b/php/src/Google/Protobuf/Internal/Type.php deleted file mode 100644 index 088f0e0c..00000000 --- a/php/src/Google/Protobuf/Internal/Type.php +++ /dev/null @@ -1,175 +0,0 @@ -low = $value & 0xFFFFFFFF; - if (PHP_INT_SIZE === 8) { - $this->high = ($value >> 32) & 0xFFFFFFFF; - } - } - - // Return 0 for unsigned integers and 1 for signed integers. - protected function sign() - { - trigger_error("Not implemented", E_ERROR); - } - - public function leftShift($count) - { - if ($count > 63) { - $this->low = 0; - $this->high = 0; - return; - } - if ($count > 32) { - $this->high = $this->low; - $this->low = 0; - $count -= 32; - } - $mask = (1 << $count) - 1; - $this->high = (($this->high << $count) & 0xFFFFFFFF) | - (($this->low >> (32 - $count)) & $mask); - $this->low = ($this->low << $count) & 0xFFFFFFFF; - - $this->high &= 0xFFFFFFFF; - $this->low &= 0xFFFFFFFF; - return $this; - } - - public function rightShift($count) - { - $sign = (($this->high & 0x80000000) >> 31) & $this->sign(); - if ($count > 63) { - $this->low = -$sign; - $this->high = -$sign; - return; - } - if ($count > 32) { - $this->low = $this->high; - $this->high = -$sign; - $count -= 32; - } - $this->low = (($this->low >> $count) & 0xFFFFFFFF) | - (($this->high << (32 - $count)) & 0xFFFFFFFF); - $this->high = (($this->high >> $count) | (-$sign << $count)); - - $this->high &= 0xFFFFFFFF; - $this->low &= 0xFFFFFFFF; - - return $this; - } - - public function bitOr($var) - { - $this->high |= $var->high; - $this->low |= $var->low; - return $this; - } - - public function bitXor($var) - { - $this->high ^= $var->high; - $this->low ^= $var->low; - return $this; - } - - public function bitAnd($var) - { - $this->high &= $var->high; - $this->low &= $var->low; - return $this; - } - - // Even: all zero; Odd: all one. - public function oddMask() - { - $low = (-($this->low & 1)) & 0xFFFFFFFF; - $high = $low; - return UInt64::newValue($high, $low); - } - - public function toInteger() - { - if (PHP_INT_SIZE === 8) { - return ($this->high << 32) | $this->low; - } else { - return $this->low; - } - } - - public function copy() - { - return static::newValue($this->high, $this->low); - } -} - -class Uint64 extends GPBInteger -{ - - public static function newValue($high, $low) - { - $uint64 = new Uint64(0); - $uint64->high = $high; - $uint64->low = $low; - return $uint64; - } - - protected function sign() - { - return 0; - } -} - -class Int64 extends GPBInteger -{ - - public static function newValue($high, $low) - { - $int64 = new Int64(0); - $int64->high = $high; - $int64->low = $low; - return $int64; - } - - protected function sign() - { - return 1; - } -} -- cgit v1.2.3