version 0.6.6 - some network fixes and download/upload optimizations

This commit is contained in:
John Preston 2014-10-30 19:23:44 +03:00
parent a8fd1c54c0
commit 5dc9cdbd3c
28 changed files with 418 additions and 159 deletions

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tlinuxupd$AppVersion" ]; then
echo "tlinuxupd$AppVersion not found!";

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tlinux32upd$AppVersion" ]; then
echo "tlinux32upd$AppVersion not found!"

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ ! -f "./../Mac/Release/deploy/$AppVersionStr/tmacupd$AppVersion" ]; then
echo "tmacupd$AppVersion not found!"

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ ! -f "./../Win32/Deploy/deploy/$AppVersionStr/tupdate$AppVersion" ]; then
echo "tupdate$AppVersion not found!"

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
echo "Deploy folder for version $AppVersionStr already exists!"

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
echo "Deploy folder for version $AppVersionStr already exists!"

View File

@ -1,5 +1,5 @@
AppVersionStr=0.6.5
AppVersion=6005
AppVersionStr=0.6.6
AppVersion=6006
if [ -d "./../Mac/Release/deploy/$AppVersionStr" ]; then
echo "Deploy folder for version $AppVersionStr already exists!"

View File

@ -1,6 +1,6 @@
cd ..\Win32\Deploy
call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.5.exe
call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.6.exe
call Prepare.exe -path Telegram.exe -path Updater.exe
mkdir deploy\0.6.5\Telegram
move deploy\0.6.5\Telegram.exe deploy\0.6.5\Telegram\
mkdir deploy\0.6.6\Telegram
move deploy\0.6.6\Telegram.exe deploy\0.6.6\Telegram\
cd ..\..\Telegram

View File

@ -3,9 +3,9 @@
#define MyAppShortName "Telegram"
#define MyAppName "Telegram Desktop"
#define MyAppVersion "0.6.5"
#define MyAppVersionZero "0.6.5"
#define MyAppFullVersion "0.6.5.0"
#define MyAppVersion "0.6.6"
#define MyAppVersionZero "0.6.6"
#define MyAppFullVersion "0.6.6.0"
#define MyAppPublisher "Telegram Messenger LLP"
#define MyAppURL "https://tdesktop.com"
#define MyAppExeName "Telegram.exe"

View File

