1 /*
2 ******************************************************************************
3 *
4 * © 2016 and later: Unicode, Inc. and others.
5 * License & terms of use: http://www.unicode.org/copyright.html
6 *
7 ******************************************************************************
8 *   file name:  ubiditransform.c
9 *   encoding:   UTF-8
10 *   tab size:   8 (not used)
11 *   indentation:4
12 *
13 *   created on: 2016jul24
14 *   created by: Lina Kemmel
15 *
16 */
17 
18 #include "cmemory.h"
19 #include "unicode/ubidi.h"
20 #include "unicode/ustring.h"
21 #include "unicode/ushape.h"
22 #include "unicode/utf16.h"
23 #include "ustr_imp.h"
24 #include "unicode/ubiditransform.h"
25 
26 /* Some convenience defines */
27 #define LTR                     UBIDI_LTR
28 #define RTL                     UBIDI_RTL
29 #define LOGICAL                 UBIDI_LOGICAL
30 #define VISUAL                  UBIDI_VISUAL
31 #define SHAPE_LOGICAL           U_SHAPE_TEXT_DIRECTION_LOGICAL
32 #define SHAPE_VISUAL            U_SHAPE_TEXT_DIRECTION_VISUAL_LTR
33 
34 #define CHECK_LEN(STR, LEN, ERROR) UPRV_BLOCK_MACRO_BEGIN { \
35     if (LEN == 0) return 0; \
36     if (LEN < -1) { *(ERROR) = U_ILLEGAL_ARGUMENT_ERROR; return 0; } \
37     if (LEN == -1) LEN = u_strlen(STR); \
38 } UPRV_BLOCK_MACRO_END
39 
40 #define MAX_ACTIONS     7
41 
42 /**
43  * Typedef for a pointer to a function, which performs some operation (such as
44  * reordering, setting "inverse" mode, character mirroring, etc.). Return value
45  * indicates whether the text was changed in the course of this operation or
46  * not.
47  */
48 typedef UBool (*UBiDiAction)(UBiDiTransform *, UErrorCode *);
49 
50 /**
51  * Structure that holds a predefined reordering scheme, including the following
52  * information:
53  * <ul>
54  * <li>an input base direction,</li>
55  * <li>an input order,</li>
56  * <li>an output base direction,</li>
57  * <li>an output order,</li>
58  * <li>a digit shaping direction,</li>
59  * <li>a letter shaping direction,</li>
60  * <li>a base direction that should be applied when the reordering engine is
61  *     invoked (which can not always be derived from the caller-defined
62  *     options),</li>
63  * <li>an array of pointers to functions that accomplish the bidi layout
64  *     transformation.</li>
65  * </ul>
66  */
67 typedef struct {
68     UBiDiLevel        inLevel;               /* input level */
69     UBiDiOrder        inOrder;               /* input order */
70     UBiDiLevel        outLevel;              /* output level */
71     UBiDiOrder        outOrder;              /* output order */
72     uint32_t          digitsDir;             /* digit shaping direction */
73     uint32_t          lettersDir;            /* letter shaping direction */
74     UBiDiLevel        baseLevel;             /* paragraph level to be used with setPara */
75     const UBiDiAction actions[MAX_ACTIONS];  /* array of pointers to functions carrying out the transformation */
76 } ReorderingScheme;
77 
78 struct UBiDiTransform {
79     UBiDi                   *pBidi;             /* pointer to a UBiDi object */
80     const ReorderingScheme  *pActiveScheme;     /* effective reordering scheme */
81     UChar                   *src;               /* input text */
82     UChar                   *dest;              /* output text */
83     uint32_t                srcLength;          /* input text length - not really needed as we are zero-terminated and can u_strlen */
84     uint32_t                srcSize;            /* input text capacity excluding the trailing zero */
85     uint32_t                destSize;           /* output text capacity */
86     uint32_t                *pDestLength;       /* number of UChars written to dest */
87     uint32_t                reorderingOptions;  /* reordering options - currently only suppot DO_MIRRORING */
88     uint32_t                digits;             /* digit option for ArabicShaping */
89     uint32_t                letters;            /* letter option for ArabicShaping */
90 };
91 
92 U_CAPI UBiDiTransform* U_EXPORT2
ubiditransform_open(UErrorCode * pErrorCode)93 ubiditransform_open(UErrorCode *pErrorCode)
94 {
95     UBiDiTransform *pBiDiTransform = NULL;
96     if (U_SUCCESS(*pErrorCode)) {
97         pBiDiTransform = (UBiDiTransform*) uprv_calloc(1, sizeof(UBiDiTransform));
98         if (pBiDiTransform == NULL) {
99             *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
100         }
101     }
102     return pBiDiTransform;
103 }
104 
105 U_CAPI void U_EXPORT2
ubiditransform_close(UBiDiTransform * pBiDiTransform)106 ubiditransform_close(UBiDiTransform *pBiDiTransform)
107 {
108     if (pBiDiTransform != NULL) {
109         if (pBiDiTransform->pBidi != NULL) {
110             ubidi_close(pBiDiTransform->pBidi);
111         }
112         if (pBiDiTransform->src != NULL) {
113             uprv_free(pBiDiTransform->src);
114         }
115         uprv_free(pBiDiTransform);
116     }
117 }
118 
119 /**
120  * Performs Bidi resolution of text.
121  *
122  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
123  * @param pErrorCode Pointer to the error code value.
124  *
125  * @return Whether or not this function modifies the text. Besides the return
126  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
127  */
128 static UBool
action_resolve(UBiDiTransform * pTransform,UErrorCode * pErrorCode)129 action_resolve(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
130 {
131     ubidi_setPara(pTransform->pBidi, pTransform->src, pTransform->srcLength,
132             pTransform->pActiveScheme->baseLevel, NULL, pErrorCode);
133     return FALSE;
134 }
135 
136 /**
137  * Performs basic reordering of text (Logical -> Visual LTR).
138  *
139  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
140  * @param pErrorCode Pointer to the error code value.
141  *
142  * @return Whether or not this function modifies the text. Besides the return
143  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
144  */
145 static UBool
action_reorder(UBiDiTransform * pTransform,UErrorCode * pErrorCode)146 action_reorder(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
147 {
148     ubidi_writeReordered(pTransform->pBidi, pTransform->dest, pTransform->destSize,
149             static_cast<uint16_t>(pTransform->reorderingOptions), pErrorCode);
150 
151     *pTransform->pDestLength = pTransform->srcLength;
152     pTransform->reorderingOptions = UBIDI_REORDER_DEFAULT;
153     return TRUE;
154 }
155 
156 /**
157  * Sets "inverse" mode on the <code>UBiDi</code> object.
158  *
159  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
160  * @param pErrorCode Pointer to the error code value.
161  *
162  * @return Whether or not this function modifies the text. Besides the return
163  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
164  */
165 static UBool
action_setInverse(UBiDiTransform * pTransform,UErrorCode * pErrorCode)166 action_setInverse(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
167 {
168     (void)pErrorCode;
169     ubidi_setInverse(pTransform->pBidi, TRUE);
170     ubidi_setReorderingMode(pTransform->pBidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT);
171     return FALSE;
172 }
173 
174 /**
175  * Sets "runs only" reordering mode indicating a Logical LTR <-> Logical RTL
176  * transformation.
177  *
178  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
179  * @param pErrorCode Pointer to the error code value.
180  *
181  * @return Whether or not this function modifies the text. Besides the return
182  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
183  */
184 static UBool
action_setRunsOnly(UBiDiTransform * pTransform,UErrorCode * pErrorCode)185 action_setRunsOnly(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
186 {
187     (void)pErrorCode;
188     ubidi_setReorderingMode(pTransform->pBidi, UBIDI_REORDER_RUNS_ONLY);
189     return FALSE;
190 }
191 
192 /**
193  * Performs string reverse.
194  *
195  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
196  * @param pErrorCode Pointer to the error code value.
197  *
198  * @return Whether or not this function modifies the text. Besides the return
199  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
200  */
201 static UBool
action_reverse(UBiDiTransform * pTransform,UErrorCode * pErrorCode)202 action_reverse(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
203 {
204     ubidi_writeReverse(pTransform->src, pTransform->srcLength,
205             pTransform->dest, pTransform->destSize,
206             UBIDI_REORDER_DEFAULT, pErrorCode);
207     *pTransform->pDestLength = pTransform->srcLength;
208     return TRUE;
209 }
210 
211 /**
212  * Applies a new value to the text that serves as input at the current
213  * processing step. This value is identical to the original one when we begin
214  * the processing, but usually changes as the transformation progresses.
215  *
216  * @param pTransform A pointer to the <code>UBiDiTransform</code> structure.
217  * @param newSrc A pointer whose value is to be used as input text.
218  * @param newLength A length of the new text in <code>UChar</code>s.
219  * @param newSize A new source capacity in <code>UChar</code>s.
220  * @param pErrorCode Pointer to the error code value.
221  */
222 static void
updateSrc(UBiDiTransform * pTransform,const UChar * newSrc,uint32_t newLength,uint32_t newSize,UErrorCode * pErrorCode)223 updateSrc(UBiDiTransform *pTransform, const UChar *newSrc, uint32_t newLength,
224         uint32_t newSize, UErrorCode *pErrorCode)
225 {
226     if (newSize < newLength) {
227         *pErrorCode = U_BUFFER_OVERFLOW_ERROR;
228         return;
229     }
230     if (newSize > pTransform->srcSize) {
231         newSize += 50; // allocate slightly more than needed right now
232         if (pTransform->src != NULL) {
233             uprv_free(pTransform->src);
234             pTransform->src = NULL;
235         }
236         pTransform->src = (UChar *)uprv_malloc(newSize * sizeof(UChar));
237         if (pTransform->src == NULL) {
238             *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
239             //pTransform->srcLength = pTransform->srcSize = 0;
240             return;
241         }
242         pTransform->srcSize = newSize;
243     }
244     u_strncpy(pTransform->src, newSrc, newLength);
245     pTransform->srcLength = u_terminateUChars(pTransform->src,
246     		pTransform->srcSize, newLength, pErrorCode);
247 }
248 
249 /**
250  * Calls a lower level shaping function.
251  *
252  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
253  * @param options Shaping options.
254  * @param pErrorCode Pointer to the error code value.
255  */
256 static void
doShape(UBiDiTransform * pTransform,uint32_t options,UErrorCode * pErrorCode)257 doShape(UBiDiTransform *pTransform, uint32_t options, UErrorCode *pErrorCode)
258 {
259     *pTransform->pDestLength = u_shapeArabic(pTransform->src,
260             pTransform->srcLength, pTransform->dest, pTransform->destSize,
261             options, pErrorCode);
262 }
263 
264 /**
265  * Performs digit and letter shaping.
266  *
267  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
268  * @param pErrorCode Pointer to the error code value.
269  *
270  * @return Whether or not this function modifies the text. Besides the return
271  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
272  */
273 static UBool
action_shapeArabic(UBiDiTransform * pTransform,UErrorCode * pErrorCode)274 action_shapeArabic(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
275 {
276     if ((pTransform->letters | pTransform->digits) == 0) {
277         return FALSE;
278     }
279     if (pTransform->pActiveScheme->lettersDir == pTransform->pActiveScheme->digitsDir) {
280         doShape(pTransform, pTransform->letters | pTransform->digits | pTransform->pActiveScheme->lettersDir,
281                 pErrorCode);
282     } else {
283         doShape(pTransform, pTransform->digits | pTransform->pActiveScheme->digitsDir, pErrorCode);
284         if (U_SUCCESS(*pErrorCode)) {
285             updateSrc(pTransform, pTransform->dest, *pTransform->pDestLength,
286                     *pTransform->pDestLength, pErrorCode);
287             doShape(pTransform, pTransform->letters | pTransform->pActiveScheme->lettersDir,
288                     pErrorCode);
289         }
290     }
291     return TRUE;
292 }
293 
294 /**
295  * Performs character mirroring.
296  *
297  * @param pTransform Pointer to the <code>UBiDiTransform</code> structure.
298  * @param pErrorCode Pointer to the error code value.
299  *
300  * @return Whether or not this function modifies the text. Besides the return
301  * value, the caller should also check <code>U_SUCCESS(*pErrorCode)</code>.
302  */
303 static UBool
action_mirror(UBiDiTransform * pTransform,UErrorCode * pErrorCode)304 action_mirror(UBiDiTransform *pTransform, UErrorCode *pErrorCode)
305 {
306     UChar32 c;
307     uint32_t i = 0, j = 0;
308     if (0 == (pTransform->reorderingOptions & UBIDI_DO_MIRRORING)) {
309         return FALSE;
310     }
311     if (pTransform->destSize < pTransform->srcLength) {
312         *pErrorCode = U_BUFFER_OVERFLOW_ERROR;
313         return FALSE;
314     }
315     do {
316         UBool isOdd = ubidi_getLevelAt(pTransform->pBidi, i) & 1;
317         U16_NEXT(pTransform->src, i, pTransform->srcLength, c);
318         U16_APPEND_UNSAFE(pTransform->dest, j, isOdd ? u_charMirror(c) : c);
319     } while (i < pTransform->srcLength);
320 
321     *pTransform->pDestLength = pTransform->srcLength;
322     pTransform->reorderingOptions = UBIDI_REORDER_DEFAULT;
323     return TRUE;
324 }
325 
326 /**
327  * All possible reordering schemes.
328  *
329  */
330 static const ReorderingScheme Schemes[] =
331 {
332     /* 0: Logical LTR => Visual LTR */
333     {LTR, LOGICAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR,
334             {action_shapeArabic, action_resolve, action_reorder, NULL}},
335     /* 1: Logical RTL => Visual LTR */
336     {RTL, LOGICAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL,
337             {action_resolve, action_reorder, action_shapeArabic, NULL}},
338     /* 2: Logical LTR => Visual RTL */
339     {LTR, LOGICAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR,
340             {action_shapeArabic, action_resolve, action_reorder, action_reverse, NULL}},
341     /* 3: Logical RTL => Visual RTL */
342     {RTL, LOGICAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL,
343             {action_resolve, action_reorder, action_shapeArabic, action_reverse, NULL}},
344     /* 4: Visual LTR => Logical RTL */
345     {LTR, VISUAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL,
346             {action_shapeArabic, action_setInverse, action_resolve, action_reorder, NULL}},
347     /* 5: Visual RTL => Logical RTL */
348     {RTL, VISUAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL,
349             {action_reverse, action_shapeArabic, action_setInverse, action_resolve, action_reorder, NULL}},
350     /* 6: Visual LTR => Logical LTR */
351     {LTR, VISUAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR,
352             {action_setInverse, action_resolve, action_reorder, action_shapeArabic, NULL}},
353     /* 7: Visual RTL => Logical LTR */
354     {RTL, VISUAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR,
355             {action_reverse, action_setInverse, action_resolve, action_reorder, action_shapeArabic, NULL}},
356     /* 8: Logical LTR => Logical RTL */
357     {LTR, LOGICAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR,
358             {action_shapeArabic, action_resolve, action_mirror, action_setRunsOnly, action_resolve, action_reorder, NULL}},
359     /* 9: Logical RTL => Logical LTR */
360     {RTL, LOGICAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, RTL,
361             {action_resolve, action_mirror, action_setRunsOnly, action_resolve, action_reorder, action_shapeArabic, NULL}},
362     /* 10: Visual LTR => Visual RTL */
363     {LTR, VISUAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR,
364             {action_shapeArabic, action_setInverse, action_resolve, action_mirror, action_reverse, NULL}},
365     /* 11: Visual RTL => Visual LTR */
366     {RTL, VISUAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR,
367             {action_reverse, action_shapeArabic, action_setInverse, action_resolve, action_mirror, NULL}},
368     /* 12: Logical LTR => Logical LTR */
369     {LTR, LOGICAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR,
370             {action_resolve, action_mirror, action_shapeArabic, NULL}},
371     /* 13: Logical RTL => Logical RTL */
372     {RTL, LOGICAL, RTL, LOGICAL, SHAPE_VISUAL, SHAPE_LOGICAL, RTL,
373             {action_resolve, action_mirror, action_shapeArabic, NULL}},
374     /* 14: Visual LTR => Visual LTR */
375     {LTR, VISUAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR,
376             {action_resolve, action_mirror, action_shapeArabic, NULL}},
377     /* 15: Visual RTL => Visual RTL */
378     {RTL, VISUAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR,
379             {action_reverse, action_resolve, action_mirror, action_shapeArabic, action_reverse, NULL}}
380 };
381 
382 static const uint32_t nSchemes = sizeof(Schemes) / sizeof(*Schemes);
383 
384 /**
385  * When the direction option is <code>UBIDI_DEFAULT_LTR</code> or
386  * <code>UBIDI_DEFAULT_RTL</code>, resolve the base direction according to that
387  * of the first strong bidi character.
388  */
389 static void
resolveBaseDirection(const UChar * text,uint32_t length,UBiDiLevel * pInLevel,UBiDiLevel * pOutLevel)390 resolveBaseDirection(const UChar *text, uint32_t length,
391         UBiDiLevel *pInLevel, UBiDiLevel *pOutLevel)
392 {
393     switch (*pInLevel) {
394         case UBIDI_DEFAULT_LTR:
395         case UBIDI_DEFAULT_RTL: {
396             UBiDiLevel level = static_cast<UBiDiLevel>(ubidi_getBaseDirection(text, length));
397 #ifdef U_STRINGI_PATCHES
398             *pInLevel = static_cast<UBiDiLevel>(level != UBIDI_NEUTRAL) ? level
399                     : ((*pInLevel == UBIDI_DEFAULT_RTL) ? static_cast<UBiDiLevel>(RTL) : static_cast<UBiDiLevel>(LTR));
400 #else /* !U_STRINGI_PATCHES */
401             *pInLevel = static_cast<UBiDiLevel>(level != UBIDI_NEUTRAL) ? level
402                     : *pInLevel == UBIDI_DEFAULT_RTL ? static_cast<UBiDiLevel>(RTL) : static_cast<UBiDiLevel>(LTR);
403 #endif /* U_STRINGI_PATCHES */
404             break;
405         }
406         default:
407             *pInLevel &= 1;
408             break;
409     }
410     switch (*pOutLevel) {
411         case UBIDI_DEFAULT_LTR:
412         case UBIDI_DEFAULT_RTL:
413             *pOutLevel = *pInLevel;
414             break;
415         default:
416             *pOutLevel &= 1;
417             break;
418     }
419 }
420 
421 /**
422  * Finds a valid <code>ReorderingScheme</code> matching the
423  * caller-defined scheme.
424  *
425  * @return A valid <code>ReorderingScheme</code> object or NULL
426  */
427 static const ReorderingScheme*
findMatchingScheme(UBiDiLevel inLevel,UBiDiLevel outLevel,UBiDiOrder inOrder,UBiDiOrder outOrder)428 findMatchingScheme(UBiDiLevel inLevel, UBiDiLevel outLevel,
429         UBiDiOrder inOrder, UBiDiOrder outOrder)
430 {
431     uint32_t i;
432     for (i = 0; i < nSchemes; i++) {
433         const ReorderingScheme *pScheme = Schemes + i;
434         if (inLevel == pScheme->inLevel && outLevel == pScheme->outLevel
435                 && inOrder == pScheme->inOrder && outOrder == pScheme->outOrder) {
436             return pScheme;
437         }
438     }
439     return NULL;
440 }
441 
442 U_CAPI uint32_t U_EXPORT2
ubiditransform_transform(UBiDiTransform * pBiDiTransform,const UChar * src,int32_t srcLength,UChar * dest,int32_t destSize,UBiDiLevel inParaLevel,UBiDiOrder inOrder,UBiDiLevel outParaLevel,UBiDiOrder outOrder,UBiDiMirroring doMirroring,uint32_t shapingOptions,UErrorCode * pErrorCode)443 ubiditransform_transform(UBiDiTransform *pBiDiTransform,
444             const UChar *src, int32_t srcLength,
445             UChar *dest, int32_t destSize,
446             UBiDiLevel inParaLevel, UBiDiOrder inOrder,
447             UBiDiLevel outParaLevel, UBiDiOrder outOrder,
448             UBiDiMirroring doMirroring, uint32_t shapingOptions,
449             UErrorCode *pErrorCode)
450 {
451     uint32_t destLength = 0;
452     UBool textChanged = FALSE;
453     const UBiDiTransform *pOrigTransform = pBiDiTransform;
454     const UBiDiAction *action = NULL;
455 
456     if (U_FAILURE(*pErrorCode)) {
457         return 0;
458     }
459     if (src == NULL || dest == NULL) {
460         *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
461         return 0;
462     }
463     CHECK_LEN(src, srcLength, pErrorCode);
464     CHECK_LEN(dest, destSize, pErrorCode);
465 
466     if (pBiDiTransform == NULL) {
467         pBiDiTransform = ubiditransform_open(pErrorCode);
468         if (U_FAILURE(*pErrorCode)) {
469             return 0;
470         }
471     }
472     /* Current limitation: in multiple paragraphs will be resolved according
473        to the 1st paragraph */
474     resolveBaseDirection(src, srcLength, &inParaLevel, &outParaLevel);
475 
476     pBiDiTransform->pActiveScheme = findMatchingScheme(inParaLevel, outParaLevel,
477             inOrder, outOrder);
478     if (pBiDiTransform->pActiveScheme == NULL) {
479         goto cleanup;
480     }
481     pBiDiTransform->reorderingOptions = doMirroring ? UBIDI_DO_MIRRORING
482             : UBIDI_REORDER_DEFAULT;
483 
484     /* Ignore TEXT_DIRECTION_* flags, as we apply our own depending on the text
485        scheme at the time shaping is invoked. */
486     shapingOptions &= ~U_SHAPE_TEXT_DIRECTION_MASK;
487     pBiDiTransform->digits = shapingOptions & ~U_SHAPE_LETTERS_MASK;
488     pBiDiTransform->letters = shapingOptions & ~U_SHAPE_DIGITS_MASK;
489 
490     updateSrc(pBiDiTransform, src, srcLength, destSize > srcLength ? destSize : srcLength, pErrorCode);
491     if (U_FAILURE(*pErrorCode)) {
492         goto cleanup;
493     }
494     if (pBiDiTransform->pBidi == NULL) {
495         pBiDiTransform->pBidi = ubidi_openSized(0, 0, pErrorCode);
496         if (U_FAILURE(*pErrorCode)) {
497             goto cleanup;
498         }
499     }
500     pBiDiTransform->dest = dest;
501     pBiDiTransform->destSize = destSize;
502     pBiDiTransform->pDestLength = &destLength;
503 
504     /* Checking for U_SUCCESS() within the loop to bail out on first failure. */
505     for (action = pBiDiTransform->pActiveScheme->actions; *action && U_SUCCESS(*pErrorCode); action++) {
506         if ((*action)(pBiDiTransform, pErrorCode)) {
507             if (action + 1) {
508                 updateSrc(pBiDiTransform, pBiDiTransform->dest, *pBiDiTransform->pDestLength,
509                         *pBiDiTransform->pDestLength, pErrorCode);
510             }
511             textChanged = TRUE;
512         }
513     }
514     ubidi_setInverse(pBiDiTransform->pBidi, FALSE);
515 
516     if (!textChanged && U_SUCCESS(*pErrorCode)) {
517         /* Text was not changed - just copy src to dest */
518         if (destSize < srcLength) {
519             *pErrorCode = U_BUFFER_OVERFLOW_ERROR;
520         } else {
521             u_strncpy(dest, src, srcLength);
522             destLength = srcLength;
523         }
524     }
525 cleanup:
526     if (pOrigTransform != pBiDiTransform) {
527         ubiditransform_close(pBiDiTransform);
528     } else {
529         pBiDiTransform->dest = NULL;
530         pBiDiTransform->pDestLength = NULL;
531         pBiDiTransform->srcLength = 0;
532         pBiDiTransform->destSize = 0;
533     }
534     return U_FAILURE(*pErrorCode) ? 0 : destLength;
535 }
536