1/**
2 @file compress.c
3 @brief An adaptive order-2 PPM range coder
4*/
5#define ENET_BUILDING_LIB 1
6#include <string.h>
7#include "enet/enet.h"
8
9typedef struct _ENetSymbol
10{
11 /* binary indexed tree of symbols */
12 enet_uint8 value;
13 enet_uint8 count;
14 enet_uint16 under;
15 enet_uint16 left, right;
16
17 /* context defined by this symbol */
18 enet_uint16 symbols;
19 enet_uint16 escapes;
20 enet_uint16 total;
21 enet_uint16 parent;
22} ENetSymbol;
23
24/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */
25enum
26{
27 ENET_RANGE_CODER_TOP = 1<<24,
28 ENET_RANGE_CODER_BOTTOM = 1<<16,
29
30 ENET_CONTEXT_SYMBOL_DELTA = 3,
31 ENET_CONTEXT_SYMBOL_MINIMUM = 1,
32 ENET_CONTEXT_ESCAPE_MINIMUM = 1,
33
34 ENET_SUBCONTEXT_ORDER = 2,
35 ENET_SUBCONTEXT_SYMBOL_DELTA = 2,
36 ENET_SUBCONTEXT_ESCAPE_DELTA = 5
37};
38
39/* context exclusion roughly halves compression speed, so disable for now */
40#undef ENET_CONTEXT_EXCLUSION
41
42typedef struct _ENetRangeCoder
43{
44 /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */
45 ENetSymbol symbols[4096];
46} ENetRangeCoder;
47
48void *
49enet_range_coder_create (void)
50{
51 ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder));
52 if (rangeCoder == NULL)
53 return NULL;
54
55 return rangeCoder;
56}
57
58void
59enet_range_coder_destroy (void * context)
60{
61 ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
62 if (rangeCoder == NULL)
63 return;
64
65 enet_free (rangeCoder);
66}
67
68#define ENET_SYMBOL_CREATE(symbol, value_, count_) \
69{ \
70 symbol = & rangeCoder -> symbols [nextSymbol ++]; \
71 symbol -> value = value_; \
72 symbol -> count = count_; \
73 symbol -> under = count_; \
74 symbol -> left = 0; \
75 symbol -> right = 0; \
76 symbol -> symbols = 0; \
77 symbol -> escapes = 0; \
78 symbol -> total = 0; \
79 symbol -> parent = 0; \
80}
81
82#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \
83{ \
84 ENET_SYMBOL_CREATE (context, 0, 0); \
85 (context) -> escapes = escapes_; \
86 (context) -> total = escapes_ + 256*minimum; \
87 (context) -> symbols = 0; \
88}
89
90static enet_uint16
91enet_symbol_rescale (ENetSymbol * symbol)
92{
93 enet_uint16 total = 0;
94 for (;;)
95 {
96 symbol -> count -= symbol->count >> 1;
97 symbol -> under = symbol -> count;
98 if (symbol -> left)
99 symbol -> under += enet_symbol_rescale (symbol + symbol -> left);
100 total += symbol -> under;
101 if (! symbol -> right) break;
102 symbol += symbol -> right;
103 }
104 return total;
105}
106
107#define ENET_CONTEXT_RESCALE(context, minimum) \
108{ \
109 (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \
110 (context) -> escapes -= (context) -> escapes >> 1; \
111 (context) -> total += (context) -> escapes + 256*minimum; \
112}
113
114#define ENET_RANGE_CODER_OUTPUT(value) \
115{ \
116 if (outData >= outEnd) \
117 return 0; \
118 * outData ++ = value; \
119}
120
121#define ENET_RANGE_CODER_ENCODE(under, count, total) \
122{ \
123 encodeRange /= (total); \
124 encodeLow += (under) * encodeRange; \
125 encodeRange *= (count); \
126 for (;;) \
127 { \
128 if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \
129 { \
130 if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
131 encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
132 } \
133 ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
134 encodeRange <<= 8; \
135 encodeLow <<= 8; \
136 } \
137}
138
139#define ENET_RANGE_CODER_FLUSH \
140{ \
141 while (encodeLow) \
142 { \
143 ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
144 encodeLow <<= 8; \
145 } \
146}
147
148#define ENET_RANGE_CODER_FREE_SYMBOLS \
149{ \
150 if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \
151 { \
152 nextSymbol = 0; \
153 ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \
154 predicted = 0; \
155 order = 0; \
156 } \
157}
158
159#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \
160{ \
161 under_ = value*minimum; \
162 count_ = minimum; \
163 if (! (context) -> symbols) \
164 { \
165 ENET_SYMBOL_CREATE (symbol_, value_, update); \
166 (context) -> symbols = symbol_ - (context); \
167 } \
168 else \
169 { \
170 ENetSymbol * node = (context) + (context) -> symbols; \
171 for (;;) \
172 { \
173 if (value_ < node -> value) \
174 { \
175 node -> under += update; \
176 if (node -> left) { node += node -> left; continue; } \
177 ENET_SYMBOL_CREATE (symbol_, value_, update); \
178 node -> left = symbol_ - node; \
179 } \
180 else \
181 if (value_ > node -> value) \
182 { \
183 under_ += node -> under; \
184 if (node -> right) { node += node -> right; continue; } \
185 ENET_SYMBOL_CREATE (symbol_, value_, update); \
186 node -> right = symbol_ - node; \
187 } \
188 else \
189 { \
190 count_ += node -> count; \
191 under_ += node -> under - node -> count; \
192 node -> under += update; \
193 node -> count += update; \
194 symbol_ = node; \
195 } \
196 break; \
197 } \
198 } \
199}
200
201#ifdef ENET_CONTEXT_EXCLUSION
202static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
203
204#define ENET_CONTEXT_WALK(context, body) \
205{ \
206 const ENetSymbol * node = (context) + (context) -> symbols; \
207 const ENetSymbol * stack [256]; \
208 size_t stackSize = 0; \
209 while (node -> left) \
210 { \
211 stack [stackSize ++] = node; \
212 node += node -> left; \
213 } \
214 for (;;) \
215 { \
216 body; \
217 if (node -> right) \
218 { \
219 node += node -> right; \
220 while (node -> left) \
221 { \
222 stack [stackSize ++] = node; \
223 node += node -> left; \
224 } \
225 } \
226 else \
227 if (stackSize <= 0) \
228 break; \
229 else \
230 node = stack [-- stackSize]; \
231 } \
232}
233
234#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \
235ENET_CONTEXT_WALK(context, { \
236 if (node -> value != value_) \
237 { \
238 enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \
239 if (node -> value < value_) \
240 under -= parentCount; \
241 total -= parentCount; \
242 } \
243})
244#endif
245
246size_t
247enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit)
248{
249 ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
250 enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
251 const enet_uint8 * inData, * inEnd;
252 enet_uint32 encodeLow = 0, encodeRange = ~0;
253 ENetSymbol * root;
254 enet_uint16 predicted = 0;
255 size_t order = 0, nextSymbol = 0;
256
257 if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0)
258 return 0;
259
260 inData = (const enet_uint8 *) inBuffers -> data;
261 inEnd = & inData [inBuffers -> dataLength];
262 inBuffers ++;
263 inBufferCount --;
264
265 ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
266
267 for (;;)
268 {
269 ENetSymbol * subcontext, * symbol;
270#ifdef ENET_CONTEXT_EXCLUSION
271 const ENetSymbol * childContext = & emptyContext;
272#endif
273 enet_uint8 value;
274 enet_uint16 count, under, * parent = & predicted, total;
275 if (inData >= inEnd)
276 {
277 if (inBufferCount <= 0)
278 break;
279 inData = (const enet_uint8 *) inBuffers -> data;
280 inEnd = & inData [inBuffers -> dataLength];
281 inBuffers ++;
282 inBufferCount --;
283 }
284 value = * inData ++;
285
286 for (subcontext = & rangeCoder -> symbols [predicted];
287 subcontext != root;
288#ifdef ENET_CONTEXT_EXCLUSION
289 childContext = subcontext,
290#endif
291 subcontext = & rangeCoder -> symbols [subcontext -> parent])
292 {
293 ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
294 * parent = symbol - rangeCoder -> symbols;
295 parent = & symbol -> parent;
296 total = subcontext -> total;
297#ifdef ENET_CONTEXT_EXCLUSION
298 if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
299 ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0);
300#endif
301 if (count > 0)
302 {
303 ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total);
304 }
305 else
306 {
307 if (subcontext -> escapes > 0 && subcontext -> escapes < total)
308 ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total);
309 subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
310 subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
311 }
312 subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
313 if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
314 ENET_CONTEXT_RESCALE (subcontext, 0);
315 if (count > 0) goto nextInput;
316 }
317
318 ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM);
319 * parent = symbol - rangeCoder -> symbols;
320 parent = & symbol -> parent;
321 total = root -> total;
322#ifdef ENET_CONTEXT_EXCLUSION
323 if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
324 ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM);
325#endif
326 ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total);
327 root -> total += ENET_CONTEXT_SYMBOL_DELTA;
328 if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
329 ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
330
331 nextInput:
332 if (order >= ENET_SUBCONTEXT_ORDER)
333 predicted = rangeCoder -> symbols [predicted].parent;
334 else
335 order ++;
336 ENET_RANGE_CODER_FREE_SYMBOLS;
337 }
338
339 ENET_RANGE_CODER_FLUSH;
340
341 return (size_t) (outData - outStart);
342}
343
344#define ENET_RANGE_CODER_SEED \
345{ \
346 if (inData < inEnd) decodeCode |= * inData ++ << 24; \
347 if (inData < inEnd) decodeCode |= * inData ++ << 16; \
348 if (inData < inEnd) decodeCode |= * inData ++ << 8; \
349 if (inData < inEnd) decodeCode |= * inData ++; \
350}
351
352#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total)))
353
354#define ENET_RANGE_CODER_DECODE(under, count, total) \
355{ \
356 decodeLow += (under) * decodeRange; \
357 decodeRange *= (count); \
358 for (;;) \
359 { \
360 if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \
361 { \
362 if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
363 decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
364 } \
365 decodeCode <<= 8; \
366 if (inData < inEnd) \
367 decodeCode |= * inData ++; \
368 decodeRange <<= 8; \
369 decodeLow <<= 8; \
370 } \
371}
372
373#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \
374{ \
375 under_ = 0; \
376 count_ = minimum; \
377 if (! (context) -> symbols) \
378 { \
379 createRoot; \
380 } \
381 else \
382 { \
383 ENetSymbol * node = (context) + (context) -> symbols; \
384 for (;;) \
385 { \
386 enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \
387 visitNode; \
388 if (code >= after) \
389 { \
390 under_ += node -> under; \
391 if (node -> right) { node += node -> right; continue; } \
392 createRight; \
393 } \
394 else \
395 if (code < after - before) \
396 { \
397 node -> under += update; \
398 if (node -> left) { node += node -> left; continue; } \
399 createLeft; \
400 } \
401 else \
402 { \
403 value_ = node -> value; \
404 count_ += node -> count; \
405 under_ = after - before; \
406 node -> under += update; \
407 node -> count += update; \
408 symbol_ = node; \
409 } \
410 break; \
411 } \
412 } \
413}
414
415#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
416ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0)
417
418#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
419ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \
420 { \
421 value_ = code / minimum; \
422 under_ = code - code%minimum; \
423 ENET_SYMBOL_CREATE (symbol_, value_, update); \
424 (context) -> symbols = symbol_ - (context); \
425 }, \
426 exclude (node -> value, after, before), \
427 { \
428 value_ = node->value + 1 + (code - after)/minimum; \
429 under_ = code - (code - after)%minimum; \
430 ENET_SYMBOL_CREATE (symbol_, value_, update); \
431 node -> right = symbol_ - node; \
432 }, \
433 { \
434 value_ = node->value - 1 - (after - before - code - 1)/minimum; \
435 under_ = code - (after - before - code - 1)%minimum; \
436 ENET_SYMBOL_CREATE (symbol_, value_, update); \
437 node -> left = symbol_ - node; \
438 }) \
439
440#ifdef ENET_CONTEXT_EXCLUSION
441typedef struct _ENetExclude
442{
443 enet_uint8 value;
444 enet_uint16 under;
445} ENetExclude;
446
447#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \
448{ \
449 enet_uint16 under = 0; \
450 nextExclude = excludes; \
451 ENET_CONTEXT_WALK (context, { \
452 under += rangeCoder -> symbols [node -> parent].count + minimum; \
453 nextExclude -> value = node -> value; \
454 nextExclude -> under = under; \
455 nextExclude ++; \
456 }); \
457 total -= under; \
458}
459
460#define ENET_CONTEXT_EXCLUDED(value_, after, before) \
461{ \
462 size_t low = 0, high = nextExclude - excludes; \
463 for(;;) \
464 { \
465 size_t mid = (low + high) >> 1; \
466 const ENetExclude * exclude = & excludes [mid]; \
467 if (value_ < exclude -> value) \
468 { \
469 if (low + 1 < high) \
470 { \
471 high = mid; \
472 continue; \
473 } \
474 if (exclude > excludes) \
475 after -= exclude [-1].under; \
476 } \
477 else \
478 { \
479 if (value_ > exclude -> value) \
480 { \
481 if (low + 1 < high) \
482 { \
483 low = mid; \
484 continue; \
485 } \
486 } \
487 else \
488 before = 0; \
489 after -= exclude -> under; \
490 } \
491 break; \
492 } \
493}
494#endif
495
496#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before)
497
498size_t
499enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit)
500{
501 ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
502 enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
503 const enet_uint8 * inEnd = & inData [inLimit];
504 enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0;
505 ENetSymbol * root;
506 enet_uint16 predicted = 0;
507 size_t order = 0, nextSymbol = 0;
508#ifdef ENET_CONTEXT_EXCLUSION
509 ENetExclude excludes [256];
510 ENetExclude * nextExclude = excludes;
511#endif
512
513 if (rangeCoder == NULL || inLimit <= 0)
514 return 0;
515
516 ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
517
518 ENET_RANGE_CODER_SEED;
519
520 for (;;)
521 {
522 ENetSymbol * subcontext, * symbol, * patch;
523#ifdef ENET_CONTEXT_EXCLUSION
524 const ENetSymbol * childContext = & emptyContext;
525#endif
526 enet_uint8 value = 0;
527 enet_uint16 code, under, count, bottom, * parent = & predicted, total;
528
529 for (subcontext = & rangeCoder -> symbols [predicted];
530 subcontext != root;
531#ifdef ENET_CONTEXT_EXCLUSION
532 childContext = subcontext,
533#endif
534 subcontext = & rangeCoder -> symbols [subcontext -> parent])
535 {
536 if (subcontext -> escapes <= 0)
537 continue;
538 total = subcontext -> total;
539#ifdef ENET_CONTEXT_EXCLUSION
540 if (childContext -> total > 0)
541 ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0);
542#endif
543 if (subcontext -> escapes >= total)
544 continue;
545 code = ENET_RANGE_CODER_READ (total);
546 if (code < subcontext -> escapes)
547 {
548 ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total);
549 continue;
550 }
551 code -= subcontext -> escapes;
552#ifdef ENET_CONTEXT_EXCLUSION
553 if (childContext -> total > 0)
554 {
555 ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED);
556 }
557 else
558#endif
559 {
560 ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED);
561 }
562 bottom = symbol - rangeCoder -> symbols;
563 ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total);
564 subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
565 if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
566 ENET_CONTEXT_RESCALE (subcontext, 0);
567 goto patchContexts;
568 }
569
570 total = root -> total;
571#ifdef ENET_CONTEXT_EXCLUSION
572 if (childContext -> total > 0)
573 ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM);
574#endif
575 code = ENET_RANGE_CODER_READ (total);
576 if (code < root -> escapes)
577 {
578 ENET_RANGE_CODER_DECODE (0, root -> escapes, total);
579 break;
580 }
581 code -= root -> escapes;
582#ifdef ENET_CONTEXT_EXCLUSION
583 if (childContext -> total > 0)
584 {
585 ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED);
586 }
587 else
588#endif
589 {
590 ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED);
591 }
592 bottom = symbol - rangeCoder -> symbols;
593 ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total);
594 root -> total += ENET_CONTEXT_SYMBOL_DELTA;
595 if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
596 ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
597
598 patchContexts:
599 for (patch = & rangeCoder -> symbols [predicted];
600 patch != subcontext;
601 patch = & rangeCoder -> symbols [patch -> parent])
602 {
603 ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
604 * parent = symbol - rangeCoder -> symbols;
605 parent = & symbol -> parent;
606 if (count <= 0)
607 {
608 patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
609 patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
610 }
611 patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
612 if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
613 ENET_CONTEXT_RESCALE (patch, 0);
614 }
615 * parent = bottom;
616
617 ENET_RANGE_CODER_OUTPUT (value);
618
619 if (order >= ENET_SUBCONTEXT_ORDER)
620 predicted = rangeCoder -> symbols [predicted].parent;
621 else
622 order ++;
623 ENET_RANGE_CODER_FREE_SYMBOLS;
624 }
625
626 return (size_t) (outData - outStart);
627}
628
629/** @defgroup host ENet host functions
630 @{
631*/
632
633/** Sets the packet compressor the host should use to the default range coder.
634 @param host host to enable the range coder for
635 @returns 0 on success, < 0 on failure
636*/
637int
638enet_host_compress_with_range_coder (ENetHost * host)
639{
640 ENetCompressor compressor;
641 memset (& compressor, 0, sizeof (compressor));
642 compressor.context = enet_range_coder_create();
643 if (compressor.context == NULL)
644 return -1;
645 compressor.compress = enet_range_coder_compress;
646 compressor.decompress = enet_range_coder_decompress;
647 compressor.destroy = enet_range_coder_destroy;
648 enet_host_compress (host, & compressor);
649 return 0;
650}
651
652/** @} */
653
654
655