Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.wchar.wcsspn
libc.src.wchar.wmemcmp
libc.src.wchar.wmemcpy
libc.src.wchar.wcsstr

# sys/uio.h entrypoints
libc.src.sys.uio.writev
Expand Down
7 changes: 7 additions & 0 deletions libc/include/wchar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,10 @@ functions:
- type: __restrict wchar_t *
- type: const __restrict wchar_t *
- type: size_t
- name: wcsstr
standards:
- stdc
return_type: const wchar_t *
arguments:
- type: const wchar_t *
- type: const wchar_t *
12 changes: 12 additions & 0 deletions libc/src/wchar/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,15 @@ add_entrypoint_object(
libc.hdr.wchar_macros
libc.src.__support.wctype_utils
)

add_entrypoint_object(
wcsstr
SRCS
wcsstr.cpp
HDRS
wcsstr.h
DEPENDS
libc.hdr.types.size_t
libc.hdr.wchar_macros
libc.src.string.string_utils
)
39 changes: 39 additions & 0 deletions libc/src/wchar/wcsstr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//===-- Implementation of wcsstr ------------------------------------------===//
//
// 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/wcsstr.h"

#include "hdr/types/size_t.h"
#include "hdr/types/wchar_t.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/string/string_utils.h"

namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(const wchar_t *, wcsstr,
(const wchar_t *s1, const wchar_t *s2)) {
size_t s1_len = internal::string_length(s1);
size_t s2_len = internal::string_length(s2);
// If string to be found has length 0, return s1.
if (s2_len == 0)
return s1;
// If string to be found has length longer than s1, return nullptr.
if (s2_len > s1_len)
return nullptr;
for (size_t i = 0; i <= (s1_len - s2_len); ++i) {
size_t j = 0;
for (; j < s2_len && s1[i + j] == s2[j]; ++j)
;
if (j == s2_len)
return (s1 + i);
}
return nullptr;
}

} // namespace LIBC_NAMESPACE_DECL
21 changes: 21 additions & 0 deletions libc/src/wchar/wcsstr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for wcsstr ----------------------------------===//
//
// 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_WCSSTR_H
#define LLVM_LIBC_SRC_WCHAR_WCSSTR_H

#include "hdr/types/wchar_t.h"
#include "src/__support/macros/config.h"

namespace LIBC_NAMESPACE_DECL {

const wchar_t *wcsstr(const wchar_t *s1, const wchar_t *s2);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_WCHAR_WCSSTR_H
10 changes: 10 additions & 0 deletions libc/test/src/wchar/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,13 @@ add_libc_test(
DEPENDS
libc.src.wchar.wmemcpy
)

add_libc_test(
wcsstr_test
SUITE
libc_wchar_unittests
SRCS
wcsstr_test.cpp
DEPENDS
libc.src.wchar.wcsstr
)
113 changes: 113 additions & 0 deletions libc/test/src/wchar/wcsstr_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//===-- Unittests for wcsstr ----------------------------------------------===//
//
// 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/wcsstr.h"
#include "test/UnitTest/Test.h"

TEST(LlvmLibcWCSStrTest, NeedleNotInHaystack) {
// Should return nullptr if string is not found.
const wchar_t *haystack = L"12345";
const wchar_t *needle = L"a";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), nullptr);
}

TEST(LlvmLibcWCSStrTest, NeedleIsEmptyString) {
// Should return pointer to first character if needle is empty.
const wchar_t *haystack = L"12345";
const wchar_t *needle = L"";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack);
}

TEST(LlvmLibcWCSStrTest, HaystackIsEmptyString) {
// Should return nullptr since haystack is empty.
const wchar_t *needle = L"12345";
const wchar_t *haystack = L"";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), nullptr);
}

TEST(LlvmLibcWCSStrTest, HaystackAndNeedleAreEmptyStrings) {
// Should point to haystack since needle is empty.
const wchar_t *needle = L"";
const wchar_t *haystack = L"";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack);
}

TEST(LlvmLibcWCSStrTest, HaystackAndNeedleAreSingleCharacters) {
const wchar_t *haystack = L"a";
// Should point to haystack.
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"a"), haystack);
// Should return nullptr.
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"b"), nullptr);
}

TEST(LlvmLibcWCSStrTest, NeedleEqualToHaystack) {
const wchar_t *haystack = L"12345";
// Should point to haystack.
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"12345"), haystack);
}

TEST(LlvmLibcWCSStrTest, NeedleLargerThanHaystack) {
const wchar_t *haystack = L"123";
// Should return nullptr.
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"12345"), nullptr);
}

TEST(LlvmLibcWCSStrTest, NeedleAtBeginning) {
const wchar_t *haystack = L"12345";
const wchar_t *needle = L"12";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack);
}

TEST(LlvmLibcWCSStrTest, NeedleInMiddle) {
const wchar_t *haystack = L"abcdefghi";
const wchar_t *needle = L"def";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack + 3);
}

TEST(LlvmLibcWCSStrTest, NeedleDirectlyBeforeNullTerminator) {
const wchar_t *haystack = L"abcdefghi";
const wchar_t *needle = L"ghi";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack + 6);
}

TEST(LlvmLibcWCSStrTest, NeedlePastNullTerminator) {
const wchar_t haystack[5] = {L'1', L'2', L'\0', L'3', L'4'};
// Shouldn't find anything after the null terminator.
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, /*needle=*/L"3"), nullptr);
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, /*needle=*/L"4"), nullptr);
}

TEST(LlvmLibcWCSStrTest, PartialNeedle) {
const wchar_t *haystack = L"la_ap_lap";
const wchar_t *needle = L"lap";
// Shouldn't find la or ap.
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack + 6);
}

TEST(LlvmLibcWCSStrTest, MisspelledNeedle) {
const wchar_t *haystack = L"atalloftwocities...wait, tale";
const wchar_t *needle = L"tale";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack + 25);
}

TEST(LlvmLibcWCSStrTest, AnagramNeedle) {
const wchar_t *haystack = L"dgo_ogd_god_odg_gdo_dog";
const wchar_t *needle = L"dog";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, needle), haystack + 20);
}

TEST(LlvmLibcWCSStrTest, MorphedNeedle) {
// Changes a single letter in the needle to mismatch with the haystack.
const wchar_t *haystack = L"once upon a time";
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"time"), haystack + 12);
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"lime"), nullptr);
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"tome"), nullptr);
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"tire"), nullptr);
ASSERT_EQ(LIBC_NAMESPACE::wcsstr(haystack, L"timo"), nullptr);
}
Loading