%% options
copyright owner = Dirk Krause
copyright year = 2015-xxxx
license = bsd
%% header
/** @file dk4rld.h Run-length decoder.
Use dk4rld_init() to initialize the decoder before feeding data to it.
Feed the encoded data into the decoder byte by byte, use the
dk4rld_add() function.
If the function returns DK4_EDSTM_ACCEPT, the input was successfully stored
into the internal data structures, not need for further action.
If the function returns DK4_EDSTM_FINISHED, an output secquence was
completed, either a run or a literal byte sequence. Use dk4rld_output()
to retrieve output data.
If the dk4rld_add function returns DK4_EDSTM_STOP, an end of data
(EOD) marker was found; do not feed further bytes into the decoder.
After feeding all input bytes into the decoder, use dk4rld_finish() to check
syntax. If the function returns DK4_EDSTM_ERROR, the input data stream
was not properly run-length encoded.
*/
#include "dk4error.h"
/** Run length decoder.
*/
typedef struct {
unsigned char ob[128]; /**< Output buffer. */
size_t oused; /**< Number of used bytes in ob. */
size_t ll; /**< Number of expected bytes. */
int st; /**< Current state. */
} dk4_rl_dec_t;
#ifdef __cplusplus
extern "C" {
#endif
/** Initialize decoder.
@param dec Decoder to initialize.
@param erp Error report, may be NULL.
Error codes:
- DK4_E_INVALID_ARGUMENTS if enc is NULL.
*/
void
dk4rld_init(dk4_rl_dec_t *dec, dk4_er_t *erp);
/** Add one encoded byte to decoder.
@param dec Decoder.
@param inbyte Byte to process.
@param erp Error report, may be NULL.
@return Action to take, one from:
- DK4_EDSTM_ACCEPT
if the byte was successfully added
to the internal data structures, no further action necessary.
- DK4_EDSTM_FINISHED
if output is available, use dk4rld_output() to retrieve output.
- DK4_EDSTM_STOP
if an EOD marker was detected. Do not feed further bytes
to the decoder.
- DK4_EDSTM_ERROR
if an error occured.
Error codes:
- DK4_E_INVALID_ARGUMENTS if enc is NULL.
*/
int
dk4rld_add(dk4_rl_dec_t *dec, unsigned char inbyte, dk4_er_t *erp);
/** Finish decoding.
@param dec Decoder.
@param erp Error report, may be NULL.
@return Action to take, one from:
- DK4_EDSTM_ACCEPT
if no remaining data is stored in the decoder, no further
action necessary.
- DK4_EDSTM_FINISHED
if output is available, use dk4rld_output() to retrieve output.
- DK4_EDSTM_ERROR
if an error occured.
Error codes:
- DK4_E_INVALID_ARGUMENTS
if enc is NULL.
- DK4_E_SYNTAX
if data indicated by the run-length byte or literally-length
byte is missing.
*/
int
dk4rld_finish(dk4_rl_dec_t *dec, dk4_er_t *erp);
/** Retrieve decoder output.
@param dptr Address of pointer to set to decoder output,
should be initialized to NULL.
@param szptr Address of variable to receive buffer size,
should be initialized to 0.
@param dec Decoder.
@param erp Error report, may be NULL.
@return 1 if data is available, 0 otherwise.
Error codes:
- DK4_E_INVALID_ARGUMENTS if enc is NULL.
*/
int
dk4rld_output(
const unsigned char **dptr, size_t *szptr, dk4_rl_dec_t *dec, dk4_er_t *erp
);
#ifdef __cplusplus
}
#endif
%% module
#include "dk4rld.h"
#include "dk4edstm.h"
#include "dk4mem.h"
$!trace-include
/** Decoder states.
*/
enum {
/** Start state, exepect run length
or literal data length.
*/
RLD_ST_EXPECT_KEY_BYTE = 0,
/** Expect byte value for run.
*/
RLD_ST_EXPECT_RUN_BYTE ,
/** Expect literal byte.
*/
RLD_ST_EXPECT_LITERAL_BYTE ,
/** No further input accepted as
EOD marker was found.
*/
RLD_ST_EOD_FOUND
};
void
dk4rld_init(dk4_rl_dec_t *dec, dk4_er_t *erp)
{
$? "+ dk4rld_init"
if (NULL != dec) {
DK4_MEMRES(dec, sizeof(dk4_rl_dec_t));
dec->oused = 0;
dec->ll = 0;
dec->st = RLD_ST_EXPECT_KEY_BYTE;
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
} $? "- dk4rld_init"
}
int
dk4rld_add(dk4_rl_dec_t *dec, unsigned char inbyte, dk4_er_t *erp)
{
int back = DK4_EDSTM_ERROR;
$? "+ dk4rld_add"
if (NULL != dec) {
switch (dec->st) {
case RLD_ST_EXPECT_KEY_BYTE : {
if ((unsigned char)128 > inbyte) {
/*
Literal byte sequence
*/
dec->st = RLD_ST_EXPECT_LITERAL_BYTE;
dec->ll = (size_t)inbyte + 1;
back = DK4_EDSTM_ACCEPT;
} else {
if ((unsigned char)128 < inbyte) {
/*
Run of equal bytes
*/
dec->st = RLD_ST_EXPECT_RUN_BYTE;
dec->ll = (size_t)257 - (size_t)inbyte;
back = DK4_EDSTM_ACCEPT;
} else {
/*
EOD marker found
*/
dec->st = RLD_ST_EOD_FOUND;
back = DK4_EDSTM_STOP;
}
}
} break;
case RLD_ST_EXPECT_RUN_BYTE : {
dk4mem_set(&(dec->ob[0]), dec->ll, inbyte, NULL);
dec->oused = dec->ll;
dec->st = RLD_ST_EXPECT_KEY_BYTE;
back = DK4_EDSTM_FINISHED;
} break;
case RLD_ST_EXPECT_LITERAL_BYTE : {
dec->ob[0] = inbyte;
dec->oused = 1;
dec->ll -= 1;
if (0 == dec->ll) {
dec->st = RLD_ST_EXPECT_KEY_BYTE;
}
back = DK4_EDSTM_FINISHED;
} break;
default : {
dk4error_set_simple_error_code(erp, DK4_E_SYNTAX);
} break;
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
} $? "- dk4rld_add %d", back
return back;
}
int
dk4rld_finish(dk4_rl_dec_t *dec, dk4_er_t *erp)
{
int back = DK4_EDSTM_ERROR;
$? "+ dk4rld_finish"
if (NULL != dec) {
switch (dec->st) {
case RLD_ST_EXPECT_KEY_BYTE : case RLD_ST_EOD_FOUND : {
back = DK4_EDSTM_ACCEPT;
} break;
default : {
dk4error_set_simple_error_code(erp, DK4_E_SYNTAX);
} break;
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
} $? "- dk4rld_finish %d", back
return back;
}
int
dk4rld_output(
const unsigned char **dptr, size_t *szptr, dk4_rl_dec_t *dec, dk4_er_t *erp
)
{
int back = 0;
$? "+ dk4rld_output"
if (NULL != dec) {
if (0 < dec->oused) {
*dptr = &(dec->ob[0]);
*szptr = dec->oused;
back = 1;
} else {
*dptr = NULL;
*szptr = 0;
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
} $? "- dk4rld_output %d", back
return back;
}