Index: libc/config/linux/aarch64/entrypoints.txt =================================================================== --- libc/config/linux/aarch64/entrypoints.txt +++ libc/config/linux/aarch64/entrypoints.txt @@ -65,6 +65,7 @@ libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr + libc.src.string.strsep libc.src.string.strsignal libc.src.string.strspn libc.src.string.strstr Index: libc/config/linux/arm/entrypoints.txt =================================================================== --- libc/config/linux/arm/entrypoints.txt +++ libc/config/linux/arm/entrypoints.txt @@ -52,6 +52,7 @@ libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr + libc.src.string.strsep libc.src.string.strspn libc.src.string.strstr libc.src.string.strtok Index: libc/config/linux/riscv64/entrypoints.txt =================================================================== --- libc/config/linux/riscv64/entrypoints.txt +++ libc/config/linux/riscv64/entrypoints.txt @@ -66,6 +66,7 @@ libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr + libc.src.string.strsep libc.src.string.strsignal libc.src.string.strspn libc.src.string.strstr Index: libc/config/linux/x86_64/entrypoints.txt =================================================================== --- libc/config/linux/x86_64/entrypoints.txt +++ libc/config/linux/x86_64/entrypoints.txt @@ -66,6 +66,7 @@ libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr + libc.src.string.strsep libc.src.string.strsignal libc.src.string.strspn libc.src.string.strstr Index: libc/src/string/CMakeLists.txt =================================================================== --- libc/src/string/CMakeLists.txt +++ libc/src/string/CMakeLists.txt @@ -325,6 +325,16 @@ strrchr.h ) +add_entrypoint_object( + strsep + SRCS + strsep.cpp + HDRS + strsep.h + DEPENDS + .string_utils +) + add_entrypoint_object( strsignal SRCS Index: libc/src/string/string_utils.h =================================================================== --- libc/src/string/string_utils.h +++ libc/src/string/string_utils.h @@ -182,6 +182,7 @@ // is found is then stored within 'context' for subsequent calls. Subsequent // calls will use 'context' when a nullptr is passed in for 'src'. Once the null // terminating character is reached, returns a nullptr. +template LIBC_INLINE char *string_token(char *__restrict src, const char *__restrict delimiter_string, char **__restrict saveptr) { @@ -193,8 +194,9 @@ for (; *delimiter_string != '\0'; ++delimiter_string) delimiter_set.set(*delimiter_string); - for (; *src != '\0' && delimiter_set.test(*src); ++src) - ; + if (SkipDelim) + for (; *src != '\0' && delimiter_set.test(*src); ++src) + ; if (*src == '\0') { *saveptr = src; return nullptr; Index: libc/src/string/strsep.h =================================================================== --- /dev/null +++ libc/src/string/strsep.h @@ -0,0 +1,18 @@ +//===-- Implementation header for strsep ------------------------*- 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_STRING_STRSEP_H +#define LLVM_LIBC_SRC_STRING_STRSEP_H + +namespace __llvm_libc { + +char *strsep(char **stringp, const char *delim); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRSEP_H Index: libc/src/string/strsep.cpp =================================================================== --- /dev/null +++ libc/src/string/strsep.cpp @@ -0,0 +1,21 @@ +//===-- Implementation of strsep ------------------------------------------===// +// +// 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/string/strsep.h" + +#include "src/string/string_utils.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, strsep, (char **stringp, const char *delim)) { + if (!*stringp) + return nullptr; + return internal::string_token(*stringp, delim, stringp); +} + +} // namespace __llvm_libc Index: libc/test/src/string/CMakeLists.txt =================================================================== --- libc/test/src/string/CMakeLists.txt +++ libc/test/src/string/CMakeLists.txt @@ -308,6 +308,16 @@ libc.src.string.strrchr ) +add_libc_unittest( + strsep_test + SUITE + libc_string_unittests + SRCS + strsep_test.cpp + DEPENDS + libc.src.string.strsep +) + add_libc_unittest( strsignal_test SUITE Index: libc/test/src/string/strsep_test.cpp =================================================================== --- /dev/null +++ libc/test/src/string/strsep_test.cpp @@ -0,0 +1,53 @@ +//===-- Unittests for strsep ----------------------------------------------===// +// +// 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/string/strsep.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcStrsepTest, NullSrc) { + char *string = nullptr; + EXPECT_STREQ(__llvm_libc::strsep(&string, ""), nullptr); +} + +TEST(LlvmLibcStrsepTest, NoTokenFound) { + { + char s[] = ""; + char *string = s, *orig = s; + EXPECT_STREQ(__llvm_libc::strsep(&string, ""), nullptr); + EXPECT_EQ(orig, string); + } + { + char s[] = "abcde"; + char *string = s, *orig = s; + EXPECT_STREQ(__llvm_libc::strsep(&string, ""), orig); + EXPECT_EQ(string, orig + 5); + } + { + char s[] = "abcde"; + char *string = s, *orig = s; + EXPECT_STREQ(__llvm_libc::strsep(&string, "fghijk"), orig); + EXPECT_EQ(string, orig + 5); + } +} + +TEST(LlvmLibcStrsepTest, TokenFound) { + char s[] = "abacd"; + char *string = s; + EXPECT_STREQ(__llvm_libc::strsep(&string, "c"), "aba"); + EXPECT_STREQ(string, "d"); +} + +TEST(LlvmLibcStrsepTest, DelimitersShouldNotBeIncludedInToken) { + char s[] = "__ab__:cd_:_ef_:_"; + char *string = s; + const char *expected[] = {"", "", "ab", "", "", "cd", "", + "", "ef", "", "", "", nullptr}; + for (int i = 0; expected[i]; i++) { + ASSERT_STREQ(__llvm_libc::strsep(&string, "_:"), expected[i]); + } +}