Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/libfreerdp/codec/xcrush.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * XCrush (RDP6.1) Bulk Data Compression
4
 *
5
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2017 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2017 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <winpr/assert.h>
23
#include <freerdp/config.h>
24
25
#include <winpr/crt.h>
26
#include <winpr/print.h>
27
#include <winpr/bitstream.h>
28
29
#include <freerdp/log.h>
30
#include "xcrush.h"
31
32
#define TAG FREERDP_TAG("codec")
33
34
#pragma pack(push, 1)
35
36
typedef struct
37
{
38
  UINT32 MatchOffset;
39
  UINT32 ChunkOffset;
40
  UINT32 MatchLength;
41
} XCRUSH_MATCH_INFO;
42
43
typedef struct
44
{
45
  UINT32 offset;
46
  UINT32 next;
47
} XCRUSH_CHUNK;
48
49
typedef struct
50
{
51
  UINT16 seed;
52
  UINT16 size;
53
} XCRUSH_SIGNATURE;
54
55
typedef struct
56
{
57
  UINT16 MatchLength;
58
  UINT16 MatchOutputOffset;
59
  UINT32 MatchHistoryOffset;
60
} RDP61_MATCH_DETAILS;
61
62
typedef struct
63
{
64
  BYTE Level1ComprFlags;
65
  BYTE Level2ComprFlags;
66
  UINT16 MatchCount;
67
  RDP61_MATCH_DETAILS* MatchDetails;
68
  BYTE* Literals;
69
} RDP61_COMPRESSED_DATA;
70
71
#pragma pack(pop)
72
73
struct s_XCRUSH_CONTEXT
74
{
75
  ALIGN64 BOOL Compressor;
76
  ALIGN64 MPPC_CONTEXT* mppc;
77
  ALIGN64 BYTE* HistoryPtr;
78
  ALIGN64 UINT32 HistoryOffset;
79
  ALIGN64 UINT32 HistoryBufferSize;
80
  ALIGN64 BYTE HistoryBuffer[2000000];
81
  ALIGN64 BYTE BlockBuffer[16384];
82
  ALIGN64 UINT32 CompressionFlags;
83
  ALIGN64 UINT32 SignatureIndex;
84
  ALIGN64 UINT32 SignatureCount;
85
  ALIGN64 XCRUSH_SIGNATURE Signatures[1000];
86
  ALIGN64 UINT32 ChunkHead;
87
  ALIGN64 UINT32 ChunkTail;
88
  ALIGN64 XCRUSH_CHUNK Chunks[65534];
89
  ALIGN64 UINT16 NextChunks[65536];
90
  ALIGN64 UINT32 OriginalMatchCount;
91
  ALIGN64 UINT32 OptimizedMatchCount;
92
  ALIGN64 XCRUSH_MATCH_INFO OriginalMatches[1000];
93
  ALIGN64 XCRUSH_MATCH_INFO OptimizedMatches[1000];
94
};
95
96
//#define DEBUG_XCRUSH 1
97
#if defined(DEBUG_XCRUSH)
98
static const char* xcrush_get_level_2_compression_flags_string(UINT32 flags)
99
{
100
  flags &= 0xE0;
101
102
  if (flags == 0)
103
    return "PACKET_UNCOMPRESSED";
104
  else if (flags == PACKET_COMPRESSED)
105
    return "PACKET_COMPRESSED";
106
  else if (flags == PACKET_AT_FRONT)
107
    return "PACKET_AT_FRONT";
108
  else if (flags == PACKET_FLUSHED)
109
    return "PACKET_FLUSHED";
110
  else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT))
111
    return "PACKET_COMPRESSED | PACKET_AT_FRONT";
112
  else if (flags == (PACKET_COMPRESSED | PACKET_FLUSHED))
113
    return "PACKET_COMPRESSED | PACKET_FLUSHED";
114
  else if (flags == (PACKET_AT_FRONT | PACKET_FLUSHED))
115
    return "PACKET_AT_FRONT | PACKET_FLUSHED";
116
  else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED))
117
    return "PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED";
118
119
  return "PACKET_UNKNOWN";
