root/ext/intl/converter/converter.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_converter_throw_failure
  2. php_converter_default_callback
  3. ZEND_BEGIN_ARG_INFO_EX
  4. ZEND_BEGIN_ARG_INFO_EX
  5. php_converter_check_limits
  6. php_converter_append_toUnicode_target
  7. php_converter_to_u_callback
  8. php_converter_append_fromUnicode_target
  9. php_converter_from_u_callback
  10. php_converter_set_callbacks
  11. php_converter_set_encoding
  12. ZEND_BEGIN_ARG_INFO_EX
  13. PHP_METHOD
  14. PHP_METHOD
  15. ZEND_BEGIN_ARG_INFO_EX
  16. PHP_METHOD
  17. PHP_METHOD
  18. ZEND_BEGIN_ARG_INFO_EX
  19. PHP_METHOD
  20. PHP_METHOD
  21. php_converter_resolve_callback
  22. ZEND_BEGIN_ARG_INFO_EX
  23. ZEND_BEGIN_ARG_INFO_EX
  24. ZEND_BEGIN_ARG_INFO_EX
  25. php_converter_do_convert
  26. ZEND_BEGIN_ARG_INFO_EX
  27. ZEND_BEGIN_ARG_INFO_EX
  28. ZEND_BEGIN_ARG_INFO_EX
  29. ZEND_BEGIN_ARG_INFO_EX
  30. ZEND_BEGIN_ARG_INFO_EX
  31. ZEND_BEGIN_ARG_INFO_EX
  32. ZEND_BEGIN_ARG_INFO_EX
  33. ZEND_BEGIN_ARG_INFO_EX
  34. php_converter_free_object
  35. php_converter_object_ctor
  36. php_converter_create_object
  37. php_converter_clone_object
  38. php_converter_minit

/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: Sara Golemon <pollita@php.net>                              |
   +----------------------------------------------------------------------+
 */

#include "converter.h"
#include "zend_exceptions.h"

#include <unicode/utypes.h>
#include <unicode/ucnv.h>
#include <unicode/ustring.h>

#include "../intl_error.h"

typedef struct _php_converter_object {
        zend_object obj;
#ifdef ZTS
        void ***tsrm_ls;
#endif
        UConverter *src, *dest;
        zend_fcall_info to_cb, from_cb;
        zend_fcall_info_cache to_cache, from_cache;
        intl_error error;
} php_converter_object;

static zend_class_entry     *php_converter_ce;
static zend_object_handlers  php_converter_object_handlers;

#define CONV_GET(pzv)  ((php_converter_object*)zend_objects_get_address((pzv) TSRMLS_CC))
#define THROW_UFAILURE(obj, fname, error) php_converter_throw_failure(obj, error TSRMLS_CC, \
                                          fname "() returned error %ld: %s", (long)error, u_errorName(error))

/* {{{ php_converter_throw_failure */
static inline void php_converter_throw_failure(php_converter_object *objval, UErrorCode error TSRMLS_DC, const char *format, ...) {
        intl_error *err = objval ? &(objval->error) : NULL;
        char message[1024];
        va_list vargs;

        va_start(vargs, format);
        vsnprintf(message, sizeof(message), format, vargs);
        va_end(vargs);

        intl_errors_set(err, error, message, 1 TSRMLS_CC);
}
/* }}} */

/* {{{ php_converter_default_callback */
static void php_converter_default_callback(zval *return_value, zval *zobj, long reason, zval *error TSRMLS_DC) {
        zval_dtor(error);
        ZVAL_LONG(error, U_ZERO_ERROR);
        /* Basic functionality so children can call parent::toUCallback() */
        switch (reason) {
                case UCNV_UNASSIGNED:
                case UCNV_ILLEGAL:
                case UCNV_IRREGULAR:
                {
                        php_converter_object *objval = (php_converter_object*)CONV_GET(zobj);
                        char chars[127];
                        int8_t chars_len = sizeof(chars);
                        UErrorCode uerror = U_ZERO_ERROR;
            if(!objval->src) {
                php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Source Converter has not been initialized yet");
                                chars[0] = 0x1A;
                                chars[1] = 0;
                                chars_len = 1;
                ZVAL_LONG(error, U_INVALID_STATE_ERROR);
                RETVAL_STRINGL(chars, chars_len, 1);
                return;
            }

                        /* Yes, this is fairly wasteful at first glance,
                         * but considering that the alternative is to store
                         * what's sent into setSubstChars() and the fact
                         * that this is an extremely unlikely codepath
                         * I'd rather take the CPU hit here, than waste time
                         * storing a value I'm unlikely to use.
                         */
                        ucnv_getSubstChars(objval->src, chars, &chars_len, &uerror);
                        if (U_FAILURE(uerror)) {
                                THROW_UFAILURE(objval, "ucnv_getSubstChars", uerror);
                                chars[0] = 0x1A;
                                chars[1] = 0;
                                chars_len = 1;
                ZVAL_LONG(error, uerror);
                        }
                        RETVAL_STRINGL(chars, chars_len, 1);
                }
        }
}
/* }}} */

