1/*
2******************************************************************************
3*
4*   Copyright (C) 2000-2011, International Business Machines
5*   Corporation and others.  All Rights Reserved.
6*
7******************************************************************************
8*   file name:  ucnvscsu.c
9*   encoding:   US-ASCII
10*   tab size:   8 (not used)
11*   indentation:4
12*
13*   created on: 2000nov18
14*   created by: Markus W. Scherer
15*
16*   This is an implementation of the Standard Compression Scheme for Unicode
17*   as defined in http://www.unicode.org/unicode/reports/tr6/ .
18*   Reserved commands and window settings are treated as illegal sequences and
19*   will result in callback calls.
20*/
21
22#include "unicode/utypes.h"
23
24#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_NON_HTML5_CONVERSION
25
26#include "unicode/ucnv.h"
27#include "unicode/ucnv_cb.h"
28#include "unicode/utf16.h"
29#include "ucnv_bld.h"
30#include "ucnv_cnv.h"
31#include "cmemory.h"
32
33/* SCSU definitions --------------------------------------------------------- */
34
35/* SCSU command byte values */
36enum {
37    SQ0=0x01, /* Quote from window pair 0 */
38    SQ7=0x08, /* Quote from window pair 7 */
39    SDX=0x0B, /* Define a window as extended */
40    Srs=0x0C, /* reserved */
41    SQU=0x0E, /* Quote a single Unicode character */
42    SCU=0x0F, /* Change to Unicode mode */
43    SC0=0x10, /* Select window 0 */
44    SC7=0x17, /* Select window 7 */
45    SD0=0x18, /* Define and select window 0 */
46    SD7=0x1F, /* Define and select window 7 */
47
48    UC0=0xE0, /* Select window 0 */
49    UC7=0xE7, /* Select window 7 */
50    UD0=0xE8, /* Define and select window 0 */
51    UD7=0xEF, /* Define and select window 7 */
52    UQU=0xF0, /* Quote a single Unicode character */
53    UDX=0xF1, /* Define a Window as extended */
54    Urs=0xF2  /* reserved */
55};
56
57enum {
58    /*
59     * Unicode code points from 3400 to E000 are not adressible by
60     * dynamic window, since in these areas no short run alphabets are
61     * found. Therefore add gapOffset to all values from gapThreshold.
62     */
63    gapThreshold=0x68,
64    gapOffset=0xAC00,
65
66    /* values between reservedStart and fixedThreshold are reserved */
67    reservedStart=0xA8,
68
69    /* use table of predefined fixed offsets for values from fixedThreshold */
70    fixedThreshold=0xF9
71};
72
73/* constant offsets for the 8 static windows */
74static const uint32_t staticOffsets[8]={
75    0x0000, /* ASCII for quoted tags */
76    0x0080, /* Latin - 1 Supplement (for access to punctuation) */
77    0x0100, /* Latin Extended-A */
78    0x0300, /* Combining Diacritical Marks */
79    0x2000, /* General Punctuation */
80    0x2080, /* Currency Symbols */
81    0x2100, /* Letterlike Symbols and Number Forms */
82    0x3000  /* CJK Symbols and punctuation */
83};
84
85/* initial offsets for the 8 dynamic (sliding) windows */
86static const uint32_t initialDynamicOffsets[8]={
87    0x0080, /* Latin-1 */
88    0x00C0, /* Latin Extended A */
89    0x0400, /* Cyrillic */
90    0x0600, /* Arabic */
91    0x0900, /* Devanagari */
92    0x3040, /* Hiragana */
93    0x30A0, /* Katakana */
94    0xFF00  /* Fullwidth ASCII */
95};
96
97/* Table of fixed predefined Offsets */
98static const uint32_t fixedOffsets[]={
99    /* 0xF9 */ 0x00C0, /* Latin-1 Letters + half of Latin Extended A */
100    /* 0xFA */ 0x0250, /* IPA extensions */
101    /* 0xFB */ 0x0370, /* Greek */
102    /* 0xFC */ 0x0530, /* Armenian */
103    /* 0xFD */ 0x3040, /* Hiragana */
104    /* 0xFE */ 0x30A0, /* Katakana */
105    /* 0xFF */ 0xFF60  /* Halfwidth Katakana */
106};
107
108/* state values */
109enum {
110    readCommand,
111    quotePairOne,
112    quotePairTwo,
113    quoteOne,
114    definePairOne,
115    definePairTwo,
116    defineOne
117};
118
119typedef struct SCSUData {
120    /* dynamic window offsets, intitialize to default values from initialDynamicOffsets */
121    uint32_t toUDynamicOffsets[8];
122    uint32_t fromUDynamicOffsets[8];
123
124    /* state machine state - toUnicode */
125    UBool toUIsSingleByteMode;
126    uint8_t toUState;
127    int8_t toUQuoteWindow, toUDynamicWindow;
128    uint8_t toUByteOne;
129    uint8_t toUPadding[3];
130
131    /* state machine state - fromUnicode */
132    UBool fromUIsSingleByteMode;
133    int8_t fromUDynamicWindow;
134
135    /*
136     * windowUse[] keeps track of the use of the dynamic windows:
137     * At nextWindowUseIndex there is the least recently used window,
138     * and the following windows (in a wrapping manner) are more and more
139     * recently used.
140     * At nextWindowUseIndex-1 there is the most recently used window.
141     */
142    uint8_t locale;
143    int8_t nextWindowUseIndex;
144    int8_t windowUse[8];
145} SCSUData;
146
147static const int8_t initialWindowUse[8]={ 7, 0, 3, 2, 4, 5, 6, 1 };
148static const int8_t initialWindowUse_ja[8]={ 3, 2, 4, 1, 0, 7, 5, 6 };
149
150enum {
151    lGeneric, l_ja
152};
153
154/* SCSU setup functions ----------------------------------------------------- */
155
156static void
157_SCSUReset(UConverter *cnv, UConverterResetChoice choice) {
158    SCSUData *scsu=(SCSUData *)cnv->extraInfo;
159
160    if(choice<=UCNV_RESET_TO_UNICODE) {
161        /* reset toUnicode */
162        uprv_memcpy(scsu->toUDynamicOffsets, initialDynamicOffsets, 32);
163
164        scsu->toUIsSingleByteMode=TRUE;
165        scsu->toUState=readCommand;
166        scsu->toUQuoteWindow=scsu->toUDynamicWindow=0;
167        scsu->toUByteOne=0;
168
169        cnv->toULength=0;
170    }
171    if(choice!=UCNV_RESET_TO_UNICODE) {
172        /* reset fromUnicode */
173        uprv_memcpy(scsu->fromUDynamicOffsets, initialDynamicOffsets, 32);
174
175        scsu->fromUIsSingleByteMode=TRUE;
176        scsu->fromUDynamicWindow=0;
177
178        scsu->nextWindowUseIndex=0;
179        switch(scsu->locale) {
180        case l_ja:
181            uprv_memcpy(scsu->windowUse, initialWindowUse_ja, 8);
182            break;
183        default:
184            uprv_memcpy(scsu->windowUse, initialWindowUse, 8);
185            break;
186        }
187
188        cnv->fromUChar32=0;
189    }
190}
191
192static void
193_SCSUOpen(UConverter *cnv,
194          UConverterLoadArgs *pArgs,
195          UErrorCode *pErrorCode) {
196    const char *locale=pArgs->locale;
197    if(pArgs->onlyTestIsLoadable) {
198        return;
199    }
200    cnv->extraInfo=uprv_malloc(sizeof(SCSUData));
201    if(cnv->extraInfo!=NULL) {
202        if(locale!=NULL && locale[0]=='j' && locale[1]=='a' && (locale[2]==0 || locale[2]=='_')) {
203            ((SCSUData *)cnv->extraInfo)->locale=l_ja;
204        } else {
205            ((SCSUData *)cnv->extraInfo)->locale=lGeneric;
206        }
207        _SCSUReset(cnv, UCNV_RESET_BOTH);
208    } else {
209        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
210    }
211
212    /* Set the substitution character U+fffd as a Unicode string. */
213    cnv->subUChars[0]=0xfffd;
214    cnv->subCharLen=-1;
215}
216
217static void
218_SCSUClose(UConverter *cnv) {
219    if(cnv->extraInfo!=NULL) {
220        if(!cnv->isExtraLocal) {
221            uprv_free(cnv->extraInfo);
222        }
223        cnv->extraInfo=NULL;
224    }
225}
226
227/* SCSU-to-Unicode conversion functions ------------------------------------- */
228
229static void
230_SCSUToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs,
231                          UErrorCode *pErrorCode) {
232    UConverter *cnv;
233    SCSUData *scsu;
234    const uint8_t *source, *sourceLimit;
235    UChar *target;
236    const UChar *targetLimit;
237    int32_t *offsets;
238    UBool isSingleByteMode;
239    uint8_t state, byteOne;
240    int8_t quoteWindow, dynamicWindow;
241
242    int32_t sourceIndex, nextSourceIndex;
243
244    uint8_t b;
245
246    /* set up the local pointers */
247    cnv=pArgs->converter;
248    scsu=(SCSUData *)cnv->extraInfo;
249
250    source=(const uint8_t *)pArgs->source;
251    sourceLimit=(const uint8_t *)pArgs->sourceLimit;
252    target=pArgs->target;
253    targetLimit=pArgs->targetLimit;
254    offsets=pArgs->offsets;
255
256    /* get the state machine state */
257    isSingleByteMode=scsu->toUIsSingleByteMode;
258    state=scsu->toUState;
259    quoteWindow=scsu->toUQuoteWindow;
260    dynamicWindow=scsu->toUDynamicWindow;
261    byteOne=scsu->toUByteOne;
262
263    /* sourceIndex=-1 if the current character began in the previous buffer */
264    sourceIndex=state==readCommand ? 0 : -1;
265    nextSourceIndex=0;
266
267    /*
268     * conversion "loop"
269     *
270     * For performance, this is not a normal C loop.
271     * Instead, there are two code blocks for the two SCSU modes.
272     * The function branches to either one, and a change of the mode is done with a goto to
273     * the other branch.
274     *
275     * Each branch has two conventional loops:
276     * - a fast-path loop for the most common codes in the mode
277     * - a loop for all other codes in the mode
278     * When the fast-path runs into a code that it cannot handle, its loop ends and it
279     * runs into the following loop to handle the other codes.
280     * The end of the input or output buffer is also handled by the slower loop.
281     * The slow loop jumps (goto) to the fast-path loop again as soon as possible.
282     *
283     * The callback handling is done by returning with an error code.
284     * The conversion framework actually calls the callback function.
285     */
286    if(isSingleByteMode) {
287        /* fast path for single-byte mode */
288        if(state==readCommand) {
289fastSingle:
290            while(source<sourceLimit && target<targetLimit && (b=*source)>=0x20) {
291                ++source;
292                ++nextSourceIndex;
293                if(b<=0x7f) {
294                    /* write US-ASCII graphic character or DEL */
295                    *target++=(UChar)b;
296                    if(offsets!=NULL) {
297                        *offsets++=sourceIndex;
298                    }
299                } else {
300                    /* write from dynamic window */
301                    uint32_t c=scsu->toUDynamicOffsets[dynamicWindow]+(b&0x7f);
302                    if(c<=0xffff) {
303                        *target++=(UChar)c;
304                        if(offsets!=NULL) {
305                            *offsets++=sourceIndex;
306                        }
307                    } else {
308                        /* output surrogate pair */
309                        *target++=(UChar)(0xd7c0+(c>>10));
310                        if(target<targetLimit) {
311                            *target++=(UChar)(0xdc00|(c&0x3ff));
312                            if(offsets!=NULL) {
313                                *offsets++=sourceIndex;
314                                *offsets++=sourceIndex;
315                            }
316                        } else {
317                            /* target overflow */
318                            if(offsets!=NULL) {
319                                *offsets++=sourceIndex;
320                            }
321                            cnv->UCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff));
322                            cnv->UCharErrorBufferLength=1;
323                            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
324                            goto endloop;
325                        }
326                    }
327                }
328                sourceIndex=nextSourceIndex;
329            }
330        }
331
332        /* normal state machine for single-byte mode, minus handling for what fastSingle covers */
333singleByteMode:
334        while(source<sourceLimit) {
335            if(target>=targetLimit) {
336                /* target is full */
337                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
338                break;
339            }
340            b=*source++;
341            ++nextSourceIndex;
342            switch(state) {
343            case readCommand:
344                /* redundant conditions are commented out */
345                /* here: b<0x20 because otherwise we would be in fastSingle */
346                if((1UL<<b)&0x2601 /* binary 0010 0110 0000 0001, check for b==0xd || b==0xa || b==9 || b==0 */) {
347                    /* CR/LF/TAB/NUL */
348                    *target++=(UChar)b;
349                    if(offsets!=NULL) {
350                        *offsets++=sourceIndex;
351                    }
352                    sourceIndex=nextSourceIndex;
353                    goto fastSingle;
354                } else if(SC0<=b) {
355                    if(b<=SC7) {
356                        dynamicWindow=(int8_t)(b-SC0);
357                        sourceIndex=nextSourceIndex;
358                        goto fastSingle;
359                    } else /* if(SD0<=b && b<=SD7) */ {
360                        dynamicWindow=(int8_t)(b-SD0);
361                        state=defineOne;
362                    }
363                } else if(/* SQ0<=b && */ b<=SQ7) {
364                    quoteWindow=(int8_t)(b-SQ0);
365                    state=quoteOne;
366                } else if(b==SDX) {
367                    state=definePairOne;
368                } else if(b==SQU) {
369                    state=quotePairOne;
370                } else if(b==SCU) {
371                    sourceIndex=nextSourceIndex;
372                    isSingleByteMode=FALSE;
373                    goto fastUnicode;
374                } else /* Srs */ {
375                    /* callback(illegal) */
376                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
377                    cnv->toUBytes[0]=b;
378                    cnv->toULength=1;
379                    goto endloop;
380                }
381
382                /* store the first byte of a multibyte sequence in toUBytes[] */
383                cnv->toUBytes[0]=b;
384                cnv->toULength=1;
385                break;
386            case quotePairOne:
387                byteOne=b;
388                cnv->toUBytes[1]=b;
389                cnv->toULength=2;
390                state=quotePairTwo;
391                break;
392            case quotePairTwo:
393                *target++=(UChar)((byteOne<<8)|b);
394                if(offsets!=NULL) {
395                    *offsets++=sourceIndex;
396                }
397                sourceIndex=nextSourceIndex;
398                state=readCommand;
399                goto fastSingle;
400            case quoteOne:
401                if(b<0x80) {
402                    /* all static offsets are in the BMP */
403                    *target++=(UChar)(staticOffsets[quoteWindow]+b);
404                    if(offsets!=NULL) {
405                        *offsets++=sourceIndex;
406                    }
407                } else {
408                    /* write from dynamic window */
409                    uint32_t c=scsu->toUDynamicOffsets[quoteWindow]+(b&0x7f);
410                    if(c<=0xffff) {
411                        *target++=(UChar)c;
412                        if(offsets!=NULL) {
413                            *offsets++=sourceIndex;
414                        }
415                    } else {
416                        /* output surrogate pair */
417                        *target++=(UChar)(0xd7c0+(c>>10));
418                        if(target<targetLimit) {
419                            *target++=(UChar)(0xdc00|(c&0x3ff));
420                            if(offsets!=NULL) {
421                                *offsets++=sourceIndex;
422                                *offsets++=sourceIndex;
423                            }
424                        } else {
425                            /* target overflow */
426                            if(offsets!=NULL) {
427                                *offsets++=sourceIndex;
428                            }
429                            cnv->UCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff));
430                            cnv->UCharErrorBufferLength=1;
431                            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
432                            goto endloop;
433                        }
434                    }
435                }
436                sourceIndex=nextSourceIndex;
437                state=readCommand;
438                goto fastSingle;
439            case definePairOne:
440                dynamicWindow=(int8_t)((b>>5)&7);
441                byteOne=(uint8_t)(b&0x1f);
442                cnv->toUBytes[1]=b;
443                cnv->toULength=2;
444                state=definePairTwo;
445                break;
446            case definePairTwo:
447                scsu->toUDynamicOffsets[dynamicWindow]=0x10000+(byteOne<<15UL | b<<7UL);
448                sourceIndex=nextSourceIndex;
449                state=readCommand;
450                goto fastSingle;
451            case defineOne:
452                if(b==0) {
453                    /* callback(illegal): Reserved window offset value 0 */
454                    cnv->toUBytes[1]=b;
455                    cnv->toULength=2;
456                    goto endloop;
457                } else if(b<gapThreshold) {
458                    scsu->toUDynamicOffsets[dynamicWindow]=b<<7UL;
459                } else if((uint8_t)(b-gapThreshold)<(reservedStart-gapThreshold)) {
460                    scsu->toUDynamicOffsets[dynamicWindow]=(b<<7UL)+gapOffset;
461                } else if(b>=fixedThreshold) {
462                    scsu->toUDynamicOffsets[dynamicWindow]=fixedOffsets[b-fixedThreshold];
463                } else {
464                    /* callback(illegal): Reserved window offset value 0xa8..0xf8 */
465                    cnv->toUBytes[1]=b;
466                    cnv->toULength=2;
467                    goto endloop;
468                }
469                sourceIndex=nextSourceIndex;
470                state=readCommand;
471                goto fastSingle;
472            }
473        }
474    } else {
475        /* fast path for Unicode mode */
476        if(state==readCommand) {
477fastUnicode:
478            while(source+1<sourceLimit && target<targetLimit && (uint8_t)((b=*source)-UC0)>(Urs-UC0)) {
479                *target++=(UChar)((b<<8)|source[1]);
480                if(offsets!=NULL) {
481                    *offsets++=sourceIndex;
482                }
483                sourceIndex=nextSourceIndex;
484                nextSourceIndex+=2;
485                source+=2;
486            }
487        }
488
489        /* normal state machine for Unicode mode */
490/* unicodeByteMode: */
491        while(source<sourceLimit) {
492            if(target>=targetLimit) {
493                /* target is full */
494                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
495                break;
496            }
497            b=*source++;
498            ++nextSourceIndex;
499            switch(state) {
500            case readCommand:
501                if((uint8_t)(b-UC0)>(Urs-UC0)) {
502                    byteOne=b;
503                    cnv->toUBytes[0]=b;
504                    cnv->toULength=1;
505                    state=quotePairTwo;
506                } else if(/* UC0<=b && */ b<=UC7) {
507                    dynamicWindow=(int8_t)(b-UC0);
508                    sourceIndex=nextSourceIndex;
509                    isSingleByteMode=TRUE;
510                    goto fastSingle;
511                } else if(/* UD0<=b && */ b<=UD7) {
512                    dynamicWindow=(int8_t)(b-UD0);
513                    isSingleByteMode=TRUE;
514                    cnv->toUBytes[0]=b;
515                    cnv->toULength=1;
516                    state=defineOne;
517                    goto singleByteMode;
518                } else if(b==UDX) {
519                    isSingleByteMode=TRUE;
520                    cnv->toUBytes[0]=b;
521                    cnv->toULength=1;
522                    state=definePairOne;
523                    goto singleByteMode;
524                } else if(b==UQU) {
525                    cnv->toUBytes[0]=b;
526                    cnv->toULength=1;
527                    state=quotePairOne;
528                } else /* Urs */ {
529                    /* callback(illegal) */
530                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
531                    cnv->toUBytes[0]=b;
532                    cnv->toULength=1;
533                    goto endloop;
534                }
535                break;
536            case quotePairOne:
537                byteOne=b;
538                cnv->toUBytes[1]=b;
539                cnv->toULength=2;
540                state=quotePairTwo;
541                break;
542            case quotePairTwo:
543                *target++=(UChar)((byteOne<<8)|b);
544                if(offsets!=NULL) {
545                    *offsets++=sourceIndex;
546                }
547                sourceIndex=nextSourceIndex;
548                state=readCommand;
549                goto fastUnicode;
550            }
551        }
552    }
553endloop:
554
555    /* set the converter state back into UConverter */
556    if(U_FAILURE(*pErrorCode) && *pErrorCode!=U_BUFFER_OVERFLOW_ERROR) {
557        /* reset to deal with the next character */
558        state=readCommand;
559    } else if(state==readCommand) {
560        /* not in a multi-byte sequence, reset toULength */
561        cnv->toULength=0;
562    }
563    scsu->toUIsSingleByteMode=isSingleByteMode;
564    scsu->toUState=state;
565    scsu->toUQuoteWindow=quoteWindow;
566    scsu->toUDynamicWindow=dynamicWindow;
567    scsu->toUByteOne=byteOne;
568
569    /* write back the updated pointers */
570    pArgs->source=(const char *)source;
571    pArgs->target=target;
572    pArgs->offsets=offsets;
573    return;
574}
575
576/*
577 * Identical to _SCSUToUnicodeWithOffsets but without offset handling.
578 * If a change is made in the original function, then either
579 * change this function the same way or
580 * re-copy the original function and remove the variables
581 * offsets, sourceIndex, and nextSourceIndex.
582 */
583static void
584_SCSUToUnicode(UConverterToUnicodeArgs *pArgs,
585               UErrorCode *pErrorCode) {
586    UConverter *cnv;
587    SCSUData *scsu;
588    const uint8_t *source, *sourceLimit;
589    UChar *target;
590    const UChar *targetLimit;
591    UBool isSingleByteMode;
592    uint8_t state, byteOne;
593    int8_t quoteWindow, dynamicWindow;
594
595    uint8_t b;
596
597    /* set up the local pointers */
598    cnv=pArgs->converter;
599    scsu=(SCSUData *)cnv->extraInfo;
600
601    source=(const uint8_t *)pArgs->source;
602    sourceLimit=(const uint8_t *)pArgs->sourceLimit;
603    target=pArgs->target;
604    targetLimit=pArgs->targetLimit;
605
606    /* get the state machine state */
607    isSingleByteMode=scsu->toUIsSingleByteMode;
608    state=scsu->toUState;
609    quoteWindow=scsu->toUQuoteWindow;
610    dynamicWindow=scsu->toUDynamicWindow;
611    byteOne=scsu->toUByteOne;
612
613    /*
614     * conversion "loop"
615     *
616     * For performance, this is not a normal C loop.
617     * Instead, there are two code blocks for the two SCSU modes.
618     * The function branches to either one, and a change of the mode is done with a goto to
619     * the other branch.
620     *
621     * Each branch has two conventional loops:
622     * - a fast-path loop for the most common codes in the mode
623     * - a loop for all other codes in the mode
624     * When the fast-path runs into a code that it cannot handle, its loop ends and it
625     * runs into the following loop to handle the other codes.
626     * The end of the input or output buffer is also handled by the slower loop.
627     * The slow loop jumps (goto) to the fast-path loop again as soon as possible.
628     *
629     * The callback handling is done by returning with an error code.
630     * The conversion framework actually calls the callback function.
631     */
632    if(isSingleByteMode) {
633        /* fast path for single-byte mode */
634        if(state==readCommand) {
635fastSingle:
636            while(source<sourceLimit && target<targetLimit && (b=*source)>=0x20) {
637                ++source;
638                if(b<=0x7f) {
639                    /* write US-ASCII graphic character or DEL */
640                    *target++=(UChar)b;
641                } else {
642                    /* write from dynamic window */
643                    uint32_t c=scsu->toUDynamicOffsets[dynamicWindow]+(b&0x7f);
644                    if(c<=0xffff) {
645                        *target++=(UChar)c;
646                    } else {
647                        /* output surrogate pair */
648                        *target++=(UChar)(0xd7c0+(c>>10));
649                        if(target<targetLimit) {
650                            *target++=(UChar)(0xdc00|(c&0x3ff));
651                        } else {
652                            /* target overflow */
653                            cnv->UCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff));
654                            cnv->UCharErrorBufferLength=1;
655                            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
656                            goto endloop;
657                        }
658                    }
659                }
660            }
661        }
662
663        /* normal state machine for single-byte mode, minus handling for what fastSingle covers */
664singleByteMode:
665        while(source<sourceLimit) {
666            if(target>=targetLimit) {
667                /* target is full */
668                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
669                break;
670            }
671            b=*source++;
672            switch(state) {
673            case readCommand:
674                /* redundant conditions are commented out */
675                /* here: b<0x20 because otherwise we would be in fastSingle */
676                if((1UL<<b)&0x2601 /* binary 0010 0110 0000 0001, check for b==0xd || b==0xa || b==9 || b==0 */) {
677                    /* CR/LF/TAB/NUL */
678                    *target++=(UChar)b;
679                    goto fastSingle;
680                } else if(SC0<=b) {
681                    if(b<=SC7) {
682                        dynamicWindow=(int8_t)(b-SC0);
683                        goto fastSingle;
684                    } else /* if(SD0<=b && b<=SD7) */ {
685                        dynamicWindow=(int8_t)(b-SD0);
686                        state=defineOne;
687                    }
688                } else if(/* SQ0<=b && */ b<=SQ7) {
689                    quoteWindow=(int8_t)(b-SQ0);
690                    state=quoteOne;
691                } else if(b==SDX) {
692                    state=definePairOne;
693                } else if(b==SQU) {
694                    state=quotePairOne;
695                } else if(b==SCU) {
696                    isSingleByteMode=FALSE;
697                    goto fastUnicode;
698                } else /* Srs */ {
699                    /* callback(illegal) */
700                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
701                    cnv->toUBytes[0]=b;
702                    cnv->toULength=1;
703                    goto endloop;
704                }
705
706                /* store the first byte of a multibyte sequence in toUBytes[] */
707                cnv->toUBytes[0]=b;
708                cnv->toULength=1;
709                break;
710            case quotePairOne:
711                byteOne=b;
712                cnv->toUBytes[1]=b;
713                cnv->toULength=2;
714                state=quotePairTwo;
715                break;
716            case quotePairTwo:
717                *target++=(UChar)((byteOne<<8)|b);
718                state=readCommand;
719                goto fastSingle;
720            case quoteOne:
721                if(b<0x80) {
722                    /* all static offsets are in the BMP */
723                    *target++=(UChar)(staticOffsets[quoteWindow]+b);
724                } else {
725                    /* write from dynamic window */
726                    uint32_t c=scsu->toUDynamicOffsets[quoteWindow]+(b&0x7f);
727                    if(c<=0xffff) {
728                        *target++=(UChar)c;
729                    } else {
730                        /* output surrogate pair */
731                        *target++=(UChar)(0xd7c0+(c>>10));
732                        if(target<targetLimit) {
733                            *target++=(UChar)(0xdc00|(c&0x3ff));
734                        } else {
735                            /* target overflow */
736                            cnv->UCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff));
737                            cnv->UCharErrorBufferLength=1;
738                            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
739                            goto endloop;
740                        }
741                    }
742                }
743                state=readCommand;
744                goto fastSingle;
745            case definePairOne:
746                dynamicWindow=(int8_t)((b>>5)&7);
747                byteOne=(uint8_t)(b&0x1f);
748                cnv->toUBytes[1]=b;
749                cnv->toULength=2;
750                state=definePairTwo;
751                break;
752            case definePairTwo:
753                scsu->toUDynamicOffsets[dynamicWindow]=0x10000+(byteOne<<15UL | b<<7UL);
754                state=readCommand;
755                goto fastSingle;
756            case defineOne:
757                if(b==0) {
758                    /* callback(illegal): Reserved window offset value 0 */
759                    cnv->toUBytes[1]=b;
760                    cnv->toULength=2;
761                    goto endloop;
762                } else if(b<gapThreshold) {
763                    scsu->toUDynamicOffsets[dynamicWindow]=b<<7UL;
764                } else if((uint8_t)(b-gapThreshold)<(reservedStart-gapThreshold)) {
765                    scsu->toUDynamicOffsets[dynamicWindow]=(b<<7UL)+gapOffset;
766                } else if(b>=fixedThreshold) {
767                    scsu->toUDynamicOffsets[dynamicWindow]=fixedOffsets[b-fixedThreshold];
768                } else {
769                    /* callback(illegal): Reserved window offset value 0xa8..0xf8 */
770                    cnv->toUBytes[1]=b;
771                    cnv->toULength=2;
772                    goto endloop;
773                }
774                state=readCommand;
775                goto fastSingle;
776            }
777        }
778    } else {
779        /* fast path for Unicode mode */
780        if(state==readCommand) {
781fastUnicode:
782            while(source+1<sourceLimit && target<targetLimit && (uint8_t)((b=*source)-UC0)>(Urs-UC0)) {
783                *target++=(UChar)((b<<8)|source[1]);
784                source+=2;
785            }
786        }
787
788        /* normal state machine for Unicode mode */
789/* unicodeByteMode: */
790        while(source<sourceLimit) {
791            if(target>=targetLimit) {
792                /* target is full */
793                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
794                break;
795            }
796            b=*source++;
797            switch(state) {
798            case readCommand:
799                if((uint8_t)(b-UC0)>(Urs-UC0)) {
800                    byteOne=b;
801                    cnv->toUBytes[0]=b;
802                    cnv->toULength=1;
803                    state=quotePairTwo;
804                } else if(/* UC0<=b && */ b<=UC7) {
805                    dynamicWindow=(int8_t)(b-UC0);
806                    isSingleByteMode=TRUE;
807                    goto fastSingle;
808                } else if(/* UD0<=b && */ b<=UD7) {
809                    dynamicWindow=(int8_t)(b-UD0);
810                    isSingleByteMode=TRUE;
811                    cnv->toUBytes[0]=b;
812                    cnv->toULength=1;
813                    state=defineOne;
814                    goto singleByteMode;
815                } else if(b==UDX) {
816                    isSingleByteMode=TRUE;
817                    cnv->toUBytes[0]=b;
818                    cnv->toULength=1;
819                    state=definePairOne;
820                    goto singleByteMode;
821                } else if(b==UQU) {
822                    cnv->toUBytes[0]=b;
823                    cnv->toULength=1;
824                    state=quotePairOne;
825                } else /* Urs */ {
826                    /* callback(illegal) */
827                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
828                    cnv->toUBytes[0]=b;
829                    cnv->toULength=1;
830                    goto endloop;
831                }
832                break;
833            case quotePairOne:
834                byteOne=b;
835                cnv->toUBytes[1]=b;
836                cnv->toULength=2;
837                state=quotePairTwo;
838                break;
839            case quotePairTwo:
840                *target++=(UChar)((byteOne<<8)|b);
841                state=readCommand;
842                goto fastUnicode;
843            }
844        }
845    }
846endloop:
847
848    /* set the converter state back into UConverter */
849    if(U_FAILURE(*pErrorCode) && *pErrorCode!=U_BUFFER_OVERFLOW_ERROR) {
850        /* reset to deal with the next character */
851        state=readCommand;
852    } else if(state==readCommand) {
853        /* not in a multi-byte sequence, reset toULength */
854        cnv->toULength=0;
855    }
856    scsu->toUIsSingleByteMode=isSingleByteMode;
857    scsu->toUState=state;
858    scsu->toUQuoteWindow=quoteWindow;
859    scsu->toUDynamicWindow=dynamicWindow;
860    scsu->toUByteOne=byteOne;
861
862    /* write back the updated pointers */
863    pArgs->source=(const char *)source;
864    pArgs->target=target;
865    return;
866}
867
868/* SCSU-from-Unicode conversion functions ----------------------------------- */
869
870/*
871 * This SCSU Encoder is fairly simple but uses all SCSU commands to achieve
872 * reasonable results. The lookahead is minimal.
873 * Many cases are simple:
874 * A character fits directly into the current mode, a dynamic or static window,
875 * or is not compressible. These cases are tested first.
876 * Real compression heuristics are applied to the rest, in code branches for
877 * single/Unicode mode and BMP/supplementary code points.
878 * The heuristics used here are extremely simple.
879 */
880
881/* get the number of the window that this character is in, or -1 */
882static int8_t
883getWindow(const uint32_t offsets[8], uint32_t c) {
884    int i;
885    for(i=0; i<8; ++i) {
886        if((uint32_t)(c-offsets[i])<=0x7f) {
887            return (int8_t)(i);
888        }
889    }
890    return -1;
891}
892
893/* is the character in the dynamic window starting at the offset, or in the direct-encoded range? */
894static UBool
895isInOffsetWindowOrDirect(uint32_t offset, uint32_t c) {
896    return (UBool)(c<=offset+0x7f &&
897          (c>=offset || (c<=0x7f &&
898                        (c>=0x20 || (1UL<<c)&0x2601))));
899                                /* binary 0010 0110 0000 0001,
900                                   check for b==0xd || b==0xa || b==9 || b==0 */
901}
902
903/*
904 * getNextDynamicWindow returns the next dynamic window to be redefined
905 */
906static int8_t
907getNextDynamicWindow(SCSUData *scsu) {
908    int8_t window=scsu->windowUse[scsu->nextWindowUseIndex];
909    if(++scsu->nextWindowUseIndex==8) {
910        scsu->nextWindowUseIndex=0;
911    }
912    return window;
913}
914
915/*
916 * useDynamicWindow() adjusts
917 * windowUse[] and nextWindowUseIndex for the algorithm to choose
918 * the next dynamic window to be defined;
919 * a subclass may override it and provide its own algorithm.
920 */
921static void
922useDynamicWindow(SCSUData *scsu, int8_t window) {
923    /*
924     * move the existing window, which just became the most recently used one,
925     * up in windowUse[] to nextWindowUseIndex-1
926     */
927
928    /* first, find the index of the window - backwards to favor the more recently used windows */
929    int i, j;
930
931    i=scsu->nextWindowUseIndex;
932    do {
933        if(--i<0) {
934            i=7;
935        }
936    } while(scsu->windowUse[i]!=window);
937
938    /* now copy each windowUse[i+1] to [i] */
939    j=i+1;
940    if(j==8) {
941        j=0;
942    }
943    while(j!=scsu->nextWindowUseIndex) {
944        scsu->windowUse[i]=scsu->windowUse[j];
945        i=j;
946        if(++j==8) { j=0; }
947    }
948
949    /* finally, set the window into the most recently used index */
950    scsu->windowUse[i]=window;
951}
952
953/*
954 * calculate the offset and the code for a dynamic window that contains the character
955 * takes fixed offsets into account
956 * the offset of the window is stored in the offset variable,
957 * the code is returned
958 *
959 * return offset code: -1 none  <=0xff code for SDn/UDn  else code for SDX/UDX, subtract 0x200 to get the true code
960 */
961static int
962getDynamicOffset(uint32_t c, uint32_t *pOffset) {
963    int i;
964
965    for(i=0; i<7; ++i) {
966        if((uint32_t)(c-fixedOffsets[i])<=0x7f) {
967            *pOffset=fixedOffsets[i];
968            return 0xf9+i;
969        }
970    }
971
972    if(c<0x80) {
973        /* No dynamic window for US-ASCII. */
974        return -1;
975    } else if(c<0x3400 ||
976              (uint32_t)(c-0x10000)<(0x14000-0x10000) ||
977              (uint32_t)(c-0x1d000)<=(0x1ffff-0x1d000)
978    ) {
979        /* This character is in a code range for a "small", i.e., reasonably windowable, script. */
980        *pOffset=c&0x7fffff80;
981        return (int)(c>>7);
982    } else if(0xe000<=c && c!=0xfeff && c<0xfff0) {
983        /* For these characters we need to take the gapOffset into account. */
984        *pOffset=c&0x7fffff80;
985        return (int)((c-gapOffset)>>7);
986    } else {
987        return -1;
988    }
989}
990
991/*
992 * Idea for compression:
993 *  - save SCSUData and other state before really starting work
994 *  - at endloop, see if compression could be better with just unicode mode
995 *  - don't do this if a callback has been called
996 *  - if unicode mode would be smaller, then override the results with it - may need SCU at the beginning
997 *  - different buffer handling!
998 *
999 * Drawback or need for corrective handling:
1000 * it is desirable to encode U+feff as SQU fe ff for the SCSU signature, and
1001 * it is desirable to start a document in US-ASCII/Latin-1 for as long as possible
1002 * not only for compression but also for HTML/XML documents with following charset/encoding announcers.
1003 *
1004 * How to achieve both?
1005 *  - Only replace the result after an SDX or SCU?
1006 */
1007
1008static void
1009_SCSUFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs,
1010                            UErrorCode *pErrorCode) {
1011    UConverter *cnv;
1012    SCSUData *scsu;
1013    const UChar *source, *sourceLimit;
1014    uint8_t *target;
1015    int32_t targetCapacity;
1016    int32_t *offsets;
1017
1018    UBool isSingleByteMode;
1019    uint8_t dynamicWindow;
1020    uint32_t currentOffset;
1021
1022    uint32_t c, delta;
1023
1024    int32_t sourceIndex, nextSourceIndex;
1025
1026    int32_t length;
1027
1028    /* variables for compression heuristics */
1029    uint32_t offset;
1030    UChar lead, trail;
1031    int code;
1032    int8_t window;
1033
1034    /* set up the local pointers */
1035    cnv=pArgs->converter;
1036    scsu=(SCSUData *)cnv->extraInfo;
1037
1038    /* set up the local pointers */
1039    source=pArgs->source;
1040    sourceLimit=pArgs->sourceLimit;
1041    target=(uint8_t *)pArgs->target;
1042    targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target);
1043    offsets=pArgs->offsets;
1044
1045    /* get the state machine state */
1046    isSingleByteMode=scsu->fromUIsSingleByteMode;
1047    dynamicWindow=scsu->fromUDynamicWindow;
1048    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1049
1050    c=cnv->fromUChar32;
1051
1052    /* sourceIndex=-1 if the current character began in the previous buffer */
1053    sourceIndex= c==0 ? 0 : -1;
1054    nextSourceIndex=0;
1055
1056    /* similar conversion "loop" as in toUnicode */
1057loop:
1058    if(isSingleByteMode) {
1059        if(c!=0 && targetCapacity>0) {
1060            goto getTrailSingle;
1061        }
1062
1063        /* state machine for single-byte mode */
1064/* singleByteMode: */
1065        while(source<sourceLimit) {
1066            if(targetCapacity<=0) {
1067                /* target is full */
1068                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1069                break;
1070            }
1071            c=*source++;
1072            ++nextSourceIndex;
1073
1074            if((c-0x20)<=0x5f) {
1075                /* pass US-ASCII graphic character through */
1076                *target++=(uint8_t)c;
1077                if(offsets!=NULL) {
1078                    *offsets++=sourceIndex;
1079                }
1080                --targetCapacity;
1081            } else if(c<0x20) {
1082                if((1UL<<c)&0x2601 /* binary 0010 0110 0000 0001, check for b==0xd || b==0xa || b==9 || b==0 */) {
1083                    /* CR/LF/TAB/NUL */
1084                    *target++=(uint8_t)c;
1085                    if(offsets!=NULL) {
1086                        *offsets++=sourceIndex;
1087                    }
1088                    --targetCapacity;
1089                } else {
1090                    /* quote C0 control character */
1091                    c|=SQ0<<8;
1092                    length=2;
1093                    goto outputBytes;
1094                }
1095            } else if((delta=c-currentOffset)<=0x7f) {
1096                /* use the current dynamic window */
1097                *target++=(uint8_t)(delta|0x80);
1098                if(offsets!=NULL) {
1099                    *offsets++=sourceIndex;
1100                }
1101                --targetCapacity;
1102            } else if(U16_IS_SURROGATE(c)) {
1103                if(U16_IS_SURROGATE_LEAD(c)) {
1104getTrailSingle:
1105                    lead=(UChar)c;
1106                    if(source<sourceLimit) {
1107                        /* test the following code unit */
1108                        trail=*source;
1109                        if(U16_IS_TRAIL(trail)) {
1110                            ++source;
1111                            ++nextSourceIndex;
1112                            c=U16_GET_SUPPLEMENTARY(c, trail);
1113                            /* convert this surrogate code point */
1114                            /* exit this condition tree */
1115                        } else {
1116                            /* this is an unmatched lead code unit (1st surrogate) */
1117                            /* callback(illegal) */
1118                            *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1119                            goto endloop;
1120                        }
1121                    } else {
1122                        /* no more input */
1123                        break;
1124                    }
1125                } else {
1126                    /* this is an unmatched trail code unit (2nd surrogate) */
1127                    /* callback(illegal) */
1128                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1129                    goto endloop;
1130                }
1131
1132                /* compress supplementary character U+10000..U+10ffff */
1133                if((delta=c-currentOffset)<=0x7f) {
1134                    /* use the current dynamic window */
1135                    *target++=(uint8_t)(delta|0x80);
1136                    if(offsets!=NULL) {
1137                        *offsets++=sourceIndex;
1138                    }
1139                    --targetCapacity;
1140                } else if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) {
1141                    /* there is a dynamic window that contains this character, change to it */
1142                    dynamicWindow=window;
1143                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1144                    useDynamicWindow(scsu, dynamicWindow);
1145                    c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1146                    length=2;
1147                    goto outputBytes;
1148                } else if((code=getDynamicOffset(c, &offset))>=0) {
1149                    /* might check if there are more characters in this window to come */
1150                    /* define an extended window with this character */
1151                    code-=0x200;
1152                    dynamicWindow=getNextDynamicWindow(scsu);
1153                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1154                    useDynamicWindow(scsu, dynamicWindow);
1155                    c=((uint32_t)SDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1156                    length=4;
1157                    goto outputBytes;
1158                } else {
1159                    /* change to Unicode mode and output this (lead, trail) pair */
1160                    isSingleByteMode=FALSE;
1161                    *target++=(uint8_t)SCU;
1162                    if(offsets!=NULL) {
1163                        *offsets++=sourceIndex;
1164                    }
1165                    --targetCapacity;
1166                    c=((uint32_t)lead<<16)|trail;
1167                    length=4;
1168                    goto outputBytes;
1169                }
1170            } else if(c<0xa0) {
1171                /* quote C1 control character */
1172                c=(c&0x7f)|(SQ0+1)<<8; /* SQ0+1==SQ1 */
1173                length=2;
1174                goto outputBytes;
1175            } else if(c==0xfeff || c>=0xfff0) {
1176                /* quote signature character=byte order mark and specials */
1177                c|=SQU<<16;
1178                length=3;
1179                goto outputBytes;
1180            } else {
1181                /* compress all other BMP characters */
1182                if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) {
1183                    /* there is a window defined that contains this character - switch to it or quote from it? */
1184                    if(source>=sourceLimit || isInOffsetWindowOrDirect(scsu->fromUDynamicOffsets[window], *source)) {
1185                        /* change to dynamic window */
1186                        dynamicWindow=window;
1187                        currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1188                        useDynamicWindow(scsu, dynamicWindow);
1189                        c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1190                        length=2;
1191                        goto outputBytes;
1192                    } else {
1193                        /* quote from dynamic window */
1194                        c=((uint32_t)(SQ0+window)<<8)|(c-scsu->fromUDynamicOffsets[window])|0x80;
1195                        length=2;
1196                        goto outputBytes;
1197                    }
1198                } else if((window=getWindow(staticOffsets, c))>=0) {
1199                    /* quote from static window */
1200                    c=((uint32_t)(SQ0+window)<<8)|(c-staticOffsets[window]);
1201                    length=2;
1202                    goto outputBytes;
1203                } else if((code=getDynamicOffset(c, &offset))>=0) {
1204                    /* define a dynamic window with this character */
1205                    dynamicWindow=getNextDynamicWindow(scsu);
1206                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1207                    useDynamicWindow(scsu, dynamicWindow);
1208                    c=((uint32_t)(SD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1209                    length=3;
1210                    goto outputBytes;
1211                } else if((uint32_t)(c-0x3400)<(0xd800-0x3400) &&
1212                          (source>=sourceLimit || (uint32_t)(*source-0x3400)<(0xd800-0x3400))
1213                ) {
1214                    /*
1215                     * this character is not compressible (a BMP ideograph or similar);
1216                     * switch to Unicode mode if this is the last character in the block
1217                     * or there is at least one more ideograph following immediately
1218                     */
1219                    isSingleByteMode=FALSE;
1220                    c|=SCU<<16;
1221                    length=3;
1222                    goto outputBytes;
1223                } else {
1224                    /* quote Unicode */
1225                    c|=SQU<<16;
1226                    length=3;
1227                    goto outputBytes;
1228                }
1229            }
1230
1231            /* normal end of conversion: prepare for a new character */
1232            c=0;
1233            sourceIndex=nextSourceIndex;
1234        }
1235    } else {
1236        if(c!=0 && targetCapacity>0) {
1237            goto getTrailUnicode;
1238        }
1239
1240        /* state machine for Unicode mode */
1241/* unicodeByteMode: */
1242        while(source<sourceLimit) {
1243            if(targetCapacity<=0) {
1244                /* target is full */
1245                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1246                break;
1247            }
1248            c=*source++;
1249            ++nextSourceIndex;
1250
1251            if((uint32_t)(c-0x3400)<(0xd800-0x3400)) {
1252                /* not compressible, write character directly */
1253                if(targetCapacity>=2) {
1254                    *target++=(uint8_t)(c>>8);
1255                    *target++=(uint8_t)c;
1256                    if(offsets!=NULL) {
1257                        *offsets++=sourceIndex;
1258                        *offsets++=sourceIndex;
1259                    }
1260                    targetCapacity-=2;
1261                } else {
1262                    length=2;
1263                    goto outputBytes;
1264                }
1265            } else if((uint32_t)(c-0x3400)>=(0xf300-0x3400) /* c<0x3400 || c>=0xf300 */) {
1266                /* compress BMP character if the following one is not an uncompressible ideograph */
1267                if(!(source<sourceLimit && (uint32_t)(*source-0x3400)<(0xd800-0x3400))) {
1268                    if(((uint32_t)(c-0x30)<10 || (uint32_t)(c-0x61)<26 || (uint32_t)(c-0x41)<26)) {
1269                        /* ASCII digit or letter */
1270                        isSingleByteMode=TRUE;
1271                        c|=((uint32_t)(UC0+dynamicWindow)<<8)|c;
1272                        length=2;
1273                        goto outputBytes;
1274                    } else if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) {
1275                        /* there is a dynamic window that contains this character, change to it */
1276                        isSingleByteMode=TRUE;
1277                        dynamicWindow=window;
1278                        currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1279                        useDynamicWindow(scsu, dynamicWindow);
1280                        c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1281                        length=2;
1282                        goto outputBytes;
1283                    } else if((code=getDynamicOffset(c, &offset))>=0) {
1284                        /* define a dynamic window with this character */
1285                        isSingleByteMode=TRUE;
1286                        dynamicWindow=getNextDynamicWindow(scsu);
1287                        currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1288                        useDynamicWindow(scsu, dynamicWindow);
1289                        c=((uint32_t)(UD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1290                        length=3;
1291                        goto outputBytes;
1292                    }
1293                }
1294
1295                /* don't know how to compress this character, just write it directly */
1296                length=2;
1297                goto outputBytes;
1298            } else if(c<0xe000) {
1299                /* c is a surrogate */
1300                if(U16_IS_SURROGATE_LEAD(c)) {
1301getTrailUnicode:
1302                    lead=(UChar)c;
1303                    if(source<sourceLimit) {
1304                        /* test the following code unit */
1305                        trail=*source;
1306                        if(U16_IS_TRAIL(trail)) {
1307                            ++source;
1308                            ++nextSourceIndex;
1309                            c=U16_GET_SUPPLEMENTARY(c, trail);
1310                            /* convert this surrogate code point */
1311                            /* exit this condition tree */
1312                        } else {
1313                            /* this is an unmatched lead code unit (1st surrogate) */
1314                            /* callback(illegal) */
1315                            *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1316                            goto endloop;
1317                        }
1318                    } else {
1319                        /* no more input */
1320                        break;
1321                    }
1322                } else {
1323                    /* this is an unmatched trail code unit (2nd surrogate) */
1324                    /* callback(illegal) */
1325                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1326                    goto endloop;
1327                }
1328
1329                /* compress supplementary character */
1330                if( (window=getWindow(scsu->fromUDynamicOffsets, c))>=0 &&
1331                    !(source<sourceLimit && (uint32_t)(*source-0x3400)<(0xd800-0x3400))
1332                ) {
1333                    /*
1334                     * there is a dynamic window that contains this character and
1335                     * the following character is not uncompressible,
1336                     * change to the window
1337                     */
1338                    isSingleByteMode=TRUE;
1339                    dynamicWindow=window;
1340                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1341                    useDynamicWindow(scsu, dynamicWindow);
1342                    c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1343                    length=2;
1344                    goto outputBytes;
1345                } else if(source<sourceLimit && lead==*source && /* too lazy to check trail in same window as source[1] */
1346                          (code=getDynamicOffset(c, &offset))>=0
1347                ) {
1348                    /* two supplementary characters in (probably) the same window - define an extended one */
1349                    isSingleByteMode=TRUE;
1350                    code-=0x200;
1351                    dynamicWindow=getNextDynamicWindow(scsu);
1352                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1353                    useDynamicWindow(scsu, dynamicWindow);
1354                    c=((uint32_t)UDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1355                    length=4;
1356                    goto outputBytes;
1357                } else {
1358                    /* don't know how to compress this character, just write it directly */
1359                    c=((uint32_t)lead<<16)|trail;
1360                    length=4;
1361                    goto outputBytes;
1362                }
1363            } else /* 0xe000<=c<0xf300 */ {
1364                /* quote to avoid SCSU tags */
1365                c|=UQU<<16;
1366                length=3;
1367                goto outputBytes;
1368            }
1369
1370            /* normal end of conversion: prepare for a new character */
1371            c=0;
1372            sourceIndex=nextSourceIndex;
1373        }
1374    }
1375endloop:
1376
1377    /* set the converter state back into UConverter */
1378    scsu->fromUIsSingleByteMode=isSingleByteMode;
1379    scsu->fromUDynamicWindow=dynamicWindow;
1380
1381    cnv->fromUChar32=c;
1382
1383    /* write back the updated pointers */
1384    pArgs->source=source;
1385    pArgs->target=(char *)target;
1386    pArgs->offsets=offsets;
1387    return;
1388
1389outputBytes:
1390    /* write the output character bytes from c and length [code copied from ucnvmbcs.c] */
1391    /* from the first if in the loop we know that targetCapacity>0 */
1392    if(length<=targetCapacity) {
1393        if(offsets==NULL) {
1394            switch(length) {
1395                /* each branch falls through to the next one */
1396            case 4:
1397                *target++=(uint8_t)(c>>24);
1398            case 3: /*fall through*/
1399                *target++=(uint8_t)(c>>16);
1400            case 2: /*fall through*/
1401                *target++=(uint8_t)(c>>8);
1402            case 1: /*fall through*/
1403                *target++=(uint8_t)c;
1404            default:
1405                /* will never occur */
1406                break;
1407            }
1408        } else {
1409            switch(length) {
1410                /* each branch falls through to the next one */
1411            case 4:
1412                *target++=(uint8_t)(c>>24);
1413                *offsets++=sourceIndex;
1414            case 3: /*fall through*/
1415                *target++=(uint8_t)(c>>16);
1416                *offsets++=sourceIndex;
1417            case 2: /*fall through*/
1418                *target++=(uint8_t)(c>>8);
1419                *offsets++=sourceIndex;
1420            case 1: /*fall through*/
1421                *target++=(uint8_t)c;
1422                *offsets++=sourceIndex;
1423            default:
1424                /* will never occur */
1425                break;
1426            }
1427        }
1428        targetCapacity-=length;
1429
1430        /* normal end of conversion: prepare for a new character */
1431        c=0;
1432        sourceIndex=nextSourceIndex;
1433        goto loop;
1434    } else {
1435        uint8_t *p;
1436
1437        /*
1438         * We actually do this backwards here:
1439         * In order to save an intermediate variable, we output
1440         * first to the overflow buffer what does not fit into the
1441         * regular target.
1442         */
1443        /* we know that 0<=targetCapacity<length<=4 */
1444        /* targetCapacity==0 when SCU+supplementary where SCU used up targetCapacity==1 */
1445        length-=targetCapacity;
1446        p=(uint8_t *)cnv->charErrorBuffer;
1447        switch(length) {
1448            /* each branch falls through to the next one */
1449        case 4:
1450            *p++=(uint8_t)(c>>24);
1451        case 3: /*fall through*/
1452            *p++=(uint8_t)(c>>16);
1453        case 2: /*fall through*/
1454            *p++=(uint8_t)(c>>8);
1455        case 1: /*fall through*/
1456            *p=(uint8_t)c;
1457        default:
1458            /* will never occur */
1459            break;
1460        }
1461        cnv->charErrorBufferLength=(int8_t)length;
1462
1463        /* now output what fits into the regular target */
1464        c>>=8*length; /* length was reduced by targetCapacity */
1465        switch(targetCapacity) {
1466            /* each branch falls through to the next one */
1467        case 3:
1468            *target++=(uint8_t)(c>>16);
1469            if(offsets!=NULL) {
1470                *offsets++=sourceIndex;
1471            }
1472        case 2: /*fall through*/
1473            *target++=(uint8_t)(c>>8);
1474            if(offsets!=NULL) {
1475                *offsets++=sourceIndex;
1476            }
1477        case 1: /*fall through*/
1478            *target++=(uint8_t)c;
1479            if(offsets!=NULL) {
1480                *offsets++=sourceIndex;
1481            }
1482        default:
1483            break;
1484        }
1485
1486        /* target overflow */
1487        targetCapacity=0;
1488        *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1489        c=0;
1490        goto endloop;
1491    }
1492}
1493
1494/*
1495 * Identical to _SCSUFromUnicodeWithOffsets but without offset handling.
1496 * If a change is made in the original function, then either
1497 * change this function the same way or
1498 * re-copy the original function and remove the variables
1499 * offsets, sourceIndex, and nextSourceIndex.
1500 */
1501static void
1502_SCSUFromUnicode(UConverterFromUnicodeArgs *pArgs,
1503                 UErrorCode *pErrorCode) {
1504    UConverter *cnv;
1505    SCSUData *scsu;
1506    const UChar *source, *sourceLimit;
1507    uint8_t *target;
1508    int32_t targetCapacity;
1509
1510    UBool isSingleByteMode;
1511    uint8_t dynamicWindow;
1512    uint32_t currentOffset;
1513
1514    uint32_t c, delta;
1515
1516    int32_t length;
1517
1518    /* variables for compression heuristics */
1519    uint32_t offset;
1520    UChar lead, trail;
1521    int code;
1522    int8_t window;
1523
1524    /* set up the local pointers */
1525    cnv=pArgs->converter;
1526    scsu=(SCSUData *)cnv->extraInfo;
1527
1528    /* set up the local pointers */
1529    source=pArgs->source;
1530    sourceLimit=pArgs->sourceLimit;
1531    target=(uint8_t *)pArgs->target;
1532    targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target);
1533
1534    /* get the state machine state */
1535    isSingleByteMode=scsu->fromUIsSingleByteMode;
1536    dynamicWindow=scsu->fromUDynamicWindow;
1537    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1538
1539    c=cnv->fromUChar32;
1540
1541    /* similar conversion "loop" as in toUnicode */
1542loop:
1543    if(isSingleByteMode) {
1544        if(c!=0 && targetCapacity>0) {
1545            goto getTrailSingle;
1546        }
1547
1548        /* state machine for single-byte mode */
1549/* singleByteMode: */
1550        while(source<sourceLimit) {
1551            if(targetCapacity<=0) {
1552                /* target is full */
1553                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1554                break;
1555            }
1556            c=*source++;
1557
1558            if((c-0x20)<=0x5f) {
1559                /* pass US-ASCII graphic character through */
1560                *target++=(uint8_t)c;
1561                --targetCapacity;
1562            } else if(c<0x20) {
1563                if((1UL<<c)&0x2601 /* binary 0010 0110 0000 0001, check for b==0xd || b==0xa || b==9 || b==0 */) {
1564                    /* CR/LF/TAB/NUL */
1565                    *target++=(uint8_t)c;
1566                    --targetCapacity;
1567                } else {
1568                    /* quote C0 control character */
1569                    c|=SQ0<<8;
1570                    length=2;
1571                    goto outputBytes;
1572                }
1573            } else if((delta=c-currentOffset)<=0x7f) {
1574                /* use the current dynamic window */
1575                *target++=(uint8_t)(delta|0x80);
1576                --targetCapacity;
1577            } else if(U16_IS_SURROGATE(c)) {
1578                if(U16_IS_SURROGATE_LEAD(c)) {
1579getTrailSingle:
1580                    lead=(UChar)c;
1581                    if(source<sourceLimit) {
1582                        /* test the following code unit */
1583                        trail=*source;
1584                        if(U16_IS_TRAIL(trail)) {
1585                            ++source;
1586                            c=U16_GET_SUPPLEMENTARY(c, trail);
1587                            /* convert this surrogate code point */
1588                            /* exit this condition tree */
1589                        } else {
1590                            /* this is an unmatched lead code unit (1st surrogate) */
1591                            /* callback(illegal) */
1592                            *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1593                            goto endloop;
1594                        }
1595                    } else {
1596                        /* no more input */
1597                        break;
1598                    }
1599                } else {
1600                    /* this is an unmatched trail code unit (2nd surrogate) */
1601                    /* callback(illegal) */
1602                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1603                    goto endloop;
1604                }
1605
1606                /* compress supplementary character U+10000..U+10ffff */
1607                if((delta=c-currentOffset)<=0x7f) {
1608                    /* use the current dynamic window */
1609                    *target++=(uint8_t)(delta|0x80);
1610                    --targetCapacity;
1611                } else if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) {
1612                    /* there is a dynamic window that contains this character, change to it */
1613                    dynamicWindow=window;
1614                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1615                    useDynamicWindow(scsu, dynamicWindow);
1616                    c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1617                    length=2;
1618                    goto outputBytes;
1619                } else if((code=getDynamicOffset(c, &offset))>=0) {
1620                    /* might check if there are more characters in this window to come */
1621                    /* define an extended window with this character */
1622                    code-=0x200;
1623                    dynamicWindow=getNextDynamicWindow(scsu);
1624                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1625                    useDynamicWindow(scsu, dynamicWindow);
1626                    c=((uint32_t)SDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1627                    length=4;
1628                    goto outputBytes;
1629                } else {
1630                    /* change to Unicode mode and output this (lead, trail) pair */
1631                    isSingleByteMode=FALSE;
1632                    *target++=(uint8_t)SCU;
1633                    --targetCapacity;
1634                    c=((uint32_t)lead<<16)|trail;
1635                    length=4;
1636                    goto outputBytes;
1637                }
1638            } else if(c<0xa0) {
1639                /* quote C1 control character */
1640                c=(c&0x7f)|(SQ0+1)<<8; /* SQ0+1==SQ1 */
1641                length=2;
1642                goto outputBytes;
1643            } else if(c==0xfeff || c>=0xfff0) {
1644                /* quote signature character=byte order mark and specials */
1645                c|=SQU<<16;
1646                length=3;
1647                goto outputBytes;
1648            } else {
1649                /* compress all other BMP characters */
1650                if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) {
1651                    /* there is a window defined that contains this character - switch to it or quote from it? */
1652                    if(source>=sourceLimit || isInOffsetWindowOrDirect(scsu->fromUDynamicOffsets[window], *source)) {
1653                        /* change to dynamic window */
1654                        dynamicWindow=window;
1655                        currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1656                        useDynamicWindow(scsu, dynamicWindow);
1657                        c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1658                        length=2;
1659                        goto outputBytes;
1660                    } else {
1661                        /* quote from dynamic window */
1662                        c=((uint32_t)(SQ0+window)<<8)|(c-scsu->fromUDynamicOffsets[window])|0x80;
1663                        length=2;
1664                        goto outputBytes;
1665                    }
1666                } else if((window=getWindow(staticOffsets, c))>=0) {
1667                    /* quote from static window */
1668                    c=((uint32_t)(SQ0+window)<<8)|(c-staticOffsets[window]);
1669                    length=2;
1670                    goto outputBytes;
1671                } else if((code=getDynamicOffset(c, &offset))>=0) {
1672                    /* define a dynamic window with this character */
1673                    dynamicWindow=getNextDynamicWindow(scsu);
1674                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1675                    useDynamicWindow(scsu, dynamicWindow);
1676                    c=((uint32_t)(SD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1677                    length=3;
1678                    goto outputBytes;
1679                } else if((uint32_t)(c-0x3400)<(0xd800-0x3400) &&
1680                          (source>=sourceLimit || (uint32_t)(*source-0x3400)<(0xd800-0x3400))
1681                ) {
1682                    /*
1683                     * this character is not compressible (a BMP ideograph or similar);
1684                     * switch to Unicode mode if this is the last character in the block
1685                     * or there is at least one more ideograph following immediately
1686                     */
1687                    isSingleByteMode=FALSE;
1688                    c|=SCU<<16;
1689                    length=3;
1690                    goto outputBytes;
1691                } else {
1692                    /* quote Unicode */
1693                    c|=SQU<<16;
1694                    length=3;
1695                    goto outputBytes;
1696                }
1697            }
1698
1699            /* normal end of conversion: prepare for a new character */
1700            c=0;
1701        }
1702    } else {
1703        if(c!=0 && targetCapacity>0) {
1704            goto getTrailUnicode;
1705        }
1706
1707        /* state machine for Unicode mode */
1708/* unicodeByteMode: */
1709        while(source<sourceLimit) {
1710            if(targetCapacity<=0) {
1711                /* target is full */
1712                *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1713                break;
1714            }
1715            c=*source++;
1716
1717            if((uint32_t)(c-0x3400)<(0xd800-0x3400)) {
1718                /* not compressible, write character directly */
1719                if(targetCapacity>=2) {
1720                    *target++=(uint8_t)(c>>8);
1721                    *target++=(uint8_t)c;
1722                    targetCapacity-=2;
1723                } else {
1724                    length=2;
1725                    goto outputBytes;
1726                }
1727            } else if((uint32_t)(c-0x3400)>=(0xf300-0x3400) /* c<0x3400 || c>=0xf300 */) {
1728                /* compress BMP character if the following one is not an uncompressible ideograph */
1729                if(!(source<sourceLimit && (uint32_t)(*source-0x3400)<(0xd800-0x3400))) {
1730                    if(((uint32_t)(c-0x30)<10 || (uint32_t)(c-0x61)<26 || (uint32_t)(c-0x41)<26)) {
1731                        /* ASCII digit or letter */
1732                        isSingleByteMode=TRUE;
1733                        c|=((uint32_t)(UC0+dynamicWindow)<<8)|c;
1734                        length=2;
1735                        goto outputBytes;
1736                    } else if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) {
1737                        /* there is a dynamic window that contains this character, change to it */
1738                        isSingleByteMode=TRUE;
1739                        dynamicWindow=window;
1740                        currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1741                        useDynamicWindow(scsu, dynamicWindow);
1742                        c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1743                        length=2;
1744                        goto outputBytes;
1745                    } else if((code=getDynamicOffset(c, &offset))>=0) {
1746                        /* define a dynamic window with this character */
1747                        isSingleByteMode=TRUE;
1748                        dynamicWindow=getNextDynamicWindow(scsu);
1749                        currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1750                        useDynamicWindow(scsu, dynamicWindow);
1751                        c=((uint32_t)(UD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1752                        length=3;
1753                        goto outputBytes;
1754                    }
1755                }
1756
1757                /* don't know how to compress this character, just write it directly */
1758                length=2;
1759                goto outputBytes;
1760            } else if(c<0xe000) {
1761                /* c is a surrogate */
1762                if(U16_IS_SURROGATE_LEAD(c)) {
1763getTrailUnicode:
1764                    lead=(UChar)c;
1765                    if(source<sourceLimit) {
1766                        /* test the following code unit */
1767                        trail=*source;
1768                        if(U16_IS_TRAIL(trail)) {
1769                            ++source;
1770                            c=U16_GET_SUPPLEMENTARY(c, trail);
1771                            /* convert this surrogate code point */
1772                            /* exit this condition tree */
1773                        } else {
1774                            /* this is an unmatched lead code unit (1st surrogate) */
1775                            /* callback(illegal) */
1776                            *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1777                            goto endloop;
1778                        }
1779                    } else {
1780                        /* no more input */
1781                        break;
1782                    }
1783                } else {
1784                    /* this is an unmatched trail code unit (2nd surrogate) */
1785                    /* callback(illegal) */
1786                    *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1787                    goto endloop;
1788                }
1789
1790                /* compress supplementary character */
1791                if( (window=getWindow(scsu->fromUDynamicOffsets, c))>=0 &&
1792                    !(source<sourceLimit && (uint32_t)(*source-0x3400)<(0xd800-0x3400))
1793                ) {
1794                    /*
1795                     * there is a dynamic window that contains this character and
1796                     * the following character is not uncompressible,
1797                     * change to the window
1798                     */
1799                    isSingleByteMode=TRUE;
1800                    dynamicWindow=window;
1801                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow];
1802                    useDynamicWindow(scsu, dynamicWindow);
1803                    c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80;
1804                    length=2;
1805                    goto outputBytes;
1806                } else if(source<sourceLimit && lead==*source && /* too lazy to check trail in same window as source[1] */
1807                          (code=getDynamicOffset(c, &offset))>=0
1808                ) {
1809                    /* two supplementary characters in (probably) the same window - define an extended one */
1810                    isSingleByteMode=TRUE;
1811                    code-=0x200;
1812                    dynamicWindow=getNextDynamicWindow(scsu);
1813                    currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset;
1814                    useDynamicWindow(scsu, dynamicWindow);
1815                    c=((uint32_t)UDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80;
1816                    length=4;
1817                    goto outputBytes;
1818                } else {
1819                    /* don't know how to compress this character, just write it directly */
1820                    c=((uint32_t)lead<<16)|trail;
1821                    length=4;
1822                    goto outputBytes;
1823                }
1824            } else /* 0xe000<=c<0xf300 */ {
1825                /* quote to avoid SCSU tags */
1826                c|=UQU<<16;
1827                length=3;
1828                goto outputBytes;
1829            }
1830
1831            /* normal end of conversion: prepare for a new character */
1832            c=0;
1833        }
1834    }
1835endloop:
1836
1837    /* set the converter state back into UConverter */
1838    scsu->fromUIsSingleByteMode=isSingleByteMode;
1839    scsu->fromUDynamicWindow=dynamicWindow;
1840
1841    cnv->fromUChar32=c;
1842
1843    /* write back the updated pointers */
1844    pArgs->source=source;
1845    pArgs->target=(char *)target;
1846    return;
1847
1848outputBytes:
1849    /* write the output character bytes from c and length [code copied from ucnvmbcs.c] */
1850    /* from the first if in the loop we know that targetCapacity>0 */
1851    if(length<=targetCapacity) {
1852        switch(length) {
1853            /* each branch falls through to the next one */
1854        case 4:
1855            *target++=(uint8_t)(c>>24);
1856        case 3: /*fall through*/
1857            *target++=(uint8_t)(c>>16);
1858        case 2: /*fall through*/
1859            *target++=(uint8_t)(c>>8);
1860        case 1: /*fall through*/
1861            *target++=(uint8_t)c;
1862        default:
1863            /* will never occur */
1864            break;
1865        }
1866        targetCapacity-=length;
1867
1868        /* normal end of conversion: prepare for a new character */
1869        c=0;
1870        goto loop;
1871    } else {
1872        uint8_t *p;
1873
1874        /*
1875         * We actually do this backwards here:
1876         * In order to save an intermediate variable, we output
1877         * first to the overflow buffer what does not fit into the
1878         * regular target.
1879         */
1880        /* we know that 0<=targetCapacity<length<=4 */
1881        /* targetCapacity==0 when SCU+supplementary where SCU used up targetCapacity==1 */
1882        length-=targetCapacity;
1883        p=(uint8_t *)cnv->charErrorBuffer;
1884        switch(length) {
1885            /* each branch falls through to the next one */
1886        case 4:
1887            *p++=(uint8_t)(c>>24);
1888        case 3: /*fall through*/
1889            *p++=(uint8_t)(c>>16);
1890        case 2: /*fall through*/
1891            *p++=(uint8_t)(c>>8);
1892        case 1: /*fall through*/
1893            *p=(uint8_t)c;
1894        default:
1895            /* will never occur */
1896            break;
1897        }
1898        cnv->charErrorBufferLength=(int8_t)length;
1899
1900        /* now output what fits into the regular target */
1901        c>>=8*length; /* length was reduced by targetCapacity */
1902        switch(targetCapacity) {
1903            /* each branch falls through to the next one */
1904        case 3:
1905            *target++=(uint8_t)(c>>16);
1906        case 2: /*fall through*/
1907            *target++=(uint8_t)(c>>8);
1908        case 1: /*fall through*/
1909            *target++=(uint8_t)c;
1910        default:
1911            break;
1912        }
1913
1914        /* target overflow */
1915        targetCapacity=0;
1916        *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1917        c=0;
1918        goto endloop;
1919    }
1920}
1921
1922/* miscellaneous ------------------------------------------------------------ */
1923
1924static const char *
1925_SCSUGetName(const UConverter *cnv) {
1926    SCSUData *scsu=(SCSUData *)cnv->extraInfo;
1927
1928    switch(scsu->locale) {
1929    case l_ja:
1930        return "SCSU,locale=ja";
1931    default:
1932        return "SCSU";
1933    }
1934}
1935
1936/* structure for SafeClone calculations */
1937struct cloneSCSUStruct
1938{
1939    UConverter cnv;
1940    SCSUData mydata;
1941};
1942
1943static UConverter *
1944_SCSUSafeClone(const UConverter *cnv,
1945               void *stackBuffer,
1946               int32_t *pBufferSize,
1947               UErrorCode *status)
1948{
1949    struct cloneSCSUStruct * localClone;
1950    int32_t bufferSizeNeeded = sizeof(struct cloneSCSUStruct);
1951
1952    if (U_FAILURE(*status)){
1953        return 0;
1954    }
1955
1956    if (*pBufferSize == 0){ /* 'preflighting' request - set needed size into *pBufferSize */
1957        *pBufferSize = bufferSizeNeeded;
1958        return 0;
1959    }
1960
1961    localClone = (struct cloneSCSUStruct *)stackBuffer;
1962    /* ucnv.c/ucnv_safeClone() copied the main UConverter already */
1963
1964    uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(SCSUData));
1965    localClone->cnv.extraInfo = &localClone->mydata;
1966    localClone->cnv.isExtraLocal = TRUE;
1967
1968    return &localClone->cnv;
1969}
1970
1971
1972static const UConverterImpl _SCSUImpl={
1973    UCNV_SCSU,
1974
1975    NULL,
1976    NULL,
1977
1978    _SCSUOpen,
1979    _SCSUClose,
1980    _SCSUReset,
1981
1982    _SCSUToUnicode,
1983    _SCSUToUnicodeWithOffsets,
1984    _SCSUFromUnicode,
1985    _SCSUFromUnicodeWithOffsets,
1986    NULL,
1987
1988    NULL,
1989    _SCSUGetName,
1990    NULL,
1991    _SCSUSafeClone,
1992    ucnv_getCompleteUnicodeSet
1993};
1994
1995static const UConverterStaticData _SCSUStaticData={
1996    sizeof(UConverterStaticData),
1997    "SCSU",
1998    1212, /* CCSID for SCSU */
1999    UCNV_IBM, UCNV_SCSU,
2000    1, 3, /* one UChar generates at least 1 byte and at most 3 bytes */
2001    /*
2002     * The subchar here is ignored because _SCSUOpen() sets U+fffd as a Unicode
2003     * substitution string.
2004     */
2005    { 0x0e, 0xff, 0xfd, 0 }, 3,
2006    FALSE, FALSE,
2007    0,
2008    0,
2009    { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */
2010};
2011
2012const UConverterSharedData _SCSUData={
2013    sizeof(UConverterSharedData), ~((uint32_t)0),
2014    NULL, NULL, &_SCSUStaticData, FALSE, &_SCSUImpl,
2015    0
2016};
2017
2018#endif
2019