120
}
121
122
static const char* xcrush_get_level_1_compression_flags_string(UINT32 flags)
123
{
124
  flags &= 0x17;
125
126
  if (flags == 0)
127
    return "L1_UNKNOWN";
128
  else if (flags == L1_PACKET_AT_FRONT)
129
    return "L1_PACKET_AT_FRONT";
130
  else if (flags == L1_NO_COMPRESSION)
131
    return "L1_NO_COMPRESSION";
132
  else if (flags == L1_COMPRESSED)
133
    return "L1_COMPRESSED";
134
  else if (flags == L1_INNER_COMPRESSION)
135
    return "L1_INNER_COMPRESSION";
136
  else if (flags == (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION))
137
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION";
138
  else if (flags == (L1_PACKET_AT_FRONT | L1_COMPRESSED))
139
    return "L1_PACKET_AT_FRONT | L1_COMPRESSED";
140
  else if (flags == (L1_PACKET_AT_FRONT | L1_INNER_COMPRESSION))
141
    return "L1_PACKET_AT_FRONT | L1_INNER_COMPRESSION";
142
  else if (flags == (L1_NO_COMPRESSION | L1_COMPRESSED))
143
    return "L1_NO_COMPRESSION | L1_COMPRESSED";
144
  else if (flags == (L1_NO_COMPRESSION | L1_INNER_COMPRESSION))
145
    return "L1_NO_COMPRESSION | L1_INNER_COMPRESSION";
146
  else if (flags == (L1_COMPRESSED | L1_INNER_COMPRESSION))
147
    return "L1_COMPRESSED | L1_INNER_COMPRESSION";
148
  else if (flags == (L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION))
149
    return "L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION";
150
  else if (flags == (L1_PACKET_AT_FRONT | L1_COMPRESSED | L1_INNER_COMPRESSION))
151
    return "L1_PACKET_AT_FRONT | L1_COMPRESSED | L1_INNER_COMPRESSION";
152
  else if (flags == (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_INNER_COMPRESSION))
153
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_INNER_COMPRESSION";
154
  else if (flags == (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED))
155
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED";
156
  else if (flags ==
157
           (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION))
158
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION";
159
160
  return "L1_UNKNOWN";
161
}
162
#endif
163
164
static UINT32 xcrush_update_hash(const BYTE* data, UINT32 size)
165
0
{
166
0
  const BYTE* end = NULL;
167
0
  UINT32 seed = 5381; /* same value as in djb2 */
168
169
0
  WINPR_ASSERT(data);
170
0
  WINPR_ASSERT(size >= 4);
171
172
0
  if (size > 32)
173
0
  {
174
0
    size = 32;
175
0
    seed = 5413;
176
0
  }
177
178
0
  end = &data[size - 4];
179
180
0
  while (data < end)
181
0
  {
182
0
    seed += (data[3] ^ data[0]) + (data[1] << 8);
183
0
    data += 4;
184
0
  }
185
186
0
  return (UINT16)seed;
187
0
}
188
189
static int xcrush_append_chunk(XCRUSH_CONTEXT* xcrush, const BYTE* data, UINT32* beg, UINT32 end)
190
0
{
191
0
  UINT32 size = 0;
192
193
0
  WINPR_ASSERT(xcrush);
194
0
  WINPR_ASSERT(data);
195
0
  WINPR_ASSERT(beg);
196
197
0
  if (xcrush->SignatureIndex >= xcrush->SignatureCount)
198
0
    return 0;
199
200
0
  size = end - *beg;
201
202
0
  if (size > 65535)
203
0
    return 0;
204
205
0
  if (size >= 15)
206
0
  {
207
0
    UINT32 seed = xcrush_update_hash(&data[*beg], (UINT16)size);
208
0
    xcrush->Signatures[xcrush->SignatureIndex].size = size;
209
0
    xcrush->Signatures[xcrush->SignatureIndex].seed = seed;
210
0
    xcrush->SignatureIndex++;
211
0
    *beg = end;
212
0
  }
213
214
0
  return 1;
215
0
}
216
217
static int xcrush_compute_chunks(XCRUSH_CONTEXT* xcrush, const BYTE* data, UINT32 size,
218
                                 UINT32* pIndex)
219
0
{
220
0
  UINT32 offset = 0;
221
0
  UINT32 rotation = 0;
222
0
  UINT32 accumulator = 0;
223
224
0
  WINPR_ASSERT(xcrush);
225
0
  WINPR_ASSERT(data);
226
0
  WINPR_ASSERT(pIndex);
227
228
0
  *pIndex = 0;
229
0
  xcrush->SignatureIndex = 0;
230
231
0
  if (size < 128)
232
0
    return 0;
233
234
0
  for (UINT32 i = 0; i < 32; i++)
235
0
  {
236
0
    rotation = _rotl(accumulator, 1);
237
0
    accumulator = data[i] ^ rotation;
238
0
  }
239
240
0
  for (UINT32 i = 0; i < size - 64; i++)
241
0
  {
242
0
    rotation = _rotl(accumulator, 1);
243
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
244
245
0
    if (!(accumulator & 0x7F))
246
0
    {
247
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
248
0
        return 0;
249
0
    }
250
251
0
    i++;
252
0
    rotation = _rotl(accumulator, 1);
253
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
254
255
0
    if (!(accumulator & 0x7F))
256
0
    {
257
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
258
0
        return 0;
259
0
    }
260
261
0
    i++;
262
0
    rotation = _rotl(accumulator, 1);
263
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
264
265
0
    if (!(accumulator & 0x7F))
266
0
    {
267
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
268
0
        return 0;
269
0
    }
270
271
0
    i++;
272
0
    rotation = _rotl(accumulator, 1);
273
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
274
275
0
    if (!(accumulator & 0x7F))
276
0
    {
277
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
278
0
        return 0;
279
0
    }
280
0
  }
281
282
0
  if ((size == offset) || xcrush_append_chunk(xcrush, data, &offset, size))
283
0
  {
284
0
    *pIndex = xcrush->SignatureIndex;
285
0
    return 1;
286
0
  }
287
288
0
  return 0;
289
0
}
290
291
static UINT32 xcrush_compute_signatures(XCRUSH_CONTEXT* xcrush, const BYTE* data, UINT32 size)
292
0
{
293
0
  UINT32 index = 0;
294
295
0
  if (xcrush_compute_chunks(xcrush, data, size, &index))
296
0
    return index;
297
298
0
  return 0;
299
0
}
300
301
static void xcrush_clear_hash_table_range(XCRUSH_CONTEXT* xcrush, UINT32 beg, UINT32 end)
302
0
{
303
0
  WINPR_ASSERT(xcrush);
304
305
0
  for (UINT32 index = 0; index < 65536; index++)
306
0
  {
307
0
    if (xcrush->NextChunks[index] >= beg)
308
0
    {
309
0
      if (xcrush->NextChunks[index] <= end)
310
0
      {
311
0
        xcrush->NextChunks[index] = 0;
312
0
      }
313
0
    }
314
0
  }
315
316
0
  for (UINT32 index = 0; index < 65534; index++)
317
0
  {
318
0
    if (xcrush->Chunks[index].next >= beg)
319
0
    {
320
0
      if (xcrush->Chunks[index].next <= end)
321
0
      {
322
0
        xcrush->Chunks[index].next = 0;
323
0
      }
324
0
    }
325
0
  }
326
0
}
327
328
static int xcrush_find_next_matching_chunk(XCRUSH_CONTEXT* xcrush, XCRUSH_CHUNK* chunk,
329
                                           XCRUSH_CHUNK** pNextChunk)
