Coverage Report

Created: 2023-11-19 06:16

/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;
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;
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 i = 0;
221
0
  UINT32 offset = 0;
222
0
  UINT32 rotation = 0;
223
0
  UINT32 accumulator = 0;
224
225
0
  WINPR_ASSERT(xcrush);
226
0
  WINPR_ASSERT(data);
227
0
  WINPR_ASSERT(pIndex);
228
229
0
  *pIndex = 0;
230
0
  xcrush->SignatureIndex = 0;
231
232
0
  if (size < 128)
233
0
    return 0;
234
235
0
  for (i = 0; i < 32; i++)
236
0
  {
237
0
    rotation = _rotl(accumulator, 1);
238
0
    accumulator = data[i] ^ rotation;
239
0
  }
240
241
0
  for (i = 0; i < size - 64; i++)
242
0
  {
243
0
    rotation = _rotl(accumulator, 1);
244
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
245
246
0
    if (!(accumulator & 0x7F))
247
0
    {
248
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
249
0
        return 0;
250
0
    }
251
252
0
    i++;
253
0
    rotation = _rotl(accumulator, 1);
254
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
255
256
0
    if (!(accumulator & 0x7F))
257
0
    {
258
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
259
0
        return 0;
260
0
    }
261
262
0
    i++;
263
0
    rotation = _rotl(accumulator, 1);
264
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
265
266
0
    if (!(accumulator & 0x7F))
267
0
    {
268
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
269
0
        return 0;
270
0
    }
271
272
0
    i++;
273
0
    rotation = _rotl(accumulator, 1);
274
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
275
276
0
    if (!(accumulator & 0x7F))
277
0
    {
278
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
279
0
        return 0;
280
0
    }
281
0
  }
282
283
0
  if ((size == offset) || xcrush_append_chunk(xcrush, data, &offset, size))
284
0
  {
285
0
    *pIndex = xcrush->SignatureIndex;
286
0
    return 1;
287
0
  }
288
289
0
  return 0;
290
0
}
291
292
static UINT32 xcrush_compute_signatures(XCRUSH_CONTEXT* xcrush, const BYTE* data, UINT32 size)
293
0
{
294
0
  UINT32 index = 0;
295
296
0
  if (xcrush_compute_chunks(xcrush, data, size, &index))
297
0
    return index;
298
299
0
  return 0;
300
0
}
301
302
static void xcrush_clear_hash_table_range(XCRUSH_CONTEXT* xcrush, UINT32 beg, UINT32 end)
303
0
{
304
0
  UINT32 index;
305
306
0
  WINPR_ASSERT(xcrush);
307
308
0
  for (index = 0; index < 65536; index++)
309
0
  {
310
0
    if (xcrush->NextChunks[index] >= beg)
311
0
    {
312
0
      if (xcrush->NextChunks[index] <= end)
313
0
      {
314
0
        xcrush->NextChunks[index] = 0;
315
0
      }
316
0
    }
317
0
  }
318
319
0
  for (index = 0; index < 65534; index++)
320
0
  {
321
0
    if (xcrush->Chunks[index].next >= beg)
322
0
    {
323
0
      if (xcrush->Chunks[index].next <= end)
324
0
      {
325
0
        xcrush->Chunks[index].next = 0;
326
0
      }
327
0
    }
328
0
  }
329
0
}
330
331
static int xcrush_find_next_matching_chunk(XCRUSH_CONTEXT* xcrush, XCRUSH_CHUNK* chunk,
332
                                           XCRUSH_CHUNK** pNextChunk)
333
0
{
334
0
  UINT32 index;
335
0
  XCRUSH_CHUNK* next = NULL;
336
337
0
  WINPR_ASSERT(xcrush);
338
339
0
  if (!chunk)
340
0
    return -4001; /* error */
341
342
0
  if (chunk->next)
343
0
  {
344
0
    index = (chunk - xcrush->Chunks) / sizeof(XCRUSH_CHUNK);
345
346
0
    if (index >= 65534)
347
0
      return -4002; /* error */
348
349
0
    if ((index < xcrush->ChunkHead) || (chunk->next >= xcrush->ChunkHead))
350
0
    {
351
0
      if (chunk->next >= 65534)
352
0
        return -4003; /* error */
353
354
0
      next = &xcrush->Chunks[chunk->next];
355
0
    }
356
0
  }
357
358
0
  WINPR_ASSERT(pNextChunk);
359
0
  *pNextChunk = next;
360
0
  return 1;
361
0
}
362
363
static int xcrush_insert_chunk(XCRUSH_CONTEXT* xcrush, XCRUSH_SIGNATURE* signature, UINT32 offset,
364
                               XCRUSH_CHUNK** pPrevChunk)