@ -531,7 +531,7 @@ namespace App {
const MTPDphotoSize &d(size.c_photoSize());
if (d.vlocation.type() == mtpc_fileLocation) {
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, d.vsize.v);
}
} break;
case mtpc_photoCachedSize: {

View File

@ -154,6 +154,9 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
connect(&writeUserConfigTimer, SIGNAL(timeout()), this, SLOT(onWriteUserConfig()));
writeUserConfigTimer.setSingleShot(true);
killDownloadSessionsTimer.setSingleShot(true);
connect(&killDownloadSessionsTimer, SIGNAL(timeout()), this, SLOT(killDownloadSessions()));
if (cManyInstance()) {
startApp();
} else {
@ -326,10 +329,46 @@ void Application::writeUserConfigIn(uint64 ms) {
}
}
void Application::killDownloadSessionsStart(int32 dc) {
if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) {
killDownloadSessionTimes.insert(dc, getms() + MTPKillFileSessionTimeout);
}
if (!killDownloadSessionsTimer.isActive()) {
killDownloadSessionsTimer.start(MTPKillFileSessionTimeout + 5);
}
}
void Application::killDownloadSessionsStop(int32 dc) {
killDownloadSessionTimes.remove(dc);
if (killDownloadSessionTimes.isEmpty() && killDownloadSessionsTimer.isActive()) {
killDownloadSessionsTimer.stop();
}
}
void Application::onWriteUserConfig() {
App::writeUserConfig();
}
void Application::killDownloadSessions() {
uint64 ms = getms(), left = MTPKillFileSessionTimeout;
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
if (i.value() <= ms) {
for (int j = 1; j < MTPDownloadSessionsCount; ++j) {
MTP::killSession(MTP::dld[j] + i.key());
}
i = killDownloadSessionTimes.erase(i);
} else {
if (i.value() - ms < left) {
left = i.value() - ms;
}
++i;
}
}
if (!killDownloadSessionTimes.isEmpty()) {
killDownloadSessionsTimer.start(left);
}
}
void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) {
if (!App::self()) return;

View File

@ -70,6 +70,9 @@ public:
void writeUserConfigIn(uint64 ms);
void killDownloadSessionsStart(int32 dc);
void killDownloadSessionsStop(int32 dc);
signals:
void peerPhotoDone(PeerId peer);
@ -100,10 +103,15 @@ public slots:
void onEnableDebugMode();
void onWriteUserConfig();
void killDownloadSessions();
private:
QMap<MsgId, PeerId> photoUpdates;
QMap<int32, uint64> killDownloadSessionTimes;
QTimer killDownloadSessionsTimer;
void startApp();
typedef QPair<QLocalSocket*, QByteArray> ClientSocket;

View File

@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
static const int32 AppVersion = 6005;
static const wchar_t *AppVersionStr = L"0.6.5";
static const int32 AppVersion = 6006;
static const wchar_t *AppVersionStr = L"0.6.6";
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";
@ -32,7 +32,7 @@ enum {
MTPShortBufferSize = 65535, // of ints, 256 kb
MTPPacketSizeMax = 67108864, // 64 mb
MTPIdsBufferSize = 400, // received msgIds and wereAcked msgIds count stored
MTPCheckResendTimeout = 5000, // how much time passed from send till we resend request or check it's state, in ms
MTPCheckResendTimeout = 10000, // how much time passed from send till we resend request or check it's state, in ms
MTPCheckResendWaiting = 1000, // how much time to wait for some more requests, when resending request or checking it's state, in ms
MTPResendThreshold = 1, // how much ints should message contain for us not to resend, but to check it's state
MTPContainerLives = 600, // container lives 10 minutes in haveSent map
@ -41,6 +41,10 @@ enum {
MTPTcpConnectionWaitTimeout = 3000, // 3 seconds waiting for tcp, until we accept http
MTPMillerRabinIterCount = 30, // 30 Miller-Rabin iterations for dh_prime primality check
MTPUploadSessionsCount = 4, // max 4 upload sessions is created
MTPDownloadSessionsCount = 4, // max 4 download sessions is created
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
MTPEnumDCTimeout = 4000, // 4 seconds timeout for help_getConfig to work (them move to other dc)
MTPDebugBufferSize = 1024 * 1024, // 1 mb start size
@ -209,12 +213,12 @@ enum {
LinkCropLimit = 360, // 360px link length max
DownloadPartSize = 32 * 1024, // 32kb for photo
DownloadPartSize = 64 * 1024, // 64kb for photo
DocumentDownloadPartSize = 128 * 1024, // 128kb for document
MaxUploadPhotoSize = 10 * 1024 * 1024, // 10mb photos max
MaxUploadDocumentSize = 1500 * 1024 * 1024, // 1500mb documents max
UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb
MaxFileQueries = 32, // max 32 file parts downloaded at the same time
MaxFileQueries = 16, // max 16 file parts downloaded at the same time
UploadPartSize = 32 * 1024, // 32kb for photo
DocumentMaxPartsCount = 3000, // no more than 3000 parts
@ -223,7 +227,7 @@ enum {
DocumentUploadPartSize2 = 128 * 1024, // 128kb for small document ( <= 375mb )
DocumentUploadPartSize3 = 256 * 1024, // 256kb for medium document ( <= 750mb )
DocumentUploadPartSize4 = 512 * 1024, // 512kb for large document ( <= 1500mb )
MaxUploadFileParallelSize = 512 * 1024, // max 512kb uploaded at the same time
MaxUploadFileParallelSize = MTPUploadSessionsCount * 512 * 1024, // max 512kb uploaded at the same time in each session
UploadRequestInterval = 500, // one part each half second, if not uploaded faster
MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created

View File

@ -19,8 +19,11 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
#include "fileuploader.h"
FileUploader::FileUploader() : sentSize(0), uploading(0) {
memset(sentSizes, 0, sizeof(sentSizes));
nextTimer.setSingleShot(true);
connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext()));
killSessionsTimer.setSingleShot(true);
connect(&killSessionsTimer, SIGNAL(timeout()), this, SLOT(killSessions()));
}
void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
@ -60,16 +63,36 @@ void FileUploader::currentFailed() {
requestsSent.clear();
docRequestsSent.clear();
queue.remove(uploading);
dcMap.clear();
uploading = 0;
sentSize = 0;
for (int i = 0; i < MTPUploadSessionsCount; ++i) {
sentSizes[i] = 0;
}
sendNext();
}
void FileUploader::sendNext() {
if (sentSize >= MaxUploadFileParallelSize || queue.isEmpty()) return;
void FileUploader::killSessions() {
for (int i = 0; i < MTPUploadSessionsCount; ++i) {
MTP::killSession(MTP::upl[i]);
}
}
void FileUploader::sendNext() {
if (sentSize >= MaxUploadFileParallelSize) return;
bool killing = killSessionsTimer.isActive();
if (queue.isEmpty()) {
if (!killing) {
killSessionsTimer.start(MTPKillFileSessionTimeout);
}
return;
}
if (killing) {
killSessionsTimer.stop();
}
Queue::iterator i = uploading ? queue.find(uploading) : queue.begin();
if (!uploading) {
uploading = i.key();
@ -77,6 +100,12 @@ void FileUploader::sendNext() {
i = queue.begin();
uploading = i.key();
}
int todc = 0;
for (int dc = 1; dc < MTPUploadSessionsCount; ++dc) {
if (sentSizes[dc] < sentSizes[todc]) {
todc = dc;
}
}
if (i->media.parts.isEmpty()) {
if (i->docSentParts >= i->docPartsCount) {
if (requestsSent.isEmpty() && docRequestsSent.isEmpty()) {
@ -125,20 +154,24 @@ void FileUploader::sendNext() {
}
mtpRequestId requestId;
if (i->docSize > UseBigFilesFrom) {
requestId = MTP::send(MTPupload_SaveBigFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_int(i->docPartsCount), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl);
requestId = MTP::send(MTPupload_SaveBigFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_int(i->docPartsCount), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]);
} else {
requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl);
requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]);
}
docRequestsSent.insert(requestId, i->docSentParts);
dcMap.insert(requestId, todc);
sentSize += i->docPartSize;
sentSizes[todc] += i->docPartSize;
i->docSentParts++;
} else {
LocalFileParts::iterator part = i->media.parts.begin();
mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.jpeg_id), MTP_int(part.key()), MTP_string(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl);
mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.jpeg_id), MTP_int(part.key()), MTP_string(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]);
requestsSent.insert(requestId, part.value());
dcMap.insert(requestId, todc);
sentSize += part.value().size();
sentSizes[todc] += part.value().size();
i->media.parts.erase(part);
}
@ -168,7 +201,13 @@ void FileUploader::clear() {
MTP::cancel(i.key());
}
docRequestsSent.clear();
dcMap.clear();
sentSize = 0;
for (int32 i = 0; i < MTPUploadSessionsCount; ++i) {
MTP::killSession(MTP::upl[i]);
sentSizes[i] = 0;
}
killSessionsTimer.stop();
}
void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
@ -182,12 +221,22 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
currentFailed();
return;
} else {
QMap<mtpRequestId, int32>::iterator dcIt = dcMap.find(requestId);
if (dcIt == dcMap.cend()) { // must not happen
currentFailed();
return;
}
int32 dc = dcIt.value();
dcMap.erase(dcIt);
Queue::const_iterator k = queue.constFind(uploading);
if (i != requestsSent.cend()) {
sentSize -= i.value().size();
sentSizes[dc] -= i.value().size();
requestsSent.erase(i);
} else {
sentSize -= k->docPartSize;
sentSizes[dc] -= k->docPartSize;
docRequestsSent.erase(j);
}
if (k->media.type == ToPreparePhoto) {

View File

@ -38,6 +38,7 @@ public:
public slots:
void sendNext();
void killSessions();
signals:
@ -98,11 +99,13 @@ private:
QMap<mtpRequestId, QByteArray> requestsSent;
QMap<mtpRequestId, int32> docRequestsSent;
QMap<mtpRequestId, int32> dcMap;
uint32 sentSize;
uint32 sentSizes[MTPUploadSessionsCount];
MsgId uploading;
Queue queue;
Queue uploaded;
QTimer nextTimer;
QTimer nextTimer, killSessionsTimer;
};

View File

@ -336,7 +336,7 @@ int64 imageCacheSize() {
return globalAquiredSize;
}
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret)) {
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret, size)) {
}
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes) : w(width), h(height), loader(0) {
@ -427,11 +427,11 @@ bool StorageImage::loaded() const {
return check();
}
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) {
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) {
QByteArray key(storageKey(dc, volume, local, secret));
StorageImages::const_iterator i = storageImages.constFind(key);
if (i == storageImages.cend()) {
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret));
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, size));
}
return i.value();
}

