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