diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -239,6 +239,7 @@ # string.h entrypoints that depend on malloc libc.src.string.strdup + libc.src.string.strndup ) endif() diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -284,6 +284,11 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "strndup", + RetValSpec, + [ArgSpec,ArgSpec] + >, FunctionSpec< "strpbrk", RetValSpec, diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -153,6 +153,18 @@ strncpy.h ) +add_entrypoint_object( + strndup + SRCS + strndup.cpp + HDRS + strndup.h + DEPENDS + .memcpy + .string_utils + libc.include.stdlib +) + add_entrypoint_object( strnlen SRCS diff --git a/libc/src/string/strndup.h b/libc/src/string/strndup.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strndup.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strndup -----------------------*- 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_STRNDUP_H +#define LLVM_LIBC_SRC_STRING_STRNDUP_H + +#include + +namespace __llvm_libc { + +char *strndup(const char *src, size_t size); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRNDUP_H diff --git a/libc/src/string/strndup.cpp b/libc/src/string/strndup.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strndup.cpp @@ -0,0 +1,35 @@ +//===-- Implementation of strndup -----------------------------------------===// +// +// 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/strndup.h" +#include "src/string/memcpy.h" +#include "src/string/string_utils.h" + +#include "src/__support/common.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, strndup, (const char *src, size_t size)) { + if (src == nullptr) + return nullptr; + size_t len = internal::string_length(src); + if (len > size) + len = size; + char *dest = reinterpret_cast(::malloc(len + 1)); // NOLINT + if (dest == nullptr) + return nullptr; + char *result = + reinterpret_cast(__llvm_libc::memcpy(dest, src, len + 1)); + result[len] = '\0'; + return result; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -143,6 +143,17 @@ libc.src.string.strncpy ) +add_libc_unittest( + strndup_test + SUITE + libc_string_unittests + SRCS + strndup_test.cpp + DEPENDS + libc.include.stdlib + libc.src.string.strndup +) + add_libc_unittest( strnlen_test SUITE diff --git a/libc/test/src/string/strndup_test.cpp b/libc/test/src/string/strndup_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strndup_test.cpp @@ -0,0 +1,52 @@ +//===-- Unittests for strndup ---------------------------------------------===// +// +// 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/strndup.h" +#include "utils/UnitTest/Test.h" +#include + +TEST(LlvmLibcstrndupTest, EmptyString) { + const char *empty = ""; + + char *result = __llvm_libc::strndup(empty, 1); + ASSERT_NE(result, static_cast(nullptr)); + ASSERT_NE(empty, const_cast(result)); + ASSERT_STREQ(empty, result); + ::free(result); +} + +TEST(LlvmLibcstrndupTest, AnyString) { + const char *abc = "abc"; + + char *result = __llvm_libc::strndup(abc, 3); + + ASSERT_NE(result, static_cast(nullptr)); + ASSERT_NE(abc, const_cast(result)); + ASSERT_STREQ(abc, result); + ::free(result); + + result = __llvm_libc::strndup(abc, 1); + + ASSERT_NE(result, static_cast(nullptr)); + ASSERT_NE(abc, const_cast(result)); + ASSERT_STREQ("a", result); + ::free(result); + + result = __llvm_libc::strndup(abc, 10); + + ASSERT_NE(result, static_cast(nullptr)); + ASSERT_NE(abc, const_cast(result)); + ASSERT_STREQ(abc, result); + ::free(result); +} + +TEST(LlvmLibcstrndupTest, NullPtr) { + char *result = __llvm_libc::strndup(nullptr, 0); + + ASSERT_EQ(result, static_cast(nullptr)); +}