365
0
{
366
0
  UINT32 seed;
367
0
  UINT32 index;
368
369
0
  WINPR_ASSERT(xcrush);
370
371
0
  if (xcrush->ChunkHead >= 65530)
372
0
  {
373
0
    xcrush->ChunkHead = 1;
374
0
    xcrush->ChunkTail = 1;
375
0
  }
376
377
0
  if (xcrush->ChunkHead >= xcrush->ChunkTail)
378
0
  {
379
0
    xcrush_clear_hash_table_range(xcrush, xcrush->ChunkTail, xcrush->ChunkTail + 10000);
380
0
    xcrush->ChunkTail += 10000;
381
0
  }
382
383
0
  index = xcrush->ChunkHead++;
384
385
0
  if (xcrush->ChunkHead >= 65534)
386
0
    return -3001; /* error */
387
388
0
  xcrush->Chunks[index].offset = offset;
389
0
  seed = signature->seed;
390
391
0
  if (seed >= 65536)
392
0
    return -3002; /* error */
393
394
0
  if (xcrush->NextChunks[seed])
395
0
  {
396
0
    if (xcrush->NextChunks[seed] >= 65534)
397
0
      return -3003; /* error */
398
399
0
    WINPR_ASSERT(pPrevChunk);
400
0
    *pPrevChunk = &xcrush->Chunks[xcrush->NextChunks[seed]];
401
0
  }
402
403
0
  xcrush->Chunks[index].next = xcrush->NextChunks[seed] & 0xFFFF;
404
0
  xcrush->NextChunks[seed] = index;
405
0
  return 1;
406
0
}
407
408
static int xcrush_find_match_length(XCRUSH_CONTEXT* xcrush, UINT32 MatchOffset, UINT32 ChunkOffset,
409
                                    UINT32 HistoryOffset, UINT32 SrcSize, UINT32 MaxMatchLength,
410
                                    XCRUSH_MATCH_INFO* MatchInfo)
411
0
{
412
0
  UINT32 MatchSymbol;
413
0
  UINT32 ChunkSymbol;
414
0
  BYTE* ChunkBuffer;
415
0
  BYTE* MatchBuffer;
416
0
  BYTE* MatchStartPtr;
417
0
  BYTE* ForwardChunkPtr;
418
0
  BYTE* ReverseChunkPtr;
419
0
  BYTE* ForwardMatchPtr;
420
0
  BYTE* ReverseMatchPtr;
421
0
  BYTE* HistoryBufferEnd;
422
0
  UINT32 ReverseMatchLength = 0;
423
0
  UINT32 ForwardMatchLength = 0;
424
0
  UINT32 TotalMatchLength;
425
0
  BYTE* HistoryBuffer;
426
0
  UINT32 HistoryBufferSize;
427
428
0
  WINPR_ASSERT(xcrush);
429
0
  WINPR_ASSERT(MatchInfo);
430
431
0
  HistoryBuffer = xcrush->HistoryBuffer;
432
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
433
0
  HistoryBufferEnd = &HistoryBuffer[HistoryOffset + SrcSize];
434
435
0
  if (MatchOffset > HistoryBufferSize)
436
0
    return -2001; /* error */
437
438
0
  MatchBuffer = &HistoryBuffer[MatchOffset];
439
440
0
  if (ChunkOffset > HistoryBufferSize)
441
0
    return -2002; /* error */
442
443
0
  ChunkBuffer = &HistoryBuffer[ChunkOffset];
444
445
0
  if (MatchOffset == ChunkOffset)
446
0
    return -2003; /* error */
447
448
0
  if (MatchBuffer < HistoryBuffer)
449
0
    return -2004; /* error */
450
451
0
  if (ChunkBuffer < HistoryBuffer)
452
0
    return -2005; /* error */
453
454
0
  ForwardMatchPtr = &HistoryBuffer[MatchOffset];
455
0
  ForwardChunkPtr = &HistoryBuffer[ChunkOffset];
456
457
0
  if ((&MatchBuffer[MaxMatchLength + 1] < HistoryBufferEnd) &&
458
0
      (MatchBuffer[MaxMatchLength + 1] != ChunkBuffer[MaxMatchLength + 1]))
459
0
  {
460
0
    return 0;
461
0
  }
462
463
0
  while (1)
464
0
  {
465
0
    MatchSymbol = *ForwardMatchPtr++;
466
0
    ChunkSymbol = *ForwardChunkPtr++;
467
468
0
    if (MatchSymbol != ChunkSymbol)
469
0
      break;
470
471
0
    if (ForwardMatchPtr > HistoryBufferEnd)
472
0
      break;
473
474
0
    ForwardMatchLength++;
475
0
  }
476
477
0
  ReverseMatchPtr = MatchBuffer - 1;
478
0
  ReverseChunkPtr = ChunkBuffer - 1;
479
480
0
  while ((ReverseMatchPtr > &HistoryBuffer[HistoryOffset]) && (ReverseChunkPtr > HistoryBuffer) &&
481
0
         (*ReverseMatchPtr == *ReverseChunkPtr))
482
0
  {
483
0
    ReverseMatchLength++;
484
0
    ReverseMatchPtr--;
485
0
    ReverseChunkPtr--;
486
0
  }
487
488
0
  MatchStartPtr = MatchBuffer - ReverseMatchLength;
489
0
  TotalMatchLength = ReverseMatchLength + ForwardMatchLength;
490
491
0
  if (TotalMatchLength < 11)
492
0
    return 0;
493
494
0
  if (MatchStartPtr < HistoryBuffer)
495
0
    return -2006; /* error */
496
497
0
  MatchInfo->MatchOffset = MatchStartPtr - HistoryBuffer;
498
0
  MatchInfo->ChunkOffset = ChunkBuffer - ReverseMatchLength - HistoryBuffer;
499
0
  MatchInfo->MatchLength = TotalMatchLength;
500
0
  return (int)TotalMatchLength;
501
0
}
502
503
static int xcrush_find_all_matches(XCRUSH_CONTEXT* xcrush, UINT32 SignatureIndex,
504
                                   UINT32 HistoryOffset, UINT32 SrcOffset, UINT32 SrcSize)
