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