330
0
{
331
0
  UINT32 index = 0;
332
0
  XCRUSH_CHUNK* next = NULL;
333
334
0
  WINPR_ASSERT(xcrush);
335
336
0
  if (!chunk)
337
0
    return -4001; /* error */
338
339
0
  if (chunk->next)
340
0
  {
341
0
    index = (chunk - xcrush->Chunks) / sizeof(XCRUSH_CHUNK);
342
343
0
    if (index >= 65534)
344
0
      return -4002; /* error */
345
346
0
    if ((index < xcrush->ChunkHead) || (chunk->next >= xcrush->ChunkHead))
347
0
    {
348
0
      if (chunk->next >= 65534)
349
0
        return -4003; /* error */
350
351
0
      next = &xcrush->Chunks[chunk->next];
352
0
    }
353
0
  }
354
355
0
  WINPR_ASSERT(pNextChunk);
356
0
  *pNextChunk = next;
357
0
  return 1;
358
0
}
359
360
static int xcrush_insert_chunk(XCRUSH_CONTEXT* xcrush, XCRUSH_SIGNATURE* signature, UINT32 offset,
361
                               XCRUSH_CHUNK** pPrevChunk)
362
0
{
363
0
  UINT32 seed = 0;
364
0
  UINT32 index = 0;
365
366
0
  WINPR_ASSERT(xcrush);
367
368
0
  if (xcrush->ChunkHead >= 65530)
369
0
  {
370
0
    xcrush->ChunkHead = 1;
371
0
    xcrush->ChunkTail = 1;
372
0
  }
373
374
0
  if (xcrush->ChunkHead >= xcrush->ChunkTail)
375
0
  {
376
0
    xcrush_clear_hash_table_range(xcrush, xcrush->ChunkTail, xcrush->ChunkTail + 10000);
377
0
    xcrush->ChunkTail += 10000;
378
0
  }
379
380
0
  index = xcrush->ChunkHead++;
381
382
0
  if (xcrush->ChunkHead >= 65534)
383
0
    return -3001; /* error */
384
385
0
  xcrush->Chunks[index].offset = offset;
386
0
  seed = signature->seed;
387
388
0
  if (seed >= 65536)
389
0
    return -3002; /* error */
390
391
0
  if (xcrush->NextChunks[seed])
392
0
  {
393
0
    if (xcrush->NextChunks[seed] >= 65534)
394
0
      return -3003; /* error */
395
396
0
    WINPR_ASSERT(pPrevChunk);
397
0
    *pPrevChunk = &xcrush->Chunks[xcrush->NextChunks[seed]];
398
0
  }
399
400
0
  xcrush->Chunks[index].next = xcrush->NextChunks[seed] & 0xFFFF;
401
0
  xcrush->NextChunks[seed] = index;
402
0
  return 1;
403
0
}
404
405
static int xcrush_find_match_length(XCRUSH_CONTEXT* xcrush, UINT32 MatchOffset, UINT32 ChunkOffset,
406
                                    UINT32 HistoryOffset, UINT32 SrcSize, UINT32 MaxMatchLength,
407
                                    XCRUSH_MATCH_INFO* MatchInfo)
408
0
{
409
0
  UINT32 MatchSymbol = 0;
410
0
  UINT32 ChunkSymbol = 0;
411
0
  BYTE* ChunkBuffer = NULL;
412
0
  BYTE* MatchBuffer = NULL;
413
0
  BYTE* MatchStartPtr = NULL;
414
0
  BYTE* ForwardChunkPtr = NULL;
415
0
  BYTE* ReverseChunkPtr = NULL;
416
0
  BYTE* ForwardMatchPtr = NULL;
417
0
  BYTE* ReverseMatchPtr = NULL;
418
0
  BYTE* HistoryBufferEnd = NULL;
419
0
  UINT32 ReverseMatchLength = 0;
420
0
  UINT32 ForwardMatchLength = 0;
421
0
  UINT32 TotalMatchLength = 0;
422
0
  BYTE* HistoryBuffer = NULL;
423
0
  UINT32 HistoryBufferSize = 0;
424
425
0
  WINPR_ASSERT(xcrush);
426
0
  WINPR_ASSERT(MatchInfo);
427
428
0
  HistoryBuffer = xcrush->HistoryBuffer;
429
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
430
0
  HistoryBufferEnd = &HistoryBuffer[HistoryOffset + SrcSize];
431
432
0
  if (MatchOffset > HistoryBufferSize)
433
0
    return -2001; /* error */
434
435
0
  MatchBuffer = &HistoryBuffer[MatchOffset];
436
437
0
  if (ChunkOffset > HistoryBufferSize)
438
0
    return -2002; /* error */
439
440
0
  ChunkBuffer = &HistoryBuffer[ChunkOffset];
441
442
0
  if (MatchOffset == ChunkOffset)
443
0
    return -2003; /* error */
444
445
0
  if (MatchBuffer < HistoryBuffer)
446
0
    return -2004; /* error */
447
448
0
  if (ChunkBuffer < HistoryBuffer)
449
0
    return -2005; /* error */
450
451
0
  ForwardMatchPtr = &HistoryBuffer[MatchOffset];
452
0
  ForwardChunkPtr = &HistoryBuffer[ChunkOffset];
453
454
0
  if ((&MatchBuffer[MaxMatchLength + 1] < HistoryBufferEnd) &&
455
0
      (MatchBuffer[MaxMatchLength + 1] != ChunkBuffer[MaxMatchLength + 1]))
456
0
  {
457
0
    return 0;
458
0
  }
459
460
0
  while (1)
461
0
  {
462
0
    MatchSymbol = *ForwardMatchPtr++;
463
0
    ChunkSymbol = *ForwardChunkPtr++;
464
465
0
    if (MatchSymbol != ChunkSymbol)
466
0
      break;
467
468
0
    if (ForwardMatchPtr > HistoryBufferEnd)
469
0
      break;
470
471
0
    ForwardMatchLength++;
472
0
  }
473
474
0
  ReverseMatchPtr = MatchBuffer - 1;
475
0
  ReverseChunkPtr = ChunkBuffer - 1;
476
477
0
  while ((ReverseMatchPtr > &HistoryBuffer[HistoryOffset]) && (ReverseChunkPtr > HistoryBuffer) &&
478
0
         (*ReverseMatchPtr == *ReverseChunkPtr))
479
0
  {
480
0
    ReverseMatchLength++;
481
0
    ReverseMatchPtr--;
482
0
    ReverseChunkPtr--;
483
0
  }
484
485
0
  MatchStartPtr = MatchBuffer - ReverseMatchLength;
486
0
  TotalMatchLength = ReverseMatchLength + ForwardMatchLength;
487
488
0
  if (TotalMatchLength < 11)
489
0
    return 0;
490
491
0
  if (MatchStartPtr < HistoryBuffer)
492
0
    return -2006; /* error */
493
494
0
  MatchInfo->MatchOffset = MatchStartPtr - HistoryBuffer;
495
0
  MatchInfo->ChunkOffset = ChunkBuffer - ReverseMatchLength - HistoryBuffer;
496
0
  MatchInfo->MatchLength = TotalMatchLength;
497
0
  return (int)TotalMatchLength;
498
0
}
499
500
static int xcrush_find_all_matches(XCRUSH_CONTEXT* xcrush, UINT32 SignatureIndex,
501
                                   UINT32 HistoryOffset, UINT32 SrcOffset, UINT32 SrcSize)