505
0
{
506
0
  UINT32 i = 0;
507
0
  UINT32 j = 0;
508
0
  int status = 0;
509
0
  UINT32 ChunkIndex = 0;
510
0
  UINT32 ChunkCount = 0;
511
0
  XCRUSH_CHUNK* chunk = NULL;
512
0
  UINT32 MatchLength = 0;
513
0
  UINT32 MaxMatchLength = 0;
514
0
  UINT32 PrevMatchEnd = 0;
515
0
  XCRUSH_SIGNATURE* Signatures = NULL;
516
0
  XCRUSH_MATCH_INFO MaxMatchInfo = { 0 };
517
518
0
  WINPR_ASSERT(xcrush);
519
520
0
  Signatures = xcrush->Signatures;
521
522
0
  for (i = 0; i < SignatureIndex; i++)
523
0
  {
524
0
    XCRUSH_MATCH_INFO MatchInfo = { 0 };
525
0
    UINT32 offset = SrcOffset + HistoryOffset;
526
527
0
    if (!Signatures[i].size)
528
0
      return -1001; /* error */
529
530
0
    status = xcrush_insert_chunk(xcrush, &Signatures[i], offset, &chunk);
531
532
0
    if (status < 0)
533
0
      return status;
534
535
0
    if (chunk && (SrcOffset + HistoryOffset + Signatures[i].size >= PrevMatchEnd))
536
0
    {
537
0
      ChunkCount = 0;
538
0
      MaxMatchLength = 0;
539
540
0
      while (chunk)
541
0
      {
542
0
        if ((chunk->offset < HistoryOffset) || (chunk->offset < offset) ||
543
0
            (chunk->offset > SrcSize + HistoryOffset))
544
0
        {
545
0
          status = xcrush_find_match_length(xcrush, offset, chunk->offset, HistoryOffset,
546
0
                                            SrcSize, MaxMatchLength, &MatchInfo);
547
548
0
          if (status < 0)
549
0
            return status; /* error */
550
551
0
          MatchLength = (UINT32)status;
552
553
0
          if (MatchLength > MaxMatchLength)
554
0
          {
555
0
            MaxMatchLength = MatchLength;
556
0
            MaxMatchInfo.MatchOffset = MatchInfo.MatchOffset;
557
0
            MaxMatchInfo.ChunkOffset = MatchInfo.ChunkOffset;
558
0
            MaxMatchInfo.MatchLength = MatchInfo.MatchLength;
559
560
0
            if (MatchLength > 256)
561
0
              break;
562
0
          }
563
0
        }
564
565
0
        ChunkIndex = ChunkCount++;
566
567
0
        if (ChunkIndex > 4)
568
0
          break;
569
570
0
        status = xcrush_find_next_matching_chunk(xcrush, chunk, &chunk);
571
572
0
        if (status < 0)
573
0
          return status; /* error */
574
0
      }
575
576
0
      if (MaxMatchLength)
577
0
      {
578
0
        xcrush->OriginalMatches[j].MatchOffset = MaxMatchInfo.MatchOffset;
579
0
        xcrush->OriginalMatches[j].ChunkOffset = MaxMatchInfo.ChunkOffset;
580
0
        xcrush->OriginalMatches[j].MatchLength = MaxMatchInfo.MatchLength;
581
582
0
        if (xcrush->OriginalMatches[j].MatchOffset < HistoryOffset)
583
0
          return -1002; /* error */
584
585
0
        PrevMatchEnd =
586
0
            xcrush->OriginalMatches[j].MatchLength + xcrush->OriginalMatches[j].MatchOffset;
587
0
        j++;
588
589
0
        if (j >= 1000)
590
0
          return -1003; /* error */
591
0
      }
592
0
    }
593
594
0
    SrcOffset += Signatures[i].size;
595
596
0
    if (SrcOffset > SrcSize)
597
0
      return -1004; /* error */
598
0
  }
599
600
0
  if (SrcOffset > SrcSize)
601
0
    return -1005; /* error */
602
603
0
  return (int)j;
604
0
}
605
606
static int xcrush_optimize_matches(XCRUSH_CONTEXT* xcrush)
607
0
{
608
0
  UINT32 i, j = 0;
609
0
  UINT32 MatchDiff = 0;
610
0
  UINT32 PrevMatchEnd = 0;
611
0
  UINT32 TotalMatchLength = 0;
612
0
  UINT32 OriginalMatchCount = 0;
613
0
  UINT32 OptimizedMatchCount = 0;
614
0
  XCRUSH_MATCH_INFO* OriginalMatch;
615
0
  XCRUSH_MATCH_INFO* OptimizedMatch;
616
0
  XCRUSH_MATCH_INFO* OriginalMatches;
617
0
  XCRUSH_MATCH_INFO* OptimizedMatches;
618
619
0
  WINPR_ASSERT(xcrush);
620
621
0
  OriginalMatches = xcrush->OriginalMatches;
622
0
  OriginalMatchCount = xcrush->OriginalMatchCount;
623
0
  OptimizedMatches = xcrush->OptimizedMatches;
624
625
0
  for (i = 0; i < OriginalMatchCount; i++)
626
0
  {
627
0
    if (OriginalMatches[i].MatchOffset <= PrevMatchEnd)
628
0
    {
629
0
      if ((OriginalMatches[i].MatchOffset < PrevMatchEnd) &&
630
0
          (OriginalMatches[i].MatchLength + OriginalMatches[i].MatchOffset >
631
0
           PrevMatchEnd + 6))
632
0
      {
633
0
        MatchDiff = PrevMatchEnd - OriginalMatches[i].MatchOffset;
634
0
        OriginalMatch = &OriginalMatches[i];
635
0
        OptimizedMatch = &OptimizedMatches[j];
636
0
        OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
637
0
        OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
638
0
        OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
639
640
0
        if (OptimizedMatches[j].MatchLength <= MatchDiff)
641
0
          return -5001; /* error */
642
643
0
        if (MatchDiff >= 20000)
644
0
          return -5002; /* error */
645
646
0
        OptimizedMatches[j].MatchLength -= MatchDiff;
647
0
        OptimizedMatches[j].MatchOffset += MatchDiff;
648
0
        OptimizedMatches[j].ChunkOffset += MatchDiff;
649
0
        PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
650
0
        TotalMatchLength += OptimizedMatches[j].MatchLength;
651
0
        j++;
652
0
      }
653
0
    }
654
0
    else
655
0
    {
656
0
      OriginalMatch = &OriginalMatches[i];
657
0
      OptimizedMatch = &OptimizedMatches[j];
658
0
      OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
659
0
      OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
660
0
      OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
661
0
      PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
662
0
      TotalMatchLength += OptimizedMatches[j].MatchLength;
663
0
      j++;
664
0
    }
665
0
  }
666
667
0
  OptimizedMatchCount = j;
668
0
  xcrush->OptimizedMatchCount = OptimizedMatchCount;
669
0
  return (int)TotalMatchLength;
670
0
}
671
672
static int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, BYTE* OutputBuffer, UINT32 OutputSize,
673
                                  UINT32 HistoryOffset, UINT32* pDstSize)
