initial check-in, version 0.5.0
[musl] / src / multibyte / mbsrtowcs.c
1 /* 
2  * This code was written by Rich Felker in 2010; no copyright is claimed.
3  * This code is in the public domain. Attribution is appreciated but
4  * unnecessary.
5  */
6
7 #include <stdlib.h>
8 #include <inttypes.h>
9 #include <wchar.h>
10 #include <errno.h>
11
12 #include "internal.h"
13
14 size_t mbsrtowcs(wchar_t *ws, const char **src, size_t wn, mbstate_t *st)
15 {
16         unsigned c;
17         const unsigned char *s = *src;
18         const wchar_t *wsorig = ws;
19
20         if (!st) st = (void *)&c, c = 0;
21         else c = *(unsigned *)st;
22
23         if (c) {
24                 *(unsigned *)st = 0;
25                 if (!ws) {
26                         wn = 0;
27                         goto resume0;
28                 }
29                 goto resume;
30         }
31
32         if (!ws) for (wn=0;;) {
33                 if ((unsigned)*s-SA >= SB-SA) {
34                         while (((unsigned)s&3) && (unsigned)*s-1<0x7f) s++, wn++;
35                         while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) s+=4, wn+=4;
36                         while ((unsigned)*s-1<0x7f) s++, wn++;
37                         if (!*s) return wn;
38                         if ((unsigned)*s-SA >= SB-SA) goto ilseq2;
39                 }
40                 c = bittab[*s++-SA];
41                 do {
42 resume0:
43                         if (OOB(c,*s)) goto ilseq2; s++;
44                         c <<= 6; if (!(c&(1U<<31))) break;
45 #ifdef I_FAILED_TO_RTFM_RFC3629
46                         if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
47                         c <<= 6; if (!(c&(1U<<31))) break;
48                         if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
49                         c <<= 6; if (!(c&(1U<<31))) break;
50 #endif
51                         if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
52                         c <<= 6; if (!(c&(1U<<31))) break;
53                         if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
54                 } while (0);
55                 wn++; c = 0;
56         }
57
58         while (wn) {
59                 if ((unsigned)*s-SA >= SB-SA) {
60                         if (wn >= 7) {
61                                 while (((unsigned)s&3) && (unsigned)*s-1<0x7f) {
62                                         *ws++ = *s++;
63                                         wn--;
64                                 }
65                                 while (wn>=4 && !(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) {
66                                         *ws++ = *s++;
67                                         *ws++ = *s++;
68                                         *ws++ = *s++;
69                                         *ws++ = *s++;
70                                         wn -= 4;
71                                 }
72                         }
73                         while (wn && (unsigned)*s-1<0x7f) {
74                                 *ws++ = *s++;
75                                 wn--;
76                         }
77                         if (!wn) break;
78                         if (!*s) {
79                                 *ws = 0;
80                                 *src = 0;
81                                 return ws-wsorig;
82                         }
83                         if ((unsigned)*s-SA >= SB-SA) goto ilseq;
84                 }
85                 c = bittab[*s++-SA];
86                 do {
87 resume:
88                         if (OOB(c,*s)) goto ilseq;
89                         c = (c<<6) | *s++-0x80;
90                         if (!(c&(1U<<31))) break;
91
92 #ifdef I_FAILED_TO_RTFM_RFC3629
93                         if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
94                         c = (c<<6) | *s++-0x80;
95                         if (!(c&(1U<<31))) break;
96
97                         if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
98                         c = (c<<6) | *s++-0x80;
99                         if (!(c&(1U<<31))) break;
100 #endif
101
102                         if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
103                         c = (c<<6) | *s++-0x80;
104                         if (!(c&(1U<<31))) break;
105
106                         if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
107                         c = (c<<6) | *s++-0x80;
108                 } while (0);
109
110                 *ws++ = c; wn--; c = 0;
111         }
112         *src = s;
113         return ws-wsorig;
114 ilseq:
115         *src = s;
116 ilseq2:
117         /* enter permanently failing state */
118         *(unsigned *)st = FAILSTATE;
119         errno = EILSEQ;
120         return -1;
121 }