diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -18,6 +18,12 @@ }]; } +def FILE : TypeDecl<"FILE"> { + let Decl = [{ + typedef struct FILE FILE; + }]; +} + def AssertMacro : MacroDef<"assert"> { let Defn = [{ #undef assert @@ -159,10 +165,11 @@ def StdIOAPI : PublicAPI<"stdio.h"> { let TypeDeclarations = [ SizeT, + FILE, ]; let Functions = [ - "snprintf", + "fwrite", ]; } diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -76,6 +76,14 @@ ) add_gen_header( + stdio + DEF_FILE stdio.h.def + GEN_HDR stdio.h + DEPENDS + llvm_libc_common_h +) + +add_gen_header( stdlib DEF_FILE stdlib.h.def GEN_HDR stdlib.h diff --git a/libc/include/stdio.h.def b/libc/include/stdio.h.def new file mode 100644 --- /dev/null +++ b/libc/include/stdio.h.def @@ -0,0 +1,16 @@ +//===-- C standard library header stdio.h ---------------------------------===// +// +// 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_STDIO_H +#define LLVM_LIBC_STDIO_H + +#include <__llvm-libc-common.h> + +%%public_api() + +#endif // LLVM_LIBC_STDIO_H diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -18,6 +18,10 @@ NamedType SigHandlerT = NamedType<"__sighandler_t">; + NamedType FILE = NamedType<"FILE">; + PtrType FILEPtr = PtrType; + RestrictedPtrType FILERestrictedPtr = RestrictedPtrType; + HeaderSpec Assert = HeaderSpec< "assert.h", [ @@ -190,16 +194,17 @@ [], // Macros [ // Types SizeTType, + FILE, ], [], // Enumerations [ FunctionSpec< - "snprintf", - RetValSpec, - [ArgSpec, - ArgSpec, - ArgSpec, - ArgSpec] + "fwrite", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] >, ] >; diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(errno) add_subdirectory(math) add_subdirectory(signal) +add_subdirectory(stdio) add_subdirectory(stdlib) add_subdirectory(string) # TODO: Add this target conditional to the target OS. diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/stdio/CMakeLists.txt @@ -0,0 +1,10 @@ +add_entrypoint_object( + fwrite + SRCS + fwrite.cpp + HDRS + fwrite.h + DEPENDS + libc.src.threads.mtx_lock + libc.src.threads.mtx_unlock +) diff --git a/libc/src/stdio/FILE.h b/libc/src/stdio/FILE.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/FILE.h @@ -0,0 +1,27 @@ +//===-- Internal definition of FILE -----------------------------*- 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_STDIO_FILE_H +#define LLVM_LIBC_SRC_STDIO_FILE_H + +#include "include/threads.h" +#include + +namespace __llvm_libc { + +struct FILE { + mtx_t lock; + + using write_function_t = size_t(FILE *, const char *, size_t); + + write_function_t *write; +}; + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FILE_H diff --git a/libc/src/stdio/fwrite.h b/libc/src/stdio/fwrite.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fwrite.h @@ -0,0 +1,22 @@ +//===-- Implementation header of fwrite -------------------------*- 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_STDIO_FWRITE_H +#define LLVM_LIBC_SRC_STDIO_FWRITE_H + +#include "src/stdio/FILE.h" +#include + +namespace __llvm_libc { + +size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb, + __llvm_libc::FILE *__restrict stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FWRITE_H diff --git a/libc/src/stdio/fwrite.cpp b/libc/src/stdio/fwrite.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fwrite.cpp @@ -0,0 +1,30 @@ +//===-- Implementation of fwrite and fwrite_unlocked ------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/fwrite.h" +#include "src/stdio/FILE.h" +#include "src/threads/mtx_lock.h" +#include "src/threads/mtx_unlock.h" + +namespace __llvm_libc { + +size_t fwrite_unlocked(const void *__restrict ptr, size_t size, size_t nmeb, + __llvm_libc::FILE *__restrict stream) { + return stream->write(stream, reinterpret_cast(ptr), + size * nmeb); +} + +size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb, + __llvm_libc::FILE *__restrict stream) { + __llvm_libc::mtx_lock(&stream->lock); + size_t written = fwrite_unlocked(ptr, size, nmeb, stream); + __llvm_libc::mtx_unlock(&stream->lock); + return written; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(assert) add_subdirectory(errno) add_subdirectory(signal) +add_subdirectory(stdio) add_subdirectory(stdlib) add_subdirectory(string) add_subdirectory(sys) diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/CMakeLists.txt @@ -0,0 +1,14 @@ +add_libc_testsuite(libc_stdio_unittests) + +add_libc_unittest( + fwrite_test + SUITE + libc_stdio_unittests + SRCS + fwrite_test.cpp + DEPENDS + libc.src.stdio.fwrite + # TODO(sivachandra): remove private dependencies of fwrite + libc.src.threads.mtx_lock + libc.src.threads.mtx_unlock +) diff --git a/libc/test/src/stdio/fwrite_test.cpp b/libc/test/src/stdio/fwrite_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/fwrite_test.cpp @@ -0,0 +1,28 @@ +//===-- Unittests for fwrite ----------------------------------------------===// +// +// 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/stdio/FILE.h" +#include "src/stdio/fwrite.h" +#include "utils/CPP/Array.h" +#include "utils/UnitTest/Test.h" + +TEST(Stdio, FWriteBasic) { + struct StrcpyFile : __llvm_libc::FILE { + char *buf; + } f; + char array[6]; + f.buf = array; + f.write = +[](__llvm_libc::FILE *file, const char *ptr, size_t size) { + StrcpyFile *strcpyFile = static_cast(file); + for (size_t i = 0; i < size; ++i) + strcpyFile->buf[i] = ptr[i]; + return size; + }; + EXPECT_EQ(fwrite("hello", 1, 6, &f), 6UL); + EXPECT_STREQ(array, "hello"); +}