674
0
{
675
0
  BYTE* Literals;
676
0
  BYTE* OutputEnd;
677
0
  UINT32 MatchIndex;
678
0
  UINT32 MatchOffset;
679
0
  UINT16 MatchLength;
680
0
  UINT32 MatchCount;
681
0
  UINT32 CurrentOffset;
682
0
  UINT32 MatchOffsetDiff;
683
0
  UINT32 HistoryOffsetDiff;
684
0
  RDP61_MATCH_DETAILS* MatchDetails;
685
686
0
  WINPR_ASSERT(xcrush);
687
0
  WINPR_ASSERT(OutputBuffer);
688
0
  WINPR_ASSERT(OutputSize >= 2);
689
0
  WINPR_ASSERT(pDstSize);
690
691
0
  MatchCount = xcrush->OptimizedMatchCount;
692
0
  OutputEnd = &OutputBuffer[OutputSize];
693
694
0
  if (&OutputBuffer[2] >= &OutputBuffer[OutputSize])
695
0
    return -6001; /* error */
696
697
0
  Data_Write_UINT16(OutputBuffer, MatchCount);
698
0
  MatchDetails = (RDP61_MATCH_DETAILS*)&OutputBuffer[2];
699
0
  Literals = (BYTE*)&MatchDetails[MatchCount];
700
701
0
  if (Literals > OutputEnd)
702
0
    return -6002; /* error */
703
704
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
705
0
  {
706
0
    Data_Write_UINT16(&MatchDetails[MatchIndex].MatchLength,
707
0
                      xcrush->OptimizedMatches[MatchIndex].MatchLength);
708
0
    Data_Write_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset,
709
0
                      xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset);
710
0
    Data_Write_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset,
711
0
                      xcrush->OptimizedMatches[MatchIndex].ChunkOffset);