/* {{{ proto void UConverter::toUCallback(long $reason,
                                          string $source, string $codeUnits,
                                          long &$error) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_toUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4)
        ZEND_ARG_INFO(0, reason)
        ZEND_ARG_INFO(0, source)
        ZEND_ARG_INFO(0, codeUnits)
        ZEND_ARG_INFO(1, error)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, toUCallback) {
        long reason;
        zval *source, *codeUnits, *error;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lzzz",
                &reason, &source, &codeUnits, &error) == FAILURE) {
                return;
        }

        php_converter_default_callback(return_value, getThis(), reason, error TSRMLS_CC);
}
/* }}} */

/* {{{ proto void UConverter::fromUCallback(long $reason,
                                            Array $source, long $codePoint,
                                            long &$error) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_fromUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4)
        ZEND_ARG_INFO(0, reason)
        ZEND_ARG_INFO(0, source)
        ZEND_ARG_INFO(0, codePoint)
        ZEND_ARG_INFO(1, error)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, fromUCallback) {
        long reason;
        zval *source, *codePoint, *error;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lzzz",
                &reason, &source, &codePoint, &error) == FAILURE) {
                return;
        }

        php_converter_default_callback(return_value, getThis(), reason, error TSRMLS_CC);
}
/* }}} */

/* {{{ php_converter_check_limits */
static inline zend_bool php_converter_check_limits(php_converter_object *objval, long available, long needed TSRMLS_DC) {
        if (available < needed) {
                php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR TSRMLS_CC, "Buffer overrun %ld bytes needed, %ld available", needed, available);
                return 0;
        }
        return 1;
}
/* }}} */

#define TARGET_CHECK(cnvargs, needed) php_converter_check_limits(objval, cnvargs->targetLimit - cnvargs->target, needed TSRMLS_CC)

/* {{{ php_converter_append_toUnicode_target */
static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicodeArgs *args, php_converter_object *objval TSRMLS_DC) {
        switch (Z_TYPE_P(val)) {
                case IS_NULL:
                        /* Code unit is being skipped */
                        return;
                case IS_LONG:
                {
                        long lval = Z_LVAL_P(val);
                        if ((lval < 0) || (lval > 0x10FFFF)) {
                                php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, "Invalid codepoint U+%04lx", lval);
                                return;
                        }
                        if (lval > 0xFFFF) {
                                /* Supplemental planes U+010000 - U+10FFFF */
                                if (TARGET_CHECK(args, 2)) {
                                        /* TODO: Find the ICU call which does this properly */
                                        *(args->target++) = (UChar)(((lval - 0x10000) >> 10)   | 0xD800);
                                        *(args->target++) = (UChar)(((lval - 0x10000) & 0x3FF) | 0xDC00);
                                }
                                return;
                        }
                        /* Non-suggogate BMP codepoint */
                        if (TARGET_CHECK(args, 1)) {
                                *(args->target++) = (UChar)lval;
                        }
                        return;
                }
                case IS_STRING:
                {
                        const char *strval = Z_STRVAL_P(val);
                        int i = 0, strlen = Z_STRLEN_P(val);

                        while((i != strlen) && TARGET_CHECK(args, 1)) {
                                UChar c;
                                U8_NEXT(strval, i, strlen, c);
                                *(args->target++) = c;
                        }
                        return;
                }
                case IS_ARRAY:
                {
                        HashTable *ht = Z_ARRVAL_P(val);
                        HashPosition pos;
                        zval **tmpzval;

                        for(zend_hash_internal_pointer_reset_ex(ht, &pos);
                                zend_hash_get_current_data_ex(ht, (void**)&tmpzval, &pos) == SUCCESS;
                                zend_hash_move_forward_ex(ht, &pos)) {
                                php_converter_append_toUnicode_target(*tmpzval, args, objval TSRMLS_CC);
                        }
                        return;
                }
                default:
                        php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC,
                                                    "toUCallback() specified illegal type for substitution character");
        }
}
/* }}} */

/* {{{ php_converter_to_u_callback */
static void php_converter_to_u_callback(const void *context,
                                        UConverterToUnicodeArgs *args,
                                        const char *codeUnits, int32_t length,
                                        UConverterCallbackReason reason,
                                        UErrorCode *pErrorCode) {
        php_converter_object *objval = (php_converter_object*)context;
        zval *zreason, *zsource, *zcodeunits, *zerror, *retval = NULL;
        zval **zargs[4];
#ifdef ZTS
        TSRMLS_D = objval->tsrm_ls;
#endif

        MAKE_STD_ZVAL(zreason);
        ZVAL_LONG(zreason, reason);
        zargs[0] = &zreason;

        MAKE_STD_ZVAL(zsource);
        ZVAL_STRINGL(zsource, args->source, args->sourceLimit - args->source, 1);
        zargs[1] = &zsource;

        MAKE_STD_ZVAL(zcodeunits);
        ZVAL_STRINGL(zcodeunits, codeUnits, length, 1);
        zargs[2] = &zcodeunits;

        MAKE_STD_ZVAL(zerror);
        ZVAL_LONG(zerror, *pErrorCode);
        zargs[3] = &zerror;

        objval->to_cb.param_count    = 4;
        objval->to_cb.params         = zargs;
        objval->to_cb.retval_ptr_ptr = &retval;
        objval->to_cb.no_separation  = 0;
        if (zend_call_function(&(objval->to_cb), &(objval->to_cache) TSRMLS_CC) == FAILURE) {
                /* Unlikely */
                php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Unexpected failure calling toUCallback()");
        } else if (retval) {
                php_converter_append_toUnicode_target(retval, args, objval TSRMLS_CC);
                zval_ptr_dtor(&retval);
        }

        if (Z_TYPE_P(zerror) == IS_LONG) {
                *pErrorCode = Z_LVAL_P(zerror);
        }

        zval_ptr_dtor(&zreason);
        zval_ptr_dtor(&zsource);
        zval_ptr_dtor(&zcodeunits);
        zval_ptr_dtor(&zerror);
}
/* }}} */

