diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index b70ca3fe8bacb..7a33e842413a8 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -366,6 +366,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.wchar.wctob libc.src.wchar.wmemset libc.src.wchar.wcschr + libc.src.wchar.wcspbrk libc.src.wchar.wcsspn libc.src.wchar.wmemcmp diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index de96b393433b0..c98d4fdcae728 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -42,6 +42,13 @@ functions: arguments: - type: const wchar_t * - type: wchar_t + - name: wcspbrk + standards: + - stdc + return_type: const wchar_t * + arguments: + - type: const wchar_t * + - type: const wchar_t * - name: wcsspn standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 0105176acb4b5..50b17c617258e 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -57,6 +57,17 @@ add_entrypoint_object( libc.src.__support.wctype_utils ) +add_entrypoint_object( + wcspbrk + SRCS + wcspbrk.cpp + HDRS + wcspbrk.h + DEPENDS + libc.hdr.wchar_macros + libc.src.__support.wctype_utils +) + add_entrypoint_object( wcsspn SRCS diff --git a/libc/src/wchar/wcspbrk.cpp b/libc/src/wchar/wcspbrk.cpp new file mode 100644 index 0000000000000..bf305a5dbe125 --- /dev/null +++ b/libc/src/wchar/wcspbrk.cpp @@ -0,0 +1,34 @@ +//===-- Implementation of wcspbrk -----------------------------------------===// +// +// 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/wcspbrk.h" + +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE_DECL { + +bool contains_char(const wchar_t *str, wchar_t target) { + for (; *str != L'\0'; str++) + if (*str == target) + return true; + + return false; +} + +LLVM_LIBC_FUNCTION(const wchar_t *, wcspbrk, + (const wchar_t *src, const wchar_t *breakset)) { + // currently O(n * m), can be further optimized to O(n + m) with a hash set + for (int src_idx = 0; src[src_idx] != 0; src_idx++) + if (contains_char(breakset, src[src_idx])) + return src + src_idx; + + return nullptr; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wcspbrk.h b/libc/src/wchar/wcspbrk.h new file mode 100644 index 0000000000000..531651b0b723a --- /dev/null +++ b/libc/src/wchar/wcspbrk.h @@ -0,0 +1,21 @@ +//===-- Implementation header for wcspbrk ---------------------------------===// +// +// 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_WCSPBRK_H +#define LLVM_LIBC_SRC_WCHAR_WCSPBRK_H + +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +const wchar_t *wcspbrk(const wchar_t *src, const wchar_t *breakset); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCSPBRK_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index 780b5b5d25acb..b5e218047ed6d 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -55,6 +55,16 @@ add_libc_test( libc.src.wchar.wcschr ) +add_libc_test( + wcspbrk_test + SUITE + libc_wchar_unittests + SRCS + wcspbrk_test.cpp + DEPENDS + libc.src.wchar.wcspbrk +) + add_libc_test( wcsspn_test SUITE diff --git a/libc/test/src/wchar/wcspbrk_test.cpp b/libc/test/src/wchar/wcspbrk_test.cpp new file mode 100644 index 0000000000000..f7754c0b324e9 --- /dev/null +++ b/libc/test/src/wchar/wcspbrk_test.cpp @@ -0,0 +1,62 @@ +//===-- Unittests for wcspbrk ---------------------------------------------===// +// +// 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/wchar/wcspbrk.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWCSPBrkTest, EmptyStringShouldReturnNullptr) { + // The search should not include the null terminator. + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(L"", L""), nullptr); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(L"_", L""), nullptr); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(L"", L"_"), nullptr); +} + +TEST(LlvmLibcWCSPBrkTest, ShouldNotFindAnythingAfterNullTerminator) { + const wchar_t src[4] = {'a', 'b', '\0', 'c'}; + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"c"), nullptr); +} + +TEST(LlvmLibcWCSPBrkTest, ShouldReturnNullptrIfNoCharactersFound) { + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(L"12345", L"abcdef"), nullptr); +} + +TEST(LlvmLibcWCSPBrkTest, FindsFirstCharacter) { + const wchar_t *src = L"12345"; + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"1"), src); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"-1"), src); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"1_"), src); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"f1_"), src); +} + +TEST(LlvmLibcWCSPBrkTest, FindsMiddleCharacter) { + const wchar_t *src = L"12345"; + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"3"), src + 2); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"?3"), src + 2); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"3F"), src + 2); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"z3_"), src + 2); +} + +TEST(LlvmLibcWCSPBrkTest, FindsLastCharacter) { + const wchar_t *src = L"12345"; + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"5"), src + 4); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"r5"), src + 4); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"59"), src + 4); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"n5_"), src + 4); +} + +TEST(LlvmLibcWCSPBrkTest, FindsFirstOfRepeated) { + const wchar_t *src = L"A,B,C,D"; + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L","), src + 1); +} + +TEST(LlvmLibcWCSPBrkTest, FindsFirstInBreakset) { + const wchar_t *src = L"12345"; + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"34"), src + 2); + EXPECT_EQ(LIBC_NAMESPACE::wcspbrk(src, L"43"), src + 2); +}