Пример #1
0
int fts_icu_transliterator_create(const char *id,
                                  UTransliterator **transliterator_r,
                                  const char **error_r)
{
    UErrorCode err = U_ZERO_ERROR;
    UParseError perr;
    buffer_t *id_utf16_buf = buffer_create_dynamic(pool_datastack_create(), 2 * strlen(id));
    UChar *id_utf16;
    memset(&perr, 0, sizeof(perr));

    fts_icu_utf8_to_utf16(id_utf16_buf, id);
    id_utf16 = (UChar *)str_c(id_utf16_buf);
    *transliterator_r = utrans_openU(id_utf16,
                                     id_utf16_buf->used / sizeof(UChar),
                                     UTRANS_FORWARD, NULL, 0, &perr, &err);
    if (U_FAILURE(err)) {
        string_t *str = t_str_new(128);

        str_printfa(str, "Failed to open transliterator for id '%s': %s",
                    id, u_errorName(err));
        if (perr.line >= 1) {
            /* we have only one line in our ID */
            str_printfa(str, " (parse error on offset %u)",
                        perr.offset);
        }
        *error_r = str_c(str);
        return -1;
    }
    return 0;
}
Пример #2
0
static void TestDataVariantsCompounds() {
    const TransIDSourceTarg* itemsPtr;
    for (itemsPtr = dataVarCompItems; itemsPtr->transID != NULL; itemsPtr++) {
        UErrorCode status = U_ZERO_ERROR;
        UChar utrid[kUBufMax];
        int32_t utridlen = u_unescape(itemsPtr->transID, utrid, kUBufMax);
        UTransliterator* utrans = utrans_openU(utrid, utridlen, UTRANS_FORWARD, NULL, 0, NULL, &status);
        if (U_FAILURE(status)) {
            log_data_err("FAIL: utrans_openRules(%s) failed, error=%s (Are you missing data?)\n", itemsPtr->transID, u_errorName(status));
            continue;
        }
        UChar text[kUBufMax];
        int32_t textLen =  u_unescape(itemsPtr->sourceText, text, kUBufMax);
        int32_t textLim = textLen;
        utrans_transUChars(utrans, text, &textLen, kUBufMax, 0, &textLim, &status);
        if (U_FAILURE(status)) {
            log_err("FAIL: utrans_transUChars(%s) failed, error=%s\n", itemsPtr->transID, u_errorName(status));
        } else {
            UChar expect[kUBufMax];
            int32_t expectLen =  u_unescape(itemsPtr->targetText, expect, kUBufMax);
            if (textLen != expectLen || u_strncmp(text, expect, textLen) != 0) {
                char btext[kBBufMax], bexpect[kBBufMax];
                u_austrncpy(btext, text, textLen);
                u_austrncpy(bexpect, expect, expectLen);
                log_err("FAIL: utrans_transUChars(%s),\n       expect %s\n       get    %s\n", itemsPtr->transID, bexpect, btext);
            }
        }
        utrans_close(utrans);
    }
}
Пример #3
0
U_CAPI UTransliterator* U_EXPORT2
utrans_open(const char* id,
            UTransDirection dir,
            const UChar* rules,         /* may be Null */
            int32_t rulesLength,        /* -1 if null-terminated */
            UParseError* parseError,    /* may be Null */
            UErrorCode* status) {
    UnicodeString ID(id, -1, US_INV); // use invariant converter
    return utrans_openU(ID.getBuffer(), ID.length(), dir,
                        rules, rulesLength,
                        parseError, status);
}
Пример #4
0
static void TestGetRulesAndSourceSet() {
    UErrorCode status = U_ZERO_ERROR;
    UTransliterator *utrans = utrans_openU(transSimpleID, -1, UTRANS_FORWARD, NULL, 0, NULL, &status);
    if ( U_SUCCESS(status) ) {
        USet* uset;
        UChar ubuf[kUBufMax];
        int32_t ulen;

        status = U_ZERO_ERROR;
        ulen = utrans_toRules(utrans, FALSE, ubuf, kUBufMax, &status);
        if ( U_FAILURE(status) || ulen <= 50 || ulen >= 100) {
            log_err("FAIL: utrans_toRules unescaped, expected noErr and len 50-100, got error=%s and len=%d\n",
                    u_errorName(status), ulen);
        }

        status = U_ZERO_ERROR;
        ulen = utrans_toRules(utrans, FALSE, NULL, 0, &status);
        if ( status != U_BUFFER_OVERFLOW_ERROR || ulen <= 50 || ulen >= 100) {
            log_err("FAIL: utrans_toRules unescaped, expected U_BUFFER_OVERFLOW_ERROR and len 50-100, got error=%s and len=%d\n",
                    u_errorName(status), ulen);
        }

        status = U_ZERO_ERROR;
        ulen = utrans_toRules(utrans, TRUE, ubuf, kUBufMax, &status);
        if ( U_FAILURE(status) || ulen <= 100 || ulen >= 200) {
            log_err("FAIL: utrans_toRules escaped, expected noErr and len 100-200, got error=%s and len=%d\n",
                    u_errorName(status), ulen);
        }

        status = U_ZERO_ERROR;
        uset = utrans_getSourceSet(utrans, FALSE, NULL, &status);
        ulen = uset_toPattern(uset, ubuf, kUBufMax, FALSE, &status);
        uset_close(uset);
        if ( U_FAILURE(status) || ulen <= 4 || ulen >= 20) {
            log_err("FAIL: utrans_getSourceSet useFilter, expected noErr and len 4-20, got error=%s and len=%d\n",
                    u_errorName(status), ulen);
        }

        status = U_ZERO_ERROR;
        uset = utrans_getSourceSet(utrans, TRUE, NULL, &status);
        ulen = uset_toPattern(uset, ubuf, kUBufMax, FALSE, &status);
        uset_close(uset);
        if ( U_FAILURE(status) || ulen <= 4 || ulen >= 20) {
            log_err("FAIL: utrans_getSourceSet ignoreFilter, expected noErr and len 4-20, got error=%s and len=%d\n",
                    u_errorName(status), ulen);
        }

        utrans_close(utrans);
    } else {
        log_data_err("FAIL: utrans_openRules(%s) failed, error=%s (Are you missing data?)\n",
                transSimpleCName, u_errorName(status));
    }
}
Пример #5
0
static UTransliterator *get_translit(const char *id)
{
	UTransliterator *translit;
	buffer_t *id_utf16;
	UErrorCode err = U_ZERO_ERROR;
	UParseError perr;

	id_utf16 = buffer_create_dynamic(pool_datastack_create(), 16);
	fts_icu_utf8_to_utf16(id_utf16, id);
	translit = utrans_openU(id_utf16->data, id_utf16->used/sizeof(UChar),
				UTRANS_FORWARD, NULL, 0, &perr, &err);
	test_assert(!U_FAILURE(err));
	return translit;
}
Пример #6
0
static UTransliterator* get_trans(VALUE transform) {
    UChar str[BUF_SIZE];
    int32_t len = 0;
    UTransliterator *trans;
    UErrorCode status = U_ZERO_ERROR;
    VALUE obj;

    obj = rb_hash_aref(trans_hash, transform);
    if (NIL_P(obj)) {
        to_utf16(transform, str, &len);
        trans = utrans_openU(str, len, UTRANS_FORWARD, NULL, 0, NULL, &status);
        if (trans) {
            obj = Data_Wrap_Struct(rb_cObject, 0, trans_free, trans);
            rb_hash_aset(trans_hash, transform, obj);
        } else {
            rb_raise(rb_eArgError, "invalid transform: %s", RSTRING_PTR(transform));
        }
    }

    Data_Get_Struct(obj, UTransliterator, trans);
    return trans;
}
Пример #7
0
static gboolean
initable_init (GInitable *initable,
	       GCancellable *cancellable,
	       GError **error)
{
  TransliteratorIcu *icu = TRANSLITERATOR_ICU (initable);
  gchar *name;
  UChar *id;
  int32_t idLength;
  UErrorCode errorCode;

  g_object_get (G_OBJECT (initable),
		"name", &name,
		NULL);

  id = ustring_from_utf8 (name, &idLength);
  g_free (name);

  errorCode = 0;
  icu->trans = utrans_openU (id, idLength,
			     UTRANS_FORWARD,
			     NULL, -1,
			     NULL,
			     &errorCode);
  g_free (id);

  if (icu->trans == NULL)
    {
      g_set_error (error,
		   TRANSLIT_ERROR,
		   TRANSLIT_ERROR_LOAD_FAILED,
		   "can't open ICU utrans");
      return FALSE;
    }
  return TRUE;
}
Пример #8
0
// Try LocalXyzPointer types with NULL pointers.
void LocalPointerTest::TestLocalXyzPointerNull() {
    {
        IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUConverterSelectorPointer");
        static const char *const encoding="ISO-8859-1";
        LocalUConverterSelectorPointer null;
        LocalUConverterSelectorPointer sel(
            ucnvsel_open(&encoding, 1, NULL, UCNV_ROUNDTRIP_SET, errorCode));
        sel.adoptInstead(NULL);
    }
#if !UCONFIG_NO_FORMATTING
    {
        IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUCalendarPointer");
        LocalUCalendarPointer null;
        LocalUCalendarPointer cal(ucal_open(NULL, 0, "root", UCAL_GREGORIAN, errorCode));
        if(!errorCode.logDataIfFailureAndReset("ucal_open()")) {
            cal.adoptInstead(NULL);
        }
    }
    {
        IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUDateTimePatternGeneratorPointer");
        LocalUDateTimePatternGeneratorPointer null;
        LocalUDateTimePatternGeneratorPointer patgen(udatpg_open("root", errorCode));
        patgen.adoptInstead(NULL);
    }
    {
        IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUMessageFormatPointer");
        UnicodeString hello=UNICODE_STRING_SIMPLE("Hello {0}!");
        LocalUMessageFormatPointer null;
        LocalUMessageFormatPointer msg(
            umsg_open(hello.getBuffer(), hello.length(), "root", NULL, errorCode));
        msg.adoptInstead(NULL);
    }
#endif /* !UCONFIG_NO_FORMATTING */

#if !UCONFIG_NO_REGULAR_EXPRESSIONS
    {
        IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalURegularExpressionPointer");
        UnicodeString pattern=UNICODE_STRING_SIMPLE("abc|xy+z");
        LocalURegularExpressionPointer null;
        LocalURegularExpressionPointer regex(
            uregex_open(pattern.getBuffer(), pattern.length(), 0, NULL, errorCode));
        if(!errorCode.logDataIfFailureAndReset("urege_open()")) {
            regex.adoptInstead(NULL);
        }
    }
#endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */

#if !UCONFIG_NO_TRANSLITERATION
    {
        IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUTransliteratorPointer");
        UnicodeString id=UNICODE_STRING_SIMPLE("Grek-Latn");
        LocalUTransliteratorPointer null;
        LocalUTransliteratorPointer trans(
            utrans_openU(id.getBuffer(), id.length(), UTRANS_FORWARD, NULL, 0, NULL, errorCode));
        if(!errorCode.logDataIfFailureAndReset("utrans_openU()")) {
            trans.adoptInstead(NULL);
        }
    }
#endif /* !UCONFIG_NO_TRANSLITERATION */

}
Пример #9
0
// Use LocalXyzPointer types that are not covered elsewhere in the intltest suite.
void LocalPointerTest::TestLocalXyzPointer() {
    IcuTestErrorCode errorCode(*this, "TestLocalXyzPointer");

    static const char *const encoding="ISO-8859-1";
    LocalUConverterSelectorPointer sel(
        ucnvsel_open(&encoding, 1, NULL, UCNV_ROUNDTRIP_SET, errorCode));
    if(errorCode.logIfFailureAndReset("ucnvsel_open()")) {
        return;
    }
    if(sel.isNull()) {
        errln("LocalUConverterSelectorPointer failure");
        return;
    }

#if !UCONFIG_NO_FORMATTING
    LocalUCalendarPointer cal(ucal_open(NULL, 0, "root", UCAL_GREGORIAN, errorCode));
    if(errorCode.logDataIfFailureAndReset("ucal_open()")) {
        return;
    }
    if(cal.isNull()) {
        errln("LocalUCalendarPointer failure");
        return;
    }

    LocalUDateTimePatternGeneratorPointer patgen(udatpg_open("root", errorCode));
    if(errorCode.logDataIfFailureAndReset("udatpg_open()")) {
        return;
    }
    if(patgen.isNull()) {
        errln("LocalUDateTimePatternGeneratorPointer failure");
        return;
    }

    LocalULocaleDisplayNamesPointer ldn(uldn_open("de-CH", ULDN_STANDARD_NAMES, errorCode));
    if(errorCode.logIfFailureAndReset("uldn_open()")) {
        return;
    }
    if(ldn.isNull()) {
        errln("LocalULocaleDisplayNamesPointer failure");
        return;
    }

    UnicodeString hello=UNICODE_STRING_SIMPLE("Hello {0}!");
    LocalUMessageFormatPointer msg(
        umsg_open(hello.getBuffer(), hello.length(), "root", NULL, errorCode));
    if(errorCode.logIfFailureAndReset("umsg_open()")) {
        return;
    }
    if(msg.isNull()) {
        errln("LocalUMessageFormatPointer failure");
        return;
    }
#endif  /* UCONFIG_NO_FORMATTING  */

#if !UCONFIG_NO_NORMALIZATION
    const UNormalizer2 *nfc=unorm2_getNFCInstance(errorCode);
    UnicodeSet emptySet;
    LocalUNormalizer2Pointer fn2(unorm2_openFiltered(nfc, emptySet.toUSet(), errorCode));
    if(errorCode.logIfFailureAndReset("unorm2_openFiltered()")) {
        return;
    }
    if(fn2.isNull()) {
        errln("LocalUNormalizer2Pointer failure");
        return;
    }
#endif /* !UCONFIG_NO_NORMALIZATION */

#if !UCONFIG_NO_IDNA
    LocalUIDNAPointer idna(uidna_openUTS46(0, errorCode));
    if(errorCode.logIfFailureAndReset("uidna_openUTS46()")) {
        return;
    }
    if(idna.isNull()) {
        errln("LocalUIDNAPointer failure");
        return;
    }
#endif  /* !UCONFIG_NO_IDNA */

#if !UCONFIG_NO_REGULAR_EXPRESSIONS
    UnicodeString pattern=UNICODE_STRING_SIMPLE("abc|xy+z");
    LocalURegularExpressionPointer regex(
        uregex_open(pattern.getBuffer(), pattern.length(), 0, NULL, errorCode));
    if(errorCode.logIfFailureAndReset("uregex_open()")) {
        return;
    }
    if(regex.isNull()) {
        errln("LocalURegularExpressionPointer failure");
        return;
    }
#endif /* UCONFIG_NO_REGULAR_EXPRESSIONS */

#if !UCONFIG_NO_TRANSLITERATION
    UnicodeString id=UNICODE_STRING_SIMPLE("Grek-Latn");
    LocalUTransliteratorPointer trans(
        utrans_openU(id.getBuffer(), id.length(), UTRANS_FORWARD, NULL, 0, NULL, errorCode));
    if(errorCode.logIfFailureAndReset("utrans_open()")) {
        return;
    }
    if(trans.isNull()) {
        errln("LocalUTransliteratorPointer failure");
        return;
    }
#endif /* !UCONFIG_NO_TRANSLITERATION */

    // destructors
}
Пример #10
0
static int create_transliterator( char *str_id, int str_id_len, zend_long direction, zval *object )
{
	Transliterator_object *to;
	UChar	              *ustr_id    = NULL;
	int32_t               ustr_id_len = 0;
	UTransliterator       *utrans;
	UParseError           parse_error   = {0, -1};

	intl_error_reset( NULL );

	if( ( direction != TRANSLITERATOR_FORWARD ) && (direction != TRANSLITERATOR_REVERSE ) )
	{
		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
			"transliterator_create: invalid direction", 0 );
		return FAILURE;
	}

	object_init_ex( object, Transliterator_ce_ptr );
	TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; /* fetch zend object from zval "object" into "to" */

	/* Convert transliterator id to UTF-16 */
	intl_convert_utf8_to_utf16( &ustr_id, &ustr_id_len, str_id, str_id_len, TRANSLITERATOR_ERROR_CODE_P( to ) );
	if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
	{
		intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) );
		intl_error_set_custom_msg( NULL, "String conversion of id to UTF-16 failed", 0 );
		zval_dtor( object );
		return FAILURE;
	}

	/* Open ICU Transliterator. */
	utrans = utrans_openU( ustr_id, ustr_id_len, (UTransDirection ) direction,
		NULL, -1, &parse_error, TRANSLITERATOR_ERROR_CODE_P( to ) );
	if (ustr_id) {
		efree( ustr_id );
	}
	
	if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
	{
		char *buf = NULL;
		intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) );
		spprintf( &buf, 0, "transliterator_create: unable to open ICU transliterator"
			" with id \"%s\"", str_id );
		if( buf == NULL ) {
			intl_error_set_custom_msg( NULL,
				"transliterator_create: unable to open ICU transliterator", 0 );
		}
		else
		{
			intl_error_set_custom_msg( NULL, buf, /* copy message */ 1 );
			efree( buf );
		}
		zval_dtor( object );
		return FAILURE;
	}
	
	transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) );
	/* no need to close the transliterator manually on construction error */
	if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
	{
		intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) );
		intl_error_set_custom_msg( NULL,
			"transliterator_create: internal constructor call failed", 0 );
		zval_dtor( object );
		return FAILURE;
	}

	return SUCCESS;
}
Пример #11
0
static void TestRegisterUnregister() {
    UErrorCode status=U_ZERO_ERROR;
    UTransliterator* t1=NULL;
    UTransliterator* rules=NULL, *rules2;
    UTransliterator* inverse1=NULL;
    UChar rule[]= { 0x0061, 0x003c, 0x003e, 0x0063}; /*a<>b*/

    U_STRING_DECL(ID, "TestA-TestB", 11);
    U_STRING_INIT(ID, "TestA-TestB", 11);

    /* Make sure it doesn't exist */
    t1=utrans_open("TestA-TestB", UTRANS_FORWARD,NULL,0,NULL, &status);
    if(t1 != NULL || U_SUCCESS(status)) {
        log_err("FAIL: TestA-TestB already registered\n");
        return;
    }
    status=U_ZERO_ERROR;
    /* Check inverse too */
    inverse1=utrans_open("TestA-TestB", UTRANS_REVERSE, NULL,0,NULL,&status);
    if(inverse1 != NULL || U_SUCCESS(status)) {
        log_err("FAIL: TestA-TestB already registered\n");
        return;
    }
    status=U_ZERO_ERROR;
    /* Create it */
    rules=utrans_open("TestA-TestB",UTRANS_FORWARD, rule, 4, NULL, &status);
    if(U_FAILURE(status)) {
        log_err("FAIL: utrans_openRules(a<>B) failed with error=%s\n", myErrorName(status));
        return;
    }

    /* clone it so we can register it a second time */
    rules2=utrans_clone(rules, &status);
    if(U_FAILURE(status)) {
        log_err("FAIL: utrans_clone(a<>B) failed with error=%s\n", myErrorName(status));
        return;
    }

    status=U_ZERO_ERROR;
    /* Register it */
    utrans_register(rules, &status);
    if(U_FAILURE(status)) {
        log_err("FAIL: utrans_register failed with error=%s\n", myErrorName(status));
        return;
    }
    status=U_ZERO_ERROR;
    /* Now check again -- should exist now*/
    t1= utrans_open("TestA-TestB", UTRANS_FORWARD, NULL,0,NULL,&status);
    if(U_FAILURE(status) || t1 == NULL) {
        log_err("FAIL: TestA-TestB not registered\n");
        return;
    }
    utrans_close(t1);

    /*unregister the instance*/
    status=U_ZERO_ERROR;
    utrans_unregister("TestA-TestB");
    /* now Make sure it doesn't exist */
    t1=utrans_open("TestA-TestB", UTRANS_FORWARD,NULL,0,NULL, &status);
    if(U_SUCCESS(status) || t1 != NULL) {
        log_err("FAIL: TestA-TestB isn't unregistered\n");
        return;
    }
    utrans_close(t1);

    /* now with utrans_unregisterID(const UChar *) */
    status=U_ZERO_ERROR;
    utrans_register(rules2, &status);
    if(U_FAILURE(status)) {
        log_err("FAIL: 2nd utrans_register failed with error=%s\n", myErrorName(status));
        return;
    }
    status=U_ZERO_ERROR;
    /* Now check again -- should exist now*/
    t1= utrans_open("TestA-TestB", UTRANS_FORWARD, NULL,0,NULL,&status);
    if(U_FAILURE(status) || t1 == NULL) {
        log_err("FAIL: 2nd TestA-TestB not registered\n");
        return;
    }
    utrans_close(t1);

    /*unregister the instance*/
    status=U_ZERO_ERROR;
    utrans_unregisterID(ID, -1);
    /* now Make sure it doesn't exist */
    t1=utrans_openU(ID, -1, UTRANS_FORWARD,NULL,0,NULL, &status);
    if(U_SUCCESS(status) || t1 != NULL) {
        log_err("FAIL: 2nd TestA-TestB isn't unregistered\n");
        return;
    }

    utrans_close(t1);
    utrans_close(inverse1);
}
Пример #12
0
static void TestUnicodeIDs() {
    UEnumeration *uenum;
    UTransliterator *utrans;
    const UChar *id, *id2;
    int32_t idLength, id2Length, count, count2;

    UErrorCode errorCode;

    errorCode=U_ZERO_ERROR;
    uenum=utrans_openIDs(&errorCode);
    if(U_FAILURE(errorCode)) {
        log_err("utrans_openIDs() failed - %s\n", u_errorName(errorCode));
        return;
    }

    count=uenum_count(uenum, &errorCode);
    if(U_FAILURE(errorCode) || count<1) {
        log_err("uenum_count(transliterator IDs)=%d - %s\n", count, u_errorName(errorCode));
    }

    count=0;
    for(;;) {
        id=uenum_unext(uenum, &idLength, &errorCode);
        if(U_FAILURE(errorCode)) {
            log_err("uenum_unext(transliterator ID %d) failed - %s\n", count, u_errorName(errorCode));
            break;
        }
        if(id==NULL) {
            break;
        }

        if(++count>10) {
            /* try to actually open only a few transliterators */
            continue;
        }

        utrans=utrans_openU(id, idLength, UTRANS_FORWARD, NULL, 0, NULL, &errorCode);
        if(U_FAILURE(errorCode)) {
            log_err("utrans_openU(%s) failed - %s\n", aescstrdup(id, idLength), u_errorName(errorCode));
            continue;
        }

        id2=utrans_getUnicodeID(utrans, &id2Length);
        if(idLength!=id2Length || 0!=u_memcmp(id, id2, idLength)) {
            log_err("utrans_getUnicodeID(%s) does not match the original ID\n", aescstrdup(id, idLength));
        }

        utrans_close(utrans);
    }

    uenum_reset(uenum, &errorCode);
    if(U_FAILURE(errorCode) || count<1) {
        log_err("uenum_reset(transliterator IDs) failed - %s\n", u_errorName(errorCode));
    } else {
        count2=uenum_count(uenum, &errorCode);
        if(U_FAILURE(errorCode) || count<1) {
            log_err("2nd uenum_count(transliterator IDs)=%d - %s\n", count2, u_errorName(errorCode));
        } else if(count!=count2) {
            log_err("uenum_unext(transliterator IDs) returned %d IDs but uenum_count() after uenum_reset() claims there are %d\n", count, count2);
        }
    }

    uenum_close(uenum);
}
Пример #13
0
static inline UTransliterator *utrans_find(CFStringRef transform, UTransDirection dir, UErrorCode *error) {
    UEnumeration *uenum = NULL;
    UTransliterator *trans = NULL;
    do {
        uenum = utrans_openIDs(error);
        if (U_FAILURE(*error)) {
            DEBUG_LOG("%s", u_errorName(*error));
            break;
        }

        int32_t count = uenum_count(uenum, error);
        if (U_FAILURE(*error)) {
            DEBUG_LOG("%s", u_errorName(*error));
            break;
        }
        int32_t trans_idx = 0;
        while (trans_idx < count && trans == NULL) {
            int32_t idLen = 0;
            const UChar *uid = uenum_unext(uenum, &idLen, error);
            if (U_FAILURE(*error)) {
                DEBUG_LOG("%s", u_errorName(*error));
                break;
            }
            // this seems rather unlikely since we should have already broken
            // by the trans_idx exceeding the count
            if (uid == NULL) {
                break;
            }

            CFStringRef name = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, uid, idLen, kCFAllocatorNull);
            // It would have been nice if these stirng constants were actually defined somewhere in icu, but sadly they are runtime metadata...
            if ((CFEqual(name, CFSTR("Any-Remove")) && CFEqual(transform, kCFStringTransformStripCombiningMarks)) ||
                (CFEqual(name, CFSTR("Any-Latin")) && CFEqual(transform, kCFStringTransformToLatin)) ||
                (CFEqual(name, CFSTR("Latin-Katakana")) && CFEqual(transform, kCFStringTransformLatinKatakana)) ||
                (CFEqual(name, CFSTR("Latin-Hiragana")) && CFEqual(transform, kCFStringTransformLatinHiragana)) ||
                (CFEqual(name, CFSTR("Hiragana-Katakana")) && CFEqual(transform, kCFStringTransformHiraganaKatakana)) ||
                (CFEqual(name, CFSTR("Latin-Hangul")) && CFEqual(transform, kCFStringTransformLatinHangul)) ||
                (CFEqual(name, CFSTR("Latin-Arabic")) && CFEqual(transform, kCFStringTransformLatinArabic)) ||
                (CFEqual(name, CFSTR("Latin-Hebrew")) && CFEqual(transform, kCFStringTransformLatinHebrew)) ||
                (CFEqual(name, CFSTR("Latin-Thai")) && CFEqual(transform, kCFStringTransformLatinThai)) ||
                (CFEqual(name, CFSTR("Latin-Cyrillic")) && CFEqual(transform, kCFStringTransformLatinCyrillic)) ||
                (CFEqual(name, CFSTR("Latin-Greek")) && CFEqual(transform, kCFStringTransformLatinGreek)) ||
                (CFEqual(name, CFSTR("Any-Hex/XML")) && CFEqual(transform, kCFStringTransformToXMLHex)) ||
                (CFEqual(name, CFSTR("Any-Name")) && CFEqual(transform, kCFStringTransformToUnicodeName)) ||
                (CFEqual(name, CFSTR("Accents-Any")) && CFEqual(transform, kCFStringTransformStripDiacritics))) {
                trans = utrans_openU(uid, idLen, dir, NULL, 0, NULL, error);
            }
            CFRelease(name);
            trans_idx++;
        }
    } while (0);

    if (uenum != NULL) {
        uenum_reset(uenum, error);
        uenum_close(uenum);
    }

    if (trans == NULL && (CFEqual(transform, kCFStringTransformStripCombiningMarks) ||
                          CFEqual(transform, kCFStringTransformToLatin) ||
                          CFEqual(transform, kCFStringTransformLatinKatakana) ||
                          CFEqual(transform, kCFStringTransformLatinHiragana) ||
                          CFEqual(transform, kCFStringTransformHiraganaKatakana) ||
                          CFEqual(transform, kCFStringTransformLatinHangul) ||
                          CFEqual(transform, kCFStringTransformLatinArabic) ||
                          CFEqual(transform, kCFStringTransformLatinHebrew) ||
                          CFEqual(transform, kCFStringTransformLatinCyrillic) ||
                          CFEqual(transform, kCFStringTransformLatinGreek) ||
                          CFEqual(transform, kCFStringTransformToXMLHex) ||
                          CFEqual(transform, kCFStringTransformToUnicodeName) ||
                          CFEqual(transform, kCFStringTransformStripDiacritics))) {
        static dispatch_once_t once = 0L;
        dispatch_once(&once, ^{
            RELEASE_LOG("Unable to find transliterators in icu data: likely this is from not including the Transliterators section in building your icu.dat file");
        });
    }