/* {{{ php_converter_append_fromUnicode_target */
static void php_converter_append_fromUnicode_target(zval *val, UConverterFromUnicodeArgs *args, php_converter_object *objval TSRMLS_DC) {
        switch (Z_TYPE_P(val)) {
                case IS_NULL:
                        /* Ignore */
                        return;
                case IS_LONG:
                        if (TARGET_CHECK(args, 1)) {
                                *(args->target++) = Z_LVAL_P(val);
                        }
                        return;
                case IS_STRING:
                {
                        int vallen = Z_STRLEN_P(val);
                        if (TARGET_CHECK(args, vallen)) {
                                memcpy(args->target, Z_STRVAL_P(val), vallen);
                                args->target += vallen;
                        }
                        return;
                }
                case IS_ARRAY:
                {
                        HashTable *ht = Z_ARRVAL_P(val);
                        HashPosition pos;
                        zval **tmpzval;
                        for(zend_hash_internal_pointer_reset_ex(ht, &pos);
                                zend_hash_get_current_data_ex(ht, (void**)&tmpzval, &pos) == SUCCESS;
                                zend_hash_move_forward_ex(ht, &pos)) {
                                php_converter_append_fromUnicode_target(*tmpzval, args, objval TSRMLS_CC);
                        }
                        return;
                }
                default:
                        php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, "fromUCallback() specified illegal type for substitution character");
        }
}
/* }}} */

/* {{{ php_converter_from_u_callback */
static void php_converter_from_u_callback(const void *context,
                                          UConverterFromUnicodeArgs *args,
                                          const UChar *codeUnits, int32_t length, UChar32 codePoint,
                                          UConverterCallbackReason reason,
                                          UErrorCode *pErrorCode) {
        php_converter_object *objval = (php_converter_object*)context;
        zval *zreason, *zsource, *zcodepoint, *zerror, *retval = NULL;
        zval **zargs[4];
        int i;
#ifdef ZTS
        TSRMLS_D = objval->tsrm_ls;
#endif

        MAKE_STD_ZVAL(zreason);
        ZVAL_LONG(zreason, reason);
        zargs[0] = &zreason;

        MAKE_STD_ZVAL(zsource);
        array_init(zsource);
        i = 0;
        while (i < length) {
                UChar32 c;
                U16_NEXT(codeUnits, i, length, c);
                add_next_index_long(zsource, c);
        }
        zargs[1] = &zsource;

        MAKE_STD_ZVAL(zcodepoint);
        ZVAL_LONG(zcodepoint, codePoint);
        zargs[2] = &zcodepoint;

        MAKE_STD_ZVAL(zerror);
        ZVAL_LONG(zerror, *pErrorCode);
        zargs[3] = &zerror;

        objval->from_cb.param_count    = 4;
        objval->from_cb.params         = zargs;
        objval->from_cb.retval_ptr_ptr = &retval;
        objval->from_cb.no_separation  = 0;
        if (zend_call_function(&(objval->from_cb), &(objval->from_cache) TSRMLS_CC) == FAILURE) {
                /* Unlikely */
                php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Unexpected failure calling fromUCallback()");
        } else if (retval) {
                php_converter_append_fromUnicode_target(retval, args, objval TSRMLS_CC);
                zval_ptr_dtor(&retval);
        }

        if (Z_TYPE_P(zerror) == IS_LONG) {
                *pErrorCode = Z_LVAL_P(zerror);
        }

        zval_ptr_dtor(&zreason);
        zval_ptr_dtor(&zsource);
        zval_ptr_dtor(&zcodepoint);
        zval_ptr_dtor(&zerror);
}
/* }}} */

/* {{{ php_converter_set_callbacks */
static inline zend_bool php_converter_set_callbacks(php_converter_object *objval, UConverter *cnv TSRMLS_DC) {
        zend_bool ret = 1;
        UErrorCode error = U_ZERO_ERROR;

        if (objval->obj.ce == php_converter_ce) {
                /* Short-circuit having to go through method calls and data marshalling
                 * when we're using default behavior
                 */
                return 1;
        }

        ucnv_setToUCallBack(cnv, (UConverterToUCallback)php_converter_to_u_callback, (const void*)objval,
                                 NULL, NULL, &error);
        if (U_FAILURE(error)) {
                THROW_UFAILURE(objval, "ucnv_setToUCallBack", error);
                ret = 0;
        }

        error = U_ZERO_ERROR;
        ucnv_setFromUCallBack(cnv, (UConverterFromUCallback)php_converter_from_u_callback, (const void*)objval,
                                    NULL, NULL, &error);
        if (U_FAILURE(error)) {
                THROW_UFAILURE(objval, "ucnv_setFromUCallBack", error);
                ret = 0;
        }
        return ret;
}
/* }}} */

