%% options
copyright owner = Dirk Krause
copyright year = 2015-xxxx
license = bsd
%% header
/** @file
Encode and decode 32 bit characters
in UTF-16 encoding.
CRT on Windows: Not used.
*/
#ifndef DK4CONF_H_INCLUDED
#include "dk4conf.h"
#endif
#ifndef DK4CONF_H_INCLUDED
#include "dk4types.h"
#endif
#ifndef DK4ERROR_H_INCLUDED
#include "dk4error.h"
#endif
#ifndef DK4DEC_H_INCLUDED
#include "dk4edstm.h"
#endif
/** Decode UTF-16 encoded data.
*/
typedef struct {
dk4_c32_t uc; /**< Unicode character to produce. */
int state; /**< Decoder state. */
} dk4_utf16_decoder_t;
/** Decode UTF-16 encoded bytes.
*/
typedef struct {
dk4_utf16_decoder_t dec; /**< Decoder UTF-16 to 32-bit char. */
int msb; /**< Flag: Most significant byte comes first. */
int state; /**< Decoder state. */
dk4_c16_t saved; /**< Partial input character saved. */
} dk4_utf16_byte_decoder_t;
#ifdef __cplusplus
extern "C" {
#endif
/** Initialize the UTF-16 decoder.
@param ptr Decoder to initialize.
*/
void
dk4utf16_init(dk4_utf16_decoder_t *ptr);
/** Add one word to the decoder.
@param ptr Decoder to use.
@param word Word to add.
@return Operation result, one from:
DK4_EDSTM_ACCEPT if the byte was added to the next output
without completing output,
DK4_EDSTM_FINISHED, if the byte was added to the next output
completing the output, or
DK4_EDSTM_ERROR on error.
*/
int
dk4utf16_add(dk4_utf16_decoder_t *ptr, dk4_c16_t word);
/** Retrieve a completed Unicode character from the decoder.
@param ptr Decoder to obtain character from.
*/
dk4_c32_t
dk4utf16_get(dk4_utf16_decoder_t *ptr);
/** Check whether the decoder is empty.
@param ptr Decoder to check.
@return 1 on success, 0 on error.
*/
int
dk4utf16_is_empty(dk4_utf16_decoder_t *ptr);
/** UTF-16 encode UNICODE character.
@param rb Result buffer, should be at least 3 dk4_c16_t.
@param pszrb Size of the result buffer, in: available, out: used.
@param c Character to encode.
@param erp Error report, may be NULL.
@return 1 on success, 0 on error.
Error codes:
- DK4_E_INVALID_ARGUMENTS
if rb or pszrb is NULL,
- DK4_E_BUFFER_TOO_SMALL
if the buffer size is less than 3,
- DK4_E_SYNTAX
if c is out of the encodable range.
*/
int
dk4utf16_encode(dk4_c16_t *rb, size_t *pszrb, dk4_c32_t c, dk4_er_t *erp);
/** Initialize the UTF-16 decoder.
@param ptr Decoder to initialize.
@param msb Flag: Most significant byte comes first.
*/
void
dk4utf16_byte_init(dk4_utf16_byte_decoder_t *ptr, int msb);
/** Add one byte to the decoder.
@param ptr Decoder to use.
@param byte Byte to add.
@return Operation result, one from:
DK4_EDSTM_ACCEPT if the byte was added to the next output
without completing output,
DK4_EDSTM_FINISHED, if the byte was added to the next output
completing the output, or
DK4_EDSTM_ERROR on error.
*/
int
dk4utf16_byte_add(dk4_utf16_byte_decoder_t *ptr, unsigned char byte);
/** Retrieve a completed Unicode character from the decoder.
@param ptr Decoder to obtain character from.
*/
dk4_c32_t
dk4utf16_byte_get(dk4_utf16_byte_decoder_t *ptr);
/** Check whether a decoder is empty.
@param ptr Decoder to check.
@return 1 on success, 0 on error.
*/
int
dk4utf16_byte_is_empty(dk4_utf16_byte_decoder_t *ptr);
/** UTF-16 encode UNICODE character, write to bytes.
@param rb Result buffer, should be at least 5 bytes.
@param pszrb Size of the result buffer (number of bytes),
in: available, out: used.
@param c Character to encode.
@param msb Flag: Write most significant byte first.
@param erp Error report, may be NULL.
@return 1 on success, 0 on error.
Error codes:
- DK4_E_INVALID_ARGUMENTS
if rb or pszrb is NULL,
- DK4_E_BUFFER_TOO_SMALL
if the buffer size is less than 3,
- DK4_E_SYNTAX
if c is out of the encodable range.
*/
int
dk4utf16_byte_encode(
unsigned char *rb, size_t *pszrb, dk4_c32_t c, int msb, dk4_er_t *erp
);
#ifdef __cplusplus
}
#endif
%% module
#include "dk4conf.h"
#include "dk4types.h"
#include "dk4error.h"
#include "dk4utf16.h"
#include "dk4mem.h"
$!trace-include
/** State value in dk4_utf16_decoder_t.
*/
enum {
/** No surrogate yet.
*/
DK4_UTF16_DEC_ST_EMPTY = 0,
/** Found high surrogate,
expecting corresponding
low surrogate.
*/
DK4_UTF16_DEC_ST_HAVE_HIGH_SURROGATE ,
/** Found low surrogate,
expecting corresponding
high surrogate.
*/
DK4_UTF16_DEC_ST_HAVE_LOW_SURROGATE
};
void
dk4utf16_init(dk4_utf16_decoder_t *ptr)
{
if (NULL != ptr) {
ptr->uc = dkC32(0);
ptr->state = DK4_UTF16_DEC_ST_EMPTY;
}
}
int
dk4utf16_add(dk4_utf16_decoder_t *ptr, dk4_c16_t word)
{
int back = DK4_EDSTM_ERROR;
if (NULL != ptr) {
switch (ptr->state) {
case DK4_UTF16_DEC_ST_EMPTY: {
if ((dkC32(0x0000DC00) & ((dk4_c32_t)word)) == dkC32(0x0000DC00)) {
ptr->state = DK4_UTF16_DEC_ST_HAVE_LOW_SURROGATE;
ptr->uc = ((dk4_c32_t)word) & dkC32(0x000003FF);
back = DK4_EDSTM_ACCEPT;
} else {
if ((dkC32(0x0000DC00) & ((dk4_c32_t)word)) == dkC32(0x0000D800)) {
ptr->state = DK4_UTF16_DEC_ST_HAVE_HIGH_SURROGATE;
ptr->uc = ((((dk4_c32_t)word) << 10) & dkC32(0x000FFC00));
back = DK4_EDSTM_ACCEPT;
} else {
ptr->uc = ((dk4_c32_t)word) & dkC32(0x0000FFFF);
back = DK4_EDSTM_FINISHED;
}
}
} break;
case DK4_UTF16_DEC_ST_HAVE_HIGH_SURROGATE: {
if ((dkC32(0x0000DC00) & ((dk4_c32_t)word)) == dkC32(0x0000DC00)) {
ptr->uc |= (((dk4_c32_t)word) & dkC32(0x000003FF));
ptr->uc += dkC32(0x00010000);
ptr->state = DK4_UTF16_DEC_ST_EMPTY;
back = DK4_EDSTM_FINISHED;
} else {
back = DK4_EDSTM_ERROR;
ptr->state = DK4_UTF16_DEC_ST_EMPTY;
}
} break;
case DK4_UTF16_DEC_ST_HAVE_LOW_SURROGATE: {
if ((((dk4_c32_t)word) & dkC32(0x0000DC00)) == dkC32(0x0000D800)) {
ptr->uc |= ((((dk4_c32_t)word) << 10) & dkC32(0x000FFC00));
ptr->uc += dkC32(0x00010000);
ptr->state = DK4_UTF16_DEC_ST_EMPTY;
back = DK4_EDSTM_FINISHED;
} else {
back = DK4_EDSTM_ERROR;
ptr->state = DK4_UTF16_DEC_ST_EMPTY;
}
} break;
}
}
return back;
}
dk4_c32_t
dk4utf16_get(dk4_utf16_decoder_t *ptr)
{
dk4_c32_t back = dkC32(0);
if (NULL != ptr) {
back = ptr->uc;
}
return back;
}
int
dk4utf16_is_empty(dk4_utf16_decoder_t *ptr)
{
int back = 0;
if (NULL != ptr) {
if (DK4_UTF16_DEC_ST_EMPTY == ptr->state) { back = 1; }
}
return back;
}
int
dk4utf16_encode(dk4_c16_t *rb, size_t *pszrb, dk4_c32_t c, dk4_er_t *erp)
{
int back = 0;
$? "+ dk4utf16_encode %08lx", (unsigned long)c
if ((NULL != rb) && (NULL != pszrb)) {
if (2 < *pszrb) { $? ". args ok"
if ( dkC32(0x00010000) <= c ) { $? ". surrogate pair"
if ( dkC32(0x0010FFFF) >= c ) { $? ". in range"
back = 1;
*pszrb = 2;
rb[0] =
(dk4_c16_t)(((c >> 10) & dkC32(0x000003FF)) | dkC32(0x0000D800));
rb[1] =
(dk4_c16_t)((c & dkC32(0x000003FF)) | dkC32(0x0000DC00));
rb[2] = (dk4_c16_t)0;
} else { $? "! out of range"
dk4error_set_simple_error_code(erp, DK4_E_SYNTAX);
}
} else { $? ". single 16-bit char"
#if VERSION_BEFORE_20141125
if ((0x0000DC00 & c) != dkC32(0x0000DC00)) { $? ". range 1 ok"
if ((0x0000DC00 & c) != dkC32(0x0000D800)) { $? ". range 2 ok"
back = 1;
*pszrb = 1;
rb[0] = (dk4_c16_t)(c & dkC32(0x0000FFFF));
rb[1] = (dk4_c16_t)0;
} else { $? "! range check 2"
dk4error_set_simple_error_code(erp, DK4_E_SYNTAX);
}
} else { $? "! range check 1"
dk4error_set_simple_error_code(erp, DK4_E_SYNTAX);
}
#else
if ((dkC32(0x0000DFFF) < c) || (dkC32(0x0000D800) > c)) {
$? ". range ok, not in surrogates range"
back = 1;
*pszrb = 1;
rb[0] = (dk4_c16_t)(c & dkC32(0x0000FFFF));
rb[1] = (dk4_c16_t)0;
} else { $? "! range check, in surrogates range"
dk4error_set_simple_error_code(erp, DK4_E_SYNTAX);
}
#endif
}
} else { $? "! buffer size"
dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL);
}
} else { $? "! args"
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
} $? "- dk4utf16_encode %d", back
return back;
}
void
dk4utf16_byte_init(dk4_utf16_byte_decoder_t *ptr, int msb)
{
if (NULL != ptr) {
dk4utf16_init(&(ptr->dec));
ptr->msb = msb;
ptr->state = 0;
ptr->saved = (dk4_c16_t)0;
}
}
int
dk4utf16_byte_add(dk4_utf16_byte_decoder_t *ptr, unsigned char byte)
{
int back = DK4_EDSTM_ERROR;
if (NULL != ptr) {
if (0 != ptr->state) {
ptr->state = 0;
if (0 != ptr->msb) {
ptr->saved |= (((dk4_c16_t)byte) & dkC16(0x00FF));
} else {
ptr->saved |= ((((dk4_c16_t)byte) << 8) & dkC16(0xFF00));
}
back = dk4utf16_add(&(ptr->dec), ptr->saved);
ptr->saved = (dk4_c16_t)0;
} else {
ptr->state = 1;
if (0 != ptr->msb) {
ptr->saved = ((((dk4_c16_t)byte) << 8) & dkC16(0xFF00));
} else {
ptr->saved = (((dk4_c16_t)byte) & dkC16(0x00FF));
}
back = DK4_EDSTM_ACCEPT;
}
}
return back;
}
dk4_c32_t
dk4utf16_byte_get(dk4_utf16_byte_decoder_t *ptr)
{
dk4_c32_t back = dkC32(0);
if (NULL != ptr) {
back = dk4utf16_get(&(ptr->dec));
}
return back;
}
int
dk4utf16_byte_is_empty(dk4_utf16_byte_decoder_t *ptr)
{
int back = 0;
if (NULL != ptr) {
back = dk4utf16_is_empty(&(ptr->dec));
if (0 != ptr->state) { back = 0; }
}
return back;
}
int
dk4utf16_byte_encode(
unsigned char *rb, size_t *pszrb, dk4_c32_t c, int msb, dk4_er_t *erp
)
{
dk4_c16_t buf[3];
size_t sz;
int back = 0;
if ((NULL != rb) && (NULL != pszrb)) {
if (4 < *pszrb) {
sz = DK4_SIZEOF(buf,dk4_c16_t);
if (0 != dk4utf16_encode(buf, &sz, c, erp)) {
back = 1;
if (0 != msb) {
rb[0] = (unsigned char)((buf[0] >> 8) & dkC16(0x00FF));
rb[1] = (unsigned char)(buf[0] & dkC16(0x00FF));
} else {
rb[1] = (unsigned char)((buf[0] >> 8) & dkC16(0x00FF));
rb[0] = (unsigned char)(buf[0] & dkC16(0x00FF));
}
if (1 < sz) {
if (0 != msb) {
rb[2] = (unsigned char)((buf[1] >> 8) & dkC16(0x00FF));
rb[3] = (unsigned char)(buf[1] & dkC16(0x00FF));
} else {
rb[3] = (unsigned char)((buf[1] >> 8) & dkC16(0x00FF));
rb[2] = (unsigned char)(buf[1] & dkC16(0x00FF));
}
*pszrb = 4;
rb[4] = '\0';
} else {
*pszrb = 2;
rb[2] = '\0';
}
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL);
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
}
return back;
}