1*eb8650a7SLouis Dionne //===----------------------------------------------------------------------===//
24247381eSMuiez Ahmed //
34247381eSMuiez Ahmed // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44247381eSMuiez Ahmed // See https://llvm.org/LICENSE.txt for license information.
54247381eSMuiez Ahmed // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64247381eSMuiez Ahmed //
74247381eSMuiez Ahmed //===----------------------------------------------------------------------===//
84247381eSMuiez Ahmed
94247381eSMuiez Ahmed #include <cwchar> // mbstate_t
104247381eSMuiez Ahmed #include <limits.h> // MB_LEN_MAX
114247381eSMuiez Ahmed #include <stdlib.h> // MB_CUR_MAX, size_t
124247381eSMuiez Ahmed #include <string.h> // memcpy
134247381eSMuiez Ahmed
144247381eSMuiez Ahmed // Converts `max_source_chars` from the wide character buffer pointer to by *`src`,
154247381eSMuiez Ahmed // into the multi byte character sequence buffer stored at `dst`, which must be
164247381eSMuiez Ahmed // `dst_size_bytes` bytes in size. Returns the number of bytes in the sequence
174247381eSMuiez Ahmed // converted from *src, excluding the null terminator.
184247381eSMuiez Ahmed // Returns (size_t) -1 if an error occurs and sets errno.
194247381eSMuiez Ahmed // If `dst` is NULL, `dst_size_bytes` is ignored and no bytes are copied to `dst`.
204247381eSMuiez Ahmed _LIBCPP_FUNC_VIS
wcsnrtombs(char * __restrict dst,const wchar_t ** __restrict src,size_t max_source_chars,size_t dst_size_bytes,mbstate_t * __restrict ps)214247381eSMuiez Ahmed size_t wcsnrtombs(char *__restrict dst, const wchar_t **__restrict src,
224247381eSMuiez Ahmed size_t max_source_chars, size_t dst_size_bytes,
234247381eSMuiez Ahmed mbstate_t *__restrict ps) {
244247381eSMuiez Ahmed
254247381eSMuiez Ahmed const size_t invalid_wchar = static_cast<size_t>(-1);
264247381eSMuiez Ahmed
274247381eSMuiez Ahmed size_t source_converted;
284247381eSMuiez Ahmed size_t dest_converted;
294247381eSMuiez Ahmed size_t result = 0;
304247381eSMuiez Ahmed
314247381eSMuiez Ahmed // If `dst` is null then `dst_size_bytes` should be ignored according to the
324247381eSMuiez Ahmed // standard. Setting dst_size_bytes to a large value has this effect.
334247381eSMuiez Ahmed if (dst == nullptr)
344247381eSMuiez Ahmed dst_size_bytes = static_cast<size_t>(-1);
354247381eSMuiez Ahmed
364247381eSMuiez Ahmed for (dest_converted = source_converted = 0;
374247381eSMuiez Ahmed source_converted < max_source_chars && (!dst || dest_converted < dst_size_bytes);
384247381eSMuiez Ahmed ++source_converted, dest_converted += result) {
394247381eSMuiez Ahmed wchar_t c = (*src)[source_converted];
404247381eSMuiez Ahmed size_t dest_remaining = dst_size_bytes - dest_converted;
414247381eSMuiez Ahmed
424247381eSMuiez Ahmed if (dst == nullptr) {
434247381eSMuiez Ahmed result = wcrtomb(NULL, c, ps);
444247381eSMuiez Ahmed } else if (dest_remaining >= static_cast<size_t>(MB_CUR_MAX)) {
454247381eSMuiez Ahmed // dst has enough space to translate in-place.
464247381eSMuiez Ahmed result = wcrtomb(dst + dest_converted, c, ps);
474247381eSMuiez Ahmed } else {
484247381eSMuiez Ahmed /*
494247381eSMuiez Ahmed * dst may not have enough space, so use a temporary buffer.
504247381eSMuiez Ahmed *
514247381eSMuiez Ahmed * We need to save a copy of the conversion state
524247381eSMuiez Ahmed * here so we can restore it if the multibyte
534247381eSMuiez Ahmed * character is too long for the buffer.
544247381eSMuiez Ahmed */
554247381eSMuiez Ahmed char buff[MB_LEN_MAX];
564247381eSMuiez Ahmed mbstate_t mbstate_tmp;
574247381eSMuiez Ahmed
584247381eSMuiez Ahmed if (ps != nullptr)
594247381eSMuiez Ahmed mbstate_tmp = *ps;
604247381eSMuiez Ahmed result = wcrtomb(buff, c, ps);
614247381eSMuiez Ahmed
624247381eSMuiez Ahmed if (result > dest_remaining) {
634247381eSMuiez Ahmed // Multi-byte sequence for character won't fit.
644247381eSMuiez Ahmed if (ps != nullptr)
654247381eSMuiez Ahmed *ps = mbstate_tmp;
664247381eSMuiez Ahmed if (result != invalid_wchar)
674247381eSMuiez Ahmed break;
684247381eSMuiez Ahmed } else {
694247381eSMuiez Ahmed // The buffer was used, so we need copy the translation to dst.
704247381eSMuiez Ahmed memcpy(dst, buff, result);
714247381eSMuiez Ahmed }
724247381eSMuiez Ahmed }
734247381eSMuiez Ahmed
744247381eSMuiez Ahmed // result (char_size) contains the size of the multi-byte-sequence converted.
754247381eSMuiez Ahmed // Otherwise, result (char_size) is (size_t) -1 and wcrtomb() sets the errno.
764247381eSMuiez Ahmed if (result == invalid_wchar) {
774247381eSMuiez Ahmed if (dst)
784247381eSMuiez Ahmed *src = *src + source_converted;
794247381eSMuiez Ahmed return invalid_wchar;
804247381eSMuiez Ahmed }
814247381eSMuiez Ahmed
824247381eSMuiez Ahmed if (c == L'\0') {
834247381eSMuiez Ahmed if (dst)
844247381eSMuiez Ahmed *src = NULL;
854247381eSMuiez Ahmed return dest_converted;
864247381eSMuiez Ahmed }
874247381eSMuiez Ahmed }
884247381eSMuiez Ahmed
894247381eSMuiez Ahmed if (dst)
904247381eSMuiez Ahmed *src = *src + source_converted;
914247381eSMuiez Ahmed
924247381eSMuiez Ahmed return dest_converted;
934247381eSMuiez Ahmed }
94