712
0
  }
713
714
0
  CurrentOffset = HistoryOffset;
715
716
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
717
0
  {
718
0
    MatchLength = (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchLength);
719
0
    MatchOffset = xcrush->OptimizedMatches[MatchIndex].MatchOffset;
720
721
0
    if (MatchOffset <= CurrentOffset)
722
0
    {
723
0
      if (MatchOffset != CurrentOffset)
724
0
        return -6003; /* error */
725
726
0
      CurrentOffset = MatchOffset + MatchLength;
727
0
    }
728
0
    else
729
0
    {
730
0
      MatchOffsetDiff = MatchOffset - CurrentOffset;
731
732
0
      if (Literals + MatchOffset - CurrentOffset >= OutputEnd)
733
0
        return -6004; /* error */
734
735
0
      CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], MatchOffsetDiff);
736
737
0
      if (Literals >= OutputEnd)
738
0
        return -6005; /* error */
739
740
0
      Literals += MatchOffsetDiff;
741
0
      CurrentOffset = MatchOffset + MatchLength;
742
0
    }
743
0
  }
744
745
0
  HistoryOffsetDiff = xcrush->HistoryOffset - CurrentOffset;
746
747
0
  if (Literals + HistoryOffsetDiff >= OutputEnd)
748
0
    return -6006; /* error */
749
750
0
  CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], HistoryOffsetDiff);
751
0
  *pDstSize = Literals + HistoryOffsetDiff - OutputBuffer;
752
0
  return 1;
753
0
}
754
755
static INLINE size_t xcrush_copy_bytes(BYTE* dst, const BYTE* src, size_t num)
756
0
{
757
0
  WINPR_ASSERT(dst);
758
0
  WINPR_ASSERT(src);
759
760
0
  if (src + num < dst || src > dst + num)
761
0
    memcpy(dst, src, num);
762
0
  else if (src != dst)
763
0
  {
764
    // src and dst overlaps
765
    // we should copy the area that doesn't overlap repeatly
766
0
    const size_t diff = (dst > src) ? dst - src : src - dst;
767
0
    const size_t rest = num % diff;
768
0
    const size_t end = num - rest;
769
770
0
    for (size_t a = 0; a < end; a += diff)
771
0
      memcpy(&dst[a], &src[a], diff);
772
773
0
    if (rest != 0)
774
0
      memcpy(&dst[end], &src[end], rest);
775
0
  }
776
777
0
  return num;
778
0
}
779
780
static int xcrush_decompress_l1(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize,
781
                                const BYTE** ppDstData, UINT32* pDstSize, UINT32 flags)
782
0
{
783
0
  const BYTE* pSrcEnd = NULL;
784
0
  const BYTE* Literals = NULL;
785
0
  UINT16 MatchCount = 0;
786
0
  UINT16 MatchIndex = 0;
787
0
  BYTE* OutputPtr = NULL;
788
0
  size_t OutputLength = 0;
789
0
  UINT32 OutputOffset = 0;
790
0
  BYTE* HistoryPtr = NULL;
791
0
  BYTE* HistoryBuffer = NULL;
792
0
  BYTE* HistoryBufferEnd = NULL;
793
0
  UINT32 HistoryBufferSize = 0;
794
0
  UINT16 MatchLength = 0;
795
0
  UINT16 MatchOutputOffset = 0;
796
0
  UINT32 MatchHistoryOffset = 0;
797
0
  const RDP61_MATCH_DETAILS* MatchDetails = NULL;
798
799
0
  WINPR_ASSERT(xcrush);
800
801
0
  if (SrcSize < 1)
802
0
    return -1001;
803
804
0
  WINPR_ASSERT(pSrcData);
805
0
  WINPR_ASSERT(ppDstData);
806
0
  WINPR_ASSERT(pDstSize);
807
808
0
  if (flags & L1_PACKET_AT_FRONT)
809
0
    xcrush->HistoryOffset = 0;
810
811
0
  pSrcEnd = &pSrcData[SrcSize];
812
0
  HistoryBuffer = xcrush->HistoryBuffer;
813
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
814
0
  HistoryBufferEnd = &(HistoryBuffer[HistoryBufferSize]);
815
0
  xcrush->HistoryPtr = HistoryPtr = &(HistoryBuffer[xcrush->HistoryOffset]);
816
817
0
  if (flags & L1_NO_COMPRESSION)
818
0
  {
819
0
    Literals = pSrcData;
820
0
  }
821
0
  else
822
0
  {
823
0
    if (!(flags & L1_COMPRESSED))
824
0
      return -1002;
825
826
0
    if ((pSrcData + 2) > pSrcEnd)
827
0
      return -1003;
828
829
0
    Data_Read_UINT16(pSrcData, MatchCount);
830
0
    MatchDetails = (const RDP61_MATCH_DETAILS*)&pSrcData[2];
831
0
    Literals = (const BYTE*)&MatchDetails[MatchCount];
832
0
    OutputOffset = 0;
833
834
0
    if (Literals > pSrcEnd)
835
0
      return -1004;
836
837
0
    for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
838
0
    {
839
0
      Data_Read_UINT16(&MatchDetails[MatchIndex].MatchLength, MatchLength);
840
0
      Data_Read_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset, MatchOutputOffset);
841
0
      Data_Read_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset, MatchHistoryOffset);
842
843
0
      if (MatchOutputOffset < OutputOffset)
844
0
        return -1005;
845
846
0
      if (MatchLength > HistoryBufferSize)
847
0
        return -1006;
848
849
0
      if (MatchHistoryOffset > HistoryBufferSize)
850
0
        return -1007;
851
852
0
      OutputLength = MatchOutputOffset - OutputOffset;
853
854
0
      if ((MatchOutputOffset - OutputOffset) > HistoryBufferSize)
855
0
        return -1008;
856
857
0
      if (OutputLength > 0)
858
0
      {
859
0
        if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (Literals >= pSrcEnd) ||
860
0
            (&Literals[OutputLength] > pSrcEnd))
861
0
          return -1009;
862
863
0
        xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
864
0
        HistoryPtr += OutputLength;
865
0
        Literals += OutputLength;
866
0
        OutputOffset += OutputLength;
867
868
0
        if (Literals > pSrcEnd)
869
0
          return -1010;
870
0
      }
