1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include <cwchar> // mbstate_t |
10 | #include <limits.h> // MB_LEN_MAX |
11 | #include <stdlib.h> // MB_CUR_MAX, size_t |
12 | #include <string.h> // memcpy |
13 | |
14 | // Converts `max_source_chars` from the wide character buffer pointer to by *`src`, |
15 | // into the multi byte character sequence buffer stored at `dst`, which must be |
16 | // `dst_size_bytes` bytes in size. Returns the number of bytes in the sequence |
17 | // converted from *src, excluding the null terminator. |
18 | // Returns (size_t) -1 if an error occurs and sets errno. |
19 | // If `dst` is NULL, `dst_size_bytes` is ignored and no bytes are copied to `dst`. |
20 | _LIBCPP_EXPORTED_FROM_ABI size_t wcsnrtombs( |
21 | char* __restrict dst, |
22 | const wchar_t** __restrict src, |
23 | size_t max_source_chars, |
24 | size_t dst_size_bytes, |
25 | mbstate_t* __restrict ps) { |
26 | const size_t invalid_wchar = static_cast<size_t>(-1); |
27 | |
28 | size_t source_converted; |
29 | size_t dest_converted; |
30 | size_t result = 0; |
31 | |
32 | // If `dst` is null then `dst_size_bytes` should be ignored according to the |
33 | // standard. Setting dst_size_bytes to a large value has this effect. |
34 | if (dst == nullptr) |
35 | dst_size_bytes = static_cast<size_t>(-1); |
36 | |
37 | for (dest_converted = source_converted = 0; |
38 | source_converted < max_source_chars && (!dst || dest_converted < dst_size_bytes); |
39 | ++source_converted, dest_converted += result) { |
40 | wchar_t c = (*src)[source_converted]; |
41 | size_t dest_remaining = dst_size_bytes - dest_converted; |
42 | |
43 | if (dst == nullptr) { |
44 | result = wcrtomb(NULL, c, ps); |
45 | } else if (dest_remaining >= static_cast<size_t>(MB_CUR_MAX)) { |
46 | // dst has enough space to translate in-place. |
47 | result = wcrtomb(dst + dest_converted, c, ps); |
48 | } else { |
49 | /* |
50 | * dst may not have enough space, so use a temporary buffer. |
51 | * |
52 | * We need to save a copy of the conversion state |
53 | * here so we can restore it if the multibyte |
54 | * character is too long for the buffer. |
55 | */ |
56 | char buff[MB_LEN_MAX]; |
57 | mbstate_t mbstate_tmp; |
58 | |
59 | if (ps != nullptr) |
60 | mbstate_tmp = *ps; |
61 | result = wcrtomb(buff, c, ps); |
62 | |
63 | if (result > dest_remaining) { |
64 | // Multi-byte sequence for character won't fit. |
65 | if (ps != nullptr) |
66 | *ps = mbstate_tmp; |
67 | if (result != invalid_wchar) |
68 | break; |
69 | } else { |
70 | // The buffer was used, so we need copy the translation to dst. |
71 | memcpy(dst, buff, result); |
72 | } |
73 | } |
74 | |
75 | // result (char_size) contains the size of the multi-byte-sequence converted. |
76 | // Otherwise, result (char_size) is (size_t) -1 and wcrtomb() sets the errno. |
77 | if (result == invalid_wchar) { |
78 | if (dst) |
79 | *src = *src + source_converted; |
80 | return invalid_wchar; |
81 | } |
82 | |
83 | if (c == L'\0') { |
84 | if (dst) |
85 | *src = NULL; |
86 | return dest_converted; |
87 | } |
88 | } |
89 | |
90 | if (dst) |
91 | *src = *src + source_converted; |
92 | |
93 | return dest_converted; |
94 | } |
95 | |