|
Packit |
1fb8d4 |
/**
|
|
Packit |
1fb8d4 |
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
Packit |
1fb8d4 |
* Bulk Compression
|
|
Packit |
1fb8d4 |
*
|
|
Packit |
1fb8d4 |
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
|
Packit |
1fb8d4 |
*
|
|
Packit |
1fb8d4 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
Packit |
1fb8d4 |
* you may not use this file except in compliance with the License.
|
|
Packit |
1fb8d4 |
* You may obtain a copy of the License at
|
|
Packit |
1fb8d4 |
*
|
|
Packit |
1fb8d4 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
1fb8d4 |
*
|
|
Packit |
1fb8d4 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
1fb8d4 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
1fb8d4 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
1fb8d4 |
* See the License for the specific language governing permissions and
|
|
Packit |
1fb8d4 |
* limitations under the License.
|
|
Packit |
1fb8d4 |
*/
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#ifdef HAVE_CONFIG_H
|
|
Packit |
1fb8d4 |
#include "config.h"
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#include "bulk.h"
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#define TAG "com.freerdp.core"
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
//#define WITH_BULK_DEBUG 1
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
static INLINE const char* bulk_get_compression_flags_string(UINT32 flags)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
flags &= BULK_COMPRESSION_FLAGS_MASK;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (flags == 0)
|
|
Packit |
1fb8d4 |
return "PACKET_UNCOMPRESSED";
|
|
Packit |
1fb8d4 |
else if (flags == PACKET_COMPRESSED)
|
|
Packit |
1fb8d4 |
return "PACKET_COMPRESSED";
|
|
Packit |
1fb8d4 |
else if (flags == PACKET_AT_FRONT)
|
|
Packit |
1fb8d4 |
return "PACKET_AT_FRONT";
|
|
Packit |
1fb8d4 |
else if (flags == PACKET_FLUSHED)
|
|
Packit |
1fb8d4 |
return "PACKET_FLUSHED";
|
|
Packit |
1fb8d4 |
else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT))
|
|
Packit |
1fb8d4 |
return "PACKET_COMPRESSED | PACKET_AT_FRONT";
|
|
Packit |
1fb8d4 |
else if (flags == (PACKET_COMPRESSED | PACKET_FLUSHED))
|
|
Packit |
1fb8d4 |
return "PACKET_COMPRESSED | PACKET_FLUSHED";
|
|
Packit |
1fb8d4 |
else if (flags == (PACKET_AT_FRONT | PACKET_FLUSHED))
|
|
Packit |
1fb8d4 |
return "PACKET_AT_FRONT | PACKET_FLUSHED";
|
|
Packit |
1fb8d4 |
else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED))
|
|
Packit |
1fb8d4 |
return "PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED";
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
return "PACKET_UNKNOWN";
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
UINT32 bulk_compression_level(rdpBulk* bulk)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
rdpSettings* settings = bulk->context->settings;
|
|
Packit |
1fb8d4 |
bulk->CompressionLevel = (settings->CompressionLevel >= PACKET_COMPR_TYPE_RDP61) ?
|
|
Packit |
1fb8d4 |
PACKET_COMPR_TYPE_RDP61 : settings->CompressionLevel;
|
|
Packit |
1fb8d4 |
return bulk->CompressionLevel;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
UINT32 bulk_compression_max_size(rdpBulk* bulk)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
bulk_compression_level(bulk);
|
|
Packit |
1fb8d4 |
bulk->CompressionMaxSize = (bulk->CompressionLevel < PACKET_COMPR_TYPE_64K) ? 8192 : 65536;
|
|
Packit |
1fb8d4 |
return bulk->CompressionMaxSize;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
static INLINE int bulk_compress_validate(rdpBulk* bulk, BYTE* pSrcData, UINT32 SrcSize,
|
|
Packit |
1fb8d4 |
BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
int status;
|
|
Packit |
1fb8d4 |
BYTE* _pSrcData = NULL;
|
|
Packit |
1fb8d4 |
BYTE* _pDstData = NULL;
|
|
Packit |
1fb8d4 |
UINT32 _SrcSize = 0;
|
|
Packit |
1fb8d4 |
UINT32 _DstSize = 0;
|
|
Packit |
1fb8d4 |
UINT32 _Flags = 0;
|
|
Packit |
1fb8d4 |
_pSrcData = *ppDstData;
|
|
Packit |
1fb8d4 |
_SrcSize = *pDstSize;
|
|
Packit |
1fb8d4 |
_Flags = *pFlags | bulk->CompressionLevel;
|
|
Packit |
1fb8d4 |
status = bulk_decompress(bulk, _pSrcData, _SrcSize, &_pDstData, &_DstSize, _Flags);
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (status < 0)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG, "compression/decompression failure");
|
|
Packit |
1fb8d4 |
return status;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (_DstSize != SrcSize)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG, "compression/decompression size mismatch: Actual: %"PRIu32", Expected: %"PRIu32"",
|
|
Packit |
1fb8d4 |
_DstSize, SrcSize);
|
|
Packit |
1fb8d4 |
return -1;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (memcmp(_pDstData, pSrcData, SrcSize) != 0)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG, "compression/decompression input/output mismatch! flags: 0x%08"PRIX32"", _Flags);
|
|
Packit |
1fb8d4 |
#if 1
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG, "Actual:");
|
|
Packit |
1fb8d4 |
winpr_HexDump(TAG, WLOG_DEBUG, _pDstData, SrcSize);
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG, "Expected:");
|
|
Packit |
1fb8d4 |
winpr_HexDump(TAG, WLOG_DEBUG, pSrcData, SrcSize);
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
return -1;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
return status;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
int bulk_decompress(rdpBulk* bulk, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData,
|
|
Packit |
1fb8d4 |
UINT32* pDstSize, UINT32 flags)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
UINT32 type;
|
|
Packit |
1fb8d4 |
int status = -1;
|
|
Packit |
1fb8d4 |
rdpMetrics* metrics;
|
|
Packit |
1fb8d4 |
UINT32 CompressedBytes;
|
|
Packit |
1fb8d4 |
UINT32 UncompressedBytes;
|
|
Packit |
1fb8d4 |
double CompressionRatio;
|
|
Packit |
1fb8d4 |
metrics = bulk->context->metrics;
|
|
Packit |
1fb8d4 |
bulk_compression_max_size(bulk);
|
|
Packit |
1fb8d4 |
type = flags & BULK_COMPRESSION_TYPE_MASK;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (flags & BULK_COMPRESSION_FLAGS_MASK)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
switch (type)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
case PACKET_COMPR_TYPE_8K:
|
|
Packit |
1fb8d4 |
mppc_set_compression_level(bulk->mppcRecv, 0);
|
|
Packit |
1fb8d4 |
status = mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
|
|
Packit |
1fb8d4 |
break;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
case PACKET_COMPR_TYPE_64K:
|
|
Packit |
1fb8d4 |
mppc_set_compression_level(bulk->mppcRecv, 1);
|
|
Packit |
1fb8d4 |
status = mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
|
|
Packit |
1fb8d4 |
break;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
case PACKET_COMPR_TYPE_RDP6:
|
|
Packit |
1fb8d4 |
status = ncrush_decompress(bulk->ncrushRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
|
|
Packit |
1fb8d4 |
break;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
case PACKET_COMPR_TYPE_RDP61:
|
|
Packit |
1fb8d4 |
status = xcrush_decompress(bulk->xcrushRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
|
|
Packit |
1fb8d4 |
break;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
case PACKET_COMPR_TYPE_RDP8:
|
|
Packit |
1fb8d4 |
status = -1;
|
|
Packit |
1fb8d4 |
break;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
else
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
*ppDstData = pSrcData;
|
|
Packit |
1fb8d4 |
*pDstSize = SrcSize;
|
|
Packit |
1fb8d4 |
status = 0;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (status >= 0)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
CompressedBytes = SrcSize;
|
|
Packit |
1fb8d4 |
UncompressedBytes = *pDstSize;
|
|
Packit |
1fb8d4 |
CompressionRatio = metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
|
|
Packit |
1fb8d4 |
#ifdef WITH_BULK_DEBUG
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG,
|
|
Packit |
1fb8d4 |
"Decompress Type: %"PRIu32" Flags: %s (0x%08"PRIX32") Compression Ratio: %f (%"PRIu32" / %"PRIu32"), Total: %f (%"PRIu64" / %"PRIu64")",
|
|
Packit |
1fb8d4 |
type, bulk_get_compression_flags_string(flags), flags,
|
|
Packit |
1fb8d4 |
CompressionRatio, CompressedBytes, UncompressedBytes,
|
|
Packit |
1fb8d4 |
metrics->TotalCompressionRatio, metrics->TotalCompressedBytes,
|
|
Packit |
1fb8d4 |
metrics->TotalUncompressedBytes);
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
else
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
WLog_ERR(TAG, "Decompression failure!");
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
return status;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
int bulk_compress(rdpBulk* bulk, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize,
|
|
Packit |
1fb8d4 |
UINT32* pFlags)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
int status = -1;
|
|
Packit |
1fb8d4 |
rdpMetrics* metrics;
|
|
Packit |
1fb8d4 |
UINT32 CompressedBytes;
|
|
Packit |
1fb8d4 |
UINT32 UncompressedBytes;
|
|
Packit |
1fb8d4 |
double CompressionRatio;
|
|
Packit |
1fb8d4 |
metrics = bulk->context->metrics;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if ((SrcSize <= 50) || (SrcSize >= 16384))
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
*ppDstData = pSrcData;
|
|
Packit |
1fb8d4 |
*pDstSize = SrcSize;
|
|
Packit |
1fb8d4 |
return 0;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
*ppDstData = bulk->OutputBuffer;
|
|
Packit |
1fb8d4 |
*pDstSize = sizeof(bulk->OutputBuffer);
|
|
Packit |
1fb8d4 |
bulk_compression_level(bulk);
|
|
Packit |
1fb8d4 |
bulk_compression_max_size(bulk);
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if ((bulk->CompressionLevel == PACKET_COMPR_TYPE_8K) ||
|
|
Packit |
1fb8d4 |
(bulk->CompressionLevel == PACKET_COMPR_TYPE_64K))
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
mppc_set_compression_level(bulk->mppcSend, bulk->CompressionLevel);
|
|
Packit |
1fb8d4 |
status = mppc_compress(bulk->mppcSend, pSrcData, SrcSize, ppDstData, pDstSize, pFlags);
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
else if (bulk->CompressionLevel == PACKET_COMPR_TYPE_RDP6)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
status = ncrush_compress(bulk->ncrushSend, pSrcData, SrcSize, ppDstData, pDstSize, pFlags);
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
else if (bulk->CompressionLevel == PACKET_COMPR_TYPE_RDP61)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
status = xcrush_compress(bulk->xcrushSend, pSrcData, SrcSize, ppDstData, pDstSize, pFlags);
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
else
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
status = -1;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (status >= 0)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
CompressedBytes = *pDstSize;
|
|
Packit |
1fb8d4 |
UncompressedBytes = SrcSize;
|
|
Packit |
1fb8d4 |
CompressionRatio = metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
|
|
Packit |
1fb8d4 |
#ifdef WITH_BULK_DEBUG
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
WLog_DBG(TAG,
|
|
Packit |
1fb8d4 |
"Compress Type: %"PRIu32" Flags: %s (0x%08"PRIX32") Compression Ratio: %f (%"PRIu32" / %"PRIu32"), Total: %f (%"PRIu64" / %"PRIu64")",
|
|
Packit |
1fb8d4 |
bulk->CompressionLevel, bulk_get_compression_flags_string(*pFlags), *pFlags,
|
|
Packit |
1fb8d4 |
CompressionRatio, CompressedBytes, UncompressedBytes,
|
|
Packit |
1fb8d4 |
metrics->TotalCompressionRatio, metrics->TotalCompressedBytes,
|
|
Packit |
1fb8d4 |
metrics->TotalUncompressedBytes);
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#if 0
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (bulk_compress_validate(bulk, pSrcData, SrcSize, ppDstData, pDstSize, pFlags) < 0)
|
|
Packit |
1fb8d4 |
status = -1;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
return status;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
void bulk_reset(rdpBulk* bulk)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
mppc_context_reset(bulk->mppcSend, FALSE);
|
|
Packit |
1fb8d4 |
mppc_context_reset(bulk->mppcRecv, FALSE);
|
|
Packit |
1fb8d4 |
ncrush_context_reset(bulk->ncrushRecv, FALSE);
|
|
Packit |
1fb8d4 |
ncrush_context_reset(bulk->ncrushSend, FALSE);
|
|
Packit |
1fb8d4 |
xcrush_context_reset(bulk->xcrushRecv, FALSE);
|
|
Packit |
1fb8d4 |
xcrush_context_reset(bulk->xcrushSend, FALSE);
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
rdpBulk* bulk_new(rdpContext* context)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
rdpBulk* bulk;
|
|
Packit |
1fb8d4 |
bulk = (rdpBulk*) calloc(1, sizeof(rdpBulk));
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
if (bulk)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
bulk->context = context;
|
|
Packit |
1fb8d4 |
bulk->mppcSend = mppc_context_new(1, TRUE);
|
|
Packit |
1fb8d4 |
bulk->mppcRecv = mppc_context_new(1, FALSE);
|
|
Packit |
1fb8d4 |
bulk->ncrushRecv = ncrush_context_new(FALSE);
|
|
Packit |
1fb8d4 |
bulk->ncrushSend = ncrush_context_new(TRUE);
|
|
Packit |
1fb8d4 |
bulk->xcrushRecv = xcrush_context_new(FALSE);
|
|
Packit |
1fb8d4 |
bulk->xcrushSend = xcrush_context_new(TRUE);
|
|
Packit |
1fb8d4 |
bulk->CompressionLevel = context->settings->CompressionLevel;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
return bulk;
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
void bulk_free(rdpBulk* bulk)
|
|
Packit |
1fb8d4 |
{
|
|
Packit |
1fb8d4 |
if (!bulk)
|
|
Packit |
1fb8d4 |
return;
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
mppc_context_free(bulk->mppcSend);
|
|
Packit |
1fb8d4 |
mppc_context_free(bulk->mppcRecv);
|
|
Packit |
1fb8d4 |
ncrush_context_free(bulk->ncrushRecv);
|
|
Packit |
1fb8d4 |
ncrush_context_free(bulk->ncrushSend);
|
|
Packit |
1fb8d4 |
xcrush_context_free(bulk->xcrushRecv);
|
|
Packit |
1fb8d4 |
xcrush_context_free(bulk->xcrushSend);
|
|
Packit |
1fb8d4 |
free(bulk);
|
|
Packit |
1fb8d4 |
}
|