|
Message-Id: <976b592b08c82bd6b1f8834904071db6602f3885.1685534402.git.Jens.Gustedt@inria.fr> Date: Wed, 31 May 2023 16:01:43 +0200 From: Jens Gustedt <Jens.Gustedt@...ia.fr> To: musl@...ts.openwall.com Subject: [C23 string conversion 1/2] C23: implement the c8rtomb and mbrtoc8 functions The implementations each separate two cases: a fast path that corresponds to calls with state variables that are in the initial state and a leading input character that is ASCII; the other cases are handled by a function for the slow path. These functions are implemented such that the fast versions should not need stack variables of their own, and thus can tail call into the slow path with a jmp instruction if necessary. The fast versions could typically be also used as shallow inline wrapper, if we like to go that way. Only the implementation of mbrtoc8 is a bit tricky. This is because of the weird conventions for dripping the output bytes one call after the other. This is handled by using the state variable as a stack for the up to three characters that are still to be sent to the output. --- include/uchar.h | 6 +++++ src/multibyte/c8rtomb.c | 31 +++++++++++++++++++++++++ src/multibyte/mbrtoc8.c | 51 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 src/multibyte/c8rtomb.c create mode 100644 src/multibyte/mbrtoc8.c diff --git a/include/uchar.h b/include/uchar.h index 7e5c4d40..9f6a3706 100644 --- a/include/uchar.h +++ b/include/uchar.h @@ -9,6 +9,9 @@ extern "C" { typedef unsigned short char16_t; typedef unsigned char32_t; #endif +#if __cplusplus < 201811L +typedef unsigned char char8_t; +#endif #define __NEED_mbstate_t #define __NEED_size_t @@ -16,6 +19,9 @@ typedef unsigned char32_t; #include <features.h> #include <bits/alltypes.h> +size_t c8rtomb(char *__restrict, char8_t, mbstate_t *__restrict); +size_t mbrtoc8(char8_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict); + size_t c16rtomb(char *__restrict, char16_t, mbstate_t *__restrict); size_t mbrtoc16(char16_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict); diff --git a/src/multibyte/c8rtomb.c b/src/multibyte/c8rtomb.c new file mode 100644 index 00000000..8645e112 --- /dev/null +++ b/src/multibyte/c8rtomb.c @@ -0,0 +1,31 @@ +#include <uchar.h> +#include <wchar.h> + +__attribute__((__noinline__)) +static size_t c8rtomb_slow(char *__restrict s, unsigned char c8, mbstate_t *__restrict st) +{ + // We need an internal state different from mbrtowc. + static mbstate_t internal_state; + if (!st) st = &internal_state; + wchar_t wc; + + // mbrtowc has return values -2, -1, 0, 1. + size_t res = mbrtowc(&wc, (char const*)&c8, 1, st); + switch (res) { + // our implementation of wcrtomb ignores the state + case 1: res = wcrtomb(s, wc, 0); break; + case 0: res = 1; if (s) *s = 0; break; + } + return res; +} + +static size_t __c8rtomb(char *__restrict s, unsigned char c8, mbstate_t *__restrict st) +{ + if (st && !*(unsigned*)st && (c8 < 0x80)) { + if (s) *s = c8; + return 1; + } + return c8rtomb_slow(s, c8, st); +} + +weak_alias(__c8rtomb, c8rtomb); diff --git a/src/multibyte/mbrtoc8.c b/src/multibyte/mbrtoc8.c new file mode 100644 index 00000000..0c73fa10 --- /dev/null +++ b/src/multibyte/mbrtoc8.c @@ -0,0 +1,51 @@ +#include <stdlib.h> +#include <wchar.h> +#include <uchar.h> +#include <errno.h> +#include "internal.h" + +__attribute__((__noinline__)) +static size_t mbrtoc8_slow(unsigned char *__restrict pc8, const char *__restrict src, size_t n, mbstate_t *__restrict st) +{ + static unsigned internal_state; + wchar_t wc; + unsigned* pending = st ? (void*)st : &internal_state; + + // The high bit is set if there is still missing input. If it + // is not set and the value is not zero, a previous call has + // stacked the missing output bytes withing the state. + if ((-*pending) > INT_MIN) { + if (pc8) *pc8 = *pending; + (*pending) >>= 8; + return -3; + } + + // mbrtowc has return values -2, -1, 0, 1, ..., 4. + size_t res = mbrtowc(&wc, src, n, (void*)pending); + if (res <= 4) { + _Alignas(unsigned) unsigned char s[8] = { 0 }; + // Write the result bytes to s over the word boundary + // as we need it. Our wcrtomb implementation ignores + // the state variable, there will be no errors and the + // return value can be ignored. + wcrtomb((void*)(s+3), wc, 0); + // Depending on the endianess this maybe a single load + // instruction. We want to be sure that the bytes are + // in this order, and that the high-order byte is 0. + *pending = (0u|s[4])<<000 | (0u|s[5])<<010 | (0u|s[6])<<020 | (0u|s[7])<<030; + if (pc8) *pc8 = s[3]; + } + return res; +} + +static size_t __mbrtoc8(unsigned char *__restrict pc8, const char *__restrict src, size_t n, mbstate_t *__restrict st) +{ + unsigned char *s = (void*)src; + if (st && !*(unsigned*)st && (s[0] < 0x80u)) { + if (pc8) *pc8 = s[0]; + return s[0]>0; + } + return mbrtoc8_slow(pc8, src, n, st); +} + +weak_alias(__mbrtoc8, mbrtoc8); -- 2.34.1
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.