1570af302Sopenharmony_ci#include <stdint.h>
2570af302Sopenharmony_ci#include <wchar.h>
3570af302Sopenharmony_ci#include <errno.h>
4570af302Sopenharmony_ci#include <string.h>
5570af302Sopenharmony_ci#include <stdlib.h>
6570af302Sopenharmony_ci#include "internal.h"
7570af302Sopenharmony_ci
8570af302Sopenharmony_cisize_t mbsrtowcs(wchar_t *restrict ws, const char **restrict src, size_t wn, mbstate_t *restrict st)
9570af302Sopenharmony_ci{
10570af302Sopenharmony_ci	const unsigned char *s = (const void *)*src;
11570af302Sopenharmony_ci	size_t wn0 = wn;
12570af302Sopenharmony_ci	unsigned c = 0;
13570af302Sopenharmony_ci
14570af302Sopenharmony_ci	if (st && (c = *(unsigned *)st)) {
15570af302Sopenharmony_ci		if (ws) {
16570af302Sopenharmony_ci			*(unsigned *)st = 0;
17570af302Sopenharmony_ci			goto resume;
18570af302Sopenharmony_ci		} else {
19570af302Sopenharmony_ci			goto resume0;
20570af302Sopenharmony_ci		}
21570af302Sopenharmony_ci	}
22570af302Sopenharmony_ci
23570af302Sopenharmony_ci	if (MB_CUR_MAX==1) {
24570af302Sopenharmony_ci		if (!ws) return strlen((const char *)s);
25570af302Sopenharmony_ci		for (;;) {
26570af302Sopenharmony_ci			if (!wn) {
27570af302Sopenharmony_ci				*src = (const void *)s;
28570af302Sopenharmony_ci				return wn0;
29570af302Sopenharmony_ci			}
30570af302Sopenharmony_ci			if (!*s) break;
31570af302Sopenharmony_ci			c = *s++;
32570af302Sopenharmony_ci			*ws++ = CODEUNIT(c);
33570af302Sopenharmony_ci			wn--;
34570af302Sopenharmony_ci		}
35570af302Sopenharmony_ci		*ws = 0;
36570af302Sopenharmony_ci		*src = 0;
37570af302Sopenharmony_ci		return wn0-wn;
38570af302Sopenharmony_ci	}
39570af302Sopenharmony_ci
40570af302Sopenharmony_ci	if (!ws) for (;;) {
41570af302Sopenharmony_ci#ifdef __GNUC__
42570af302Sopenharmony_ci		typedef uint32_t __attribute__((__may_alias__)) w32;
43570af302Sopenharmony_ci		if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) {
44570af302Sopenharmony_ci			while (!(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) {
45570af302Sopenharmony_ci				s += 4;
46570af302Sopenharmony_ci				wn -= 4;
47570af302Sopenharmony_ci			}
48570af302Sopenharmony_ci		}
49570af302Sopenharmony_ci#endif
50570af302Sopenharmony_ci		if (*s-1u < 0x7f) {
51570af302Sopenharmony_ci			s++;
52570af302Sopenharmony_ci			wn--;
53570af302Sopenharmony_ci			continue;
54570af302Sopenharmony_ci		}
55570af302Sopenharmony_ci		if (*s-SA > SB-SA) break;
56570af302Sopenharmony_ci		c = bittab[*s++-SA];
57570af302Sopenharmony_ciresume0:
58570af302Sopenharmony_ci		if (OOB(c,*s)) { s--; break; }
59570af302Sopenharmony_ci		s++;
60570af302Sopenharmony_ci		if (c&(1U<<25)) {
61570af302Sopenharmony_ci			if (*s-0x80u >= 0x40) { s-=2; break; }
62570af302Sopenharmony_ci			s++;
63570af302Sopenharmony_ci			if (c&(1U<<19)) {
64570af302Sopenharmony_ci				if (*s-0x80u >= 0x40) { s-=3; break; }
65570af302Sopenharmony_ci				s++;
66570af302Sopenharmony_ci			}
67570af302Sopenharmony_ci		}
68570af302Sopenharmony_ci		wn--;
69570af302Sopenharmony_ci		c = 0;
70570af302Sopenharmony_ci	} else for (;;) {
71570af302Sopenharmony_ci		if (!wn) {
72570af302Sopenharmony_ci			*src = (const void *)s;
73570af302Sopenharmony_ci			return wn0;
74570af302Sopenharmony_ci		}
75570af302Sopenharmony_ci#ifdef __GNUC__
76570af302Sopenharmony_ci		typedef uint32_t __attribute__((__may_alias__)) w32;
77570af302Sopenharmony_ci		if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) {
78570af302Sopenharmony_ci			while (wn>=5 && !(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) {
79570af302Sopenharmony_ci				*ws++ = *s++;
80570af302Sopenharmony_ci				*ws++ = *s++;
81570af302Sopenharmony_ci				*ws++ = *s++;
82570af302Sopenharmony_ci				*ws++ = *s++;
83570af302Sopenharmony_ci				wn -= 4;
84570af302Sopenharmony_ci			}
85570af302Sopenharmony_ci		}
86570af302Sopenharmony_ci#endif
87570af302Sopenharmony_ci		if (*s-1u < 0x7f) {
88570af302Sopenharmony_ci			*ws++ = *s++;
89570af302Sopenharmony_ci			wn--;
90570af302Sopenharmony_ci			continue;
91570af302Sopenharmony_ci		}
92570af302Sopenharmony_ci		if (*s-SA > SB-SA) break;
93570af302Sopenharmony_ci		c = bittab[*s++-SA];
94570af302Sopenharmony_ciresume:
95570af302Sopenharmony_ci		if (OOB(c,*s)) { s--; break; }
96570af302Sopenharmony_ci		c = (c<<6) | *s++-0x80;
97570af302Sopenharmony_ci		if (c&(1U<<31)) {
98570af302Sopenharmony_ci			if (*s-0x80u >= 0x40) { s-=2; break; }
99570af302Sopenharmony_ci			c = (c<<6) | *s++-0x80;
100570af302Sopenharmony_ci			if (c&(1U<<31)) {
101570af302Sopenharmony_ci				if (*s-0x80u >= 0x40) { s-=3; break; }
102570af302Sopenharmony_ci				c = (c<<6) | *s++-0x80;
103570af302Sopenharmony_ci			}
104570af302Sopenharmony_ci		}
105570af302Sopenharmony_ci		*ws++ = c;
106570af302Sopenharmony_ci		wn--;
107570af302Sopenharmony_ci		c = 0;
108570af302Sopenharmony_ci	}
109570af302Sopenharmony_ci
110570af302Sopenharmony_ci	if (!c && !*s) {
111570af302Sopenharmony_ci		if (ws) {
112570af302Sopenharmony_ci			*ws = 0;
113570af302Sopenharmony_ci			*src = 0;
114570af302Sopenharmony_ci		}
115570af302Sopenharmony_ci		return wn0-wn;
116570af302Sopenharmony_ci	}
117570af302Sopenharmony_ci	errno = EILSEQ;
118570af302Sopenharmony_ci	if (ws) *src = (const void *)s;
119570af302Sopenharmony_ci	return -1;
120570af302Sopenharmony_ci}
121