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