502
0
{
503
0
  UINT32 j = 0;
504
0
  int status = 0;
505
0
  UINT32 ChunkIndex = 0;
506
0
  UINT32 ChunkCount = 0;
507
0
  XCRUSH_CHUNK* chunk = NULL;
508
0
  UINT32 MatchLength = 0;
509
0
  UINT32 MaxMatchLength = 0;
510
0
  UINT32 PrevMatchEnd = 0;
511
0
  XCRUSH_SIGNATURE* Signatures = NULL;
512
0
  XCRUSH_MATCH_INFO MaxMatchInfo = { 0 };
513
514
0
  WINPR_ASSERT(xcrush);
515
516
0
  Signatures = xcrush->Signatures;
517
518
0
  for (UINT32 i = 0; i < SignatureIndex; i++)
519
0
  {
520
0
    XCRUSH_MATCH_INFO MatchInfo = { 0 };
521
0
    UINT32 offset = SrcOffset + HistoryOffset;
522
523
0
    if (!Signatures[i].size)
524
0
      return -1001; /* error */
525
526
0
    status = xcrush_insert_chunk(xcrush, &Signatures[i], offset, &chunk);
527
528
0
    if (status < 0)
529
0
      return status;
530
531
0
    if (chunk && (SrcOffset + HistoryOffset + Signatures[i].size >= PrevMatchEnd))
532
0
    {
533
0
      ChunkCount = 0;
534
0
      MaxMatchLength = 0;
535
536
0
      while (chunk)
537
0
      {
538
0
        if ((chunk->offset < HistoryOffset) || (chunk->offset < offset) ||
539
0
            (chunk->offset > SrcSize + HistoryOffset))
540
0
        {
541
0
          status = xcrush_find_match_length(xcrush, offset, chunk->offset, HistoryOffset,
542
0
                                            SrcSize, MaxMatchLength, &MatchInfo);
543
544
0
          if (status < 0)
545
0
            return status; /* error */
546
547
0
          MatchLength = (UINT32)status;
548
549
0
          if (MatchLength > MaxMatchLength)
550
0
          {
551
0
            MaxMatchLength = MatchLength;
552
0
            MaxMatchInfo.MatchOffset = MatchInfo.MatchOffset;
553
0
            MaxMatchInfo.ChunkOffset = MatchInfo.ChunkOffset;
554
0
            MaxMatchInfo.MatchLength = MatchInfo.MatchLength;
555
556
0
            if (MatchLength > 256)
557
0
              break;
558
0
          }
559
0
        }
560
561
0
        ChunkIndex = ChunkCount++;
562
563
0
        if (ChunkIndex > 4)
564
0
          break;
565
566
0
        status = xcrush_find_next_matching_chunk(xcrush, chunk, &chunk);
567
568
0
        if (status < 0)
569
0
          return status; /* error */
570
0
      }
571
572
0
      if (MaxMatchLength)
573
0
      {
574
0
        xcrush->OriginalMatches[j].MatchOffset = MaxMatchInfo.MatchOffset;
575
0
        xcrush->OriginalMatches[j].ChunkOffset = MaxMatchInfo.ChunkOffset;
576
0
        xcrush->OriginalMatches[j].MatchLength = MaxMatchInfo.MatchLength;
577
578
0
        if (xcrush->OriginalMatches[j].MatchOffset < HistoryOffset)
579
0
          return -1002; /* error */
580
581
0
        PrevMatchEnd =
582
0
            xcrush->OriginalMatches[j].MatchLength + xcrush->OriginalMatches[j].MatchOffset;
583
0
        j++;
584
585
0
        if (j >= 1000)
586
0
          return -1003; /* error */
587
0
      }
588
0
    }
589
590
0
    SrcOffset += Signatures[i].size;
591
592
0
    if (SrcOffset > SrcSize)
593
0
      return -1004; /* error */
594
0
  }
595
596
0
  if (SrcOffset > SrcSize)
597
0
    return -1005; /* error */
598
599
0
  return (int)j;
600
0
}
601
602
static int xcrush_optimize_matches(XCRUSH_CONTEXT* xcrush)
603
0
{
604
0
  UINT32 j = 0;
605
0
  UINT32 MatchDiff = 0;
606
0
  UINT32 PrevMatchEnd = 0;
607
0
  UINT32 TotalMatchLength = 0;
608
0
  UINT32 OriginalMatchCount = 0;
609
0
  UINT32 OptimizedMatchCount = 0;
610
0
  XCRUSH_MATCH_INFO* OriginalMatch = NULL;
611
0
  XCRUSH_MATCH_INFO* OptimizedMatch = NULL;
612
0
  XCRUSH_MATCH_INFO* OriginalMatches = NULL;
613
0
  XCRUSH_MATCH_INFO* OptimizedMatches = NULL;
614
615
0
  WINPR_ASSERT(xcrush);
616
617
0
  OriginalMatches = xcrush->OriginalMatches;
618
0
  OriginalMatchCount = xcrush->OriginalMatchCount;
619
0
  OptimizedMatches = xcrush->OptimizedMatches;
620
621
0
  for (UINT32 i = 0; i < OriginalMatchCount; i++)
622
0
  {
623
0
    if (OriginalMatches[i].MatchOffset <= PrevMatchEnd)
624
0
    {
625
0
      if ((OriginalMatches[i].MatchOffset < PrevMatchEnd) &&
626
0
          (OriginalMatches[i].MatchLength + OriginalMatches[i].MatchOffset >
627
0
           PrevMatchEnd + 6))
628
0
      {
629
0
        MatchDiff = PrevMatchEnd - OriginalMatches[i].MatchOffset;
630
0
        OriginalMatch = &OriginalMatches[i];
631
0
        OptimizedMatch = &OptimizedMatches[j];
632
0
        OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
633
0
        OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
634
0
        OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
635
636
0
        if (OptimizedMatches[j].MatchLength <= MatchDiff)
637
0
          return -5001; /* error */
638
639
0
        if (MatchDiff >= 20000)
640
0
          return -5002; /* error */
641
642
0
        OptimizedMatches[j].MatchLength -= MatchDiff;
643
0
        OptimizedMatches[j].MatchOffset += MatchDiff;
644
0
        OptimizedMatches[j].ChunkOffset += MatchDiff;
645
0
        PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
646
0
        TotalMatchLength += OptimizedMatches[j].MatchLength;
647
0
        j++;
648
0
      }
649
0
    }
650
0
    else
651
0
    {
652
0
      OriginalMatch = &OriginalMatches[i];
653
0
      OptimizedMatch = &OptimizedMatches[j];
654
0
      OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
655
0
      OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
656
0
      OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
657
0
      PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
658
0
      TotalMatchLength += OptimizedMatches[j].MatchLength;
659
0
      j++;
660
0
    }
661
0
  }
662
663
0
  OptimizedMatchCount = j;
664
0
  xcrush->OptimizedMatchCount = OptimizedMatchCount;
665
0
  return (int)TotalMatchLength;
666
0
}
667
668
static int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, BYTE* OutputBuffer, UINT32 OutputSize,
669
                                  UINT32 HistoryOffset, UINT32* pDstSize)
