FreeRDP
Loading...
Searching...
No Matches
bulk.c
1
20#include <math.h>
21#include <winpr/assert.h>
22
23#include <freerdp/config.h>
24
25#include "../core/settings.h"
26#include "bulk.h"
27#include "../codec/mppc.h"
28#include "../codec/ncrush.h"
29#include "../codec/xcrush.h"
30
31#include <freerdp/log.h>
32#define TAG FREERDP_TAG("core")
33
34//#define WITH_BULK_DEBUG 1
35
36struct rdp_bulk
37{
38 ALIGN64 rdpContext* context;
39 ALIGN64 UINT32 CompressionLevel;
40 ALIGN64 UINT16 CompressionMaxSize;
41 ALIGN64 MPPC_CONTEXT* mppcSend;
42 ALIGN64 MPPC_CONTEXT* mppcRecv;
43 ALIGN64 NCRUSH_CONTEXT* ncrushRecv;
44 ALIGN64 NCRUSH_CONTEXT* ncrushSend;
45 ALIGN64 XCRUSH_CONTEXT* xcrushRecv;
46 ALIGN64 XCRUSH_CONTEXT* xcrushSend;
47 ALIGN64 BYTE OutputBuffer[65536];
48};
49
50#if defined(WITH_BULK_DEBUG)
51static inline const char* bulk_get_compression_flags_string(UINT32 flags)
52{
53 flags &= BULK_COMPRESSION_FLAGS_MASK;
54
55 if (flags == 0)
56 return "PACKET_UNCOMPRESSED";
57 else if (flags == PACKET_COMPRESSED)
58 return "PACKET_COMPRESSED";
59 else if (flags == PACKET_AT_FRONT)
60 return "PACKET_AT_FRONT";
61 else if (flags == PACKET_FLUSHED)
62 return "PACKET_FLUSHED";
63 else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT))
64 return "PACKET_COMPRESSED | PACKET_AT_FRONT";
65 else if (flags == (PACKET_COMPRESSED | PACKET_FLUSHED))
66 return "PACKET_COMPRESSED | PACKET_FLUSHED";
67 else if (flags == (PACKET_AT_FRONT | PACKET_FLUSHED))
68 return "PACKET_AT_FRONT | PACKET_FLUSHED";
69 else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED))
70 return "PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED";
71
72 return "PACKET_UNKNOWN";
73}
74#endif
75
76WINPR_ATTR_NODISCARD
77static UINT32 bulk_compression_level(rdpBulk* WINPR_RESTRICT bulk)
78{
79 WINPR_ASSERT(bulk);
80 WINPR_ASSERT(bulk->context);
81 const rdpSettings* settings = bulk->context->settings;
82 WINPR_ASSERT(settings);
83
84 const UINT32 CompressionLevel = freerdp_settings_get_uint32(settings, FreeRDP_CompressionLevel);
85 bulk->CompressionLevel =
86 (CompressionLevel >= PACKET_COMPR_TYPE_RDP61) ? PACKET_COMPR_TYPE_RDP61 : CompressionLevel;
87 WINPR_ASSERT(bulk->CompressionLevel <= UINT16_MAX);
88 return bulk->CompressionLevel;
89}
90
91static void bulk_update_compression_max_size(rdpBulk* WINPR_RESTRICT bulk)
92{
93 WINPR_ASSERT(bulk);
94 const UINT32 CompressionLevel = bulk_compression_level(bulk);
95 bulk->CompressionMaxSize = (CompressionLevel < PACKET_COMPR_TYPE_64K) ? 8192 : UINT16_MAX;
96}
97UINT16 bulk_compression_max_size(rdpBulk* WINPR_RESTRICT bulk)
98{
99 bulk_update_compression_max_size(bulk);
100 return bulk->CompressionMaxSize;
101}
102
103#if defined(WITH_BULK_DEBUG)
104static inline int bulk_compress_validate(rdpBulk* bulk, const BYTE* pSrcData, UINT32 SrcSize,
105 const BYTE* pDstData, UINT32 DstSize, UINT32 Flags)
106{
107 int status;
108 const BYTE* v_pSrcData = nullptr;
109 const BYTE* v_pDstData = nullptr;
110 UINT32 v_SrcSize = 0;
111 UINT32 v_DstSize = 0;
112 UINT32 v_Flags = 0;
113
114 WINPR_ASSERT(bulk);
115 WINPR_ASSERT(pSrcData);
116 WINPR_ASSERT(pDstData);
117
118 v_pSrcData = pDstData;
119 v_SrcSize = DstSize;
120 v_Flags = Flags | bulk->CompressionLevel;
121 status = bulk_decompress(bulk, v_pSrcData, v_SrcSize, &v_pDstData, &v_DstSize, v_Flags);
122
123 if (status < 0)
124 {
125 WLog_DBG(TAG, "compression/decompression failure");
126 return status;
127 }
128
129 if (v_DstSize != SrcSize)
130 {
131 WLog_DBG(TAG,
132 "compression/decompression size mismatch: Actual: %" PRIu32 ", Expected: %" PRIu32
133 "",
134 v_DstSize, SrcSize);
135 return -1;
136 }
137
138 if (memcmp(v_pDstData, pSrcData, SrcSize) != 0)
139 {
140 WLog_DBG(TAG, "compression/decompression input/output mismatch! flags: 0x%08" PRIX32 "",
141 v_Flags);
142#if 1
143 WLog_DBG(TAG, "Actual:");
144 winpr_HexDump(TAG, WLOG_DEBUG, v_pDstData, SrcSize);
145 WLog_DBG(TAG, "Expected:");
146 winpr_HexDump(TAG, WLOG_DEBUG, pSrcData, SrcSize);
147#endif
148 return -1;
149 }
150
151 return status;
152}
153#endif
154
155int bulk_decompress(rdpBulk* WINPR_RESTRICT bulk, const BYTE* WINPR_RESTRICT pSrcData,
156 UINT32 SrcSize, const BYTE** WINPR_RESTRICT ppDstData,
157 UINT32* WINPR_RESTRICT pDstSize, UINT32 flags)
158{
159 int status = -1;
160
161 WINPR_ASSERT(bulk);
162 WINPR_ASSERT(bulk->context);
163 WINPR_ASSERT(pSrcData);
164 WINPR_ASSERT(ppDstData);
165 WINPR_ASSERT(pDstSize);
166
167 rdpMetrics* metrics = bulk->context->metrics;
168 WINPR_ASSERT(metrics);
169
170 bulk_update_compression_max_size(bulk);
171 const UINT32 type = flags & BULK_COMPRESSION_TYPE_MASK;
172
173 if (flags & BULK_COMPRESSION_FLAGS_MASK)
174 {
175 switch (type)
176 {
177 case PACKET_COMPR_TYPE_8K:
178 mppc_set_compression_level(bulk->mppcRecv, 0);
179 status =
180 mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
181 break;
182
183 case PACKET_COMPR_TYPE_64K:
184 mppc_set_compression_level(bulk->mppcRecv, 1);
185 status =
186 mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
187 break;
188
189 case PACKET_COMPR_TYPE_RDP6:
190 status = ncrush_decompress(bulk->ncrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
191 flags);
192 break;
193
194 case PACKET_COMPR_TYPE_RDP61:
195 status = xcrush_decompress(bulk->xcrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
196 flags);
197 break;
198
199 case PACKET_COMPR_TYPE_RDP8:
200 WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32,
201 bulk->CompressionLevel);
202 status = -1;
203 break;
204 default:
205 WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
206 status = -1;
207 break;
208 }
209 }
210 else
211 {
212 *ppDstData = pSrcData;
213 *pDstSize = SrcSize;
214 status = 0;
215 }
216
217 if (status >= 0)
218 {
219 const UINT32 CompressedBytes = SrcSize;
220 const UINT32 UncompressedBytes = *pDstSize;
221 const double CompressionRatio =
222 metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
223#ifdef WITH_BULK_DEBUG
224 {
225 WLog_DBG(TAG,
226 "Decompress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
227 ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
228 " / %" PRIu64 ")",
229 type, bulk_get_compression_flags_string(flags), flags, CompressionRatio,
230 CompressedBytes, UncompressedBytes, metrics->TotalCompressionRatio,
231 metrics->TotalCompressedBytes, metrics->TotalUncompressedBytes);
232 }
233#else
234 WINPR_UNUSED(CompressionRatio);
235#endif
236 }
237 else
238 {
239 WLog_ERR(TAG, "Decompression failure!");
240 }
241
242 return status;
243}
244
245int bulk_compress(rdpBulk* WINPR_RESTRICT bulk, const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
246 const BYTE** WINPR_RESTRICT ppDstData, UINT32* WINPR_RESTRICT pDstSize,
247 UINT32* WINPR_RESTRICT pFlags)
248{
249 int status = -1;
250
251 WINPR_ASSERT(bulk);
252 WINPR_ASSERT(bulk->context);
253 WINPR_ASSERT(pSrcData);
254 WINPR_ASSERT(ppDstData);
255 WINPR_ASSERT(pDstSize);
256
257 rdpMetrics* metrics = bulk->context->metrics;
258 WINPR_ASSERT(metrics);
259
260 if ((SrcSize <= 50) || (SrcSize >= 16384))
261 {
262 *ppDstData = pSrcData;
263 *pDstSize = SrcSize;
264 return 0;
265 }
266
267 *pDstSize = sizeof(bulk->OutputBuffer);
268 const UINT32 CompressionLevel = bulk_compression_level(bulk);
269 bulk_update_compression_max_size(bulk);
270
271 switch (CompressionLevel)
272 {
273 case PACKET_COMPR_TYPE_8K:
274 case PACKET_COMPR_TYPE_64K:
275 mppc_set_compression_level(bulk->mppcSend, CompressionLevel);
276 status = mppc_compress(bulk->mppcSend, pSrcData, SrcSize, bulk->OutputBuffer, ppDstData,
277 pDstSize, pFlags);
278 break;
279 case PACKET_COMPR_TYPE_RDP6:
280 status = ncrush_compress(bulk->ncrushSend, pSrcData, SrcSize, bulk->OutputBuffer,
281 ppDstData, pDstSize, pFlags);
282 break;
283 case PACKET_COMPR_TYPE_RDP61:
284 status = xcrush_compress(bulk->xcrushSend, pSrcData, SrcSize, bulk->OutputBuffer,
285 ppDstData, pDstSize, pFlags);
286 break;
287 case PACKET_COMPR_TYPE_RDP8:
288 WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32, CompressionLevel);
289 status = -1;
290 break;
291 default:
292 WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, CompressionLevel);
293 status = -1;
294 break;
295 }
296
297 if (status >= 0)
298 {
299 const UINT32 CompressedBytes = *pDstSize;
300 const UINT32 UncompressedBytes = SrcSize;
301 const double CompressionRatio =
302 metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
303#ifdef WITH_BULK_DEBUG
304 {
305 WLog_DBG(TAG,
306 "Compress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
307 ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
308 " / %" PRIu64 ")",
309 bulk->CompressionLevel, bulk_get_compression_flags_string(*pFlags), *pFlags,
310 CompressionRatio, CompressedBytes, UncompressedBytes,
311 metrics->TotalCompressionRatio, metrics->TotalCompressedBytes,
312 metrics->TotalUncompressedBytes);
313 }
314#else
315 WINPR_UNUSED(CompressionRatio);
316#endif
317 }
318
319#if defined(WITH_BULK_DEBUG)
320
321 if (bulk_compress_validate(bulk, pSrcData, SrcSize, *ppDstData, *pDstSize, *pFlags) < 0)
322 status = -1;
323
324#endif
325 return status;
326}
327
328void bulk_reset(rdpBulk* WINPR_RESTRICT bulk)
329{
330 WINPR_ASSERT(bulk);
331
332 mppc_context_reset(bulk->mppcSend, FALSE);
333 mppc_context_reset(bulk->mppcRecv, FALSE);
334 ncrush_context_reset(bulk->ncrushRecv, FALSE);
335 ncrush_context_reset(bulk->ncrushSend, FALSE);
336 xcrush_context_reset(bulk->xcrushRecv, FALSE);
337 xcrush_context_reset(bulk->xcrushSend, FALSE);
338}
339
340rdpBulk* bulk_new(rdpContext* context)
341{
342 rdpBulk* bulk = nullptr;
343 WINPR_ASSERT(context);
344
345 bulk = (rdpBulk*)calloc(1, sizeof(rdpBulk));
346
347 if (!bulk)
348 goto fail;
349
350 bulk->context = context;
351 bulk->mppcSend = mppc_context_new(1, TRUE);
352 if (!bulk->mppcSend)
353 goto fail;
354 bulk->mppcRecv = mppc_context_new(1, FALSE);
355 if (!bulk->mppcRecv)
356 goto fail;
357 bulk->ncrushRecv = ncrush_context_new(FALSE);
358 if (!bulk->ncrushRecv)
359 goto fail;
360 bulk->ncrushSend = ncrush_context_new(TRUE);
361 if (!bulk->ncrushSend)
362 goto fail;
363 bulk->xcrushRecv = xcrush_context_new(FALSE);
364 if (!bulk->xcrushRecv)
365 goto fail;
366 bulk->xcrushSend = xcrush_context_new(TRUE);
367 if (!bulk->xcrushSend)
368 goto fail;
369 bulk->CompressionLevel =
370 freerdp_settings_get_uint32(context->settings, FreeRDP_CompressionLevel);
371
372 return bulk;
373fail:
374 WINPR_PRAGMA_DIAG_PUSH
375 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
376 bulk_free(bulk);
377 WINPR_PRAGMA_DIAG_POP
378 return nullptr;
379}
380
381void bulk_free(rdpBulk* bulk)
382{
383 if (!bulk)
384 return;
385
386 mppc_context_free(bulk->mppcSend);
387 mppc_context_free(bulk->mppcRecv);
388 ncrush_context_free(bulk->ncrushRecv);
389 ncrush_context_free(bulk->ncrushSend);
390 xcrush_context_free(bulk->xcrushRecv);
391 xcrush_context_free(bulk->xcrushSend);
392 free(bulk);
393}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.