View File

@ -107,7 +107,7 @@ LocalImage *getImage(const QPixmap &pixmap, QByteArray format);
class StorageImage : public Image {
public:
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret);
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes);
int32 width() const;
@ -155,7 +155,7 @@ private:
mutable mtpFileLoader *loader;
};
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret);
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes);
Image *getImage(int32 width, int32 height, const MTPFileLocation &location);
@ -166,7 +166,7 @@ public:
}
ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(getImage(pixmap, format)) {
}
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) : Parent(getImage(width, height, dc, volume, local, secret)) {
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0) : Parent(getImage(width, height, dc, volume, local, secret, size)) {
}
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) : Parent(getImage(width, height, dc, volume, local, secret, bytes)) {
}

View File

@ -36,6 +36,7 @@ namespace {
typedef QMap<mtpRequestId, RPCResponseHandler> ParserMap;
ParserMap parserMap;
QMutex parserMapLock;
typedef QMap<mtpRequestId, mtpRequest> RequestMap;
RequestMap requestMap;
@ -268,6 +269,8 @@ namespace _mtp_internal {
}
void unregisterRequest(mtpRequestId requestId) {
requestMap.remove(requestId);
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
@ -283,9 +286,10 @@ namespace _mtp_internal {
mtpRequestId res = reqid();
request->requestId = res;
if (parser.onDone || parser.onFail) {
QMutexLocker locker(&parserMapLock);
parserMap.insert(res, parser);
requestMap.insert(res, request);
}
requestMap.insert(res, request);
return res;
}
@ -298,14 +302,21 @@ namespace _mtp_internal {
}
void clearCallbacks(mtpRequestId requestId, int32 errorCode) {
ParserMap::iterator i = parserMap.find(requestId);
if (i != parserMap.end()) {
if (errorCode) {
rpcErrorOccured(requestId, i.value(), rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode)));
RPCResponseHandler h;
bool found = false;
{
QMutexLocker locker(&parserMapLock);
ParserMap::iterator i = parserMap.find(requestId);
if (i != parserMap.end()) {
h = i.value();
found = true;
parserMap.erase(i);
}
parserMap.erase(i);
}
requestMap.remove(requestId);
if (errorCode && found) {
rpcErrorOccured(requestId, h, rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode)));
}
_mtp_internal::unregisterRequest(requestId);
}
@ -336,6 +347,7 @@ namespace _mtp_internal {
if (!toClear.isEmpty()) {
for (RPCCallbackClears::iterator i = toClear.begin(), e = toClear.end(); i != e; ++i) {
if (cDebug()) {
QMutexLocker locker(&parserMapLock);
if (parserMap.find(i->requestId) != parserMap.end()) {
DEBUG_LOG(("RPC Info: clearing delayed callback %1, error code %2").arg(i->requestId).arg(i->errorCode));
}
@ -347,12 +359,18 @@ namespace _mtp_internal {
}
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) {
ParserMap::iterator i = parserMap.find(requestId);
if (i != parserMap.cend()) {
RPCResponseHandler h(i.value());
parserMap.erase(i);
RPCResponseHandler h;
{
QMutexLocker locker(&parserMapLock);
ParserMap::iterator i = parserMap.find(requestId);
if (i != parserMap.cend()) {
h = i.value();
parserMap.erase(i);
DEBUG_LOG(("RPC Info: found parser for request %1, trying to parse response..").arg(requestId));
DEBUG_LOG(("RPC Info: found parser for request %1, trying to parse response..").arg(requestId));
}
}
if (h.onDone || h.onFail) {
try {
if (from >= end) throw mtpErrorInsufficient();
@ -360,6 +378,7 @@ namespace _mtp_internal {
RPCError err(MTPRpcError(from, end));
DEBUG_LOG(("RPC Info: error received, code %1, type %2, description: %3").arg(err.code()).arg(err.type()).arg(err.description()));
if (!rpcErrorOccured(requestId, h, err)) {
QMutexLocker locker(&parserMapLock);
parserMap.insert(requestId, h);
return;
}
@ -368,17 +387,23 @@ namespace _mtp_internal {
}
} catch (Exception &e) {
if (!rpcErrorOccured(requestId, h, rpcClientError("RESPONSE_PARSE_FAILED", QString("exception text: ") + e.what()))) {
QMutexLocker locker(&parserMapLock);
parserMap.insert(requestId, h);
return;
}
}
requestMap.remove(requestId);
} else {
DEBUG_LOG(("RPC Info: parser not found for %1").arg(requestId));
}
unregisterRequest(requestId);
}
bool hasCallbacks(mtpRequestId requestId) {
QMutexLocker locker(&parserMapLock);
ParserMap::iterator i = parserMap.find(requestId);
return (i != parserMap.cend());
}
void globalCallback(const mtpPrime *from, const mtpPrime *end) {
if (globalHandler.onDone) (*globalHandler.onDone)(0, from, end); // some updates were received
}

View File

@ -36,6 +36,7 @@ namespace _mtp_internal {
void clearCallbacksDelayed(const RPCCallbackClears &requestIds);
void performDelayedClear();
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end);
bool hasCallbacks(mtpRequestId requestId);
void globalCallback(const mtpPrime *from, const mtpPrime *end);
void onStateChange(int32 dc, int32 state);
void onSessionReset(int32 dc);
@ -58,9 +59,19 @@ namespace MTP {
mtpAuthKey &localKey();
void createLocalKey(const QByteArray &pass, QByteArray *salt = 0);
static const uint32 dld = 1 * _mtp_internal::dcShift; // send(req, callbacks, MTP::dld + dc) - for download
static const uint32 upl = 2 * _mtp_internal::dcShift; // send(req, callbacks, MTP::upl + dc) - for upload
static const uint32 cfg = 3 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum
static const uint32 cfg = 1 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum
static const uint32 dld[MTPDownloadSessionsCount] = { // send(req, callbacks, MTP::dld[i] + dc) - for download
0x10 * _mtp_internal::dcShift,
0x11 * _mtp_internal::dcShift,
0x12 * _mtp_internal::dcShift,
0x13 * _mtp_internal::dcShift,
};
static const uint32 upl[MTPUploadSessionsCount] = { // send(req, callbacks, MTP::upl[i] + dc) - for upload
0x20 * _mtp_internal::dcShift,
0x21 * _mtp_internal::dcShift,
0x22 * _mtp_internal::dcShift,
0x23 * _mtp_internal::dcShift,
};
void start();
void restart();

View File

@ -679,6 +679,15 @@ void MTPautoConnection::tcpSend(mtpBuffer &buffer) {
TCP_LOG(("TCP Info: write %1 packet %2 bytes").arg(packetNum).arg(len));
sock.write((const char*)&buffer[0], len);
//int64 b = sock.bytesToWrite();
//if (b > 100000) {
// int a = 0;
//}
//sock.flush();
//int64 b2 = sock.bytesToWrite();
//if (b2 > 0) {
// TCP_LOG(("TCP Info: writing many, %1 left to write").arg(b2));
//}
}
void MTPautoConnection::httpSend(mtpBuffer &buffer) {
@ -1576,6 +1585,11 @@ void MTProtoConnectionPrivate::onSentSome(uint64 size) {
DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain));
}
}
if (dc >= MTP::upl[0] && dc < MTP::upl[MTPUploadSessionsCount - 1] + _mtp_internal::dcShift) {
remain *= MTPUploadSessionsCount;
} else if (dc >= MTP::dld[0] && dc < MTP::dld[MTPDownloadSessionsCount - 1] + _mtp_internal::dcShift) {
remain *= MTPDownloadSessionsCount;
}
connCheckTimer.start(remain);
}
if (!firstSentAt) firstSentAt = getms();
@ -2070,15 +2084,19 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(reqMsgId));
return (badTime ? 0 : 1);
}
if (serverSalt) sessionData->setSalt(serverSalt); // requestsFixTimeSalt with no lookup
unixtimeSet(serverTime, true);
if (badTime) {
if (serverSalt) sessionData->setSalt(serverSalt); // requestsFixTimeSalt with no lookup
unixtimeSet(serverTime, true);
DEBUG_LOG(("Message Info: unixtime updated from mtpc_msgs_state_info, now %1").arg(serverTime));
DEBUG_LOG(("Message Info: unixtime updated from mtpc_msgs_state_info, now %1").arg(serverTime));
badTime = false;
badTime = false;
}
requestBuffer = replyTo.value();
}
QVector<MTPlong> toAck(1, MTP_long(reqMsgId));
QVector<MTPlong> toAckReq(1, MTP_long(reqMsgId)), toAck;
requestsAcked(toAck, true);
if (requestBuffer->size() < 9) {
LOG(("Message Error: bad request %1 found in requestMap, size: %2").arg(reqMsgId).arg(requestBuffer->size()));
return -1;
@ -2207,7 +2225,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
return 0;
}
}
requestsAcked(ids);
requestsAcked(ids, true);
if (typeId == mtpc_gzip_packed) {
DEBUG_LOG(("RPC Info: gzip container"));
@ -2294,7 +2312,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
return 0;
}
}
requestsAcked(ids);
requestsAcked(ids, true);
retryTimeout = 1; // reset restart() timer
} return 1;
@ -2385,7 +2403,7 @@ bool MTProtoConnectionPrivate::requestsFixTimeSalt(const QVector<MTPlong> &ids,
return false;
}
void MTProtoConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids) {
void MTProtoConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids, bool byResponse) {
uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: requests acked, ids %1").arg(logVectorLong(ids)));
@ -2412,10 +2430,20 @@ void MTProtoConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids) {
for (uint32 j = 0; j < inContCount; ++j) {
toAckMore.push_back(MTP_long(*(inContId++)));
}
haveSent.erase(req);
} else {
wereAcked.insert(msgId, req.value()->requestId);
mtpRequestId reqId = req.value()->requestId;
bool moveToAcked = byResponse;
if (!moveToAcked) { // ignore ACK, if we need a response (if we have a handler)
moveToAcked = !_mtp_internal::hasCallbacks(reqId);
}
if (moveToAcked) {
wereAcked.insert(msgId, reqId);
haveSent.erase(req);
} else {
DEBUG_LOG(("Message Info: ignoring ACK for msgId %1 because request %2 requires a response").arg(msgId).arg(reqId));
}
}
haveSent.erase(req);
} else {
DEBUG_LOG(("Message Info: msgId %1 was not found in recent sent, while acking requests, searching in resend..").arg(msgId));
QWriteLocker locker3(sessionData->toResendMutex());
@ -2423,21 +2451,29 @@ void MTProtoConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids) {
mtpRequestIdsMap::iterator reqIt = toResend.find(msgId);
if (reqIt != toResend.cend()) {
mtpRequestId reqId = reqIt.value();
QWriteLocker locker4(sessionData->toSendMutex());
mtpPreRequestMap &toSend(sessionData->toSendMap());
mtpPreRequestMap::iterator req = toSend.find(reqId);
if (req != toSend.cend()) {
wereAcked.insert(msgId, req.value()->requestId);
if (req.value()->requestId != reqId) {
DEBUG_LOG(("Message Error: for msgId %1 found resent request, requestId %2, contains requestId %3").arg(msgId).arg(reqId).arg(req.value()->requestId));
} else {
DEBUG_LOG(("Message Info: acked msgId %1 that was prepared to resend, requestId %2").arg(msgId).arg(reqId));
}
toSend.erase(req);
} else {
DEBUG_LOG(("Message Info: msgId %1 was found in recent resent, requestId %2 was not found in prepared to send").arg(msgId));
bool moveToAcked = byResponse;
if (!moveToAcked) { // ignore ACK, if we need a response (if we have a handler)
moveToAcked = !_mtp_internal::hasCallbacks(reqId);
}
if (moveToAcked) {
QWriteLocker locker4(sessionData->toSendMutex());
mtpPreRequestMap &toSend(sessionData->toSendMap());
mtpPreRequestMap::iterator req = toSend.find(reqId);
if (req != toSend.cend()) {
wereAcked.insert(msgId, req.value()->requestId);
if (req.value()->requestId != reqId) {
DEBUG_LOG(("Message Error: for msgId %1 found resent request, requestId %2, contains requestId %3").arg(msgId).arg(reqId).arg(req.value()->requestId));
} else {
DEBUG_LOG(("Message Info: acked msgId %1 that was prepared to resend, requestId %2").arg(msgId).arg(reqId));
}
toSend.erase(req);
} else {
DEBUG_LOG(("Message Info: msgId %1 was found in recent resent, requestId %2 was not found in prepared to send").arg(msgId));
}
toResend.erase(reqIt);
} else {
DEBUG_LOG(("Message Info: ignoring ACK for msgId %1 because request %2 requires a response").arg(msgId).arg(reqId));
}
toResend.erase(reqIt);
} else {
DEBUG_LOG(("Message Info: msgId %1 was not found in recent resent either").arg(msgId));
}

View File

@ -374,7 +374,7 @@ private:
bool requestsFixTimeSalt(const QVector<MTPlong> &ids, int32 serverTime, uint64 serverSalt);
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
void requestsAcked(const QVector<MTPlong> &ids);
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
mtpPingId pingId, toSendPingId;
mtpMsgId pingMsgId;

View File

@ -57,8 +57,10 @@ void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpP
QString str = QString::fromUtf8(strUtf8);
if (str.toUtf8() == strUtf8) {
to.add("\"").add(str.replace('\\', "\\\\").replace('"', "\\\"").replace('\n', "\\n")).add("\" [STRING]");
} else {
} else if (strUtf8.size() < 64) {
to.add(mb(strUtf8.constData(), strUtf8.size()).str()).add(" [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
} else {
to.add(mb(strUtf8.constData(), 16).str()).add(".. [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
}
} break;

View File

@ -19,8 +19,17 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
#include "mainwidget.h"
#include "window.h"
#include "application.h"
namespace {
int32 _priority = 1;
struct DataRequested {
DataRequested() {
memset(v, 0, sizeof(v));
}
int64 v[MTPDownloadSessionsCount];
};
QMap<int32, DataRequested> _dataRequested;
}
struct mtpFileLoaderQueue {
mtpFileLoaderQueue() : queries(0), start(0), end(0) {
@ -34,10 +43,10 @@ namespace {
LoaderQueues queues;
}
mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret) : prev(0), next(0),
priority(0), inQueue(false), complete(false), requestId(0),
dc(dc), locationType(0), volume(volume), local(local), secret(secret),
id(0), access(0), initialSize(0), size(0), type(MTP_storage_fileUnknown()) {
mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : prev(0), next(0),
priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
dc(dc), locationType(0), volume(volume), local(local), secret(secret),
id(0), access(0), size(size), type(MTP_storage_fileUnknown()) {
LoaderQueues::iterator i = queues.find(dc);
if (i == queues.cend()) {
i = queues.insert(dc, mtpFileLoaderQueue());
@ -46,23 +55,23 @@ mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const i
}
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size) : prev(0), next(0),
priority(0), inQueue(false), complete(false), requestId(0),
priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
dc(dc), locationType(locType),
id(id), access(access), file(to), duplicateInData(false), initialSize(size), type(MTP_storage_fileUnknown()) {
LoaderQueues::iterator i = queues.find(MTP::dld + dc);
id(id), access(access), file(to), duplicateInData(false), size(size), type(MTP_storage_fileUnknown()) {
LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc);
if (i == queues.cend()) {
i = queues.insert(MTP::dld + dc, mtpFileLoaderQueue());
i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue());
}
queue = &i.value();
}
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size, bool todata) : prev(0), next(0),
priority(0), inQueue(false), complete(false), requestId(0),
priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
dc(dc), locationType(locType),
id(id), access(access), file(to), duplicateInData(todata), initialSize(size), type(MTP_storage_fileUnknown()) {
LoaderQueues::iterator i = queues.find(MTP::dld + dc);
id(id), access(access), file(to), duplicateInData(todata), size(size), type(MTP_storage_fileUnknown()) {
LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc);
if (i == queues.cend()) {
i = queues.insert(MTP::dld + dc, mtpFileLoaderQueue());
i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue());
}
queue = &i.value();
}
@ -89,8 +98,8 @@ float64 mtpFileLoader::currentProgress() const {
return float64(currentOffset()) / fullSize();
}
int32 mtpFileLoader::currentOffset() const {
return file.isOpen() ? file.size() : data.size();
int32 mtpFileLoader::currentOffset(bool includeSkipped) const {
return (file.isOpen() ? file.size() : data.size()) - (includeSkipped ? 0 : skippedBytes);
}
int32 mtpFileLoader::fullSize() const {
@ -109,17 +118,18 @@ uint64 mtpFileLoader::objId() const {
void mtpFileLoader::loadNext() {
if (queue->queries >= MaxFileQueries) return;
for (mtpFileLoader *i = queue->start; i; i = i->next) {
if (i->loadPart() && queue->queries >= MaxFileQueries) return;
for (mtpFileLoader *i = queue->start; i;) {
if (i->loadPart()) {
if (queue->queries >= MaxFileQueries) return;
} else {
i = i->next;
}
}
}
void mtpFileLoader::finishFail() {
bool started = currentOffset() > 0;
if (requestId) {
requestId = 0;
--queue->queries;
}
bool started = currentOffset(true) > 0;
cancelRequests();
type = MTP_storage_fileUnknown();
complete = true;
if (file.isOpen()) {
@ -133,7 +143,8 @@ void mtpFileLoader::finishFail() {
}
bool mtpFileLoader::loadPart() {
if (complete || requestId) return false;
if (complete || lastComplete || !requests.isEmpty() && !size) return false;
if (size && nextRequestOffset >= size) return false;
int32 limit = DocumentDownloadPartSize;
MTPInputFileLocation loc;
@ -148,54 +159,100 @@ bool mtpFileLoader::loadPart() {
break;
}
++queue->queries;
int32 offset = currentOffset();
int32 offset = nextRequestOffset, dcIndex = 0;
DataRequested &dr(_dataRequested[dc]);
if (size) {
for (int32 i = 1; i < MTPDownloadSessionsCount; ++i) {
if (dr.v[i] < dr.v[dcIndex]) {
dcIndex = i;
}
}
}
if (dcIndex) {
App::app()->killDownloadSessionsStop(dc);
}
MTPupload_GetFile request(MTPupload_getFile(loc, MTP_int(offset), MTP_int(limit)));
requestId = MTP::send(request, rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld + dc, 50);
mtpRequestId reqId = MTP::send(request, rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld[dcIndex] + dc, 50);
++queue->queries;
dr.v[dcIndex] += limit;
requests.insert(reqId, dcIndex);
nextRequestOffset += limit;
return true;
}
void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result) {
if (requestId) {
--queue->queries;
requestId = 0;
}
if (offset == currentOffset()) {
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
const MTPDupload_file &d(result.c_upload_file());
const string &bytes(d.vbytes.c_string().v);
if (bytes.size()) {
if (file.isOpen()) {
if (file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) {
return finishFail();
}
} else {
data.append(bytes.data(), bytes.size());
void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req) {
Requests::iterator i = requests.find(req);
if (i == requests.cend()) return;
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
int32 dcIndex = i.value();
_dataRequested[dc].v[dcIndex] -= limit;
--queue->queries;
requests.erase(i);
const MTPDupload_file &d(result.c_upload_file());
const string &bytes(d.vbytes.c_string().v);
if (bytes.size()) {
if (file.isOpen()) {
int64 fsize = file.size();
if (offset < fsize) {
skippedBytes -= bytes.size();
} else if (offset > fsize) {
skippedBytes += offset - fsize;
}
file.seek(offset);
if (file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) {
return finishFail();
}
}
if (bytes.size() && !(bytes.size() % 1024)) { // good next offset
// offset += bytes.size();
} else {
if (duplicateInData && !file.fileName().isEmpty()) {
if (!file.open(QIODevice::WriteOnly)) {
return finishFail();
}
if (file.write(data) != qint64(data.size())) {
return finishFail();
}
data.reserve(offset + bytes.size());
if (offset > data.size()) {
skippedBytes += offset - data.size();
data.resize(offset);
}
type = d.vtype;
complete = true;
if (file.isOpen()) {
file.close();
psPostprocessFile(QFileInfo(file).absoluteFilePath());
if (offset == data.size()) {
data.append(bytes.data(), bytes.size());
} else {
skippedBytes -= bytes.size();
if (offset + bytes.size() > data.size()) {
data.resize(offset + bytes.size());
}
memcpy(data.data() + offset, bytes.data(), bytes.size());
}
removeFromQueue();
App::wnd()->update();
App::wnd()->notifyUpdateAllPhotos();
}
emit progress(this);
}
if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset
lastComplete = true;
}
if (requests.isEmpty() && (lastComplete || (size && nextRequestOffset >= size))) {
if (duplicateInData && !file.fileName().isEmpty()) {
if (!file.open(QIODevice::WriteOnly)) {
return finishFail();
}
if (file.write(data) != qint64(data.size())) {
return finishFail();
}
}
type = d.vtype;
complete = true;
if (file.isOpen()) {
file.close();
psPostprocessFile(QFileInfo(file).absoluteFilePath());
}
removeFromQueue();
App::wnd()->update();
App::wnd()->notifyUpdateAllPhotos();
if (!queue->queries && dcIndex) {
App::app()->killDownloadSessionsStart(dc);
}
}
emit progress(this);
loadNext();
}
@ -325,11 +382,7 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
}
void mtpFileLoader::cancel() {
bool started = currentOffset() > 0;
if (requestId) {
requestId = 0;
--queue->queries;
}
cancelRequests();
type = MTP_storage_fileUnknown();
complete = true;
if (file.isOpen()) {
@ -342,6 +395,26 @@ void mtpFileLoader::cancel() {
loadNext();
}
void mtpFileLoader::cancelRequests() {
if (requests.isEmpty()) return;
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
bool wasIndex = false;
DataRequested &dr(_dataRequested[dc]);
for (Requests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
MTP::cancel(i.key());
int32 dcIndex = i.value();
dr.v[dcIndex] -= limit;
if (dcIndex) wasIndex = true;
}
queue->queries -= requests.size();
requests.clear();
if (!queue->queries && wasIndex) {
App::app()->killDownloadSessionsStart(dc);
}
}
bool mtpFileLoader::loading() const {
return inQueue;
}
@ -353,6 +426,7 @@ void mtpFileLoader::started(bool loadFirst, bool prior) {
mtpFileLoader::~mtpFileLoader() {
removeFromQueue();
cancelRequests();
}
namespace MTP {

View File

@ -27,7 +27,7 @@ class mtpFileLoader : public QObject, public RPCSender {
public:
mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret);
mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size);
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size, bool todata);
bool done() const;
@ -35,7 +35,7 @@ public:
const QByteArray &bytes() const;
QString fileName() const;
float64 currentProgress() const;
int32 currentOffset() const;
int32 currentOffset(bool includeSkipped = false) const;
int32 fullSize() const;
void setFileName(const QString &filename); // set filename for duplicateInData loader
@ -61,14 +61,22 @@ private:
mtpFileLoaderQueue *queue;
bool inQueue, complete;
int32 requestId;
void cancelRequests();
typedef QMap<mtpRequestId, int32> Requests;
Requests requests;
int32 skippedBytes;
int32 nextRequestOffset;
bool lastComplete;
void started(bool loadFirst, bool prior);
void removeFromQueue();
void loadNext();
void finishFail();
bool loadPart();
void partLoaded(int32 offset, const MTPupload_File &result);
void partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req);
bool partFailed(const RPCError &error);
int32 dc;
@ -82,7 +90,6 @@ private:
uint64 access;
QFile file;
bool duplicateInData;
int32 initialSize;
QByteArray data;

View File

@ -123,6 +123,7 @@ void unixtimeSet(int32 serverTime, bool force) {
}
unixtimeWasSet = true;
unixtimeDelta = serverTime + 1 - myunixtime();
DEBUG_LOG(("MTP Info: now unixtimeDelta is %1").arg(unixtimeDelta));
}
_initMsgIdConstants();
}

View File

@ -11,7 +11,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.6.5</string>
<string>0.6.6</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NOTE</key>

Binary file not shown.

View File

@ -1515,7 +1515,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.6.5;
CURRENT_PROJECT_VERSION = 0.6.6;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@ -1533,7 +1533,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 0.6.5;
CURRENT_PROJECT_VERSION = 0.6.6;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_OPTIMIZATION_LEVEL = fast;
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@ -1559,10 +1559,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.6.5;
CURRENT_PROJECT_VERSION = 0.6.6;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.6;
DYLIB_CURRENT_VERSION = 0.6.5;
DYLIB_CURRENT_VERSION = 0.6.6;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@ -1701,10 +1701,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.6.5;
CURRENT_PROJECT_VERSION = 0.6.6;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.6;
DYLIB_CURRENT_VERSION = 0.6.5;
DYLIB_CURRENT_VERSION = 0.6.6;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;