From b5b18227b93be40940934d3f531d99648e058153 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Mon, 24 May 2010 11:36:08 +0000 Subject: [PATCH] range coder cleanups git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@5028 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/enet/ChangeLog | 2 +- source/enet/compress.c | 338 ++++++++++++++++++++++++++++------------- source/lib/enet.lib | Bin 42476 -> 44726 bytes 3 files changed, 237 insertions(+), 103 deletions(-) diff --git a/source/enet/ChangeLog b/source/enet/ChangeLog index ce4e8ff0..5d3ba749 100644 --- a/source/enet/ChangeLog +++ b/source/enet/ChangeLog @@ -4,7 +4,7 @@ ENet 1.3.0 (May 20, 2010): a parameter * enet_host_connect() now accepts a data parameter which is supplied to the receiving receiving host in the event data field for a connect event -* added an adaptive order-1 range coder as a built-in compressor option +* added an adaptive order-2 PPM range coder as a built-in compressor option which can be set with enet_host_compress_with_range_coder() * added support for packet compression configurable with a callback * improved session number handling to not rely on the packet checksum diff --git a/source/enet/compress.c b/source/enet/compress.c index e8726648..97449dca 100644 --- a/source/enet/compress.c +++ b/source/enet/compress.c @@ -1,6 +1,6 @@ /** @file compress.c - @brief An order-2 adaptive range coder + @brief An adaptive order-2 PPM range coder */ #define ENET_BUILDING_LIB 1 #include @@ -21,6 +21,7 @@ typedef struct _ENetSymbol enet_uint16 parent; } ENetSymbol; +/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */ enum { ENET_RANGE_CODER_TOP = 1<<24, @@ -35,9 +36,12 @@ enum ENET_SUBCONTEXT_ESCAPE_DELTA = 5 }; +/* context exclusion roughly halves compression speed, so disable for now */ +#define ENET_CONTEXT_EXCLUSION + typedef struct _ENetRangeCoder { - /* only allocate enough symbols for reasonable MTUs */ + /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */ ENetSymbol symbols[4096]; } ENetRangeCoder; @@ -143,7 +147,7 @@ enet_symbol_rescale (ENetSymbol * symbol) #define ENET_RANGE_CODER_FREE_SYMBOLS \ { \ - if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER) \ + if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \ { \ nextSymbol = 0; \ ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \ @@ -152,16 +156,14 @@ enet_symbol_rescale (ENetSymbol * symbol) } \ } -#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update) \ +#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \ { \ - ENetSymbol * child; \ - under_ = 0; \ - count_ = 0; \ + under_ = value*minimum; \ + count_ = minimum; \ if (! (context) -> symbols) \ { \ - ENET_SYMBOL_CREATE (child, value_, update); \ - (context) -> symbols = child - (context); \ - symbol_ = child; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + (context) -> symbols = symbol_ - (context); \ } \ else \ { \ @@ -172,23 +174,21 @@ enet_symbol_rescale (ENetSymbol * symbol) { \ node -> under += update; \ if (node -> left) { node += node -> left; continue; } \ - ENET_SYMBOL_CREATE (child, value_, update); \ - node -> left = child - node; \ - symbol_ = child; \ + 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 (child, value_, update); \ - node -> right = child - node; \ - symbol_ = child; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> right = symbol_ - node; \ } \ else \ { \ - count_ = node -> count; \ - under_ += node -> under - count_; \ + count_ += node -> count; \ + under_ += node -> under - node -> count; \ node -> under += update; \ node -> count += update; \ symbol_ = node; \ @@ -198,6 +198,51 @@ enet_symbol_rescale (ENetSymbol * symbol) } \ } +#ifdef ENET_CONTEXT_EXCLUSION +static const ENetSymbol enet_context_empty = { 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) { @@ -222,9 +267,11 @@ enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t for (;;) { ENetSymbol * subcontext, * symbol; +#ifdef ENET_CONTEXT_EXCLUSION + const ENetSymbol * childContext = & enet_context_empty; +#endif enet_uint8 value; - enet_uint16 under, count, * parent = & predicted; - size_t level = order; + enet_uint16 count, under, * parent = & predicted, total; if (inData >= inEnd) { if (inBufferCount <= 0) @@ -235,22 +282,30 @@ enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount --; } value = * inData ++; - + for (subcontext = & rangeCoder -> symbols [predicted]; subcontext != root; - subcontext = & rangeCoder -> symbols [subcontext -> parent]) +#ifdef ENET_CONTEXT_EXCLUSION + childContext = subcontext, +#endif + subcontext = & rangeCoder -> symbols [subcontext -> parent]) { - ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA); + 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, subcontext -> total); + ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total); } else { - if (subcontext -> escapes > 0) - ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, subcontext -> total); + 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; } @@ -260,12 +315,17 @@ enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t if (count > 0) goto nextInput; } - ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA); + ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); * parent = symbol - rangeCoder -> symbols; parent = & symbol -> parent; - ENET_RANGE_CODER_ENCODE (root -> escapes + under + value*ENET_CONTEXT_SYMBOL_MINIMUM, count + ENET_CONTEXT_SYMBOL_MINIMUM, root -> total); + 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 || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + 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: @@ -310,88 +370,39 @@ enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t } \ } -#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update) \ +#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \ { \ under_ = 0; \ - count_ = 0; \ - if (! (context) -> symbols) \ - return 0; \ - else \ - { \ - ENetSymbol * node = (context) + (context) -> symbols; \ - for (;;) \ - { \ - if (code >= under_ + node -> under) \ - { \ - under_ += node -> under; \ - if (! node -> right) return 0; \ - node += node -> right; \ - } \ - else \ - if (code < under_ + node -> under - node -> count) \ - { \ - node -> under += update; \ - if (! node -> left) return 0; \ - node += node -> left; \ - } \ - else \ - { \ - value_ = node -> value; \ - count_ = node -> count; \ - under_ += node -> under - count_; \ - node -> under += update; \ - node -> count += update; \ - symbol_ = node; \ - break; \ - } \ - } \ - } \ -} - -#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum) \ -{ \ - ENetSymbol * child; \ - int lowValue = -1; \ - under_ = 0; \ - count_ = 0; \ + count_ = minimum; \ if (! (context) -> symbols) \ { \ - value_ = code / minimum; \ - ENET_SYMBOL_CREATE (child, value_, update); \ - (context) -> symbols = child - (context); \ - symbol_ = child; \ + createRoot; \ } \ else \ { \ ENetSymbol * node = (context) + (context) -> symbols; \ for (;;) \ { \ - enet_uint16 total = under_ + node -> under + (node -> value + 1)*minimum; \ - if (code >= total) \ + enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \ + visitNode; \ + if (code >= after) \ { \ under_ += node -> under; \ - lowValue = node -> value; \ if (node -> right) { node += node -> right; continue; } \ - value_ = lowValue + 1 + (code - under_ - (lowValue + 1)*minimum) / minimum; \ - ENET_SYMBOL_CREATE (child, value_, update); \ - node -> right = child - node; \ - symbol_ = child; \ + createRight; \ } \ else \ - if (code < total - node -> count - minimum) \ + if (code < after - before) \ { \ node -> under += update; \ if (node -> left) { node += node -> left; continue; } \ - value_ = lowValue + 1 + (code - under_ - (lowValue + 1)*minimum) / minimum; \ - ENET_SYMBOL_CREATE (child, value_, update); \ - node -> left = child - node; \ - symbol_ = child; \ + createLeft; \ } \ else \ { \ value_ = node -> value; \ - count_ = node -> count; \ - under_ += node -> under - count_; \ + count_ += node -> count; \ + under_ = after - before; \ node -> under += update; \ node -> count += update; \ symbol_ = node; \ @@ -401,6 +412,89 @@ enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t } \ } +#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) { @@ -411,7 +505,11 @@ enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t i 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; @@ -422,43 +520,79 @@ enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t i for (;;) { ENetSymbol * subcontext, * symbol, * patch; +#ifdef ENET_CONTEXT_EXCLUSION + const ENetSymbol * childContext = & enet_context_empty; +#endif enet_uint8 value = 0; - enet_uint16 code, under, count, bottom, * parent = & predicted; + enet_uint16 code, under, count, bottom, * parent = & predicted, total; for (subcontext = & rangeCoder -> symbols [predicted]; subcontext != root; - subcontext = & rangeCoder -> symbols [subcontext -> parent]) +#ifdef ENET_CONTEXT_EXCLUSION + childContext = subcontext, +#endif + subcontext = & rangeCoder -> symbols [subcontext -> parent]) { if (subcontext -> escapes <= 0) continue; - code = ENET_RANGE_CODER_READ (subcontext -> total); + 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, subcontext -> total); + ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total); continue; } code -= subcontext -> escapes; - ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA); +#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, subcontext -> total); + 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; } - code = ENET_RANGE_CODER_READ (root -> total); + 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, root -> total); + ENET_RANGE_CODER_DECODE (0, root -> escapes, total); break; } code -= root -> escapes; - ENET_CONTEXT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); +#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 + value*ENET_CONTEXT_SYMBOL_MINIMUM, count + ENET_CONTEXT_SYMBOL_MINIMUM, root -> total); + ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total); root -> total += ENET_CONTEXT_SYMBOL_DELTA; - if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + 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: @@ -466,7 +600,7 @@ enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t i patch != subcontext; patch = & rangeCoder -> symbols [patch -> parent]) { - ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA); + ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); * parent = symbol - rangeCoder -> symbols; parent = & symbol -> parent; if (count <= 0) diff --git a/source/lib/enet.lib b/source/lib/enet.lib index 20f8c516bf63c8cd74905c1a364e97a6319728aa..08bca25deb39bcb3eb65f609a0942a70a3701432 100644 GIT binary patch delta 7650 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 delta 5059 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 DlF1lt