xref: /llvm-project/libcxx/src/support/ibm/wcsnrtombs.cpp (revision d0438d2d087e78571a671c98cbb42308e4dcfcec)
1eb8650a7SLouis 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`.
209783f28cSLouis Dionne _LIBCPP_EXPORTED_FROM_ABI size_t wcsnrtombs(
219783f28cSLouis Dionne     char* __restrict dst,
229783f28cSLouis Dionne     const wchar_t** __restrict src,
239783f28cSLouis Dionne     size_t max_source_chars,
249783f28cSLouis Dionne     size_t dst_size_bytes,
254247381eSMuiez Ahmed     mbstate_t* __restrict ps) {
264247381eSMuiez Ahmed   const size_t invalid_wchar = static_cast<size_t>(-1);
274247381eSMuiez Ahmed 
284247381eSMuiez Ahmed   size_t source_converted;
294247381eSMuiez Ahmed   size_t dest_converted;
304247381eSMuiez Ahmed   size_t result = 0;
314247381eSMuiez Ahmed 
324247381eSMuiez Ahmed   // If `dst` is null then `dst_size_bytes` should be ignored according to the
334247381eSMuiez Ahmed   // standard. Setting dst_size_bytes to a large value has this effect.
344247381eSMuiez Ahmed   if (dst == nullptr)
354247381eSMuiez Ahmed     dst_size_bytes = static_cast<size_t>(-1);
364247381eSMuiez Ahmed 
374247381eSMuiez Ahmed   for (dest_converted = source_converted = 0;
384247381eSMuiez Ahmed        source_converted < max_source_chars && (!dst || dest_converted < dst_size_bytes);
394247381eSMuiez Ahmed        ++source_converted, dest_converted += result) {
404247381eSMuiez Ahmed     wchar_t c             = (*src)[source_converted];
414247381eSMuiez Ahmed     size_t dest_remaining = dst_size_bytes - dest_converted;
424247381eSMuiez Ahmed 
434247381eSMuiez Ahmed     if (dst == nullptr) {
44*d0438d2dSLouis Dionne       result = wcrtomb(nullptr, c, ps);
454247381eSMuiez Ahmed     } else if (dest_remaining >= static_cast<size_t>(MB_CUR_MAX)) {
464247381eSMuiez Ahmed       // dst has enough space to translate in-place.
474247381eSMuiez Ahmed       result = wcrtomb(dst + dest_converted, c, ps);
484247381eSMuiez Ahmed     } else {
494247381eSMuiez Ahmed       /*
504247381eSMuiez Ahmed        * dst may not have enough space, so use a temporary buffer.
514247381eSMuiez Ahmed        *
524247381eSMuiez Ahmed        * We need to save a copy of the conversion state
534247381eSMuiez Ahmed        * here so we can restore it if the multibyte
544247381eSMuiez Ahmed        * character is too long for the buffer.
554247381eSMuiez Ahmed        */
564247381eSMuiez Ahmed       char buff[MB_LEN_MAX];
574247381eSMuiez Ahmed       mbstate_t mbstate_tmp;
584247381eSMuiez Ahmed 
594247381eSMuiez Ahmed       if (ps != nullptr)
604247381eSMuiez Ahmed         mbstate_tmp = *ps;
614247381eSMuiez Ahmed       result = wcrtomb(buff, c, ps);
624247381eSMuiez Ahmed 
634247381eSMuiez Ahmed       if (result > dest_remaining) {
644247381eSMuiez Ahmed         // Multi-byte sequence for character won't fit.
654247381eSMuiez Ahmed         if (ps != nullptr)
664247381eSMuiez Ahmed           *ps = mbstate_tmp;
674247381eSMuiez Ahmed         if (result != invalid_wchar)
684247381eSMuiez Ahmed           break;
694247381eSMuiez Ahmed       } else {
704247381eSMuiez Ahmed         // The buffer was used, so we need copy the translation to dst.
714247381eSMuiez Ahmed         memcpy(dst, buff, result);
724247381eSMuiez Ahmed       }
734247381eSMuiez Ahmed     }
744247381eSMuiez Ahmed 
754247381eSMuiez Ahmed     // result (char_size) contains the size of the multi-byte-sequence converted.
764247381eSMuiez Ahmed     // Otherwise, result (char_size) is (size_t) -1 and wcrtomb() sets the errno.
774247381eSMuiez Ahmed     if (result == invalid_wchar) {
784247381eSMuiez Ahmed       if (dst)
794247381eSMuiez Ahmed         *src = *src + source_converted;
804247381eSMuiez Ahmed       return invalid_wchar;
814247381eSMuiez Ahmed     }
824247381eSMuiez Ahmed 
834247381eSMuiez Ahmed     if (c == L'\0') {
844247381eSMuiez Ahmed       if (dst)
85*d0438d2dSLouis Dionne         *src = nullptr;
864247381eSMuiez Ahmed       return dest_converted;
874247381eSMuiez Ahmed     }
884247381eSMuiez Ahmed   }
894247381eSMuiez Ahmed 
904247381eSMuiez Ahmed   if (dst)
914247381eSMuiez Ahmed     *src = *src + source_converted;
924247381eSMuiez Ahmed 
934247381eSMuiez Ahmed   return dest_converted;
944247381eSMuiez Ahmed }
95