From f773d37ee1df1d5fd6b75ab83ddfa53dcb4bed84 Mon Sep 17 00:00:00 2001 From: cgyurgyik Date: Wed, 22 Jul 2020 18:21:00 -0400 Subject: [PATCH] [libc] Implements strnlen. Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D84247 --- libc/config/linux/aarch64/entrypoints.txt | 1 + libc/config/linux/api.td | 1 + libc/config/linux/x86_64/entrypoints.txt | 1 + libc/spec/posix.td | 21 ++++++++++++++ libc/src/string/CMakeLists.txt | 10 +++++++ libc/src/string/strnlen.cpp | 23 ++++++++++++++++ libc/src/string/strnlen.h | 20 ++++++++++++++ libc/test/src/string/CMakeLists.txt | 10 +++++++ libc/test/src/string/strnlen_test.cpp | 46 +++++++++++++++++++++++++++++++ 9 files changed, 133 insertions(+) create mode 100644 libc/src/string/strnlen.cpp create mode 100644 libc/src/string/strnlen.h create mode 100644 libc/test/src/string/strnlen_test.cpp diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index cd805d7..4a0aa28 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -12,6 +12,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memchr libc.src.string.strchr libc.src.string.strstr + libc.src.string.strnlen ) set(TARGET_LIBM_ENTRYPOINTS diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td index 22e97b8..7fc199e 100644 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -213,6 +213,7 @@ def StringAPI : PublicAPI<"string.h"> { "strtok", "strerror", "strlen", + "strnlen" ]; let TypeDeclarations = [ diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index e8a1adb..04acfb3 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -30,6 +30,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memchr libc.src.string.strchr libc.src.string.strstr + libc.src.string.strnlen # sys/mman.h entrypoints libc.src.sys.mman.mmap diff --git a/libc/spec/posix.td b/libc/spec/posix.td index 7085e81..732b6a6 100644 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -11,6 +11,8 @@ def RestrictStructSigactionPtr : RestrictedPtrType; def ConstRestrictStructSigactionPtr : ConstType; def POSIX : StandardSpec<"POSIX"> { + PtrType CharPtr = PtrType; + ConstType ConstCharPtr = ConstType; NamedType OffTType = NamedType<"off_t">; NamedType SSizeTType = NamedType<"ssize_t">; @@ -203,11 +205,30 @@ def POSIX : StandardSpec<"POSIX"> { >, ] >; + + HeaderSpec String = HeaderSpec< + "string.h", + [ + Macro<"NULL">, + ], + [ + SizeTType, + ], + [], // Enumerations + [ + FunctionSpec< + "strnlen", + RetValSpec, + [ArgSpec, ArgSpec] + >, + ] + >; let Headers = [ Errno, SysMMan, Signal, UniStd, + String ]; } diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index 98693cc..8bd7c1c 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -68,6 +68,16 @@ add_entrypoint_object( strstr.h ) +add_entrypoint_object( + strnlen + SRCS + strnlen.cpp + HDRS + strnlen.h + DEPENDS + .memchr +) + # Helper to define a function with multiple implementations # - Computes flags to satisfy required/rejected features and arch, # - Declares an entry point, diff --git a/libc/src/string/strnlen.cpp b/libc/src/string/strnlen.cpp new file mode 100644 index 0000000..17dd6e1 --- /dev/null +++ b/libc/src/string/strnlen.cpp @@ -0,0 +1,23 @@ +//===-- Implementation of strnlen------------------------------------------===// +// +// 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/strnlen.h" + +#include "src/__support/common.h" +#include "src/string/memchr.h" +#include + +namespace __llvm_libc { + +size_t LLVM_LIBC_ENTRYPOINT(strnlen)(const char *src, size_t n) { + const char *temp = + reinterpret_cast(__llvm_libc::memchr(src, '\0', n)); + return temp ? temp - src : n; +} + +} // namespace __llvm_libc diff --git a/libc/src/string/strnlen.h b/libc/src/string/strnlen.h new file mode 100644 index 0000000..2d2ee97 --- /dev/null +++ b/libc/src/string/strnlen.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strnlen ------------------------*- 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_STRNLEN_H +#define LLVM_LIBC_SRC_STRING_STRNLEN_H + +#include + +namespace __llvm_libc { + +size_t strnlen(const char *src, size_t n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRNLEN_H diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index 81ae08d..be43cc9 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -72,6 +72,16 @@ add_libc_unittest( libc.src.string.strstr ) +add_libc_unittest( + strnlen_test + SUITE + libc_string_unittests + SRCS + strnlen_test.cpp + DEPENDS + libc.src.string.strnlen +) + # Tests all implementations that can run on the host. function(add_libc_multi_impl_test name) get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations) diff --git a/libc/test/src/string/strnlen_test.cpp b/libc/test/src/string/strnlen_test.cpp new file mode 100644 index 0000000..9d8616b --- /dev/null +++ b/libc/test/src/string/strnlen_test.cpp @@ -0,0 +1,46 @@ +//===-- Unittests for strnlen----------------------------------------------===// +// +// 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/strnlen.h" +#include "utils/UnitTest/Test.h" +#include + +TEST(StrNLenTest, EmptyString) { + const char *empty = ""; + ASSERT_EQ(static_cast(0), __llvm_libc::strnlen(empty, 0)); + // If N is greater than string length, this should still return 0. + ASSERT_EQ(static_cast(0), __llvm_libc::strnlen(empty, 1)); +} + +TEST(StrNLenTest, OneCharacterString) { + const char *single = "X"; + ASSERT_EQ(static_cast(1), __llvm_libc::strnlen(single, 1)); + // If N is zero, this should return 0. + ASSERT_EQ(static_cast(0), __llvm_libc::strnlen(single, 0)); + // If N is greater than string length, this should still return 1. + ASSERT_EQ(static_cast(1), __llvm_libc::strnlen(single, 2)); +} + +TEST(StrNLenTest, ManyCharacterString) { + const char *many = "123456789"; + ASSERT_EQ(static_cast(9), __llvm_libc::strnlen(many, 9)); + // If N is smaller than the string length, it should return N. + ASSERT_EQ(static_cast(3), __llvm_libc::strnlen(many, 3)); + // If N is zero, this should return 0. + ASSERT_EQ(static_cast(0), __llvm_libc::strnlen(many, 0)); + // If N is greater than the string length, this should still return 9. + ASSERT_EQ(static_cast(9), __llvm_libc::strnlen(many, 42)); +} + +TEST(StrNLenTest, CharactersAfterNullTerminatorShouldNotBeIncluded) { + const char str[5] = {'a', 'b', 'c', '\0', 'd'}; + ASSERT_EQ(static_cast(3), __llvm_libc::strnlen(str, 3)); + // This should only read up to the null terminator. + ASSERT_EQ(static_cast(3), __llvm_libc::strnlen(str, 4)); + ASSERT_EQ(static_cast(3), __llvm_libc::strnlen(str, 5)); +} -- 2.7.4