871
872
0
      OutputPtr = &xcrush->HistoryBuffer[MatchHistoryOffset];
873
874
0
      if ((&HistoryPtr[MatchLength] >= HistoryBufferEnd) ||
875
0
          (&OutputPtr[MatchLength] >= HistoryBufferEnd))
876
0
        return -1011;
877
878
0
      xcrush_copy_bytes(HistoryPtr, OutputPtr, MatchLength);
879
0
      OutputOffset += MatchLength;
880
0
      HistoryPtr += MatchLength;
881
0
    }
882
0
  }
883
884
0
  if (Literals < pSrcEnd)
885
0
  {
886
0
    OutputLength = pSrcEnd - Literals;
887
888
0
    if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (&Literals[OutputLength] > pSrcEnd))
889
0
      return -1012;
890
891
0
    xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
892
0
    HistoryPtr += OutputLength;
893
0
  }
894
895
0
  xcrush->HistoryOffset = HistoryPtr - HistoryBuffer;
896
0
  *pDstSize = HistoryPtr - xcrush->HistoryPtr;
897
0
  *ppDstData = xcrush->HistoryPtr;
898
0
  return 1;
899
0
}
900
901
int xcrush_decompress(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize,
902
                      const BYTE** ppDstData, UINT32* pDstSize, UINT32 flags)
903
0
{
904
0
  int status = 0;
905
0
  UINT32 DstSize = 0;
906
0
  const BYTE* pDstData = NULL;
907
0
  BYTE Level1ComprFlags;
908
0
  BYTE Level2ComprFlags;
909
910
0
  WINPR_ASSERT(xcrush);
911
912
0
  if (SrcSize < 2)
913
0
    return -1;
914
915
0
  WINPR_ASSERT(pSrcData);
916
0
  WINPR_ASSERT(ppDstData);
917
0
  WINPR_ASSERT(pDstSize);
918
919
0
  Level1ComprFlags = pSrcData[0];
920
0
  Level2ComprFlags = pSrcData[1];
921
0
  pSrcData += 2;
922
0
  SrcSize -= 2;
923
924
0
  if (flags & PACKET_FLUSHED)
925
0
  {
926
0
    ZeroMemory(xcrush->HistoryBuffer, xcrush->HistoryBufferSize);
927
0
    xcrush->HistoryOffset = 0;
928
0
  }
929
930
0
  if (!(Level2ComprFlags & PACKET_COMPRESSED))
931
0
  {
932
0
    status =
933
0
        xcrush_decompress_l1(xcrush, pSrcData, SrcSize, ppDstData, pDstSize, Level1ComprFlags);
934
0
    return status;
935
0
  }
936
937
0
  status =
938
0
      mppc_decompress(xcrush->mppc, pSrcData, SrcSize, &pDstData, &DstSize, Level2ComprFlags);
939
940
0
  if (status < 0)
941
0
    return status;
942
943
0
  status = xcrush_decompress_l1(xcrush, pDstData, DstSize, ppDstData, pDstSize, Level1ComprFlags);
944
0
  return status;
945
0
}
946
947
static int xcrush_compress_l1(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize,
948
                              BYTE* pDstData, UINT32* pDstSize, UINT32* pFlags)
