Skip to content

Commit

Permalink
Fixup un/signed mess, add support for decoding SHORT in binary protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
bwoebi committed Jul 7, 2017
1 parent d7ccc09 commit f3ae090
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 52 deletions.
85 changes: 61 additions & 24 deletions lib/DataTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,24 @@ public static function decodeBinary($type, $str, &$len = 0) {
case self::MYSQL_TYPE_LONGLONG:
case self::MYSQL_TYPE_LONGLONG | 0x80:
$len = 8;
return $unsigned && ($str[7] & "\x80") ? self::decode_unsigned64($str) : self::decode_int64($str);
return $unsigned ? self::decode_unsigned64($str) : self::decode_int64($str);

case self::MYSQL_TYPE_LONG:
case self::MYSQL_TYPE_LONG | 0x80:
case self::MYSQL_TYPE_INT24:
case self::MYSQL_TYPE_INT24 | 0x80:
$len = 4;
$shift = PHP_INT_MAX >> 31 ? 32 : 0;
return $unsigned && ($str[3] & "\x80") ? self::decode_unsigned32($str) : ((self::decode_int32($str) << $shift) >> $shift);
return $unsigned ? self::decode_unsigned32($str) : self::decode_int32($str);

case self::MYSQL_TYPE_SHORT:
case self::MYSQL_TYPE_SHORT | 0x80:
$len = 2;
return $unsigned ? self::decode_unsigned16($str) : self::decode_int16($str);

case self::MYSQL_TYPE_TINY:
case self::MYSQL_TYPE_TINY | 0x80:
$len = 1;
$shift = PHP_INT_MAX >> 31 ? 56 : 24;
return $unsigned ? ord($str) : ((ord($str) << $shift) >> $shift);
return $unsigned ? \ord($str) : self::decode_int8($str);

case self::MYSQL_TYPE_DOUBLE:
$len = 8;
Expand All @@ -143,15 +146,15 @@ public static function decodeBinary($type, $str, &$len = 0) {
$year = $month = $day = $hour = $minute = $second = $microsecond = 0;
switch ($len = ord($str) + 1) {
case 12:
$microsecond = self::decode_int32(substr($str, 8));
$microsecond = self::decode_unsigned32(substr($str, 8));
case 8:
$second = ord($str[7]);
$minute = ord($str[6]);
$hour = ord($str[5]);
case 5:
$day = ord($str[4]);
$month = ord($str[3]);
$year = self::decode_int16(substr($str, 1));
$year = self::decode_unsigned16(substr($str, 1));
case 1:
break;

Expand All @@ -164,12 +167,12 @@ public static function decodeBinary($type, $str, &$len = 0) {
$negative = $day = $hour = $minute = $second = $microsecond = 0;
switch ($len = ord($str) + 1) {
case 13:
$microsecond = self::decode_int32(substr($str, 9));
$microsecond = self::decode_unsigned32(substr($str, 9));
case 9:
$second = ord($str[8]);
$minute = ord($str[7]);
$hour = ord($str[6]);
$day = self::decode_int32(substr($str, 2));
$day = self::decode_unsigned32(substr($str, 2));
$negative = ord($str[1]);
case 1:
break;
Expand All @@ -193,50 +196,50 @@ public static function decodeNullString($str, &$len = 0) {
}

public static function decodeStringOff($str, &$off) {
$len = self::decodeIntOff($str, $off);
$len = self::decodeUnsignedOff($str, $off);
$off += $len;
return substr($str, $off - $len, $len);
}

public static function decodeIntOff($str, &$off) {
public static function decodeUnsignedOff($str, &$off) {
$int = ord($str[$off]);
if ($int < 0xfb) {
$off += 1;
return $int;
} elseif ($int == 0xfc) {
$off += 3;
return self::decode_int16(substr($str, $off - 2, 2));
return self::decode_unsigned16(substr($str, $off - 2, 2));
} elseif ($int == 0xfd) {
$off += 4;
return self::decode_int24(substr($str, $off - 3, 3));
return self::decode_unsigned24(substr($str, $off - 3, 3));
} elseif ($int == 0xfe) {
$off += 9;
return self::decode_int64(substr($str, $off - 8, 8));
return self::decode_unsigned64(substr($str, $off - 8, 8));
} else {
// If that happens connection is borked...
throw new \RangeException("$int is not in ranges [0x00, 0xfa] or [0xfc, 0xfe]");
}
}

public static function decodeString($str, &$intlen = 0, &$len = 0) {
$len = self::decodeInt($str, $intlen);
$len = self::decodeUnsigned($str, $intlen);
return substr($str, $intlen, $len);
}

public static function decodeInt($str, &$len = 0) {
$int = ord($str);
public static function decodeUnsigned($str, &$len = 0) {
$int = \ord($str);
if ($int < 0xfb) {
$len = 1;
return $int;
} elseif ($int == 0xfc) {
$len = 3;
return self::decode_int16(substr($str, 1));
return self::decode_unsigned16(substr($str, 1, 2));
} elseif ($int == 0xfd) {
$len = 4;
return self::decode_int24(substr($str, 1));
return self::decode_unsigned24(substr($str, 1, 4));
} elseif ($int == 0xfe) {
$len = 9;
return self::decode_int64(substr($str, 1));
return self::decode_unsigned64(substr($str, 1, 8));
} else {
// If that happens connection is borked...
throw new \RangeException("$int is not in ranges [0x00, 0xfa] or [0xfc, 0xfe]");
Expand All @@ -252,23 +255,57 @@ public static function decode_intByLen($str, $len) {
}

public static function decode_int8($str) {
return ord($str);
$int = \ord($str);
if ($int < (1 << 7)) {
return $int;
}
$shift = PHP_INT_SIZE * 8 - 8;
return $int << $shift >> $shift;
}

public static function decode_unsigned8($str) {
return \ord($str);
}

public static function decode_int16($str) {
$int = unpack("v", $str)[1];
if ($int < (1 << 15)) {
return $int;
}
$shift = PHP_INT_SIZE * 8 - 16;
return $int << $shift >> $shift;
}

public static function decode_unsigned16($str) {
return unpack("v", $str)[1];
}

public static function decode_int24($str) {
$int = unpack("V", substr($str, 0, 3) . "\x00")[1];
if ($int < (1 << 23)) {
return $int;
}
$shift = PHP_INT_SIZE * 8 - 24;
return $int << $shift >> $shift;
}

public static function decode_unsigned24($str) {
return unpack("V", substr($str, 0, 3) . "\x00")[1];
}

public static function decode_int32($str) {
if (PHP_INT_SIZE > 4) {
$int = unpack("V", $str)[1];
if ($int < (1 << 31)) {
return $int;
}
return $int << 32 >> 32;
}
return unpack("V", $str)[1];
}

public static function decode_unsigned32($str) {
if (PHP_INT_MAX >> 31) {
if (PHP_INT_SIZE > 4) {
return unpack("V", $str)[1];
} else {
$int = unpack("v", $str)[1];
Expand All @@ -277,7 +314,7 @@ public static function decode_unsigned32($str) {
}

public static function decode_int64($str) {
if (PHP_INT_MAX >> 31) {
if (PHP_INT_SIZE > 4) {
$int = unpack("V2", $str);
return $int[1] + ($int[2] << 32);
} else {
Expand All @@ -287,7 +324,7 @@ public static function decode_int64($str) {
}

public static function decode_unsigned64($str) {
if (PHP_INT_MAX >> 31) {
if (PHP_INT_SIZE > 4) {
$int = unpack("V2", $str);
return $int[1] + $int[2] * (1 << 32);
} else {
Expand Down
56 changes: 28 additions & 28 deletions lib/Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ private function established() {
private function handleError($packet) {
$off = 1;

$this->connInfo->errorCode = DataTypes::decode_int16(substr($packet, $off, 2));
$this->connInfo->errorCode = DataTypes::decode_unsigned16(substr($packet, $off, 2));
$off += 2;

if ($this->capabilities & self::CLIENT_PROTOCOL_41) {
Expand Down Expand Up @@ -408,17 +408,17 @@ private function handleError($packet) {
private function parseOk($packet) {
$off = 1;

$this->connInfo->affectedRows = DataTypes::decodeInt(substr($packet, $off), $intlen);
$this->connInfo->affectedRows = DataTypes::decodeUnsigned(substr($packet, $off), $intlen);
$off += $intlen;

$this->connInfo->insertId = DataTypes::decodeInt(substr($packet, $off), $intlen);
$this->connInfo->insertId = DataTypes::decodeUnsigned(substr($packet, $off), $intlen);
$off += $intlen;

if ($this->capabilities & (self::CLIENT_PROTOCOL_41 | self::CLIENT_TRANSACTIONS)) {
$this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, $off));
$this->connInfo->statusFlags = DataTypes::decode_unsigned16(substr($packet, $off));
$off += 2;

$this->connInfo->warnings = DataTypes::decode_int16(substr($packet, $off));
$this->connInfo->warnings = DataTypes::decode_unsigned16(substr($packet, $off));
$off += 2;
}

Expand All @@ -433,7 +433,7 @@ private function parseOk($packet) {
while ($len < $sessionStateLen) {
$data = DataTypes::decodeString(substr($sessionState, $len + 1), $datalen, $intlen);

switch ($type = DataTypes::decode_int8(substr($sessionState, $len))) {
switch ($type = DataTypes::decode_unsigned8(substr($sessionState, $len))) {
case SessionStateTypes::SESSION_TRACK_SYSTEM_VARIABLES:
$var = DataTypes::decodeString($data, $varintlen, $strlen);
$this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_SYSTEM_VARIABLES][$var] = DataTypes::decodeString(substr($data, $varintlen + $strlen));
Expand Down Expand Up @@ -468,9 +468,9 @@ private function handleOk($packet) {
/** @see 14.1.3.3 EOF-Packet */
private function parseEof($packet) {
if ($this->capabilities & self::CLIENT_PROTOCOL_41) {
$this->connInfo->warnings = DataTypes::decode_int16(substr($packet, 1));
$this->connInfo->warnings = DataTypes::decode_unsigned16(substr($packet, 1));

$this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, 3));
$this->connInfo->statusFlags = DataTypes::decode_unsigned16(substr($packet, 3));
}
}

Expand All @@ -492,25 +492,25 @@ private function handleHandshake($packet) {
$this->connInfo->serverVersion = DataTypes::decodeNullString(substr($packet, $off), $len);
$off += $len + 1;

$this->connectionId = DataTypes::decode_int32(substr($packet, $off));
$this->connectionId = DataTypes::decode_unsigned32(substr($packet, $off));
$off += 4;

$this->authPluginData = substr($packet, $off, 8);
$off += 8;

$off += 1; // filler byte

$this->serverCapabilities = DataTypes::decode_int16(substr($packet, $off));
$this->serverCapabilities = DataTypes::decode_unsigned16(substr($packet, $off));
$off += 2;

if (\strlen($packet) > $off) {
$this->connInfo->charset = ord(substr($packet, $off));
$off += 1;

$this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, $off));
$this->connInfo->statusFlags = DataTypes::decode_unsigned16(substr($packet, $off));
$off += 2;

$this->serverCapabilities += DataTypes::decode_int16(substr($packet, $off)) << 16;
$this->serverCapabilities += DataTypes::decode_unsigned16(substr($packet, $off)) << 16;
$off += 2;

$this->authPluginDataLen = $this->serverCapabilities & self::CLIENT_PLUGIN_AUTH ? ord(substr($packet, $off)) : 0;
Expand Down Expand Up @@ -569,7 +569,7 @@ private function handleQuery($packet) {
$this->getDeferred()->succeed(new ResultSet($this->connInfo, $result = new ResultProxy));
/* we need to succeed before assigning vars, so that a when() handler won't have a partial result available */
$this->result = $result;
$result->setColumns(DataTypes::decodeInt($packet));
$result->setColumns(DataTypes::decodeUnsigned($packet));
}

/** @see 14.7.1 Binary Protocol Resultset */
Expand Down Expand Up @@ -664,16 +664,16 @@ private function parseColumnDefinition($packet) {
$column["original_table"] = DataTypes::decodeStringOff($packet, $off);
$column["name"] = DataTypes::decodeStringOff($packet, $off);
$column["original_name"] = DataTypes::decodeStringOff($packet, $off);
$fixlen = DataTypes::decodeIntOff($packet, $off);
$fixlen = DataTypes::decodeUnsignedOff($packet, $off);

$len = 0;
$column["charset"] = DataTypes::decode_int16(substr($packet, $off + $len));
$column["charset"] = DataTypes::decode_unsigned16(substr($packet, $off + $len));
$len += 2;
$column["columnlen"] = DataTypes::decode_int32(substr($packet, $off + $len));
$column["columnlen"] = DataTypes::decode_unsigned32(substr($packet, $off + $len));
$len += 4;
$column["type"] = ord($packet[$off + $len]);
$len += 1;
$column["flags"] = DataTypes::decode_int16(substr($packet, $off + $len));
$column["flags"] = DataTypes::decode_unsigned16(substr($packet, $off + $len));
$len += 2;
$column["decimals"] = ord($packet[$off + $len]);
//$len += 1;
Expand All @@ -683,21 +683,21 @@ private function parseColumnDefinition($packet) {
$column["table"] = DataTypes::decodeStringOff($packet, $off);
$column["name"] = DataTypes::decodeStringOff($packet, $off);

$collen = DataTypes::decodeIntOff($packet, $off);
$collen = DataTypes::decodeUnsignedOff($packet, $off);
$column["columnlen"] = DataTypes::decode_intByLen(substr($packet, $off), $collen);
$off += $collen;

$typelen = DataTypes::decodeIntOff($packet, $off);
$typelen = DataTypes::decodeUnsignedOff($packet, $off);
$column["type"] = DataTypes::decode_intByLen(substr($packet, $off), $typelen);
$off += $typelen;

$len = 1;
$flaglen = $this->capabilities & self::CLIENT_LONG_FLAG ? DataTypes::decodeInt(substr($packet, $off, 9), $len) : ord($packet[$off]);
$flaglen = $this->capabilities & self::CLIENT_LONG_FLAG ? DataTypes::decodeUnsigned(substr($packet, $off, 9), $len) : ord($packet[$off]);
$off += $len;

if ($flaglen > 2) {
$len = 2;
$column["flags"] = DataTypes::decode_int16(substr($packet, $off, 4));
$column["flags"] = DataTypes::decode_unsigned16(substr($packet, $off, 4));
} else {
$len = 1;
$column["flags"] = ord($packet[$off]);
Expand Down Expand Up @@ -801,18 +801,18 @@ private function handlePrepare($packet) {
}
$off = 1;

$stmtId = DataTypes::decode_int32(substr($packet, $off));
$stmtId = DataTypes::decode_unsigned32(substr($packet, $off));
$off += 4;

$columns = DataTypes::decode_int16(substr($packet, $off));
$columns = DataTypes::decode_unsigned16(substr($packet, $off));
$off += 2;

$params = DataTypes::decode_int16(substr($packet, $off));
$params = DataTypes::decode_unsigned16(substr($packet, $off));
$off += 2;

$off += 1; // filler

$this->connInfo->warnings = DataTypes::decode_int16(substr($packet, $off));
$this->connInfo->warnings = DataTypes::decode_unsigned16(substr($packet, $off));

$this->result = new ResultProxy;
$this->result->columnsToFetch = $params;
Expand Down Expand Up @@ -987,9 +987,9 @@ private function parseCompression() {
$inflated = "";
}

$size = DataTypes::decode_int24($buf);
$size = DataTypes::decode_unsigned24($buf);
$this->compressionId = ord($buf[3]);
$uncompressed = DataTypes::decode_int24(substr($buf, 4, 3));
$uncompressed = DataTypes::decode_unsigned24(substr($buf, 4, 3));

$buf = substr($buf, 7);

Expand Down Expand Up @@ -1027,7 +1027,7 @@ private function parseMysql() {
$parsed = [];
}

$len = DataTypes::decode_int24($buf);
$len = DataTypes::decode_unsigned24($buf);
$this->seqId = ord($buf[3]);
$buf = substr($buf, 4);

Expand Down

0 comments on commit f3ae090

Please sign in to comment.