diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -187,6 +187,7 @@ libc.src.unistd.readlink libc.src.unistd.readlinkat libc.src.unistd.rmdir + libc.src.unistd.swab libc.src.unistd.symlink libc.src.unistd.symlinkat libc.src.unistd.sysconf diff --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt --- a/libc/config/linux/riscv64/entrypoints.txt +++ b/libc/config/linux/riscv64/entrypoints.txt @@ -189,6 +189,7 @@ libc.src.unistd.readlink libc.src.unistd.readlinkat libc.src.unistd.rmdir + libc.src.unistd.swab libc.src.unistd.symlink libc.src.unistd.symlinkat libc.src.unistd.sysconf 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 @@ -191,6 +191,7 @@ libc.src.unistd.readlink libc.src.unistd.readlinkat libc.src.unistd.rmdir + libc.src.unistd.swab libc.src.unistd.symlink libc.src.unistd.symlinkat libc.src.unistd.sysconf diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -544,6 +544,11 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "swab", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "symlink", RetValSpec, diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -191,6 +191,13 @@ .${LIBC_TARGET_OS}.rmdir ) +add_entrypoint_object( + swab + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.swab +) + add_entrypoint_object( symlink ALIAS diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -353,6 +353,14 @@ libc.src.errno.errno ) +add_entrypoint_object( + swab + SRCS + swab.cpp + HDRS + ../swab.h +) + add_entrypoint_object( symlink SRCS diff --git a/libc/src/unistd/linux/swab.cpp b/libc/src/unistd/linux/swab.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/swab.cpp @@ -0,0 +1,29 @@ +//===-- Implementation of swab --------------------------------------------===// +// +// 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/unistd/swab.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void, swab, + (const void *__restrict from, void *__restrict to, + ssize_t n)) { + if (n < 2) + return; + + const unsigned char *f = static_cast(from); + unsigned char *t = static_cast(to); + for (ssize_t i = 1; i < n; i += 2) { + t[i - 1] = f[i]; + t[i] = f[i - 1]; + } +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/swab.h b/libc/src/unistd/swab.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/swab.h @@ -0,0 +1,20 @@ +//===-- Implementation header for swab --------------------------*- 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_UNISTD_SWAB_H +#define LLVM_LIBC_SRC_UNISTD_SWAB_H + +#include // For ssize_t + +namespace __llvm_libc { + +void swab(const void *__restrict from, void *__restrict to, ssize_t n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_SWAB_H diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -225,6 +225,16 @@ libc.src.unistd.rmdir ) +add_libc_unittest( + swab_test + SUITE + libc_unistd_unittests + SRCS + swab_test.cpp + DEPENDS + libc.src.unistd.swab +) + add_libc_unittest( readlink_test SUITE diff --git a/libc/test/src/unistd/swab_test.cpp b/libc/test/src/unistd/swab_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/swab_test.cpp @@ -0,0 +1,69 @@ + +//===-- Unittests for swab ------------------------------------------------===// +// +// 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/unistd/swab.h" + +#include "src/string/string_utils.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcSwabTest, ZeroSizeIsNoOpt) { + void *from = nullptr; + void *to = nullptr; + __llvm_libc::swab(from, to, 0); + ASSERT_EQ(to, static_cast(nullptr)); +} + +TEST(LlvmLibcSwabTest, SingleByteIsNoOpt) { + char from[] = {'a'}; + void *to = nullptr; + __llvm_libc::swab(from, to, sizeof(from)); + ASSERT_EQ(to, static_cast(nullptr)); +} + +TEST(LlvmLibcSwabTest, BytesAreSwappedWithEvenN) { + { + const char *from = "ab"; + char to[3] = {'\0'}; + __llvm_libc::swab(from, to, __llvm_libc::internal::string_length(from)); + ASSERT_STREQ(to, "ba"); + } + { + const char *from = "abcd"; + char to[5] = {'\0'}; + __llvm_libc::swab(from, to, __llvm_libc::internal::string_length(from)); + ASSERT_STREQ(to, "badc"); + } + { + const char *from = "aAaAaA"; + char to[7] = {'\0'}; + __llvm_libc::swab(from, to, __llvm_libc::internal::string_length(from)); + ASSERT_STREQ(to, "AaAaAa"); + } +} + +TEST(LlvmLibcSwabTest, LastByteIgnoredWithOddN) { + { + const char *from = "aba"; + char to[3] = {'\0'}; + __llvm_libc::swab(from, to, __llvm_libc::internal::string_length(from)); + ASSERT_STREQ(to, "ba"); + } + { + const char *from = "abcde"; + char to[5] = {'\0'}; + __llvm_libc::swab(from, to, __llvm_libc::internal::string_length(from)); + ASSERT_STREQ(to, "badc"); + } + { + const char *from = "aAaAaAx"; + char to[7] = {'\0'}; + __llvm_libc::swab(from, to, __llvm_libc::internal::string_length(from)); + ASSERT_STREQ(to, "AaAaAa"); + } +}