949
0
{
950
0
  int status = 0;
951
0
  UINT32 Flags = 0;
952
0
  UINT32 HistoryOffset = 0;
953
0
  BYTE* HistoryPtr = NULL;
954
0
  BYTE* HistoryBuffer = NULL;
955
0
  UINT32 SignatureIndex = 0;
956
957
0
  WINPR_ASSERT(xcrush);
958
0
  WINPR_ASSERT(pSrcData);
959
0
  WINPR_ASSERT(SrcSize > 0);
960
0
  WINPR_ASSERT(pDstData);
961
0
  WINPR_ASSERT(pDstSize);
962
0
  WINPR_ASSERT(pFlags);
963
964
0
  if (xcrush->HistoryOffset + SrcSize + 8 > xcrush->HistoryBufferSize)
965
0
  {
966
0
    xcrush->HistoryOffset = 0;
967
0
    Flags |= L1_PACKET_AT_FRONT;
968
0
  }
969
970
0
  HistoryOffset = xcrush->HistoryOffset;
971
0
  HistoryBuffer = xcrush->HistoryBuffer;
972
0
  HistoryPtr = &HistoryBuffer[HistoryOffset];
973
0
  MoveMemory(HistoryPtr, pSrcData, SrcSize);
974
0
  xcrush->HistoryOffset += SrcSize;
975
976
0
  if (SrcSize > 50)
977
0
  {
978
0
    SignatureIndex = xcrush_compute_signatures(xcrush, pSrcData, SrcSize);
979
980
0
    if (SignatureIndex)
981
0
    {
982
0
      status = xcrush_find_all_matches(xcrush, SignatureIndex, HistoryOffset, 0, SrcSize);
983
984
0
      if (status < 0)
985
0
        return status;
986
987
0
      xcrush->OriginalMatchCount = (UINT32)status;
988
0
      xcrush->OptimizedMatchCount = 0;
989
990
0
      if (xcrush->OriginalMatchCount)
991
0
      {
992
0
        status = xcrush_optimize_matches(xcrush);
993
994
0
        if (status < 0)
995
0
          return status;
996
0
      }
997
998
0
      if (xcrush->OptimizedMatchCount)
999
0
      {
1000
0
        status = xcrush_generate_output(xcrush, pDstData, SrcSize, HistoryOffset, pDstSize);
1001
1002
0
        if (status < 0)
1003
0
          return status;
1004
1005
0
        Flags |= L1_COMPRESSED;
1006
0
      }
1007
0
    }
1008
0
  }
1009
1010
0
  if (!(Flags & L1_COMPRESSED))
1011
0
  {
1012
0
    Flags |= L1_NO_COMPRESSION;
1013
0
    *pDstSize = SrcSize;
1014
0
  }
1015
1016
0
  *pFlags = Flags;
1017
0
  return 1;
1018
0
}
1019
1020
int xcrush_compress(XCRUSH_CONTEXT* xcrush, const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstBuffer,
1021
                    const BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags)
