source: trunk/libtransmission/ConvertUTF.c

Last change on this file was 14676, checked in by mikedld, 5 years ago

#6064: Merge ConvertUTF.c fixes from LLVM (?)

File size: 20.2 KB
Line 
1/*
2 * Copyright 2001-2004 Unicode, Inc.
3 *
4 * Disclaimer
5 *
6 * This source code is provided as is by Unicode, Inc. No claims are
7 * made as to fitness for any particular purpose. No warranties of any
8 * kind are expressed or implied. The recipient agrees to determine
9 * applicability of information provided. If this file has been
10 * purchased on magnetic or optical media from Unicode, Inc., the
11 * sole remedy for any claim will be exchange of defective media
12 * within 90 days of receipt.
13 *
14 * Limitations on Rights to Redistribute This Code
15 *
16 * Unicode, Inc. hereby grants the right to freely use the information
17 * supplied in this file in the creation of products supporting the
18 * Unicode Standard, and to make copies of this file in any form
19 * for internal or external distribution as long as this notice
20 * remains attached.
21 */
22
23/* ---------------------------------------------------------------------
24
25    Conversions between UTF32, UTF-16, and UTF-8. Source code file.
26    Author: Mark E. Davis, 1994.
27    Rev History: Rick McGowan, fixes & updates May 2001.
28    Sept 2001: fixed const & error conditions per
29        mods suggested by S. Parent & A. Lillich.
30    June 2002: Tim Dodd added detection and handling of incomplete
31        source sequences, enhanced error detection, added casts
32        to eliminate compiler warnings.
33    July 2003: slight mods to back out aggressive FFFE detection.
34    Jan 2004: updated switches in from-UTF8 conversions.
35    Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
36    May 2006: updated isLegalUTF8Sequence.
37
38    See the header file "ConvertUTF.h" for complete documentation.
39
40------------------------------------------------------------------------ */
41
42#ifdef CVTUTF_DEBUG
43 #include <stdio.h>
44#endif
45#include <string.h> /* strlen () */
46
47#include "transmission.h"
48
49#include "ConvertUTF.h"
50
51static const int halfShift  = 10; /* used for shifting by 10 bits */
52
53static const UTF32 halfBase = 0x0010000UL;
54static const UTF32 halfMask = 0x3FFUL;
55
56#define UNI_SUR_HIGH_START (UTF32)0xD800
57#define UNI_SUR_HIGH_END  (UTF32)0xDBFF
58#define UNI_SUR_LOW_START (UTF32)0xDC00
59#define UNI_SUR_LOW_END   (UTF32)0xDFFF
60#define false      0
61#define true        1
62
63/* --------------------------------------------------------------------- */
64
65ConversionResult ConvertUTF32toUTF16 (
66        const UTF32** sourceStart, const UTF32* sourceEnd, 
67        UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
68    ConversionResult result = conversionOK;
69    const UTF32* source = *sourceStart;
70    UTF16* target = *targetStart;
71    while (source < sourceEnd) {
72        UTF32 ch;
73        if (target >= targetEnd) {
74            result = targetExhausted; break;
75        }
76        ch = *source++;
77        if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
78            /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
79            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
80                if (flags == strictConversion) {
81                    --source; /* return to the illegal value itself */
82                    result = sourceIllegal;
83                    break;
84                } else {
85                    *target++ = UNI_REPLACEMENT_CHAR;
86                }
87            } else {
88                *target++ = (UTF16)ch; /* normal case */
89            }
90        } else if (ch > UNI_MAX_LEGAL_UTF32) {
91            if (flags == strictConversion) {
92                result = sourceIllegal;
93            } else {
94                *target++ = UNI_REPLACEMENT_CHAR;
95            }
96        } else {
97            /* target is a character in range 0xFFFF - 0x10FFFF. */
98            if (target + 1 >= targetEnd) {
99                --source; /* Back up source pointer! */
100                result = targetExhausted; break;
101            }
102            ch -= halfBase;
103            *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
104            *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
105        }
106    }
107    *sourceStart = source;
108    *targetStart = target;
109    return result;
110}
111
112/* --------------------------------------------------------------------- */
113
114ConversionResult ConvertUTF16toUTF32 (
115        const UTF16** sourceStart, const UTF16* sourceEnd, 
116        UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
117    ConversionResult result = conversionOK;
118    const UTF16* source = *sourceStart;
119    UTF32* target = *targetStart;
120    UTF32 ch, ch2;
121    while (source < sourceEnd) {
122        const UTF16* oldSource = source; /*  In case we have to back up because of target overflow. */
123        ch = *source++;
124        /* If we have a surrogate pair, convert to UTF32 first. */
125        if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
126            /* If the 16 bits following the high surrogate are in the source buffer... */
127            if (source < sourceEnd) {
128                ch2 = *source;
129                /* If it's a low surrogate, convert to UTF32. */
130                if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
131                    ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
132                        + (ch2 - UNI_SUR_LOW_START) + halfBase;
133                    ++source;
134                } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
135                    --source; /* return to the illegal value itself */
136                    result = sourceIllegal;
137                    break;
138                }
139            } else { /* We don't have the 16 bits following the high surrogate. */
140                --source; /* return to the high surrogate */
141                result = sourceExhausted;
142                break;
143            }
144        } else if (flags == strictConversion) {
145            /* UTF-16 surrogate values are illegal in UTF-32 */
146            if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
147                --source; /* return to the illegal value itself */
148                result = sourceIllegal;
149                break;
150            }
151        }
152        if (target >= targetEnd) {
153            source = oldSource; /* Back up source pointer! */
154            result = targetExhausted; break;
155        }
156        *target++ = ch;
157    }
158    *sourceStart = source;
159    *targetStart = target;
160#ifdef CVTUTF_DEBUG
161if (result == sourceIllegal) {
162    fprintf (stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
163    fflush (stderr);
164}
165#endif
166    return result;
167}
168
169/* --------------------------------------------------------------------- */
170
171/*
172 * Index into the table below with the first byte of a UTF-8 sequence to
173 * get the number of trailing bytes that are supposed to follow it.
174 * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
175 * left as-is for anyone who may want to do such conversion, which was
176 * allowed in earlier algorithms.
177 */
178static const char trailingBytesForUTF8[256] = {
179    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
180    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
181    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
182    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
183    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
184    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
185    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
186    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
187};
188
189/*
190 * Magic values subtracted from a buffer value during UTF8 conversion.
191 * This table contains as many values as there might be trailing bytes
192 * in a UTF-8 sequence.
193 */
194static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 
195                     0x03C82080UL, 0xFA082080UL, 0x82082080UL };
196
197/*
198 * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
199 * into the first byte, depending on how many bytes follow. There are
200 * as many entries in this table as there are UTF-8 sequence types.
201 * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
202 * for *legal* UTF-8 will be 4 or fewer bytes total.
203 */
204static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
205
206/* --------------------------------------------------------------------- */
207
208/* The interface converts a whole buffer to avoid function-call overhead.
209 * Constants have been gathered. Loops & conditionals have been removed as
210 * much as possible for efficiency, in favor of drop-through switches.
211 * (See "Note A" at the bottom of the file for equivalent code.)
212 * If your compiler supports it, the "isLegalUTF8" call can be turned
213 * into an inline function.
214 */
215
216/* --------------------------------------------------------------------- */
217
218ConversionResult ConvertUTF16toUTF8 (
219        const UTF16** sourceStart, const UTF16* sourceEnd, 
220        UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
221    ConversionResult result = conversionOK;
222    const UTF16* source = *sourceStart;
223    UTF8* target = *targetStart;
224    while (source < sourceEnd) {
225        UTF32 ch;
226        unsigned short bytesToWrite = 0;
227        const UTF32 byteMask = 0xBF;
228        const UTF32 byteMark = 0x80; 
229        const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
230        ch = *source++;
231        /* If we have a surrogate pair, convert to UTF32 first. */
232        if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
233            /* If the 16 bits following the high surrogate are in the source buffer... */
234            if (source < sourceEnd) {
235                UTF32 ch2 = *source;
236                /* If it's a low surrogate, convert to UTF32. */
237                if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
238                    ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
239                        + (ch2 - UNI_SUR_LOW_START) + halfBase;
240                    ++source;
241                } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
242                    --source; /* return to the illegal value itself */
243                    result = sourceIllegal;
244                    break;
245                }
246            } else { /* We don't have the 16 bits following the high surrogate. */
247                --source; /* return to the high surrogate */
248                result = sourceExhausted;
249                break;
250            }
251        } else if (flags == strictConversion) {
252            /* UTF-16 surrogate values are illegal in UTF-32 */
253            if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
254                --source; /* return to the illegal value itself */
255                result = sourceIllegal;
256                break;
257            }
258        }
259        /* Figure out how many bytes the result will require */
260        if (ch < (UTF32)0x80) {      bytesToWrite = 1;
261        } else if (ch < (UTF32)0x800) {     bytesToWrite = 2;
262        } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3;
263        } else if (ch < (UTF32)0x110000) {  bytesToWrite = 4;
264        } else {                            bytesToWrite = 3;
265                                            ch = UNI_REPLACEMENT_CHAR;
266        }
267
268        target += bytesToWrite;
269        if (target > targetEnd) {
270            source = oldSource; /* Back up source pointer! */
271            target -= bytesToWrite; result = targetExhausted; break;
272        }
273        switch (bytesToWrite) { /* note: everything falls through. */
274            case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
275            case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
276            case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
277            case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
278        }
279        target += bytesToWrite;
280    }
281    *sourceStart = source;
282    *targetStart = target;
283    return result;
284}
285
286/* --------------------------------------------------------------------- */
287
288/*
289 * Utility routine to tell whether a sequence of bytes is legal UTF-8.
290 * This must be called with the length pre-determined by the first byte.
291 * If not calling this from ConvertUTF8to*, then the length can be set by:
292 *  length = trailingBytesForUTF8[*source]+1;
293 * and the sequence is illegal right away if there aren't that many bytes
294 * available.
295 * If presented with a length > 4, this returns false. The Unicode
296 * definition of UTF-8 goes up to 4-byte sequences.
297 */
298
299static Boolean isLegalUTF8 (const UTF8 *source, int length) {
300    UTF8 a;
301    const UTF8 *srcptr = source+length;
302    switch (length) {
303    default: return false;
304        /* Everything else falls through when "true"... */
305    case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
306    case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
307    case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
308
309        switch (*source) {
310            /* no fall-through in this inner switch */
311            case 0xE0: if (a < 0xA0) return false; break;
312            case 0xED: if (a > 0x9F) return false; break;
313            case 0xF0: if (a < 0x90) return false; break;
314            case 0xF4: if (a > 0x8F) return false; break;
315            default:   if (a < 0x80) return false;
316        }
317
318    case 1: if (*source >= 0x80 && *source < 0xC2) return false;
319    }
320    if (*source > 0xF4) return false;
321    return true;
322}
323
324/* --------------------------------------------------------------------- */
325
326/*
327 * Exported function to return whether a UTF-8 sequence is legal or not.
328 * This is not used here; it's just exported.
329 */
330
331Boolean isLegalUTF8Sequence (const UTF8 *source, const UTF8 *sourceEnd) {
332    int length;
333    if (source == sourceEnd) {
334        return true;
335    }
336    while (true) {
337        length = trailingBytesForUTF8[*source]+1;
338        if (source+length > sourceEnd) {
339            return false;
340        }
341        if (!isLegalUTF8 (source, length)) {
342            return false;
343        }
344        source += length;
345        if (source >= sourceEnd) {
346            return true;
347        }
348    }
349}
350
351/**
352 * This is a variation of isLegalUTF8Sequence () that behaves like g_utf8_validate ().
353 * In addition to knowing if the sequence is legal, it also tells you the last good character.
354 */
355Boolean
356tr_utf8_validate (const char * str, size_t max_len, const char ** end)
357{
358    const UTF8* source = (const UTF8*) str;
359    const UTF8* sourceEnd;
360
361    if (max_len == 0)
362        return true;
363
364    if (str == NULL)
365        return false;
366
367    sourceEnd = source + (max_len == TR_BAD_SIZE ? strlen (str) : max_len);
368
369    if (source == sourceEnd)
370    {
371        if (end != NULL)
372            *end = (const char*) source;
373        return true;
374    }
375
376    for (;;)
377    {
378        const int length = trailingBytesForUTF8[*source] + 1;
379        if (source + length > sourceEnd) {
380            if (end != NULL)
381                *end = (const char*) source;
382            return false;
383        }
384        if (!isLegalUTF8 (source, length)) {
385            if (end != NULL)
386                *end = (const char*) source;
387            return false;
388        }
389        source += length;
390        if (source >= sourceEnd) {
391            if (end != NULL)
392                *end = (const char*) source;
393            return true;
394        }
395    }
396}
397
398
399/* --------------------------------------------------------------------- */
400
401ConversionResult ConvertUTF8toUTF16 (
402        const UTF8** sourceStart, const UTF8* sourceEnd, 
403        UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
404    ConversionResult result = conversionOK;
405    const UTF8* source = *sourceStart;
406    UTF16* target = *targetStart;
407    while (source < sourceEnd) {
408        UTF32 ch = 0;
409        unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
410        if (extraBytesToRead >= sourceEnd - source) {
411            result = sourceExhausted; break;
412        }
413        /* Do this check whether lenient or strict */
414        if (! isLegalUTF8 (source, extraBytesToRead+1)) {
415            result = sourceIllegal;
416            break;
417        }
418        /*
419         * The cases all fall through. See "Note A" below.
420         */
421        switch (extraBytesToRead) {
422            case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
423            case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
424            case 3: ch += *source++; ch <<= 6;
425            case 2: ch += *source++; ch <<= 6;
426            case 1: ch += *source++; ch <<= 6;
427            case 0: ch += *source++;
428        }
429        ch -= offsetsFromUTF8[extraBytesToRead];
430
431        if (target >= targetEnd) {
432            source -= (extraBytesToRead+1); /* Back up source pointer! */
433            result = targetExhausted; break;
434        }
435        if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
436            /* UTF-16 surrogate values are illegal in UTF-32 */
437            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
438                if (flags == strictConversion) {
439                    source -= (extraBytesToRead+1); /* return to the illegal value itself */
440                    result = sourceIllegal;
441                    break;
442                } else {
443                    *target++ = UNI_REPLACEMENT_CHAR;
444                }
445            } else {
446                *target++ = (UTF16)ch; /* normal case */
447            }
448        } else if (ch > UNI_MAX_UTF16) {
449            if (flags == strictConversion) {
450                result = sourceIllegal;
451                source -= (extraBytesToRead+1); /* return to the start */
452                break; /* Bail out; shouldn't continue */
453            } else {
454                *target++ = UNI_REPLACEMENT_CHAR;
455            }
456        } else {
457            /* target is a character in range 0xFFFF - 0x10FFFF. */
458            if (target + 1 >= targetEnd) {
459                source -= (extraBytesToRead+1); /* Back up source pointer! */
460                result = targetExhausted; break;
461            }
462            ch -= halfBase;
463            *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
464            *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
465        }
466    }
467    *sourceStart = source;
468    *targetStart = target;
469    return result;
470}
471
472/* --------------------------------------------------------------------- */
473
474ConversionResult ConvertUTF32toUTF8 (
475        const UTF32** sourceStart, const UTF32* sourceEnd, 
476        UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
477    ConversionResult result = conversionOK;
478    const UTF32* source = *sourceStart;
479    UTF8* target = *targetStart;
480    while (source < sourceEnd) {
481        UTF32 ch;
482        unsigned short bytesToWrite = 0;
483        const UTF32 byteMask = 0xBF;
484        const UTF32 byteMark = 0x80; 
485        ch = *source++;
486        if (flags == strictConversion) {
487            /* UTF-16 surrogate values are illegal in UTF-32 */
488            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
489                --source; /* return to the illegal value itself */
490                result = sourceIllegal;
491                break;
492            }
493        }
494        /*
495         * Figure out how many bytes the result will require. Turn any
496         * illegally large UTF32 things (> Plane 17) into replacement chars.
497         */
498        if (ch < (UTF32)0x80) {      bytesToWrite = 1;
499        } else if (ch < (UTF32)0x800) {     bytesToWrite = 2;
500        } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3;
501        } else if (ch <= UNI_MAX_LEGAL_UTF32) {  bytesToWrite = 4;
502        } else {                            bytesToWrite = 3;
503                                            ch = UNI_REPLACEMENT_CHAR;
504                                            result = sourceIllegal;
505        }
506       
507        target += bytesToWrite;
508        if (target > targetEnd) {
509            --source; /* Back up source pointer! */
510            target -= bytesToWrite; result = targetExhausted; break;
511        }
512        switch (bytesToWrite) { /* note: everything falls through. */
513            case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
514            case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
515            case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
516            case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
517        }
518        target += bytesToWrite;
519    }
520    *sourceStart = source;
521    *targetStart = target;
522    return result;
523}
524
525/* --------------------------------------------------------------------- */
526
527ConversionResult ConvertUTF8toUTF32 (
528        const UTF8** sourceStart, const UTF8* sourceEnd, 
529        UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
530    ConversionResult result = conversionOK;
531    const UTF8* source = *sourceStart;
532    UTF32* target = *targetStart;
533    while (source < sourceEnd) {
534        UTF32 ch = 0;
535        unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
536        if (extraBytesToRead >= sourceEnd - source) {
537            result = sourceExhausted; break;
538        }
539        /* Do this check whether lenient or strict */
540        if (! isLegalUTF8 (source, extraBytesToRead+1)) {
541            result = sourceIllegal;
542            break;
543        }
544        /*
545         * The cases all fall through. See "Note A" below.
546         */
547        switch (extraBytesToRead) {
548            case 5: ch += *source++; ch <<= 6;
549            case 4: ch += *source++; ch <<= 6;
550            case 3: ch += *source++; ch <<= 6;
551            case 2: ch += *source++; ch <<= 6;
552            case 1: ch += *source++; ch <<= 6;
553            case 0: ch += *source++;
554        }
555        ch -= offsetsFromUTF8[extraBytesToRead];
556
557        if (target >= targetEnd) {
558            source -= (extraBytesToRead+1); /* Back up the source pointer! */
559            result = targetExhausted; break;
560        }
561        if (ch <= UNI_MAX_LEGAL_UTF32) {
562            /*
563             * UTF-16 surrogate values are illegal in UTF-32, and anything
564             * over Plane 17 (> 0x10FFFF) is illegal.
565             */
566            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
567                if (flags == strictConversion) {
568                    source -= (extraBytesToRead+1); /* return to the illegal value itself */
569                    result = sourceIllegal;
570                    break;
571                } else {
572                    *target++ = UNI_REPLACEMENT_CHAR;
573                }
574            } else {
575                *target++ = ch;
576            }
577        } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
578            result = sourceIllegal;
579            *target++ = UNI_REPLACEMENT_CHAR;
580        }
581    }
582    *sourceStart = source;
583    *targetStart = target;
584    return result;
585}
586
587/* ---------------------------------------------------------------------
588
589    Note A.
590    The fall-through switches in UTF-8 reading code save a
591    temp variable, some decrements & conditionals. The switches
592    are equivalent to the following loop:
593        {
594            int tmpBytesToRead = extraBytesToRead+1;
595            do {
596                ch += *source++;
597                --tmpBytesToRead;
598                if (tmpBytesToRead) ch <<= 6;
599            } while (tmpBytesToRead > 0);
600        }
601    In UTF-8 writing code, the switches on "bytesToWrite" are
602    similarly unrolled loops.
603
604   --------------------------------------------------------------------- */
Note: See TracBrowser for help on using the repository browser.