/* {{{ php_converter_set_encoding */
static zend_bool php_converter_set_encoding(php_converter_object *objval,
                                            UConverter **pcnv,
                                            const char *enc, int enc_len
                                            TSRMLS_DC) {
        UErrorCode error = U_ZERO_ERROR;
        UConverter *cnv = ucnv_open(enc, &error);

        if (error == U_AMBIGUOUS_ALIAS_WARNING) {
                UErrorCode getname_error = U_ZERO_ERROR;
                const char *actual_encoding = ucnv_getName(cnv, &getname_error);
                if (U_FAILURE(getname_error)) {
                        /* Should never happen */
                        actual_encoding = "(unknown)";
                }
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ambiguous encoding specified, using %s", actual_encoding);
        } else if (U_FAILURE(error)) {
                if (objval) {
                        THROW_UFAILURE(objval, "ucnv_open", error);
                } else {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error setting encoding: %d - %s", (int)error, u_errorName(error));
                }
                return 0;
        }

        if (objval && !php_converter_set_callbacks(objval, cnv TSRMLS_CC)) {
                return 0;
        }

        if (*pcnv) {
                ucnv_close(*pcnv);
        }
        *pcnv = cnv;
        return 1;
}
/* }}} */

/* {{{ php_converter_do_set_encoding */
ZEND_BEGIN_ARG_INFO_EX(php_converter_set_encoding_arginfo, 0, ZEND_RETURN_VALUE, 1)
        ZEND_ARG_INFO(0, encoding)
ZEND_END_ARG_INFO();
static void php_converter_do_set_encoding(UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
        php_converter_object *objval = CONV_GET(getThis());
        char *enc;
        int enc_len;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &enc, &enc_len) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Bad arguments, "
                                "expected one string argument", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_errors_reset(&objval->error TSRMLS_CC);

        RETURN_BOOL(php_converter_set_encoding(objval, &(objval->src), enc, enc_len TSRMLS_CC));
}
/* }}} */

