Huge performance improvements

This commit is contained in:
Daniil Gentili 2017-05-11 01:06:17 +01:00
parent d9ca05fa86
commit c2ce149436
4 changed files with 45 additions and 69 deletions

View File

@ -230,7 +230,7 @@ trait AuthKeyHandler
* *********************************************************************** * ***********************************************************************
* Do some checks * Do some checks
*/ */
$server_DH_inner_data_length = $this->get_length(new \danog\MadelineProto\Stream($answer)); $server_DH_inner_data_length = $this->get_length($answer);
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) { if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.'); throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.');
} }

View File

@ -1,34 +0,0 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
The PWRTelegram API is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
/**
* Just a stream wrapper.
*/
class Stream
{
public $pos = 0;
public function __construct($string)
{
$this->string = $string;
}
public function read($length)
{
$d = substr($this->string, $this->pos, $length);
$this->pos += $length;
return $d;
}
}

View File

@ -89,7 +89,7 @@ trait TL
continue; continue;
} }
$name = preg_replace(['/#.*/', '/\s.*/'], '', $line); $name = preg_replace(['/#.*/', '/\s.*/'], '', $line);
if (in_array($name, ['bytes', 'int128', 'int256', 'int512'])) { if ($this->in_array($name, ['bytes', 'int128', 'int256', 'int512'])) {
continue; continue;
} }
$clean = preg_replace([ $clean = preg_replace([
@ -132,7 +132,7 @@ trait TL
$dparams = []; $dparams = [];
} }
$TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name; $TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name;
$TL_dict[$type][$key]['id'] = \danog\PHP\Struct::unpack('<i', \danog\PHP\Struct::pack('<I', hexdec($id)))[0]; $TL_dict[$type][$key]['id'] = \danog\PHP\Struct::unpack('<i', pack('V', hexdec($id)))[0];
$TL_dict[$type][$key]['params'] = []; $TL_dict[$type][$key]['params'] = [];
$TL_dict[$type][$key]['type'] = preg_replace(['/.+\s/', '/;/'], '', $line); $TL_dict[$type][$key]['type'] = preg_replace(['/.+\s/', '/;/'], '', $line);
if ($layer !== null) { if ($layer !== null) {
@ -205,7 +205,7 @@ trait TL
public function get_method_namespaces() public function get_method_namespaces()
{ {
return array_unique(array_values($this->methods->method_namespace)); return array_unique($this->array_values($this->methods->method_namespace));
} }
public function serialize_bool($bool) public function serialize_bool($bool)
@ -238,7 +238,7 @@ trait TL
throw new Exception('given value ('.$object.") isn't numeric"); throw new Exception('given value ('.$object.") isn't numeric");
} }
return \danog\PHP\Struct::pack('<I', $object); return pack('V', $object);
case 'long': case 'long':
if (is_object($object)) { if (is_object($object)) {
return str_pad(strrev($object->toBytes()), 8, chr(0)); return str_pad(strrev($object->toBytes()), 8, chr(0));
@ -247,7 +247,6 @@ trait TL
if (is_string($object) && strlen($object) === 8) { if (is_string($object) && strlen($object) === 8) {
return $object; return $object;
} }
if (!is_numeric($object)) { if (!is_numeric($object)) {
throw new Exception('given value ('.$object.") isn't numeric"); throw new Exception('given value ('.$object.") isn't numeric");
} }
@ -311,7 +310,7 @@ trait TL
} }
$auto = false; $auto = false;
if ((!is_array($object) || (isset($object['_']) && $this->constructors->find_by_predicate($object['_'])['type'] !== $type['type'])) && in_array($type['type'], ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) { if ((!$this->is_array($object) || (isset($object['_']) && $this->constructors->find_by_predicate($object['_'])['type'] !== $type['type'])) && $this->in_array($type['type'], ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) {
$object = $this->get_info($object); $object = $this->get_info($object);
if (!isset($object[$type['type']])) { if (!isset($object[$type['type']])) {
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database'); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
@ -392,7 +391,7 @@ trait TL
$arguments['flags'] = $flags; $arguments['flags'] = $flags;
foreach ($tl['params'] as $current_argument) { foreach ($tl['params'] as $current_argument) {
if (!isset($arguments[$current_argument['name']])) { if (!isset($arguments[$current_argument['name']])) {
if ($current_argument['flag'] && (in_array($current_argument['type'], ['true', 'false']) || ($flags & $current_argument['pow']) === 0)) { if ($current_argument['flag'] && ($this->in_array($current_argument['type'], ['true', 'false']) || ($flags & $current_argument['pow']) === 0)) {
//\danog\MadelineProto\Logger::log(['Skipping '.$current_argument['name'].' of type '.$current_argument['type']); //\danog\MadelineProto\Logger::log(['Skipping '.$current_argument['name'].' of type '.$current_argument['type']);
continue; continue;
} }
@ -423,7 +422,7 @@ trait TL
} }
throw new Exception('Missing required parameter ('.$current_argument['name'].')'); throw new Exception('Missing required parameter ('.$current_argument['name'].')');
} }
if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputEncryptedChat') { if (!$this->is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputEncryptedChat') {
$arguments[$current_argument['name']] = $this->secret_chats[$arguments[$current_argument['name']]]['InputEncryptedChat']; $arguments[$current_argument['name']] = $this->secret_chats[$arguments[$current_argument['name']]]['InputEncryptedChat'];
} }
if ($current_argument['type'] === 'DataJSON') { if ($current_argument['type'] === 'DataJSON') {
@ -439,9 +438,17 @@ trait TL
public function get_length($bytes_io, $type = ['type' => '']) public function get_length($bytes_io, $type = ['type' => ''])
{ {
if (is_string($bytes_io)) {
$res = fopen('php://memory', 'rw+b');
fwrite($res, $bytes_io);
fseek($res, 0);
$bytes_io = $res;
} elseif (!is_resource($bytes_io)) {
throw new Exception('An invalid bytes_io handle was provided.');
}
$this->deserialize($bytes_io, $type); $this->deserialize($bytes_io, $type);
return $bytes_io->pos; return ftell($bytes_io);
} }
/** /**
@ -450,46 +457,48 @@ trait TL
public function deserialize($bytes_io, $type = ['type' => '']) public function deserialize($bytes_io, $type = ['type' => ''])
{ {
if (is_string($bytes_io)) { if (is_string($bytes_io)) {
$bytes_io = new \danog\MadelineProto\Stream($bytes_io); $res = fopen('php://memory', 'rw+b');
} elseif (!is_object($bytes_io)) { fwrite($res, $bytes_io);
fseek($res, 0);
$bytes_io = $res;
} elseif (!is_resource($bytes_io)) {
throw new Exception('An invalid bytes_io handle was provided.'); throw new Exception('An invalid bytes_io handle was provided.');
} }
//\danog\MadelineProto\Logger::log(['Deserializing '.$type['type'].' at byte '.$bytes_io->pos]);
switch ($type['type']) { switch ($type['type']) {
case 'Bool': case 'Bool':
return $this->deserialize_bool($bytes_io->read(4)); return $this->deserialize_bool(stream_get_contents($bytes_io, 4));
case 'int': case 'int':
return \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0]; return \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
case '#': case '#':
return \danog\PHP\Struct::unpack('<I', $bytes_io->read(4))[0]; return unpack('V', stream_get_contents($bytes_io, 4))[1];
case 'long': case 'long':
return $this->bigint || isset($type['strlong']) ? $bytes_io->read(8) : \danog\PHP\Struct::unpack('<q', $bytes_io->read(8))[0]; return $this->bigint || isset($type['strlong']) ? stream_get_contents($bytes_io, 8) : \danog\PHP\Struct::unpack('<q', stream_get_contents($bytes_io, 8))[0];
case 'double': case 'double':
return \danog\PHP\Struct::unpack('<d', $bytes_io->read(8))[0]; return \danog\PHP\Struct::unpack('<d', stream_get_contents($bytes_io, 8))[0];
case 'int128': case 'int128':
return $bytes_io->read(16); return stream_get_contents($bytes_io, 16);
case 'int256': case 'int256':
return $bytes_io->read(32); return stream_get_contents($bytes_io, 32);
case 'int512': case 'int512':
return $bytes_io->read(64); return stream_get_contents($bytes_io, 64);
case 'string': case 'string':
case 'bytes': case 'bytes':
$l = \danog\PHP\Struct::unpack('<B', $bytes_io->read(1))[0]; $l = ord(stream_get_contents($bytes_io, 1));
if ($l > 254) { if ($l > 254) {
throw new Exception('Length is too big'); throw new Exception('Length is too big');
} }
if ($l === 254) { if ($l === 254) {
$long_len = \danog\PHP\Struct::unpack('<I', $bytes_io->read(3).chr(0))[0]; $long_len = unpack('V', stream_get_contents($bytes_io, 3).chr(0))[1];
$x = $bytes_io->read($long_len); $x = stream_get_contents($bytes_io, $long_len);
$resto = $this->posmod(-$long_len, 4); $resto = $this->posmod(-$long_len, 4);
if ($resto > 0) { if ($resto > 0) {
$bytes_io->pos += $resto; stream_get_contents($bytes_io, $resto);
} }
} else { } else {
$x = $bytes_io->read($l); $x = stream_get_contents($bytes_io, $l);
$resto = $this->posmod(-($l + 1), 4); $resto = $this->posmod(-($l + 1), 4);
if ($resto > 0) { if ($resto > 0) {
$bytes_io->pos += $resto; stream_get_contents($bytes_io, $resto);
} }
} }
if (!is_string($x)) { if (!is_string($x)) {
@ -500,7 +509,7 @@ trait TL
case 'true': case 'true':
return true; return true;
case 'Vector t': case 'Vector t':
$id = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0]; $id = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$constructorData = $this->constructors->find_by_id($id); $constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) { if ($constructorData === false) {
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id); throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
@ -515,7 +524,7 @@ trait TL
throw new Exception('Invalid vector constructor: '.$constructorData['predicate']); throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
} }
case 'vector': case 'vector':
$count = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0]; $count = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$result = []; $result = [];
$type['type'] = $type['subtype']; $type['type'] = $type['subtype'];
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
@ -533,7 +542,7 @@ trait TL
} else { } else {
$constructorData = $this->constructors->find_by_predicate($type['type']); $constructorData = $this->constructors->find_by_predicate($type['type']);
if ($constructorData === false) { if ($constructorData === false) {
$id = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0]; $id = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$constructorData = $this->constructors->find_by_id($id); $constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) { if ($constructorData === false) {
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id); throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
@ -576,7 +585,7 @@ trait TL
break; break;
} }
} }
if (in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id', 'key_fingerprint', 'server_salt', 'new_server_salt', 'server_public_key_fingerprints', 'ping_id', 'exchange_id'])) { if ($this->in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id', 'key_fingerprint', 'server_salt', 'new_server_salt', 'server_public_key_fingerprints', 'ping_id', 'exchange_id'])) {
$arg['strlong'] = true; $arg['strlong'] = true;
} }
@ -587,6 +596,7 @@ trait TL
$arg['datacenter'] = $type['datacenter']; $arg['datacenter'] = $type['datacenter'];
} }
$x[$arg['name']] = $this->deserialize($bytes_io, $arg); $x[$arg['name']] = $this->deserialize($bytes_io, $arg);
if ($arg['name'] === 'random_bytes') { if ($arg['name'] === 'random_bytes') {
if (strlen($x[$arg['name']]) < 15) { if (strlen($x[$arg['name']]) < 15) {
throw new \danog\MadelineProto\SecurityException('random_bytes is too small!'); throw new \danog\MadelineProto\SecurityException('random_bytes is too small!');

View File

@ -57,19 +57,19 @@ trait Tools
public function is_array($elem) public function is_array($elem)
{ {
return is_array($elem) || (is_object($elem) && get_class($elem) === 'Volatile'); return is_array($elem) || ($elem instanceof \Volatile);
} }
public function array_replace_recursive($a, ...$b) public function __call($method, $params)
{ {
return array_replace_recursive($this->array_cast_recursive($a), ...$this->array_cast_recursive($b)); return $method(...$this->array_cast_recursive($params));
} }
public function array_cast_recursive($array) public function array_cast_recursive($array)
{ {
if ($this->is_array($array)) { if ($this->is_array($array)) {
if (!is_array($array)) { if (!is_array($array)) {
$array = $array; $array = (array) $array;
} }
foreach ($array as $key => $value) { foreach ($array as $key => $value) {
$array[$key] = $this->array_cast_recursive($value); $array[$key] = $this->array_cast_recursive($value);