670
0
{
671
0
  BYTE* Literals = NULL;
672
0
  BYTE* OutputEnd = NULL;
673
0
  UINT32 MatchIndex = 0;
674
0
  UINT32 MatchOffset = 0;
675
0
  UINT16 MatchLength = 0;
676
0
  UINT32 MatchCount = 0;
677
0
  UINT32 CurrentOffset = 0;
678
0
  UINT32 MatchOffsetDiff = 0;
679
0
  UINT32 HistoryOffsetDiff = 0;
680
0
  RDP61_MATCH_DETAILS* MatchDetails = NULL;
681
682
0
  WINPR_ASSERT(xcrush);
683
0
  WINPR_ASSERT(OutputBuffer);
684
0
  WINPR_ASSERT(OutputSize >= 2);
685
0
  WINPR_ASSERT(pDstSize);
686
687
0
  MatchCount = xcrush->OptimizedMatchCount;
688
0
  OutputEnd = &OutputBuffer[OutputSize];
689
690
0
  if (&OutputBuffer[2] >= &OutputBuffer[OutputSize])
691
0
    return -6001; /* error */
692
693
0
  Data_Write_UINT16(OutputBuffer, MatchCount);
694
0
  MatchDetails = (RDP61_MATCH_DETAILS*)&OutputBuffer[2];
695
0
  Literals = (BYTE*)&MatchDetails[MatchCount];
696
697
0
  if (Literals > OutputEnd)
698
0
    return -6002; /* error */
699
700
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
701
0
  {
702
0
    Data_Write_UINT16(&MatchDetails[MatchIndex].MatchLength,
703
0
                      xcrush->OptimizedMatches[MatchIndex].MatchLength);
704
0
    Data_Write_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset,
705
0
                      xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset);
706
0
    Data_Write_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset,
707
0
                      xcrush->OptimizedMatches[MatchIndex].ChunkOffset);
708
0
  }
709
710
0
  CurrentOffset = HistoryOffset;
711
712
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
713
0
  {
714
0
    MatchLength = (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchLength);
715
0
    MatchOffset = xcrush->OptimizedMatches[MatchIndex].MatchOffset;
716
717
0
    if (MatchOffset <= CurrentOffset)
718
0
    {
719
0
      if (MatchOffset != CurrentOffset)
720
0
        return -6003; /* error */
721
722
0
      CurrentOffset = MatchOffset + MatchLength;
723
0
    }
724
0
    else
725
0
    {
726
0
      MatchOffsetDiff = MatchOffset - CurrentOffset;
727
728
0
      if (Literals + MatchOffset - CurrentOffset >= OutputEnd)
729
0
        return -6004; /* error */
730
731
0
      CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], MatchOffsetDiff);
732
733
0
      if (Literals >= OutputEnd)
734
0
        return -6005; /* error */
735
736
0
      Literals += MatchOffsetDiff;
737
0
      CurrentOffset = MatchOffset + MatchLength;
738
0
    }
739
0
  }
740
741
0
  HistoryOffsetDiff = xcrush->HistoryOffset - CurrentOffset;
742
743
0
  if (Literals + HistoryOffsetDiff >= OutputEnd)
744
0
    return -6006; /* error */
745
746
0
  CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], HistoryOffsetDiff);
747
0
  *pDstSize = Literals + HistoryOffsetDiff - OutputBuffer;
748
0
  return 1;
749
0
}
750
751
static INLINE size_t xcrush_copy_bytes(BYTE* dst, const BYTE* src, size_t num)
752
23.6k
{
753
23.6k
  WINPR_ASSERT(dst);
754
23.6k
  WINPR_ASSERT(src);
755
756
23.6k
  if (src + num < dst || src > dst + num)
757
2.05k
    memcpy(dst, src, num);
758
21.6k
  else if (src != dst)
759
101
  {
760
    // src and dst overlaps
761
    // we should copy the area that doesn't overlap repeatly
762
101
    const size_t diff = (dst > src) ? dst - src : src - dst;
763
101
    const size_t rest = num % diff;
764
101
    const size_t end = num - rest;
765
766
304k
    for (size_t a = 0; a < end; a += diff)
767
304k
      memcpy(&dst[a], &src[a], diff);
768
769
101
    if (rest != 0)
770
86
      memcpy(&dst[end], &src[end], rest);
771
101
  }
772
773
23.6k
  return num;
774
23.6k
}
775
776
static int xcrush_decompress_l1(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize,
777
                                const BYTE** ppDstData, UINT32* pDstSize, UINT32 flags)