1022
0
{
1023
0
  int status = 0;
1024
0
  UINT32 DstSize = 0;
1025
0
  BYTE* pDstData = NULL;
1026
0
  const BYTE* CompressedData = NULL;
1027
0
  UINT32 CompressedDataSize = 0;
1028
0
  BYTE* OriginalData = NULL;
1029
0
  UINT32 OriginalDataSize = 0;
1030
0
  UINT32 Level1ComprFlags = 0;
1031
0
  UINT32 Level2ComprFlags = 0;
1032
0
  UINT32 CompressionLevel = 3;
1033
1034
0
  WINPR_ASSERT(xcrush);
1035
0
  WINPR_ASSERT(pSrcData);
1036
0
  WINPR_ASSERT(SrcSize > 0);
1037
0
  WINPR_ASSERT(ppDstData);
1038
0
  WINPR_ASSERT(pDstSize);
1039
0
  WINPR_ASSERT(pFlags);
1040
1041
0
  if (SrcSize > 16384)
1042
0
    return -1001;
1043
1044
0
  if ((SrcSize + 2) > *pDstSize)
1045
0
    return -1002;
1046
1047
0
  OriginalData = pDstBuffer;
1048
0
  *ppDstData = pDstBuffer;
1049
0
  OriginalDataSize = SrcSize;
1050
0
  pDstData = xcrush->BlockBuffer;
1051
0
  CompressedDataSize = SrcSize;
1052
0
  status = xcrush_compress_l1(xcrush, pSrcData, SrcSize, pDstData, &CompressedDataSize,
1053
0
                              &Level1ComprFlags);
1054
1055
0
  if (status < 0)
1056
0
    return status;
1057
1058
0
  if (Level1ComprFlags & L1_COMPRESSED)
1059
0
  {
1060
0
    CompressedData = pDstData;
1061
1062
0
    if (CompressedDataSize > SrcSize)
1063
0
      return -1003;
1064
0
  }
1065
0
  else
1066
0
  {
1067
0
    CompressedData = pSrcData;
1068
1069
0
    if (CompressedDataSize != SrcSize)
1070
0
      return -1004;
1071
0
  }
1072
1073
0
  status = 0;
1074
0
  pDstData = &OriginalData[2];
1075
0
  DstSize = OriginalDataSize - 2;
1076
1077
0
  if (CompressedDataSize > 50)
1078
0
  {
1079
0
    const BYTE* pUnusedDstData = NULL;
1080
0
    status = mppc_compress(xcrush->mppc, CompressedData, CompressedDataSize, pDstData,
1081
0
                           &pUnusedDstData, &DstSize, &Level2ComprFlags);
1082
0
  }
1083
1084
0
  if (status < 0)
1085
0
    return status;
1086
1087
0
  if (!status || (Level2ComprFlags & PACKET_FLUSHED))
1088
0
  {
1089
0
    if (CompressedDataSize > DstSize)
1090
0
    {
1091
0
      xcrush_context_reset(xcrush, TRUE);
1092
0
      *ppDstData = pSrcData;
1093
0
      *pDstSize = SrcSize;
1094
0
      *pFlags = 0;
1095
0
      return 1;
1096
0
    }
1097
1098
0
    DstSize = CompressedDataSize;
1099
0
    CopyMemory(&OriginalData[2], CompressedData, CompressedDataSize);
1100
0
  }
1101
1102
0
  if (Level2ComprFlags & PACKET_COMPRESSED)
1103
0
  {
1104
0
    Level2ComprFlags |= xcrush->CompressionFlags;
1105
0
    xcrush->CompressionFlags = 0;
1106
0
  }
1107
0
  else if (Level2ComprFlags & PACKET_FLUSHED)
1108
0
  {
1109
0
    xcrush->CompressionFlags = PACKET_FLUSHED;
1110
0
  }
1111
1112
0
  Level1ComprFlags |= L1_INNER_COMPRESSION;
1113
0
  OriginalData[0] = (BYTE)Level1ComprFlags;
1114
0
  OriginalData[1] = (BYTE)Level2ComprFlags;
1115
#if defined(DEBUG_XCRUSH)
1116
  WLog_DBG(TAG, "XCrushCompress: Level1ComprFlags: %s Level2ComprFlags: %s",
1117
           xcrush_get_level_1_compression_flags_string(Level1ComprFlags),
1118
           xcrush_get_level_2_compression_flags_string(Level2ComprFlags));
1119
#endif
1120
1121
0
  if (*pDstSize < (DstSize + 2))
1122
0
    return -1006;
1123
1124
0
  *pDstSize = DstSize + 2;
1125
0
  *pFlags = PACKET_COMPRESSED | CompressionLevel;
1126
0
  return 1;
1127
0
}
1128
1129
void xcrush_context_reset(XCRUSH_CONTEXT* xcrush, BOOL flush)
1130
0
{
1131
0
  WINPR_ASSERT(xcrush);
1132
1133
0
  xcrush->SignatureIndex = 0;
1134
0
  xcrush->SignatureCount = 1000;
1135
0
  ZeroMemory(&(xcrush->Signatures), sizeof(XCRUSH_SIGNATURE) * xcrush->SignatureCount);
1136
0
  xcrush->CompressionFlags = 0;
1137
0
  xcrush->ChunkHead = xcrush->ChunkTail = 1;
1138
0
  ZeroMemory(&(xcrush->Chunks), sizeof(xcrush->Chunks));
1139
0
  ZeroMemory(&(xcrush->NextChunks), sizeof(xcrush->NextChunks));
1140
0
  ZeroMemory(&(xcrush->OriginalMatches), sizeof(xcrush->OriginalMatches));
1141
0
  ZeroMemory(&(xcrush->OptimizedMatches), sizeof(xcrush->OptimizedMatches));
1142
1143
0
  if (flush)
1144
0
    xcrush->HistoryOffset = xcrush->HistoryBufferSize + 1;
1145
0
  else
1146
0
    xcrush->HistoryOffset = 0;
1147
1148
0
  mppc_context_reset(xcrush->mppc, flush);
1149
0
}
1150
1151
XCRUSH_CONTEXT* xcrush_context_new(BOOL Compressor)
1152
0
{
1153
0
  XCRUSH_CONTEXT* xcrush = (XCRUSH_CONTEXT*)calloc(1, sizeof(XCRUSH_CONTEXT));
1154
1155
0
  if (!xcrush)
1156
0
    goto fail;
1157
1158
0
  xcrush->Compressor = Compressor;
1159
0
  xcrush->mppc = mppc_context_new(1, Compressor);
1160
0
  if (!xcrush->mppc)
1161
0
    goto fail;
1162
0
  xcrush->HistoryBufferSize = 2000000;
1163
0
  xcrush_context_reset(xcrush, FALSE);
1164
1165
0
  return xcrush;
1166
0
fail:
1167
0
  xcrush_context_free(xcrush);
1168
1169
0
  return NULL;
1170
0
}
1171
1172
void xcrush_context_free(XCRUSH_CONTEXT* xcrush)
1173
0
{
1174
0
  if (xcrush)
1175
0
  {
1176
0
    mppc_context_free(xcrush->mppc);
1177
0
    free(xcrush);
1178
0
  }
1179
0
}