%% 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; }