778
5.05k
{
779
5.05k
  const BYTE* pSrcEnd = NULL;
780
5.05k
  const BYTE* Literals = NULL;
781
5.05k
  UINT16 MatchCount = 0;
782
5.05k
  UINT16 MatchIndex = 0;
783
5.05k
  BYTE* OutputPtr = NULL;
784
5.05k
  size_t OutputLength = 0;
785
5.05k
  UINT32 OutputOffset = 0;
786
5.05k
  BYTE* HistoryPtr = NULL;
787
5.05k
  BYTE* HistoryBuffer = NULL;
788
5.05k
  BYTE* HistoryBufferEnd = NULL;
789
5.05k
  UINT32 HistoryBufferSize = 0;
790
5.05k
  UINT16 MatchLength = 0;
791
5.05k
  UINT16 MatchOutputOffset = 0;
792
5.05k
  UINT32 MatchHistoryOffset = 0;
793
5.05k
  const RDP61_MATCH_DETAILS* MatchDetails = NULL;
794
795
5.05k
  WINPR_ASSERT(xcrush);
796
797
5.05k
  if (SrcSize < 1)
798
4
    return -1001;
799
800
5.05k
  WINPR_ASSERT(pSrcData);
801
5.05k
  WINPR_ASSERT(ppDstData);
802
5.05k
  WINPR_ASSERT(pDstSize);
803
804
5.05k
  if (flags & L1_PACKET_AT_FRONT)
805
1.07k
    xcrush->HistoryOffset = 0;
806
807
5.05k
  pSrcEnd = &pSrcData[SrcSize];
808
5.05k
  HistoryBuffer = xcrush->HistoryBuffer;
809
5.05k
  HistoryBufferSize = xcrush->HistoryBufferSize;
810
5.05k
  HistoryBufferEnd = &(HistoryBuffer[HistoryBufferSize]);
811
5.05k
  xcrush->HistoryPtr = HistoryPtr = &(HistoryBuffer[xcrush->HistoryOffset]);
812
813
5.05k
  if (flags & L1_NO_COMPRESSION)
814
870
  {
815
870
    Literals = pSrcData;
816
870
  }
817
4.18k
  else
818
4.18k
  {
819
4.18k
    if (!(flags & L1_COMPRESSED))
820
2.68k
      return -1002;
821
822
1.49k
    if ((pSrcData + 2) > pSrcEnd)
823
11
      return -1003;
824
825
1.48k
    Data_Read_UINT16(pSrcData, MatchCount);
826
1.48k
    MatchDetails = (const RDP61_MATCH_DETAILS*)&pSrcData[2];
827
1.48k
    Literals = (const BYTE*)&MatchDetails[MatchCount];
828
1.48k
    OutputOffset = 0;
829
830
1.48k
    if (Literals > pSrcEnd)
831
688
      return -1004;
832
833
23.1k
    for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
834
22.7k
    {
835
22.7k
      Data_Read_UINT16(&MatchDetails[MatchIndex].MatchLength, MatchLength);
836
22.7k
      Data_Read_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset, MatchOutputOffset);
837
22.7k
      Data_Read_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset, MatchHistoryOffset);
838
839
22.7k
      if (MatchOutputOffset < OutputOffset)
840
82
        return -1005;
841
842
22.6k
      if (MatchLength > HistoryBufferSize)
843
0
        return -1006;
844
845
22.6k
      if (MatchHistoryOffset > HistoryBufferSize)
846
257
        return -1007;
847
848
22.3k
      OutputLength = MatchOutputOffset - OutputOffset;
849
850
22.3k
      if ((MatchOutputOffset - OutputOffset) > HistoryBufferSize)
851
0
        return -1008;
852
853
22.3k
      if (OutputLength > 0)
854
210
      {
855
210
        if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (Literals >= pSrcEnd) ||
856
210
            (&Literals[OutputLength] > pSrcEnd))
857
73
          return -1009;
858
859
137
        xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
860
137
        HistoryPtr += OutputLength;
861
137
        Literals += OutputLength;
862
137
        OutputOffset += OutputLength;
863
864
137
        if (Literals > pSrcEnd)
865
0
          return -1010;
866
137
      }
867
868
22.3k
      OutputPtr = &xcrush->HistoryBuffer[MatchHistoryOffset];
869
870
22.3k
      if ((&HistoryPtr[MatchLength] >= HistoryBufferEnd) ||
871
22.3k
          (&OutputPtr[MatchLength] >= HistoryBufferEnd))
872
2
        return -1011;
873
874
22.3k
      xcrush_copy_bytes(HistoryPtr, OutputPtr, MatchLength);
875
22.3k
      OutputOffset += MatchLength;
876
22.3k
      HistoryPtr += MatchLength;
877
22.3k
    }
878
798
  }
879
880
1.25k
  if (Literals < pSrcEnd)
881
1.22k
  {
882
1.22k
    OutputLength = pSrcEnd - Literals;
883
884
1.22k
    if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (&Literals[OutputLength] > pSrcEnd))
885
0
      return -1012;
886
887
1.22k
    xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
888
1.22k
    HistoryPtr += OutputLength;
889
1.22k
  }
890
891
1.25k
  xcrush->HistoryOffset = HistoryPtr - HistoryBuffer;
892
1.25k
  *pDstSize = HistoryPtr - xcrush->HistoryPtr;
893
1.25k
  *ppDstData = xcrush->HistoryPtr;
894
1.25k
  return 1;
895
1.25k
}
896
897
int xcrush_decompress(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize,
898
                      const BYTE** ppDstData, UINT32* pDstSize, UINT32 flags)
