Blame libfreerdp/core/bulk.c

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