QccENTArithmeticModel *QccENTArithmeticEncodeStart(const
int *num_symbols, int num_contexts, QccENTArithmeticGetContext context_function,
int target_num_bits);
int QccENTArithmeticEncodeEnd(QccENTArithmeticModel
*model, int final_context, QccBitBuffer *output_buffer);
int QccENTArithmeticEncode(const
int *symbol_stream, int symbol_stream_length, QccENTArithmeticModel *model,
QccBitBuffer *output_buffer);
int QccENTArithmeticEncodeFlush(QccENTArithmeticModel
*model, QccBitBuffer *output_buffer);
QccENTArithmeticModel *QccENTArithmeticDecodeStart(QccBitBuffer
*input_buffer, const int *num_symbols, int num_contexts, QccENTArithmeticGetContext
context_function, int target_num_bits);
int QccENTArithmeticDecodeRestart(QccBitBuffer
*input_buffer, QccENTArithmeticModel *model, int target_num_bits);
int
QccENTArithmeticDecode(QccBitBuffer *input_buffer, QccENTArithmeticModel
*model, int *symbol_stream, int symbol_stream_length);
In general, in both encoding and decoding, a num_contexts value is specified at the time of arithmetic-coding model creation, and this value indicates the number of contexts to be used for coding and decoding. num_contexts must be 1 or greater. Additionally, an array, num_symbols, is also passed at the time of model creation. num_symbols is an array of num_context integers which indicates the number of symbols for each context. Finally, during encoding and decoding, valid symbols for context i are integers greater than or equal to 0, and less than or equal num_symbols[i] - 1. The contexts themselves are assumed to be numbered from 0 up to, and including, num_contexts - 1.
QccENTArithmeticEncode() performs arithmetic encoding of the stream of symbols, symbol_stream, which is an array of valid symbols. symbol_stream_length gives the length of the stream to be encoded, while model is the pointer to the QccENTArithmeticModel structure previously returned by the call to QccENTArithmeticEncodeStart(). Finally, output_buffer is a pointer to a QccBitBuffer(3) structure; this output_buffer must already be opened for writing via a prior call to QccBitBufferStart(3) . If target_num_bits, as set during the initial call to QccENTArithmeticEncodeStart(), is not equal to QCCENT_ANYNUMBITS, then QccENTArithmeticEncode() will stop outputting bits and return prematurely when output_buffer->bit_cnt is equal to target_num_bits, regardless of whether all symbols in symbol_stream have been processed or not. If target_num_bits is equal to QCCENT_ANYNUMBITS, then QccENTArithmeticEncode() will return only when all symbols in symbol_stream have been processed. If QccENTArithmeticEncode() returns prematurely due to target_num_bits being output, then QccENTArithmeticEncode() returns with a value of 2; otherwise, it returns a 0 on success, or a 1 on failure.
QccENTArithmeticEncodeEnd() terminates arithmetic encoding by encoding a special EOF symbol to the bitstream, and then calling QccENTArithmeticEncodeFlush(). final_context is the context that is to be used for the EOF symbol. If the num_contexts in the arithmetic model is 1 (i.e., single-context coding), the value of final_context is ignored.
QccENTArithmeticEncodeFlush() flushes the state of the arithmetic encoder. Usually, after the last symbol is processed by QccENTArithmeticEncode(), there are several bits that remain to be output from the arithmetic encoder. QccENTArithmeticEncodeFlush() forces these bits to be output, and then calls QccBitBufferFlush(3) to guarantee that the bits are actually written to the output file. After a call to QccENTArithmeticEncodeFlush(), subsequent bits output will start on the next byte boundary of the output file; consequently, one can follow a call to QccENTArithmeticEncodeFlush() with a subsequent call to QccENTArithmeticEncode() to force encoded "blocks" to be "byte-aligned." Note: if such byte-alignment is done during encoding, during decoding, the decoder must be "restart" at the start of each block with a call to QccENTArithmeticDecodeRestart() (see below).
QccENTArithmeticDecode() attempts to decode symbol_stream_length symbols from input_buffer. The decoded symbols are placed in the array symbol_stream which must be allocated with space sufficient for symbol_stream_length symbols prior to the call to QccENTArithmeticDecode(). If target_num_bits was not equal to QCCENT_ANYNUMBITS with the immediately preceding call to QccENTArithmeticDecodeStart() or QccENTArithmeticDecodeRestart(), then QccENTArithmeticDecode() will return prematurely when the input_buffer->bit_cnt meets or exceeds target_num_bits. That is, QccENTArithmeticDecode() will not read past the target_num_bits bit in the bitstream, even if symbol_stream_length symbols have not been decoded. If, on the other hand, target_num_bits is equal to QCCENT_ANYNUMBITS, QccENTArithmeticDecode() will return when symbol_stream_length symbols are decoded. QccENTArithmeticDecode() returns 2 if the special EOF symbol is encountered while decoding; 1 if the end of the bitstream is reached while decoding; 1 if the target_num_bits bit limit is reached; or 0 if no error occurs.
QccENTArithmeticDecodeRestart() can be used to start the arithmetic decoder on any byte boundary. That is, if, during encoding, QccENTArithmeticEncodeFlush() is used to force byte alignment of "blocks" of bits, then one can use fseek(3) to position the bitstream to the byte at the start of the block, and then call QccENTArithmeticDecodeRestart() to restart the decoder at the byte-aligned boundary. In this case, target_num_bits can be set to input_buffer -> bit_cnt plus the length (in bits) of the block to stop the decoder again after the block has been decoded.
For a nonadaptive model, one can explicitly ("manually") change the frequency-count information in the model by passing a probability distribution to the model via a call to QccENTArithmeticSetModelProbabilities(3) .
QccENTArithmeticDecode() returns 0 on success, 1 on failure to read all requested symbols, or 2 if the special EOF symbol is encountered.
QccENTArithmeticEncodeStart() and QccENTArithmeticDecodeStart() return NULL on error, or a pointer to a QccENTArithmeticModel(3) structure on success.
QccENTArithmeticDecodeRestart() and QccENTArithmeticEncodeFlush() return 0 on success, and 1 on failure.
Encoder:
/* ENCODER */
#include "QccPack.h"
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;
QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);
/* obtain symbols here */
output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticEncodeStart(&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncode(symbol_stream,
SYMBOL_STREAM_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncodeEnd(model,
0,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}
if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
QccExit;
}
Decoder:
/* DECODER */
#include "QccPack.h"
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;
QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);
input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticDecodeStart(&input_buffer,
&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
SYMBOL_STREAM_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}
if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
/* output symbols stream here */
QccExit;
}
Encoder:
/* ENCODER */
#include "QccPack.h"
#define FIRST_CONTEXT 0
#define SECOND_CONTEXT 1
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;
int num_contexts = 2;
int num_symbols[2];
QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);
for (context = 0; context < 2; context++)
num_symbols[context] = NUM_SYMBOLS;
/* obtain symbols here */
output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticEncodeStart(num_symbols,
2,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticSetModelContext(model, FIRST_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncode(symbol_stream,
FIRST_BLOCK_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticSetModelContext(model, SECOND_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncode(&symbol_stream[FIRST_BLOCK_LENGTH],
SECOND_BLOCK_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncodeEnd(model,
SECOND_CONTEXT,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}
if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
QccExit;
}
Decoder:
/* DECODER */
#include "QccPack.h"
#define FIRST_CONTEXT 0
#define SECOND_CONTEXT 1
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;
int num_contexts = 2;
int num_symbols[2];
QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);
for (context = 0; context < 2; context++)
num_symbols[context] = NUM_SYMBOLS;
input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticDecodeStart(&input_buffer,
num_symbols,
2,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticSetModelContext(model, FIRST_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
FIRST_BLOCK_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticSetModelContext(model, SECOND_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticDecode(&input_buffer,
model,
&symbol_stream[FIRST_BLOCK_LENGTH],
SECOND_BLOCK_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}
if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
/* output symbols stream here */
QccExit;
}
Encoder:
/* ENCODER */
#include "QccPack.h"
int get_current_context(const int *symbol_stream,
int current_symbol)
{
if ((symbol_stream == NULL) || (current_symbol <= 0))
return(0);
return(symbol_stream[current_symbol - 1]);
}
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;
int num_contexts = NUM_SYMBOLS;
int num_symbols[NUM_SYMBOLS];
QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);
for (context = 0; context < NUM_SYMBOLS; context++)
num_symbols[context] = NUM_SYMBOLS;
/* obtain symbols here */
output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticEncodeStart(num_symbols,
num_contexts,
get_current_context,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncode(symbol_stream,
SYMBOL_STREAM_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncodeEnd(model,
0,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}
if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
QccExit;
}
Decoder:
/* DECODER */
#include "QccPack.h"
int get_current_context(const int *symbol_stream,
int current_symbol)
{
if ((symbol_stream == NULL) || (current_symbol <= 0))
return(0);
return(symbol_stream[current_symbol - 1]);
}
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;
int num_contexts = NUM_SYMBOLS;
int num_symbols[NUM_SYMBOLS];
QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);
for (context = 0; context < NUM_SYMBOLS; context++)
num_symbols[context] = NUM_SYMBOLS;
input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticDecodeStart(&input_buffer,
num_symbols,
num_contexts,
get_current_context,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
SYMBOL_STREAM_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}
if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
/* output symbols stream here */
QccExit;
}
Encoder:
/* ENCODER */
#include "QccPack.h"
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;
double probabilities[NUM_SYMBOLS];
QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);
/* obtain symbols and their probabilities somehow here */
output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticEncodeStart(&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticSetModelAdaption(model, QCCENT_NONADAPTIVE);
if (QccENTArithmeticSetModelProbabilities(model,
probabilities,
0))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelProbabilities()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncode(symbol_stream,
SYMBOL_STREAM_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticEncodeEnd(model,
0,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}
if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
QccExit;
}
Decoder:
/* DECODER */
#include "QccPack.h"
main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;
double probabilities[NUM_SYMBOLS];
QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);
/* obtain symbol probabilities somehow here */
input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}
if ((model = QccENTArithmeticDecodeStart(&input_buffer,
&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticSetModelAdaption(model, QCCENT_NONADAPTIVE);
if (QccENTArithmeticSetModelProbabilities(model,
probabilities,
0))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelProbabilities()",
argv[0]);
QccErrorExit();
}
if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
SYMBOL_STREAM_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}
if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}
QccENTArithmeticFreeModel(model);
/* output symbols stream here */
QccExit;
}
I. H. Witten, R. M. Neal, and J. G. Cleary, "Arithmetic Coding for Data Compression," Communications of the ACM, vol. 30, no. 6, pp. 520-540, June 1987.