| /** |
| @file compress.c |
| @brief An adaptive order-2 PPM range coder |
| */ |
| #define ENET_BUILDING_LIB 1 |
| #include <string.h> |
| #include "enet/enet.h" |
| |
| typedef struct _ENetSymbol |
| { |
| /* binary indexed tree of symbols */ |
| enet_uint8 value; |
| enet_uint8 count; |
| enet_uint16 under; |
| enet_uint16 left, right; |
| |
| /* context defined by this symbol */ |
| enet_uint16 symbols; |
| enet_uint16 escapes; |
| enet_uint16 total; |
| enet_uint16 parent; |
| } ENetSymbol; |
| |
| /* adaptation constants tuned aggressively for small packet sizes rather than large file compression */ |
| enum |
| { |
| ENET_RANGE_CODER_TOP = 1<<24, |
| ENET_RANGE_CODER_BOTTOM = 1<<16, |
| |
| ENET_CONTEXT_SYMBOL_DELTA = 3, |
| ENET_CONTEXT_SYMBOL_MINIMUM = 1, |
| ENET_CONTEXT_ESCAPE_MINIMUM = 1, |
| |
| ENET_SUBCONTEXT_ORDER = 2, |
| ENET_SUBCONTEXT_SYMBOL_DELTA = 2, |
| ENET_SUBCONTEXT_ESCAPE_DELTA = 5 |
| }; |
| |
| /* context exclusion roughly halves compression speed, so disable for now */ |
| #undef ENET_CONTEXT_EXCLUSION |
| |
| typedef struct _ENetRangeCoder |
| { |
| /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */ |
| ENetSymbol symbols[4096]; |
| } ENetRangeCoder; |
| |
| void * |
| enet_range_coder_create (void) |
| { |
| ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder)); |
| if (rangeCoder == NULL) |
| return NULL; |
| |
| return rangeCoder; |
| } |
| |
| void |
| enet_range_coder_destroy (void * context) |
| { |
| ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; |
| if (rangeCoder == NULL) |
| return; |
| |
| enet_free (rangeCoder); |
| } |
| |
| #define ENET_SYMBOL_CREATE(symbol, value_, count_) \ |
| { \ |
| symbol = & rangeCoder -> symbols [nextSymbol ++]; \ |
| symbol -> value = value_; \ |
| symbol -> count = count_; \ |
| symbol -> under = count_; \ |
| symbol -> left = 0; \ |
| symbol -> right = 0; \ |
| symbol -> symbols = 0; \ |
| symbol -> escapes = 0; \ |
| symbol -> total = 0; \ |
| symbol -> parent = 0; \ |
| } |
| |
| #define ENET_CONTEXT_CREATE(context, escapes_, minimum) \ |
| { \ |
| ENET_SYMBOL_CREATE (context, 0, 0); \ |
| (context) -> escapes = escapes_; \ |
| (context) -> total = escapes_ + 256*minimum; \ |
| (context) -> symbols = 0; \ |
| } |
| |
| static enet_uint16 |
| enet_symbol_rescale (ENetSymbol * symbol) |
| { |
| enet_uint16 total = 0; |
| for (;;) |
| { |
| symbol -> count -= symbol->count >> 1; |
| symbol -> under = symbol -> count; |
| if (symbol -> left) |
| symbol -> under += enet_symbol_rescale (symbol + symbol -> left); |
| total += symbol -> under; |
| if (! symbol -> right) break; |
| symbol += symbol -> right; |
| } |
| return total; |
| } |
| |
| #define ENET_CONTEXT_RESCALE(context, minimum) \ |
| { \ |
| (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \ |
| (context) -> escapes -= (context) -> escapes >> 1; \ |
| (context) -> total += (context) -> escapes + 256*minimum; \ |
| } |
| |
| #define ENET_RANGE_CODER_OUTPUT(value) \ |
| { \ |
| if (outData >= outEnd) \ |
| return 0; \ |
| * outData ++ = value; \ |
| } |
| |
| #define ENET_RANGE_CODER_ENCODE(under, count, total) \ |
| { \ |
| encodeRange /= (total); \ |
| encodeLow += (under) * encodeRange; \ |
| encodeRange *= (count); \ |
| for (;;) \ |
| { \ |
| if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \ |
| { \ |
| if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ |
| encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ |
| } \ |
| ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ |
| encodeRange <<= 8; \ |
| encodeLow <<= 8; \ |
| } \ |
| } |
| |
| #define ENET_RANGE_CODER_FLUSH \ |
| { \ |
| while (encodeLow) \ |
| { \ |
| ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ |
| encodeLow <<= 8; \ |
| } \ |
| } |
| |
| #define ENET_RANGE_CODER_FREE_SYMBOLS \ |
| { \ |
| if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \ |
| { \ |
| nextSymbol = 0; \ |
| ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \ |
| predicted = 0; \ |
| order = 0; \ |
| } \ |
| } |
| |
| #define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \ |
| { \ |
| under_ = value*minimum; \ |
| count_ = minimum; \ |
| if (! (context) -> symbols) \ |
| { \ |
| ENET_SYMBOL_CREATE (symbol_, value_, update); \ |
| (context) -> symbols = symbol_ - (context); \ |
| } \ |
| else \ |
| { \ |
| ENetSymbol * node = (context) + (context) -> symbols; \ |
| for (;;) \ |
| { \ |
| if (value_ < node -> value) \ |
| { \ |
| node -> under += update; \ |
| if (node -> left) { node += node -> left; continue; } \ |
| ENET_SYMBOL_CREATE (symbol_, value_, update); \ |
| node -> left = symbol_ - node; \ |
| } \ |
| else \ |
| if (value_ > node -> value) \ |
| { \ |
| under_ += node -> under; \ |
| if (node -> right) { node += node -> right; continue; } \ |
| ENET_SYMBOL_CREATE (symbol_, value_, update); \ |
| node -> right = symbol_ - node; \ |
| } \ |
| else \ |
| { \ |
| count_ += node -> count; \ |
| under_ += node -> under - node -> count; \ |
| node -> under += update; \ |
| node -> count += update; \ |
| symbol_ = node; \ |
| } \ |
| break; \ |
| } \ |
| } \ |
| } |
| |
| #ifdef ENET_CONTEXT_EXCLUSION |
| static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| |
| #define ENET_CONTEXT_WALK(context, body) \ |
| { \ |
| const ENetSymbol * node = (context) + (context) -> symbols; \ |
| const ENetSymbol * stack [256]; \ |
| size_t stackSize = 0; \ |
| while (node -> left) \ |
| { \ |
| stack [stackSize ++] = node; \ |
| node += node -> left; \ |
| } \ |
| for (;;) \ |
| { \ |
| body; \ |
| if (node -> right) \ |
| { \ |
| node += node -> right; \ |
| while (node -> left) \ |
| { \ |
| stack [stackSize ++] = node; \ |
| node += node -> left; \ |
| } \ |
| } \ |
| else \ |
| if (stackSize <= 0) \ |
| break; \ |
| else \ |
| node = stack [-- stackSize]; \ |
| } \ |
| } |
| |
| #define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \ |
| ENET_CONTEXT_WALK(context, { \ |
| if (node -> value != value_) \ |
| { \ |
| enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \ |
| if (node -> value < value_) \ |
| under -= parentCount; \ |
| total -= parentCount; \ |
| } \ |
| }) |
| #endif |
| |
| size_t |
| enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit) |
| { |
| ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; |
| enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; |
| const enet_uint8 * inData, * inEnd; |
| enet_uint32 encodeLow = 0, encodeRange = ~0; |
| ENetSymbol * root; |
| enet_uint16 predicted = 0; |
| size_t order = 0, nextSymbol = 0; |
| |
| if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0) |
| return 0; |
| |
| inData = (const enet_uint8 *) inBuffers -> data; |
| inEnd = & inData [inBuffers -> dataLength]; |
| inBuffers ++; |
| inBufferCount --; |
| |
| ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); |
| |
| for (;;) |
| { |
| ENetSymbol * subcontext, * symbol; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| const ENetSymbol * childContext = & emptyContext; |
| #endif |
| enet_uint8 value; |
| enet_uint16 count, under, * parent = & predicted, total; |
| if (inData >= inEnd) |
| { |
| if (inBufferCount <= 0) |
| break; |
| inData = (const enet_uint8 *) inBuffers -> data; |
| inEnd = & inData [inBuffers -> dataLength]; |
| inBuffers ++; |
| inBufferCount --; |
| } |
| value = * inData ++; |
| |
| for (subcontext = & rangeCoder -> symbols [predicted]; |
| subcontext != root; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| childContext = subcontext, |
| #endif |
| subcontext = & rangeCoder -> symbols [subcontext -> parent]) |
| { |
| ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); |
| * parent = symbol - rangeCoder -> symbols; |
| parent = & symbol -> parent; |
| total = subcontext -> total; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) |
| ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0); |
| #endif |
| if (count > 0) |
| { |
| ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total); |
| } |
| else |
| { |
| if (subcontext -> escapes > 0 && subcontext -> escapes < total) |
| ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total); |
| subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; |
| subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; |
| } |
| subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; |
| if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) |
| ENET_CONTEXT_RESCALE (subcontext, 0); |
| if (count > 0) goto nextInput; |
| } |
| |
| ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); |
| * parent = symbol - rangeCoder -> symbols; |
| parent = & symbol -> parent; |
| total = root -> total; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) |
| ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM); |
| #endif |
| ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total); |
| root -> total += ENET_CONTEXT_SYMBOL_DELTA; |
| if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) |
| ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); |
| |
| nextInput: |
| if (order >= ENET_SUBCONTEXT_ORDER) |
| predicted = rangeCoder -> symbols [predicted].parent; |
| else |
| order ++; |
| ENET_RANGE_CODER_FREE_SYMBOLS; |
| } |
| |
| ENET_RANGE_CODER_FLUSH; |
| |
| return (size_t) (outData - outStart); |
| } |
| |
| #define ENET_RANGE_CODER_SEED \ |
| { \ |
| if (inData < inEnd) decodeCode |= * inData ++ << 24; \ |
| if (inData < inEnd) decodeCode |= * inData ++ << 16; \ |
| if (inData < inEnd) decodeCode |= * inData ++ << 8; \ |
| if (inData < inEnd) decodeCode |= * inData ++; \ |
| } |
| |
| #define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total))) |
| |
| #define ENET_RANGE_CODER_DECODE(under, count, total) \ |
| { \ |
| decodeLow += (under) * decodeRange; \ |
| decodeRange *= (count); \ |
| for (;;) \ |
| { \ |
| if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \ |
| { \ |
| if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ |
| decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ |
| } \ |
| decodeCode <<= 8; \ |
| if (inData < inEnd) \ |
| decodeCode |= * inData ++; \ |
| decodeRange <<= 8; \ |
| decodeLow <<= 8; \ |
| } \ |
| } |
| |
| #define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \ |
| { \ |
| under_ = 0; \ |
| count_ = minimum; \ |
| if (! (context) -> symbols) \ |
| { \ |
| createRoot; \ |
| } \ |
| else \ |
| { \ |
| ENetSymbol * node = (context) + (context) -> symbols; \ |
| for (;;) \ |
| { \ |
| enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \ |
| visitNode; \ |
| if (code >= after) \ |
| { \ |
| under_ += node -> under; \ |
| if (node -> right) { node += node -> right; continue; } \ |
| createRight; \ |
| } \ |
| else \ |
| if (code < after - before) \ |
| { \ |
| node -> under += update; \ |
| if (node -> left) { node += node -> left; continue; } \ |
| createLeft; \ |
| } \ |
| else \ |
| { \ |
| value_ = node -> value; \ |
| count_ += node -> count; \ |
| under_ = after - before; \ |
| node -> under += update; \ |
| node -> count += update; \ |
| symbol_ = node; \ |
| } \ |
| break; \ |
| } \ |
| } \ |
| } |
| |
| #define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ |
| ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0) |
| |
| #define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ |
| ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \ |
| { \ |
| value_ = code / minimum; \ |
| under_ = code - code%minimum; \ |
| ENET_SYMBOL_CREATE (symbol_, value_, update); \ |
| (context) -> symbols = symbol_ - (context); \ |
| }, \ |
| exclude (node -> value, after, before), \ |
| { \ |
| value_ = node->value + 1 + (code - after)/minimum; \ |
| under_ = code - (code - after)%minimum; \ |
| ENET_SYMBOL_CREATE (symbol_, value_, update); \ |
| node -> right = symbol_ - node; \ |
| }, \ |
| { \ |
| value_ = node->value - 1 - (after - before - code - 1)/minimum; \ |
| under_ = code - (after - before - code - 1)%minimum; \ |
| ENET_SYMBOL_CREATE (symbol_, value_, update); \ |
| node -> left = symbol_ - node; \ |
| }) \ |
| |
| #ifdef ENET_CONTEXT_EXCLUSION |
| typedef struct _ENetExclude |
| { |
| enet_uint8 value; |
| enet_uint16 under; |
| } ENetExclude; |
| |
| #define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \ |
| { \ |
| enet_uint16 under = 0; \ |
| nextExclude = excludes; \ |
| ENET_CONTEXT_WALK (context, { \ |
| under += rangeCoder -> symbols [node -> parent].count + minimum; \ |
| nextExclude -> value = node -> value; \ |
| nextExclude -> under = under; \ |
| nextExclude ++; \ |
| }); \ |
| total -= under; \ |
| } |
| |
| #define ENET_CONTEXT_EXCLUDED(value_, after, before) \ |
| { \ |
| size_t low = 0, high = nextExclude - excludes; \ |
| for(;;) \ |
| { \ |
| size_t mid = (low + high) >> 1; \ |
| const ENetExclude * exclude = & excludes [mid]; \ |
| if (value_ < exclude -> value) \ |
| { \ |
| if (low + 1 < high) \ |
| { \ |
| high = mid; \ |
| continue; \ |
| } \ |
| if (exclude > excludes) \ |
| after -= exclude [-1].under; \ |
| } \ |
| else \ |
| { \ |
| if (value_ > exclude -> value) \ |
| { \ |
| if (low + 1 < high) \ |
| { \ |
| low = mid; \ |
| continue; \ |
| } \ |
| } \ |
| else \ |
| before = 0; \ |
| after -= exclude -> under; \ |
| } \ |
| break; \ |
| } \ |
| } |
| #endif |
| |
| #define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before) |
| |
| size_t |
| enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit) |
| { |
| ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; |
| enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; |
| const enet_uint8 * inEnd = & inData [inLimit]; |
| enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0; |
| ENetSymbol * root; |
| enet_uint16 predicted = 0; |
| size_t order = 0, nextSymbol = 0; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| ENetExclude excludes [256]; |
| ENetExclude * nextExclude = excludes; |
| #endif |
| |
| if (rangeCoder == NULL || inLimit <= 0) |
| return 0; |
| |
| ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); |
| |
| ENET_RANGE_CODER_SEED; |
| |
| for (;;) |
| { |
| ENetSymbol * subcontext, * symbol, * patch; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| const ENetSymbol * childContext = & emptyContext; |
| #endif |
| enet_uint8 value = 0; |
| enet_uint16 code, under, count, bottom, * parent = & predicted, total; |
| |
| for (subcontext = & rangeCoder -> symbols [predicted]; |
| subcontext != root; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| childContext = subcontext, |
| #endif |
| subcontext = & rangeCoder -> symbols [subcontext -> parent]) |
| { |
| if (subcontext -> escapes <= 0) |
| continue; |
| total = subcontext -> total; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| if (childContext -> total > 0) |
| ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0); |
| #endif |
| if (subcontext -> escapes >= total) |
| continue; |
| code = ENET_RANGE_CODER_READ (total); |
| if (code < subcontext -> escapes) |
| { |
| ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total); |
| continue; |
| } |
| code -= subcontext -> escapes; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| if (childContext -> total > 0) |
| { |
| ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED); |
| } |
| else |
| #endif |
| { |
| ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED); |
| } |
| bottom = symbol - rangeCoder -> symbols; |
| ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total); |
| subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; |
| if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) |
| ENET_CONTEXT_RESCALE (subcontext, 0); |
| goto patchContexts; |
| } |
| |
| total = root -> total; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| if (childContext -> total > 0) |
| ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM); |
| #endif |
| code = ENET_RANGE_CODER_READ (total); |
| if (code < root -> escapes) |
| { |
| ENET_RANGE_CODER_DECODE (0, root -> escapes, total); |
| break; |
| } |
| code -= root -> escapes; |
| #ifdef ENET_CONTEXT_EXCLUSION |
| if (childContext -> total > 0) |
| { |
| ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED); |
| } |
| else |
| #endif |
| { |
| ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED); |
| } |
| bottom = symbol - rangeCoder -> symbols; |
| ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total); |
| root -> total += ENET_CONTEXT_SYMBOL_DELTA; |
| if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) |
| ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); |
| |
| patchContexts: |
| for (patch = & rangeCoder -> symbols [predicted]; |
| patch != subcontext; |
| patch = & rangeCoder -> symbols [patch -> parent]) |
| { |
| ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); |
| * parent = symbol - rangeCoder -> symbols; |
| parent = & symbol -> parent; |
| if (count <= 0) |
| { |
| patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; |
| patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; |
| } |
| patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; |
| if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100) |
| ENET_CONTEXT_RESCALE (patch, 0); |
| } |
| * parent = bottom; |
| |
| ENET_RANGE_CODER_OUTPUT (value); |
| |
| if (order >= ENET_SUBCONTEXT_ORDER) |
| predicted = rangeCoder -> symbols [predicted].parent; |
| else |
| order ++; |
| ENET_RANGE_CODER_FREE_SYMBOLS; |
| } |
| |
| return (size_t) (outData - outStart); |
| } |
| |
| /** @defgroup host ENet host functions |
| @{ |
| */ |
| |
| /** Sets the packet compressor the host should use to the default range coder. |
| @param host host to enable the range coder for |
| @returns 0 on success, < 0 on failure |
| */ |
| int |
| enet_host_compress_with_range_coder (ENetHost * host) |
| { |
| ENetCompressor compressor; |
| memset (& compressor, 0, sizeof (compressor)); |
| compressor.context = enet_range_coder_create(); |
| if (compressor.context == NULL) |
| return -1; |
| compressor.compress = enet_range_coder_compress; |
| compressor.decompress = enet_range_coder_decompress; |
| compressor.destroy = enet_range_coder_destroy; |
| enet_host_compress (host, & compressor); |
| return 0; |
| } |
| |
| /** @} */ |
| |
| |