899
6.00k
{
900
6.00k
  int status = 0;
901
6.00k
  UINT32 DstSize = 0;
902
6.00k
  const BYTE* pDstData = NULL;
903
6.00k
  BYTE Level1ComprFlags = 0;
904
6.00k
  BYTE Level2ComprFlags = 0;
905
906
6.00k
  WINPR_ASSERT(xcrush);
907
908
6.00k
  if (SrcSize < 2)
909
3
    return -1;
910
911
6.00k
  WINPR_ASSERT(pSrcData);
912
6.00k
  WINPR_ASSERT(ppDstData);
913
6.00k
  WINPR_ASSERT(pDstSize);
914
915
6.00k
  Level1ComprFlags = pSrcData[0];
916
6.00k
  Level2ComprFlags = pSrcData[1];
917
6.00k
  pSrcData += 2;
918
6.00k
  SrcSize -= 2;
919
920
6.00k
  if (flags & PACKET_FLUSHED)
921
11
  {
922
11
    ZeroMemory(xcrush->HistoryBuffer, xcrush->HistoryBufferSize);
923
11
    xcrush->HistoryOffset = 0;
924
11
  }
925
926
6.00k
  if (!(Level2ComprFlags & PACKET_COMPRESSED))
927
3.48k
  {
928
3.48k
    status =
929
3.48k
        xcrush_decompress_l1(xcrush, pSrcData, SrcSize, ppDstData, pDstSize, Level1ComprFlags);
930
3.48k
    return status;
931
3.48k
  }
932
933
2.51k
  status =
934
2.51k
      mppc_decompress(xcrush->mppc, pSrcData, SrcSize, &pDstData, &DstSize, Level2ComprFlags);
935
936
2.51k
  if (status < 0)
937
942
    return status;
938
939
1.57k
  status = xcrush_decompress_l1(xcrush, pDstData, DstSize, ppDstData, pDstSize, Level1ComprFlags);
940
1.57k
  return status;
941
2.51k
}
942
943
static int xcrush_compress_l1(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize,
944
                              BYTE* pDstData, UINT32* pDstSize, UINT32* pFlags)
945
0
{
946
0
  int status = 0;
947
0
  UINT32 Flags = 0;
948
0
  UINT32 HistoryOffset = 0;
949
0
  BYTE* HistoryPtr = NULL;
950
0
  BYTE* HistoryBuffer = NULL;
951
0
  UINT32 SignatureIndex = 0;
952
953
0
  WINPR_ASSERT(xcrush);
954
0
  WINPR_ASSERT(pSrcData);
955
0
  WINPR_ASSERT(SrcSize > 0);
956
0
  WINPR_ASSERT(pDstData);
957
0
  WINPR_ASSERT(pDstSize);
958
0
  WINPR_ASSERT(pFlags);
959
960
0
  if (xcrush->HistoryOffset + SrcSize + 8 > xcrush->HistoryBufferSize)
961
0
  {
962
0
    xcrush->HistoryOffset = 0;
963
0
    Flags |= L1_PACKET_AT_FRONT;
964
0
  }
965
966
0
  HistoryOffset = xcrush->HistoryOffset;
967
0
  HistoryBuffer = xcrush->HistoryBuffer;
968
0
  HistoryPtr = &HistoryBuffer[HistoryOffset];
969
0
  MoveMemory(HistoryPtr, pSrcData, SrcSize);
970
0
  xcrush->HistoryOffset += SrcSize;
971
972
0
  if (SrcSize > 50)
973
0
  {
974
0
    SignatureIndex = xcrush_compute_signatures(xcrush, pSrcData, SrcSize);
975
976
0
    if (SignatureIndex)
977
0
    {
978
0
      status = xcrush_find_all_matches(xcrush, SignatureIndex, HistoryOffset, 0, SrcSize);
979
980
0
      if (status < 0)
981
0
        return status;
982
983
0
      xcrush->OriginalMatchCount = (UINT32)status;
984
0
      xcrush->OptimizedMatchCount = 0;
985
986
0
      if (xcrush->OriginalMatchCount)
987
0
      {
988
0
        status = xcrush_optimize_matches(xcrush);
989
990
0
        if (status < 0)
991
0
          return status;
992
0
      }
993
994
0
      if (xcrush->OptimizedMatchCount)
995
0
      {
996
0
        status = xcrush_generate_output(xcrush, pDstData, SrcSize, HistoryOffset, pDstSize);
997
998
0
        if (status < 0)
999
0
          return status;
1000
1001
0
        Flags |= L1_COMPRESSED;
1002
0
      }
1003
0
    }
1004
0
  }
1005
1006
0
  if (!(Flags & L1_COMPRESSED))
1007
0
  {
1008
0
    Flags |= L1_NO_COMPRESSION;
1009
0
    *pDstSize = SrcSize;
1010
0
  }
1011
1012
0
  *pFlags = Flags;
1013
0
  return 1;
1014
0
}
1015
1016
int xcrush_compress(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstBuffer,
1017
                    const BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags)