/* {{{ proto bool UConverter::setSourceEncoding(string encoding) */
static PHP_METHOD(UConverter, setSourceEncoding) {
        php_converter_object *objval = CONV_GET(getThis());
        php_converter_do_set_encoding(objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto bool UConverter::setDestinationEncoding(string encoding) */
static PHP_METHOD(UConverter, setDestinationEncoding) {
        php_converter_object *objval = CONV_GET(getThis());
        php_converter_do_set_encoding(objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ php_converter_do_get_encoding */
ZEND_BEGIN_ARG_INFO_EX(php_converter_get_encoding_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();
static void php_converter_do_get_encoding(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
        const char *name;

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }

        intl_errors_reset(&objval->error TSRMLS_CC);

        if (!cnv) {
                RETURN_NULL();
        }

        name = ucnv_getName(cnv, &objval->error.code);
        if (U_FAILURE(objval->error.code)) {
                THROW_UFAILURE(objval, "ucnv_getName()", objval->error.code);
                RETURN_FALSE;
        }

        RETURN_STRING(name, 1);
}
/* }}} */

/* {{{ proto string UConverter::getSourceEncoding() */
static PHP_METHOD(UConverter, getSourceEncoding) {
        php_converter_object *objval = CONV_GET(getThis());
        php_converter_do_get_encoding(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto string UConverter::getDestinationEncoding() */
static PHP_METHOD(UConverter, getDestinationEncoding) {
        php_converter_object *objval = CONV_GET(getThis());
        php_converter_do_get_encoding(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ php_converter_do_get_type */
ZEND_BEGIN_ARG_INFO_EX(php_converter_get_type_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();
static void php_converter_do_get_type(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
        UConverterType t;

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_errors_reset(&objval->error TSRMLS_CC);

        if (!cnv) {
                RETURN_NULL();
        }

        t = ucnv_getType(cnv);
        if (U_FAILURE(objval->error.code)) {
                THROW_UFAILURE(objval, "ucnv_getType", objval->error.code);
                RETURN_FALSE;
        }

        RETURN_LONG(t);
}
/* }}} */

/* {{{ proto long UConverter::getSourceType() */
static PHP_METHOD(UConverter, getSourceType) {
        php_converter_object *objval = CONV_GET(getThis());
        php_converter_do_get_type(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto long UConverter::getDestinationType() */
static PHP_METHOD(UConverter, getDestinationType) {
        php_converter_object *objval = CONV_GET(getThis());
        php_converter_do_get_type(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ php_converter_resolve_callback */
static void php_converter_resolve_callback(zval *zobj,
                                           php_converter_object *objval,
                                           const char *callback_name,
                                           zend_fcall_info *finfo,
                                           zend_fcall_info_cache *fcache TSRMLS_DC) {
        char *errstr = NULL;
        zval caller;

        array_init(&caller);
        Z_ADDREF_P(zobj);
        add_index_zval(&caller, 0, zobj);
        add_index_string(&caller, 1, callback_name, 1);
        if (zend_fcall_info_init(&caller, 0, finfo, fcache, NULL, &errstr TSRMLS_CC) == FAILURE) {
                php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Error setting converter callback: %s", errstr);
        }
        zval_dtor(&caller);
        if (errstr) {
                efree(errstr);
        }
}
/* }}} */

/* {{{ proto void UConverter::__construct([string dest = 'utf-8',[string src = 'utf-8']]) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_arginfo, 0, ZEND_RETURN_VALUE, 0)
        ZEND_ARG_INFO(0, destination_encoding)
        ZEND_ARG_INFO(0, source_encoding)
ZEND_END_ARG_INFO();

static PHP_METHOD(UConverter, __construct) {
        php_converter_object *objval = CONV_GET(getThis());
        char *src = "utf-8";
        int src_len = sizeof("utf-8") - 1;
        char *dest = src;
        int dest_len = src_len;

        intl_error_reset(NULL TSRMLS_CC);

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!",
                                  &dest, &dest_len, &src, &src_len) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::__construct(): bad arguments", 0 TSRMLS_CC);
                return;
        }

        php_converter_set_encoding(objval, &(objval->src),  src,  src_len  TSRMLS_CC);
        php_converter_set_encoding(objval, &(objval->dest), dest, dest_len TSRMLS_CC);
        php_converter_resolve_callback(getThis(), objval, "toUCallback",   &(objval->to_cb),   &(objval->to_cache) TSRMLS_CC);
        php_converter_resolve_callback(getThis(), objval, "fromUCallback", &(objval->from_cb), &(objval->from_cache) TSRMLS_CC);
}
/* }}} */

/* {{{ proto bool UConverter::setSubstChars(string $chars) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_setSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 1)
        ZEND_ARG_INFO(0, chars)
ZEND_END_ARG_INFO();

static PHP_METHOD(UConverter, setSubstChars) {
        php_converter_object *objval = CONV_GET(getThis());
        char *chars;
        int chars_len, ret = 1;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &chars, &chars_len) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::setSubstChars(): bad arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_errors_reset(&objval->error TSRMLS_CC);

        if (objval->src) {
                UErrorCode error = U_ZERO_ERROR;
                ucnv_setSubstChars(objval->src, chars, chars_len, &error);
                if (U_FAILURE(error)) {
                        THROW_UFAILURE(objval, "ucnv_setSubstChars", error);
                        ret = 0;
                }
        } else {
                php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Source Converter has not been initialized yet");
                ret = 0;
        }

        if (objval->dest) {
                UErrorCode error = U_ZERO_ERROR;
                ucnv_setSubstChars(objval->dest, chars, chars_len, &error);
                if (U_FAILURE(error)) {
                        THROW_UFAILURE(objval, "ucnv_setSubstChars", error);
                        ret = 0;
                }
        } else {
                php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Destination Converter has not been initialized yet");
                ret = 0;
        }

        RETURN_BOOL(ret);
}
/* }}} */

/* {{{ proto string UConverter::getSubstChars() */
ZEND_BEGIN_ARG_INFO_EX(php_converter_getSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();

static PHP_METHOD(UConverter, getSubstChars) {
        php_converter_object *objval = CONV_GET(getThis());
        char chars[127];
        int8_t chars_len = sizeof(chars);
        UErrorCode error = U_ZERO_ERROR;

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::getSubstChars(): expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_errors_reset(&objval->error TSRMLS_CC);

        if (!objval->src) {
                RETURN_NULL();
        }

        /* src and dest get the same subst chars set,
         * so it doesn't really matter which one we read from
         */
        ucnv_getSubstChars(objval->src, chars, &chars_len, &error);
        if (U_FAILURE(error)) {
                THROW_UFAILURE(objval, "ucnv_getSubstChars", error);
                RETURN_FALSE;
        }

        RETURN_STRINGL(chars, chars_len, 1);
}
/* }}} */

/* {{{ php_converter_do_convert */
static zend_bool php_converter_do_convert(UConverter *dest_cnv, char **pdest, int32_t *pdest_len,
                                          UConverter *src_cnv,  const char *src, int32_t src_len,
                                          php_converter_object *objval
                                          TSRMLS_DC) {
        UErrorCode      error = U_ZERO_ERROR;
        int32_t         dest_len,
                                temp_len;
        char            *dest;
        UChar           *temp;

        if (!src_cnv || !dest_cnv) {
                php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC,
                                            "Internal converters not initialized");
                return 0;
        }

        /* Get necessary buffer size first */
        temp_len = 1 + ucnv_toUChars(src_cnv, NULL, 0, src, src_len, &error);
        if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) {
                THROW_UFAILURE(objval, "ucnv_toUChars", error);
                return 0;
        }
        temp = safe_emalloc(sizeof(UChar), temp_len, sizeof(UChar));

        /* Convert to intermediate UChar* array */
        error = U_ZERO_ERROR;
        temp_len = ucnv_toUChars(src_cnv, temp, temp_len, src, src_len, &error);
        if (U_FAILURE(error)) {
                THROW_UFAILURE(objval, "ucnv_toUChars", error);
                efree(temp);
                return 0;
        }
        temp[temp_len] = 0;

        /* Get necessary output buffer size */
        dest_len = 1 + ucnv_fromUChars(dest_cnv, NULL, 0, temp, temp_len, &error);
        if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) {
                THROW_UFAILURE(objval, "ucnv_fromUChars", error);
                efree(temp);
                return 0;
        }
        dest = safe_emalloc(sizeof(char), dest_len, sizeof(char));

        /* Convert to final encoding */
        error = U_ZERO_ERROR;
        dest_len = ucnv_fromUChars(dest_cnv, dest, dest_len, temp, temp_len, &error);
        efree(temp);
        if (U_FAILURE(error)) {
                THROW_UFAILURE(objval, "ucnv_fromUChars", error);
                efree(dest);
                return 0;
        }

        *pdest = dest;
        if (pdest_len) {
                *pdest_len = dest_len;
        }

        return 1;
}
/* }}} */

/* {{{ proto string UConverter::reasonText(long reason) */
#define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1, 1);
ZEND_BEGIN_ARG_INFO_EX(php_converter_reasontext_arginfo, 0, ZEND_RETURN_VALUE, 0)
        ZEND_ARG_INFO(0, reason)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, reasonText) {
        long reason;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &reason) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::reasonText(): bad arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_error_reset(NULL TSRMLS_CC);

        switch (reason) {
                UCNV_REASON_CASE(UNASSIGNED)
                UCNV_REASON_CASE(ILLEGAL)
                UCNV_REASON_CASE(IRREGULAR)
                UCNV_REASON_CASE(RESET)
                UCNV_REASON_CASE(CLOSE)
                UCNV_REASON_CASE(CLONE)
                default:
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown UConverterCallbackReason: %ld", reason);
                        RETURN_FALSE;
        }
}
/* }}} */

/* {{{ proto string UConverter::convert(string str[, bool reverse]) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_convert_arginfo, 0, ZEND_RETURN_VALUE, 1)
        ZEND_ARG_INFO(0, str)
        ZEND_ARG_INFO(0, reverse)
ZEND_END_ARG_INFO();

static PHP_METHOD(UConverter, convert) {
        php_converter_object *objval = CONV_GET(getThis());
        char *str, *dest;
        int str_len, dest_len;
        zend_bool reverse = 0;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b",
                                  &str, &str_len, &reverse) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::convert(): bad arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_errors_reset(&objval->error TSRMLS_CC);

        if (php_converter_do_convert(reverse ? objval->src : objval->dest,
                                     &dest, &dest_len,
                                 reverse ? objval->dest : objval->src,
                                     str,   str_len,
                                     objval TSRMLS_CC)) {
                RETURN_STRINGL(dest, dest_len, 0);
        } else {
                RETURN_FALSE;
        }
}
/* }}} */

/* {{{ proto string UConverter::transcode(string $str, string $toEncoding, string $fromEncoding[, Array $options = array()]) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_transcode_arginfo, 0, ZEND_RETURN_VALUE, 3)
        ZEND_ARG_INFO(0, str)
        ZEND_ARG_INFO(0, toEncoding)
        ZEND_ARG_INFO(0, fromEncoding)
        ZEND_ARG_ARRAY_INFO(0, options, 1)
ZEND_END_ARG_INFO();

static PHP_METHOD(UConverter, transcode) {
        char *str, *src, *dest;
        int str_len, src_len, dest_len;
        zval *options = NULL;
        UConverter *src_cnv = NULL, *dest_cnv = NULL;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a!",
                        &str, &str_len, &dest, &dest_len, &src, &src_len, &options) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::transcode(): bad arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_error_reset(NULL TSRMLS_CC);

        if (php_converter_set_encoding(NULL, &src_cnv,  src,  src_len TSRMLS_CC) &&
            php_converter_set_encoding(NULL, &dest_cnv, dest, dest_len TSRMLS_CC)) {
                char *out = NULL;
                int out_len = 0;
                UErrorCode error = U_ZERO_ERROR;

                if (options && zend_hash_num_elements(Z_ARRVAL_P(options))) {
                        zval **tmpzval;

                        if (U_SUCCESS(error) &&
                                zend_hash_find(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst"), (void**)&tmpzval) == SUCCESS &&
                                Z_TYPE_PP(tmpzval) == IS_STRING) {
                                error = U_ZERO_ERROR;
                                ucnv_setSubstChars(src_cnv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) & 0x7F, &error);
                        }
                        if (U_SUCCESS(error) &&
                                zend_hash_find(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst"), (void**)&tmpzval) == SUCCESS &&
                                Z_TYPE_PP(tmpzval) == IS_STRING) {
                                error = U_ZERO_ERROR;
                                ucnv_setSubstChars(dest_cnv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) & 0x7F, &error);
                        }
                }

                if (U_SUCCESS(error) &&
                        php_converter_do_convert(dest_cnv, &out, &out_len, src_cnv, str, str_len, NULL TSRMLS_CC)) {
                        RETVAL_STRINGL(out, out_len, 0);
                }

                if (U_FAILURE(error)) {
                        THROW_UFAILURE(NULL, "transcode", error);
                        RETVAL_FALSE;
                }
        } else {
                RETVAL_FALSE;
        }
        
        if (src_cnv) {
                ucnv_close(src_cnv);
        }
        if (dest_cnv) {
                ucnv_close(dest_cnv);
        }
}
/* }}} */

/* {{{ proto int UConverter::getErrorCode() */
ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrorcode_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, getErrorCode) {
        php_converter_object *objval = CONV_GET(getThis());

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::getErrorCode(): expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }

        RETURN_LONG(intl_error_get_code(&(objval->error) TSRMLS_CC));   
}
/* }}} */

/* {{{ proto string UConverter::getErrorMessage() */
ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrormsg_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, getErrorMessage) {
        php_converter_object *objval = CONV_GET(getThis());
        char *message = intl_error_get_message(&(objval->error) TSRMLS_CC);

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::getErrorMessage(): expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }

        if (message) {
                RETURN_STRING(message, 1);
        } else {
                RETURN_NULL();
        }
}
/* }}} */

/* {{{ proto array UConverter::getAvailable() */
ZEND_BEGIN_ARG_INFO_EX(php_converter_getavailable_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, getAvailable) {
        int32_t i,
                        count = ucnv_countAvailable();

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::getErrorMessage(): expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_error_reset(NULL TSRMLS_CC);

        array_init(return_value);
        for(i = 0; i < count; i++) {
                const char *name = ucnv_getAvailableName(i);
                add_next_index_string(return_value, name, 1);
        }
}
/* }}} */

/* {{{ proto array UConverter::getAliases(string name) */
ZEND_BEGIN_ARG_INFO_EX(php_converter_getaliases_arginfo, 0, ZEND_RETURN_VALUE, 0)
        ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, getAliases) {
        char *name;
        int name_len;
        UErrorCode error = U_ZERO_ERROR;
        uint16_t i, count;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::getAliases(): bad arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_error_reset(NULL TSRMLS_CC);

        count = ucnv_countAliases(name, &error);
        if (U_FAILURE(error)) {
                THROW_UFAILURE(NULL, "ucnv_countAliases", error);
                RETURN_FALSE;
        }

        array_init(return_value);
        for(i = 0; i < count; i++) {
                const char *alias;

                error = U_ZERO_ERROR;
                alias = ucnv_getAlias(name, i, &error);
                if (U_FAILURE(error)) {
                        THROW_UFAILURE(NULL, "ucnv_getAlias", error);
                        zval_dtor(return_value);
                        RETURN_NULL();
                }
                add_next_index_string(return_value, alias, 1);
        }
}
/* }}} */

/* {{{ proto array UConverter::getStandards() */
ZEND_BEGIN_ARG_INFO_EX(php_converter_getstandards_arginfo, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(UConverter, getStandards) {
        uint16_t i, count;

        if (zend_parse_parameters_none() == FAILURE) {
                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
                        "UConverter::getStandards(): expected no arguments", 0 TSRMLS_CC);
                RETURN_FALSE;
        }
        intl_error_reset(NULL TSRMLS_CC);

        array_init(return_value);
        count = ucnv_countStandards();
        for(i = 0; i < count; i++) {
                UErrorCode error = U_ZERO_ERROR;
                const char *name = ucnv_getStandard(i, &error);
                if (U_FAILURE(error)) {
                        THROW_UFAILURE(NULL, "ucnv_getStandard", error);
                        zval_dtor(return_value);
                        RETURN_NULL();
                }
                add_next_index_string(return_value, name, 1);
        }
}
/* }}} */

static zend_function_entry php_converter_methods[] = {
        PHP_ME(UConverter, __construct,            php_converter_arginfo,                   ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)

        /* Encoding selection */
        PHP_ME(UConverter, setSourceEncoding,      php_converter_set_encoding_arginfo,      ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, setDestinationEncoding, php_converter_set_encoding_arginfo,      ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, getSourceEncoding,      php_converter_get_encoding_arginfo,      ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, getDestinationEncoding, php_converter_get_encoding_arginfo,      ZEND_ACC_PUBLIC)

        /* Introspection for algorithmic converters */
        PHP_ME(UConverter, getSourceType,          php_converter_get_type_arginfo,          ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, getDestinationType,     php_converter_get_type_arginfo,          ZEND_ACC_PUBLIC)

        /* Basic codeunit error handling */
        PHP_ME(UConverter, getSubstChars,          php_converter_getSubstChars_arginfo,     ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, setSubstChars,          php_converter_setSubstChars_arginfo,     ZEND_ACC_PUBLIC)

        /* Default callback handlers */
        PHP_ME(UConverter, toUCallback,            php_converter_toUCallback_arginfo,       ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, fromUCallback,          php_converter_fromUCallback_arginfo,     ZEND_ACC_PUBLIC)

        /* Core conversion workhorses */
        PHP_ME(UConverter, convert,                php_converter_convert_arginfo,           ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, transcode,              php_converter_transcode_arginfo,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)

        /* Error inspection */
        PHP_ME(UConverter, getErrorCode,           php_converter_geterrorcode_arginfo,      ZEND_ACC_PUBLIC)
        PHP_ME(UConverter, getErrorMessage,        php_converter_geterrormsg_arginfo,       ZEND_ACC_PUBLIC)

        /* Ennumeration and lookup */
        PHP_ME(UConverter, reasonText,             php_converter_reasontext_arginfo,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
        PHP_ME(UConverter, getAvailable,           php_converter_getavailable_arginfo,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
        PHP_ME(UConverter, getAliases,             php_converter_getaliases_arginfo,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
        PHP_ME(UConverter, getStandards,           php_converter_getstandards_arginfo,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
        { NULL, NULL, NULL }
};

/* {{{ Converter create/clone/destroy */
static void php_converter_free_object(php_converter_object *objval TSRMLS_DC) {
        if (objval->src) {
                ucnv_close(objval->src);
        }

        if (objval->dest) {
                ucnv_close(objval->dest);
        }

        intl_error_reset(&(objval->error) TSRMLS_CC);
        zend_object_std_dtor(&(objval->obj) TSRMLS_CC);

        efree(objval);
}

static zend_object_value php_converter_object_ctor(zend_class_entry *ce, php_converter_object **pobjval TSRMLS_DC) {
        php_converter_object *objval;
        zend_object_value retval;

        objval = ecalloc(1, sizeof(php_converter_object));
        objval->obj.ce = ce;

#ifdef ZTS
        objval->tsrm_ls = TSRMLS_C;
#endif
        intl_error_init(&(objval->error) TSRMLS_CC);

        retval.handle = zend_objects_store_put(objval, NULL, (zend_objects_free_object_storage_t)php_converter_free_object, NULL TSRMLS_CC);
        retval.handlers = &php_converter_object_handlers;
        *pobjval = objval;

        return retval;
}

static zend_object_value php_converter_create_object(zend_class_entry *ce TSRMLS_DC) {
        php_converter_object *objval = NULL;
        zend_object_value retval = php_converter_object_ctor(ce, &objval TSRMLS_CC);

        object_properties_init(&(objval->obj), ce);

        return retval;
}

static zend_object_value php_converter_clone_object(zval *object TSRMLS_DC) {
        php_converter_object *objval, *oldobj = (php_converter_object*)zend_objects_get_address(object TSRMLS_CC);
        zend_object_value retval = php_converter_object_ctor(Z_OBJCE_P(object), &objval TSRMLS_CC);
        UErrorCode error = U_ZERO_ERROR;

        intl_errors_reset(&oldobj->error TSRMLS_CC);

        objval->src = ucnv_safeClone(oldobj->src, NULL, NULL, &error);
        if (U_SUCCESS(error)) {
                error = U_ZERO_ERROR;
                objval->dest = ucnv_safeClone(oldobj->dest, NULL, NULL, &error);
        }
        if (U_FAILURE(error)) {
                char *err_msg;
                THROW_UFAILURE(oldobj, "ucnv_safeClone", error);

                err_msg = intl_error_get_message(&oldobj->error TSRMLS_CC);
                zend_throw_exception(NULL, err_msg, 0 TSRMLS_CC);
                efree(err_msg);

                return retval;
        }

        /* Update contexts for converter error handlers */
        php_converter_set_callbacks(objval, objval->src  TSRMLS_CC);
        php_converter_set_callbacks(objval, objval->dest TSRMLS_CC);

        zend_objects_clone_members(&(objval->obj), retval, &(oldobj->obj), Z_OBJ_HANDLE_P(object) TSRMLS_CC);

        /* Newly cloned object deliberately does not inherit error state from original object */

        return retval;
}
/* }}} */

#define CONV_REASON_CONST(v) zend_declare_class_constant_long(php_converter_ce, "REASON_" #v, sizeof("REASON_" #v) - 1, UCNV_ ## v TSRMLS_CC)
#define CONV_TYPE_CONST(v)   zend_declare_class_constant_long(php_converter_ce, #v ,          sizeof(#v) - 1,           UCNV_ ## v TSRMLS_CC)

/* {{{ php_converter_minit */
int php_converter_minit(INIT_FUNC_ARGS) {
        zend_class_entry ce;

        INIT_CLASS_ENTRY(ce, "UConverter", php_converter_methods);
        php_converter_ce = zend_register_internal_class(&ce TSRMLS_CC);
        php_converter_ce->create_object = php_converter_create_object;
        memcpy(&php_converter_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
        php_converter_object_handlers.clone_obj = php_converter_clone_object;

        /* enum UConverterCallbackReason */
        CONV_REASON_CONST(UNASSIGNED);
        CONV_REASON_CONST(ILLEGAL);
        CONV_REASON_CONST(IRREGULAR);
        CONV_REASON_CONST(RESET);
        CONV_REASON_CONST(CLOSE);
        CONV_REASON_CONST(CLONE);

        /* enum UConverterType */
        CONV_TYPE_CONST(UNSUPPORTED_CONVERTER);
        CONV_TYPE_CONST(SBCS);
        CONV_TYPE_CONST(DBCS);
        CONV_TYPE_CONST(MBCS);
        CONV_TYPE_CONST(LATIN_1);
        CONV_TYPE_CONST(UTF8);
        CONV_TYPE_CONST(UTF16_BigEndian);
        CONV_TYPE_CONST(UTF16_LittleEndian);
        CONV_TYPE_CONST(UTF32_BigEndian);
        CONV_TYPE_CONST(UTF32_LittleEndian);
        CONV_TYPE_CONST(EBCDIC_STATEFUL);
        CONV_TYPE_CONST(ISO_2022);
        CONV_TYPE_CONST(LMBCS_1);
        CONV_TYPE_CONST(LMBCS_2);
        CONV_TYPE_CONST(LMBCS_3);
        CONV_TYPE_CONST(LMBCS_4);
        CONV_TYPE_CONST(LMBCS_5);
        CONV_TYPE_CONST(LMBCS_6);
        CONV_TYPE_CONST(LMBCS_8);
        CONV_TYPE_CONST(LMBCS_11);
        CONV_TYPE_CONST(LMBCS_16);
        CONV_TYPE_CONST(LMBCS_17);
        CONV_TYPE_CONST(LMBCS_18);
        CONV_TYPE_CONST(LMBCS_19);
        CONV_TYPE_CONST(LMBCS_LAST);
        CONV_TYPE_CONST(HZ);
        CONV_TYPE_CONST(SCSU);
        CONV_TYPE_CONST(ISCII);
        CONV_TYPE_CONST(US_ASCII);
        CONV_TYPE_CONST(UTF7);
        CONV_TYPE_CONST(BOCU1);
        CONV_TYPE_CONST(UTF16);
        CONV_TYPE_CONST(UTF32);
        CONV_TYPE_CONST(CESU8);
        CONV_TYPE_CONST(IMAP_MAILBOX);

        return SUCCESS;
}
/* }}} */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */

/* [<][>][^][v][top][bottom][index][help] */