Index: libc/config/linux/api.td =================================================================== --- libc/config/linux/api.td +++ 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", ]; } Index: libc/include/CMakeLists.txt =================================================================== --- libc/include/CMakeLists.txt +++ libc/include/CMakeLists.txt @@ -75,6 +75,14 @@ ../config/${LIBC_TARGET_OS}/signal.h.in ) +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 Index: libc/include/stdio.h.def =================================================================== --- /dev/null +++ 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 Index: libc/spec/stdc.td =================================================================== --- libc/spec/stdc.td +++ 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] >, ] >; Index: libc/src/CMakeLists.txt =================================================================== --- libc/src/CMakeLists.txt +++ 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. Index: libc/src/stdio/CMakeLists.txt =================================================================== --- /dev/null +++ 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 +) Index: libc/src/stdio/FILE.h =================================================================== --- /dev/null +++ libc/src/stdio/FILE.h @@ -0,0 +1,29 @@ +//===-- 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 { + +// TOOD: This should return ssize_t but it's defined in sys/types.h +using write_function_t = size_t(void *, const char *, size_t); + +struct FILE { + mtx_t lock; + + void *cookie; + write_function_t *write; +}; + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FILE_H Index: libc/src/stdio/fwrite.h =================================================================== --- /dev/null +++ libc/src/stdio/fwrite.h @@ -0,0 +1,24 @@ +//===-- Implementation header 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 +// +//===----------------------------------------------------------------------===// + +#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_unlocked(const void *__restrict ptr, size_t size, size_t nmeb, + FILE *__restrict stream); +size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb, + FILE *__restrict stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FWRITE_H Index: libc/src/stdio/fwrite.cpp =================================================================== --- /dev/null +++ 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, + FILE *__restrict stream) { + return stream->write(stream->cookie, reinterpret_cast(ptr), + size * nmeb); +} + +size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb, + 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 Index: libc/test/src/CMakeLists.txt =================================================================== --- libc/test/src/CMakeLists.txt +++ 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) Index: libc/test/src/stdio/CMakeLists.txt =================================================================== --- /dev/null +++ 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 +) Index: libc/test/src/stdio/fwrite_test.cpp =================================================================== --- /dev/null +++ libc/test/src/stdio/fwrite_test.cpp @@ -0,0 +1,26 @@ +//===-- 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) { + __llvm_libc::FILE f; + char array[6]; + f.cookie = array; + f.write = +[](void *cookie, const char *ptr, size_t size) { + char *buf = reinterpret_cast(cookie); + for (size_t i = 0; i < size; ++i) + buf[i] = ptr[i]; + return size; + }; + EXPECT_EQ(fwrite("hello", 1, 6, &f), 6UL); + EXPECT_STREQ(array, "hello"); +}