1018
0
{
1019
0
  int status = 0;
1020
0
  UINT32 DstSize = 0;
1021
0
  BYTE* pDstData = NULL;
1022
0
  const BYTE* CompressedData = NULL;
1023
0
  UINT32 CompressedDataSize = 0;
1024
0
  BYTE* OriginalData = NULL;
1025
0
  UINT32 OriginalDataSize = 0;
1026
0
  UINT32 Level1ComprFlags = 0;
1027
0
  UINT32 Level2ComprFlags = 0;
1028
0
  UINT32 CompressionLevel = 3;
1029
1030
0
  WINPR_ASSERT(xcrush);
1031
0
  WINPR_ASSERT(pSrcData);
1032
0
  WINPR_ASSERT(SrcSize > 0);
1033
0
  WINPR_ASSERT(ppDstData);
1034
0
  WINPR_ASSERT(pDstSize);
1035
0
  WINPR_ASSERT(pFlags);
1036
1037
0
  if (SrcSize > 16384)
1038
0
    return -1001;
1039
1040
0
  if ((SrcSize + 2) > *pDstSize)
1041
0
    return -1002;
1042
1043
0
  OriginalData = pDstBuffer;
1044
0
  *ppDstData = pDstBuffer;
1045
0
  OriginalDataSize = SrcSize;
1046
0
  pDstData = xcrush->BlockBuffer;
1047
0
  CompressedDataSize = SrcSize;
1048
0
  status = xcrush_compress_l1(xcrush, pSrcData, SrcSize, pDstData, &CompressedDataSize,
1049
0
                              &Level1ComprFlags);
1050
1051
0
  if (status < 0)
1052
0
    return status;
1053
1054
0
  if (Level1ComprFlags & L1_COMPRESSED)
1055
0
  {
1056
0
    CompressedData = pDstData;
1057
1058
0
    if (CompressedDataSize > SrcSize)
1059
0
      return -1003;
1060
0
  }
1061
0
  else
1062
0
  {
1063
0
    CompressedData = pSrcData;
1064
1065
0
    if (CompressedDataSize != SrcSize)
1066
0
      return -1004;
1067
0
  }
1068
1069
0
  status = 0;
1070
0
  pDstData = &OriginalData[2];
1071
0
  DstSize = OriginalDataSize - 2;
1072
1073
0
  if (CompressedDataSize > 50)
1074
0
  {
1075
0
    const BYTE* pUnusedDstData = NULL;
1076
0
    status = mppc_compress(xcrush->mppc, CompressedData, CompressedDataSize, pDstData,
1077
0
                           &pUnusedDstData, &DstSize, &Level2ComprFlags);
1078
0
  }
1079
1080
0
  if (status < 0)
1081
0
    return status;
1082
1083
0
  if (!status || (Level2ComprFlags & PACKET_FLUSHED))
1084
0
  {
1085
0
    if (CompressedDataSize > DstSize)
1086
0
    {
1087
0
      xcrush_context_reset(xcrush, TRUE);
1088
0
      *ppDstData = pSrcData;
1089
0
      *pDstSize = SrcSize;
1090
0
      *pFlags = 0;
1091
0
      return 1;
1092
0
    }
1093
1094
0
    DstSize = CompressedDataSize;
1095
0
    CopyMemory(&OriginalData[2], CompressedData, CompressedDataSize);
1096
0
  }
1097
1098
0
  if (Level2ComprFlags & PACKET_COMPRESSED)
1099
0
  {
1100
0
    Level2ComprFlags |= xcrush->CompressionFlags;
1101
0
    xcrush->CompressionFlags = 0;
1102
0
  }
1103
0
  else if (Level2ComprFlags & PACKET_FLUSHED)
1104
0
  {
1105
0
    xcrush->CompressionFlags = PACKET_FLUSHED;
1106
0
  }
1107
1108
0
  Level1ComprFlags |= L1_INNER_COMPRESSION;
1109
0
  OriginalData[0] = (BYTE)Level1ComprFlags;
1110
0
  OriginalData[1] = (BYTE)Level2ComprFlags;
1111
#if defined(DEBUG_XCRUSH)
1112
  WLog_DBG(TAG, "XCrushCompress: Level1ComprFlags: %s Level2ComprFlags: %s",
1113
           xcrush_get_level_1_compression_flags_string(Level1ComprFlags),
1114
           xcrush_get_level_2_compression_flags_string(Level2ComprFlags));
1115
#endif
1116
1117
0
  if (*pDstSize < (DstSize + 2))
1118
0
    return -1006;
1119
1120
0
  *pDstSize = DstSize + 2;
1121
0
  *pFlags = PACKET_COMPRESSED | CompressionLevel;
1122
0
  return 1;
1123
0
}
1124
1125
void xcrush_context_reset(XCRUSH_CONTEXT* xcrush, BOOL flush)
1126
35.6k
{
1127
35.6k
  WINPR_ASSERT(xcrush);
1128
1129
35.6k
  xcrush->SignatureIndex = 0;
1130
35.6k
  xcrush->SignatureCount = 1000;
1131
35.6k
  ZeroMemory(&(xcrush->Signatures), sizeof(XCRUSH_SIGNATURE) * xcrush->SignatureCount);
1132
35.6k
  xcrush->CompressionFlags = 0;
1133
35.6k
  xcrush->ChunkHead = xcrush->ChunkTail = 1;
1134
35.6k
  ZeroMemory(&(xcrush->Chunks), sizeof(xcrush->Chunks));
1135
35.6k
  ZeroMemory(&(xcrush->NextChunks), sizeof(xcrush->NextChunks));
1136
35.6k
  ZeroMemory(&(xcrush->OriginalMatches), sizeof(xcrush->OriginalMatches));
1137
35.6k
  ZeroMemory(&(xcrush->OptimizedMatches), sizeof(xcrush->OptimizedMatches));
1138
1139
35.6k
  if (flush)
1140
0
    xcrush->HistoryOffset = xcrush->HistoryBufferSize + 1;
1141
35.6k
  else
1142
35.6k
    xcrush->HistoryOffset = 0;
1143
1144
35.6k
  mppc_context_reset(xcrush->mppc, flush);
1145
35.6k
}
1146
1147
XCRUSH_CONTEXT* xcrush_context_new(BOOL Compressor)
1148
35.6k
{
1149
35.6k
  XCRUSH_CONTEXT* xcrush = (XCRUSH_CONTEXT*)calloc(1, sizeof(XCRUSH_CONTEXT));
1150
1151
35.6k
  if (!xcrush)
1152
0
    goto fail;
1153
1154
35.6k
  xcrush->Compressor = Compressor;
1155
35.6k
  xcrush->mppc = mppc_context_new(1, Compressor);
1156
35.6k
  if (!xcrush->mppc)
1157
0
    goto fail;
1158
35.6k
  xcrush->HistoryBufferSize = 2000000;
1159
35.6k
  xcrush_context_reset(xcrush, FALSE);
1160
1161
35.6k
  return xcrush;
1162
0
fail:
1163
0
  xcrush_context_free(xcrush);
1164
1165
0
  return NULL;
1166
35.6k
}
1167
1168
void xcrush_context_free(XCRUSH_CONTEXT* xcrush)
1169
35.6k
{
1170
35.6k
  if (xcrush)
1171
35.6k
  {
1172
35.6k
    mppc_context_free(xcrush->mppc);
1173
35.6k
    free(xcrush);
1174
35.6k
  }
1175
35.6k
}