// biditest.cpp - version 2 // Test harness for use with bidi.cpp // Maintenance note: // file does not need to be added to project to compile bidi.cpp #ifdef TRACE static const char* typeNamesN[] = { "ON", "L", "R", "AN", "EN", "AL", "NSM", "CS", "ES", "ET", "BN", "S", "WS", "B", "RLO", "RLE", "LRO", "LRE", "PDF" }; static const char* typeNamesU[] = { "L", "LRE", "LRO", "R", "AL", "RLE", "RLO", "PDF", "EN", "ES", "ET", "AN", "CS", "NSM", "BN", "B", "S", "WS", "ON" }; #endif static int g_trace = 0; // access to this routine is synchronized at the Java level // INIT_CCH must be > 0xff because of masking below #define INIT_CCH 256 #define MAX_CCH 8192 static int g_storage_static[INIT_CCH * 3]; static int g_storage_cap = INIT_CCH; static int* g_p_storage = &g_storage_static[0]; // get sufficient working storage in types, // working_types, and levels return params bool ensureStorage(int required, int** types, int** working_types, int** levels) { if (required > MAX_CCH) { fprintf(stderr, "bidi input length exceeds limit\n"); return false; } if (required > g_storage_cap) { if (g_storage_cap != INIT_CCH) { free(types); } g_storage_cap = (required << 1) & ~0xff; /* !!! requires INIT_CCH > 0xff */ if (g_storage_cap > MAX_CCH) { g_storage_cap = MAX_CCH; } g_p_storage = (int*)malloc(g_storage_cap * 3 * sizeof(int)); } *types = &g_p_storage[0]; *working_types = &g_p_storage[g_storage_cap]; *levels = &g_p_storage[g_storage_cap * 2]; return true; } /* * The core bidi routine, determines base level based on flags, * then resolves types and levels. Returns base embedding level. */ int coreBidi(jint flags, jsize cch, int* types, int* working_types, int* levels, jint lb) { // save copy of original types for use in resolveWhitespace for (int i = 0; i < cch; ++i) { working_types[i] = types[i]; } #if TRACE if (g_trace) { fprintf(stdout, "\ninitial types: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%s, ", typeNamesN[working_types[i]]); } fprintf(stdout, "\n"); } #endif // set base level and compute character types int baselevel; switch (flags & 0x3) { case NativeBidi_FLAG_DIR_LTR: baselevel = 0; break; case NativeBidi_FLAG_DIR_RTL: baselevel = 1; break; default: baselevel = baseLevel(working_types, cch); break; } #if TRACE if (g_trace) { fprintf(stdout, "baselevel: %d\n", baselevel); fprintf(stdout, "after explicit: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%s, ", typeNamesN[working_types[i]]); } fprintf(stdout, "\n levels: "); for (i = 0; i < cch; ++i) { fprintf(stdout, "%2d, ", levels[i]); } fprintf(stdout, "\n"); } #endif // tables require that WS, S, be folded to ON, B to BN before resolveExplicit for (i = 0; i < cch; ++i) { switch (working_types[i]) { case WS: case S: working_types[i] = ON; break; case B: working_types[i] = BN; break; default: break; } } resolveExplicit(baselevel, N, working_types, levels, cch); #if TRACE if (g_trace) { fprintf(stdout, "after folding neutrals: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%s, ", typeNamesN[working_types[i]]); } fprintf(stdout, "\n"); } #endif // resolve weak resolveWeak(baselevel, working_types, levels, cch); #if TRACE if (g_trace) { fprintf(stdout, "after weak types: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%s, ", typeNamesN[working_types[i]]); } fprintf(stdout, "\n levels: "); for (i = 0; i < cch; ++i) { fprintf(stdout, "%2d, ", levels[i]); } fprintf(stdout, "\n"); } #endif // resolve neutrals resolveNeutrals(baselevel, working_types, levels, cch); #if TRACE if (g_trace) { fprintf(stdout, "after neutrals: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%s, ", typeNamesN[working_types[i]]); } fprintf(stdout, "\n"); } #endif // resolveImplicit resolveImplicit(working_types, levels, cch); #if TRACE if (g_trace) { fprintf(stdout, "after implicit: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%s, ", typeNamesN[working_types[i]]); } fprintf(stdout, "\n"); } #endif // resolve whitespace (requires original types) resolveWhitespace(baselevel, types, levels, cch); // resolve whitespace before line break (original types) if (lb > 0 && lb <= cch) { for (i = lb; --i >= 0 && types[i] == WS;) {} SetDeferredRun(levels, cch - i - 1, cch, baselevel); } #if TRACE if (g_trace) { fprintf(stdout, "final levels: "); for (int i = 0; i < cch; ++i) { fprintf(stdout, "%2d, ", levels[i]); } fprintf(stdout, "\n"); fflush(stdout); } #endif return baselevel; } /* * Wrap core routine with access to java embedding and result array * objects. This is common code used by the two main java * interfaces. Returns base embedding level. */ jint coreBidiWrapper(JNIEnv* env, jclass bidi, jint flags, jint len, int* types, int* working_types, int* levels, jint lb, jbyteArray result) { jint rflags = -1; jbyte* cresult = (jbyte*)env->GetPrimitiveArrayCritical(result, NULL); if (cresult) { rflags = coreBidi(flags, len, types, working_types, levels, lb); // copy resulting integer levels back to byte array for (int i = 0; i < len; ++i) { cresult[i] = levels[i]; } env->ReleasePrimitiveArrayCritical(result, cresult, JNI_COMMIT); } return rflags; } // interface to call when you have a character string JNIEXPORT jint JNICALL Java_NativeBidi_doBidiChars(JNIEnv *env, jclass bidi, jint flags, jstring str, jint lb, jbyteArray result) { const jsize len = env->GetStringLength(str); int* s_types; int* s_working_types; int* s_levels; jint rflags = -1; if (ensureStorage(len, &s_types, &s_working_types, &s_levels)) { const jchar* cstr = env->GetStringCritical(str, NULL); if (cstr) { // assign directional types // do not fold types, core does that classify(cstr, s_types, len, 0); rflags = coreBidiWrapper(env, bidi, flags, len, s_types, s_working_types, s_levels, lb, result); env->ReleaseStringCritical(str, cstr); } } return rflags; } // Map unicode types from java API into internal types static const int typemapUN[] = { // L LRE LRO R AL RLE RLO PDF EN ES ET AN CS NSM BN B S WS ON // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 L, LRE, LRO, R, AL, RLE, RLO, PDF, EN, ES, ET, AN, CS,NSM, BN, B, S, WS, ON // 1, 17, 16, 2, 5, 15, 14, 18, 4, 8, 9, 3, 7, 13, 11, 12, 0, 6, 10 -- old // 1, 17, 16, 2, 5 15, 14, 18, 4, 8, 9, 3, 7, 6, 10, 13, 11, 12, 0 -- new }; enum { UL, ULRE, ULRO, UR, UAL, URLE, URLO, UPDF, UEN, UES, UET, UAN, UCS, UNSM, UBN, UB, US, UWS, UON }; static const int typemapNU[] = { // ON L R AN EN AL NSM CS ES ET BN S WS B RLO RLE LRO LRE PDF // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 UON, UL, UR, UAN, UEN, UAL, UNSM, UCS, UES, UET, UBN, US, UWS, UB, URLO, URLE, ULRO, ULRE, UPDF // 18 0 }; JNIEXPORT void JNICALL Java_NativeBidi_setTraceOn (JNIEnv *env, jclass bidi, jboolean on) { g_trace = on; } // interface to call when you have a type array // external embs not supported in this implementation // input types are numbered from 0 according to tr#9 JNIEXPORT jint JNICALL Java_NativeBidi_doBidiTypes (JNIEnv *env, jclass bidi, jint flags, jbyteArray types, jint lb, jbyteArray result) { const jsize len = env->GetArrayLength(types); int* s_types; int* s_working_types; int* s_levels; jint rflags = -1; if (ensureStorage(len, &s_types, &s_working_types, &s_levels)) { jbyte* ctypes = (jbyte*)env->GetPrimitiveArrayCritical(types, NULL); if (ctypes) { for (int i = 0; i < len; ++i) { s_types[i] = typemapUN[ctypes[i]]; #if TRACE if (g_trace) { fprintf(stdout, "unicode '%s' -> native '%s'\n", typeNamesU[ctypes[i]], typeNamesN[s_types[i]]); } #endif } rflags = coreBidiWrapper(env, bidi, flags, len, s_types, s_working_types, s_levels, lb, result); env->ReleasePrimitiveArrayCritical(types, ctypes, JNI_ABORT); } } return rflags; }