ldso: support DT_RELR relative relocation format
[musl] / src / multibyte / mbsrtowcs.c
index 8313d37..9b2f2df 100644 (file)
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
-#include <stdlib.h>
-#include <inttypes.h>
+#include <stdint.h>
 #include <wchar.h>
 #include <errno.h>
-
+#include <string.h>
+#include <stdlib.h>
 #include "internal.h"
 
 size_t mbsrtowcs(wchar_t *restrict ws, const char **restrict src, size_t wn, mbstate_t *restrict st)
 {
-       unsigned c;
        const unsigned char *s = (const void *)*src;
-       const wchar_t *wsorig = ws;
+       size_t wn0 = wn;
+       unsigned c = 0;
 
-       if (!st) st = (void *)&c, c = 0;
-       else c = *(unsigned *)st;
-
-       if (c) {
-               *(unsigned *)st = 0;
-               if (!ws) {
-                       wn = 0;
+       if (st && (c = *(unsigned *)st)) {
+               if (ws) {
+                       *(unsigned *)st = 0;
+                       goto resume;
+               } else {
                        goto resume0;
                }
-               goto resume;
        }
 
-       if (!ws) for (wn=0;;) {
-               if (*s-SA >= SB-SA) {
-                       while (((uintptr_t)s&3) && *s-1u<0x7f) s++, wn++;
-                       while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) s+=4, wn+=4;
-                       while (*s-1u<0x7f) s++, wn++;
-                       if (!*s) return wn;
-                       if (*s-SA >= SB-SA) goto ilseq2;
+       if (MB_CUR_MAX==1) {
+               if (!ws) return strlen((const char *)s);
+               for (;;) {
+                       if (!wn) {
+                               *src = (const void *)s;
+                               return wn0;
+                       }
+                       if (!*s) break;
+                       c = *s++;
+                       *ws++ = CODEUNIT(c);
+                       wn--;
                }
-               c = bittab[*s++-SA];
-               do {
-resume0:
-                       if (OOB(c,*s)) goto ilseq2; s++;
-                       c <<= 6; if (!(c&(1U<<31))) break;
-                       if (*s++-0x80u >= 0x40) goto ilseq2;
-                       c <<= 6; if (!(c&(1U<<31))) break;
-                       if (*s++-0x80u >= 0x40) goto ilseq2;
-               } while (0);
-               wn++; c = 0;
+               *ws = 0;
+               *src = 0;
+               return wn0-wn;
        }
 
-       while (wn) {
-               if (*s-SA >= SB-SA) {
-                       if (wn >= 7) {
-                               while (((uintptr_t)s&3) && *s-1u<0x7f) {
-                                       *ws++ = *s++;
-                                       wn--;
-                               }
-                               while (wn>=4 && !(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) {
-                                       *ws++ = *s++;
-                                       *ws++ = *s++;
-                                       *ws++ = *s++;
-                                       *ws++ = *s++;
-                                       wn -= 4;
-                               }
+       if (!ws) for (;;) {
+#ifdef __GNUC__
+               typedef uint32_t __attribute__((__may_alias__)) w32;
+               if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) {
+                       while (!(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) {
+                               s += 4;
+                               wn -= 4;
                        }
-                       while (wn && *s-1u<0x7f) {
-                               *ws++ = *s++;
-                               wn--;
+               }
+#endif
+               if (*s-1u < 0x7f) {
+                       s++;
+                       wn--;
+                       continue;
+               }
+               if (*s-SA > SB-SA) break;
+               c = bittab[*s++-SA];
+resume0:
+               if (OOB(c,*s)) { s--; break; }
+               s++;
+               if (c&(1U<<25)) {
+                       if (*s-0x80u >= 0x40) { s-=2; break; }
+                       s++;
+                       if (c&(1U<<19)) {
+                               if (*s-0x80u >= 0x40) { s-=3; break; }
+                               s++;
                        }
-                       if (!wn) break;
-                       if (!*s) {
-                               *ws = 0;
-                               *src = 0;
-                               return ws-wsorig;
+               }
+               wn--;
+               c = 0;
+       } else for (;;) {
+               if (!wn) {
+                       *src = (const void *)s;
+                       return wn0;
+               }
+#ifdef __GNUC__
+               typedef uint32_t __attribute__((__may_alias__)) w32;
+               if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) {
+                       while (wn>=5 && !(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) {
+                               *ws++ = *s++;
+                               *ws++ = *s++;
+                               *ws++ = *s++;
+                               *ws++ = *s++;
+                               wn -= 4;
                        }
-                       if (*s-SA >= SB-SA) goto ilseq;
                }
+#endif
+               if (*s-1u < 0x7f) {
+                       *ws++ = *s++;
+                       wn--;
+                       continue;
+               }
+               if (*s-SA > SB-SA) break;
                c = bittab[*s++-SA];
-               do {
 resume:
-                       if (OOB(c,*s)) goto ilseq;
-                       c = (c<<6) | *s++-0x80;
-                       if (!(c&(1U<<31))) break;
-
-                       if (*s-0x80u >= 0x40) goto ilseq;
-                       c = (c<<6) | *s++-0x80;
-                       if (!(c&(1U<<31))) break;
-
-                       if (*s-0x80u >= 0x40) goto ilseq;
+               if (OOB(c,*s)) { s--; break; }
+               c = (c<<6) | *s++-0x80;
+               if (c&(1U<<31)) {
+                       if (*s-0x80u >= 0x40) { s-=2; break; }
                        c = (c<<6) | *s++-0x80;
-               } while (0);
+                       if (c&(1U<<31)) {
+                               if (*s-0x80u >= 0x40) { s-=3; break; }
+                               c = (c<<6) | *s++-0x80;
+                       }
+               }
+               *ws++ = c;
+               wn--;
+               c = 0;
+       }
 
-               *ws++ = c; wn--; c = 0;
+       if (!c && !*s) {
+               if (ws) {
+                       *ws = 0;
+                       *src = 0;
+               }
+               return wn0-wn;
        }
-       *src = (const void *)s;
-       return ws-wsorig;
-ilseq:
-       *src = (const void *)s;
-ilseq2:
-       /* enter permanently failing state */
-       *(unsigned *)st = FAILSTATE;
        errno = EILSEQ;
+       if (ws) *src = (const void *)s;
        return -1;
 }