diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 4d94f10196fd7..60f70fcba2448 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1248,6 +1248,7 @@ if(LLVM_LIBC_FULL_BUILD) # wchar.h entrypoints libc.src.wchar.mbrtowc libc.src.wchar.wcrtomb + libc.src.wchar.wctomb ) endif() diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 64eb381710668..9c798b101b53f 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -167,6 +167,13 @@ functions: - type: char *__restrict - type: wchar_t - type: mbstate_t *__restrict + - name: wctomb + standards: + - stdc + return_type: int + arguments: + - type: char * + - type: wchar_t - name: wcscpy standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index ec33caccb16d5..51d9de4534893 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -48,6 +48,19 @@ add_entrypoint_object( libc.src.__support.wchar.mbstate ) +add_entrypoint_object( + wctomb + SRCS + wctomb.cpp + HDRS + wctomb.h + DEPENDS + libc.hdr.types.wchar_t + libc.src.__support.wchar.wcrtomb + libc.src.__support.wchar.mbstate + libc.src.__support.libc_errno +) + add_entrypoint_object( mbrtowc SRCS diff --git a/libc/src/wchar/wctomb.cpp b/libc/src/wchar/wctomb.cpp new file mode 100644 index 0000000000000..142302e6ae09b --- /dev/null +++ b/libc/src/wchar/wctomb.cpp @@ -0,0 +1,35 @@ +//===-- Implementation of wctomb ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/wchar/wctomb.h" + +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/__support/wchar/mbstate.h" +#include "src/__support/wchar/wcrtomb.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, wctomb, (char *s, wchar_t wc)) { + static internal::mbstate internal_mbstate; + if (s == nullptr) + return 0; + + auto result = internal::wcrtomb(s, wc, &internal_mbstate); + + if (!result.has_value()) { // invalid wide character + libc_errno = EILSEQ; + return -1; + } + + return static_cast(result.value()); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wctomb.h b/libc/src/wchar/wctomb.h new file mode 100644 index 0000000000000..02a34e5ad229f --- /dev/null +++ b/libc/src/wchar/wctomb.h @@ -0,0 +1,22 @@ +//===-- Implementation header for wctomb ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_WCHAR_WCTOMB_H +#define LLVM_LIBC_SRC_WCHAR_WCTOMB_H + +#include "hdr/types/mbstate_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int wctomb(char *s, wchar_t wc); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCTOMB_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index 184e482c895b1..95a4374abf0a2 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -61,6 +61,17 @@ add_libc_test( libc.src.__support.libc_errno ) +add_libc_test( + wctomb_test + SUITE + libc_wchar_unittests + SRCS + wctomb_test.cpp + DEPENDS + libc.src.wchar.wctomb + libc.hdr.types.wchar_t +) + add_libc_test( wmemset_test SUITE diff --git a/libc/test/src/wchar/wctomb_test.cpp b/libc/test/src/wchar/wctomb_test.cpp new file mode 100644 index 0000000000000..09fbf52806224 --- /dev/null +++ b/libc/test/src/wchar/wctomb_test.cpp @@ -0,0 +1,73 @@ +//===-- Unittests for wctomb ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "hdr/types/wchar_t.h" +#include "src/__support/libc_errno.h" +#include "src/wchar/wctomb.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcWCToMBTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; + +TEST(LlvmLibcWCToMBTest, OneByte) { + wchar_t wc = L'U'; + char mb[4]; + int cnt = LIBC_NAMESPACE::wctomb(mb, wc); + ASSERT_EQ(cnt, 1); + ASSERT_EQ(mb[0], 'U'); +} + +TEST(LlvmLibcWCToMBTest, TwoByte) { + // testing utf32: 0xff -> utf8: 0xc3 0xbf + wchar_t wc = 0xff; + char mb[4]; + int cnt = LIBC_NAMESPACE::wctomb(mb, wc); + ASSERT_EQ(cnt, 2); + ASSERT_EQ(mb[0], static_cast(0xc3)); + ASSERT_EQ(mb[1], static_cast(0xbf)); +} + +TEST(LlvmLibcWCToMBTest, ThreeByte) { + // testing utf32: 0xac15 -> utf8: 0xea 0xb0 0x95 + wchar_t wc = 0xac15; + char mb[4]; + int cnt = LIBC_NAMESPACE::wctomb(mb, wc); + ASSERT_EQ(cnt, 3); + ASSERT_EQ(mb[0], static_cast(0xea)); + ASSERT_EQ(mb[1], static_cast(0xb0)); + ASSERT_EQ(mb[2], static_cast(0x95)); +} + +TEST(LlvmLibcWCToMBTest, FourByte) { + // testing utf32: 0x1f921 -> utf8: 0xf0 0x9f 0xa4 0xa1 + wchar_t wc = 0x1f921; + char mb[4]; + int cnt = LIBC_NAMESPACE::wctomb(mb, wc); + ASSERT_EQ(cnt, 4); + ASSERT_EQ(mb[0], static_cast(0xf0)); + ASSERT_EQ(mb[1], static_cast(0x9f)); + ASSERT_EQ(mb[2], static_cast(0xa4)); + ASSERT_EQ(mb[3], static_cast(0xa1)); +} + +TEST(LlvmLibcWCToMBTest, NullString) { + wchar_t wc = L'A'; + + int cnt = LIBC_NAMESPACE::wctomb(nullptr, wc); + + // no state-dependent encoding + ASSERT_EQ(cnt, 0); +} + +TEST(LlvmLibcWCToMBTest, InvalidWchar) { + wchar_t wc = 0x12ffff; + char mb[4]; + int cnt = LIBC_NAMESPACE::wctomb(mb, wc); + ASSERT_EQ(cnt, -1); + ASSERT_ERRNO_EQ(EILSEQ); +}