Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/llvm/llvm-project.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
authorSiva Chandra Reddy <sivachandra@google.com>2022-11-03 01:38:50 +0300
committerSiva Chandra Reddy <sivachandra@google.com>2022-11-04 10:15:13 +0300
commit4eea884959498b283335f18fc7899ba022bcb881 (patch)
tree78c49f4b0afa2a1daaf9ad3f85d3709bfbbec34e /libc
parentc9da0352a3a5480492b3c99b165240862a6eb646 (diff)
[libc] Add implementation of setbuf and setvbuf.
Reviewed By: michaelrj Differential Revision: https://reviews.llvm.org/D137356
Diffstat (limited to 'libc')
-rw-r--r--libc/config/linux/x86_64/entrypoints.txt2
-rw-r--r--libc/spec/stdc.td10
-rw-r--r--libc/src/__support/File/file.cpp49
-rw-r--r--libc/src/__support/File/file.h60
-rw-r--r--libc/src/stdio/CMakeLists.txt26
-rw-r--r--libc/src/stdio/setbuf.cpp28
-rw-r--r--libc/src/stdio/setbuf.h20
-rw-r--r--libc/src/stdio/setvbuf.cpp27
-rw-r--r--libc/src/stdio/setvbuf.h21
-rw-r--r--libc/test/src/stdio/CMakeLists.txt32
-rw-r--r--libc/test/src/stdio/setbuf_test.cpp68
-rw-r--r--libc/test/src/stdio/setvbuf_test.cpp106
-rw-r--r--libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp1
13 files changed, 435 insertions, 15 deletions
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 17f2c994c12f..2c4867f48b1b 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -393,6 +393,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.putc
libc.src.stdio.putchar
libc.src.stdio.puts
+ libc.src.stdio.setbuf
+ libc.src.stdio.setvbuf
libc.src.stdio.stderr
libc.src.stdio.stdin
libc.src.stdio.stdout
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 69a3ac1e26d8..64ee9b7c4539 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -614,6 +614,16 @@ def StdC : StandardSpec<"stdc"> {
[ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
+ "setbuf",
+ RetValSpec<VoidType>,
+ [ArgSpec<FILERestrictedPtr>, ArgSpec<CharRestrictedPtr>]
+ >,
+ FunctionSpec<
+ "setvbuf",
+ RetValSpec<IntType>,
+ [ArgSpec<FILERestrictedPtr>, ArgSpec<CharRestrictedPtr>, ArgSpec<IntType>, ArgSpec<SizeTType>]
+ >,
+ FunctionSpec<
"sprintf",
RetValSpec<IntType>,
[ArgSpec<CharRestrictedPtr>,
diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index 352b1a4d2400..9129f68b521d 100644
--- a/libc/src/__support/File/file.cpp
+++ b/libc/src/__support/File/file.cpp
@@ -330,12 +330,49 @@ int File::close() {
return 0;
}
-void File::set_buffer(void *buffer, size_t size, bool owned) {
- if (own_buf)
- free(buf);
- buf = static_cast<uint8_t *>(buffer);
- bufsize = size;
- own_buf = owned;
+int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
+ // We do not need to lock the file as this method should be called before
+ // other operations are performed on the file.
+
+ if (buffer != nullptr && size == 0)
+ return EINVAL;
+
+ switch (buffer_mode) {
+ case _IOFBF:
+ case _IOLBF:
+ case _IONBF:
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) {
+ // We exclude the case of buffer_mode == _IONBF in this branch
+ // because we don't need to allocate buffer in such a case.
+ if (own_buf) {
+ buf = realloc(buf, size);
+ } else {
+ buf = malloc(size);
+ own_buf = true;
+ }
+ bufsize = size;
+ // TODO: Handle allocation failures.
+ } else {
+ if (own_buf)
+ free(buf);
+ if (buffer_mode != _IONBF) {
+ buf = static_cast<uint8_t *>(buffer);
+ bufsize = size;
+ } else {
+ // We don't need any buffer.
+ buf = nullptr;
+ bufsize = 0;
+ }
+ own_buf = false;
+ }
+ bufmode = buffer_mode;
+ adjust_buf();
+ return 0;
}
File::ModeFlags File::mode_flags(const char *mode) {
diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h
index 7ea780d94f55..e08508bcb1d8 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -71,6 +71,11 @@ private:
Mutex mutex;
+ // For files which are readable, we should be able to support one ungetc
+ // operation even if |buf| is nullptr. So, in the constructor of File, we
+ // set |buf| to point to this buffer character.
+ char ungetc_buf;
+
void *buf; // Pointer to the stream buffer for buffered streams
size_t bufsize; // Size of the buffer pointed to by |buf|.
@@ -111,13 +116,13 @@ private:
};
protected:
- bool write_allowed() const {
+ constexpr bool write_allowed() const {
return mode & (static_cast<ModeFlags>(OpenMode::WRITE) |
static_cast<ModeFlags>(OpenMode::APPEND) |
static_cast<ModeFlags>(OpenMode::PLUS));
}
- bool read_allowed() const {
+ constexpr bool read_allowed() const {
return mode & (static_cast<ModeFlags>(OpenMode::READ) |
static_cast<ModeFlags>(OpenMode::PLUS));
}
@@ -125,15 +130,21 @@ protected:
public:
// We want this constructor to be constexpr so that global file objects
// like stdout do not require invocation of the constructor which can
- // potentially lead to static initialization order fiasco.
+ // potentially lead to static initialization order fiasco. Consequently,
+ // we will assume that the |buffer| and |buffer_size| argument are
+ // meaningful - that is, |buffer| is nullptr if and only if |buffer_size|
+ // is zero. This way, we will not have to employ the semantics of
+ // the set_buffer method and allocate a buffer.
constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf,
FlushFunc *ff, void *buffer, size_t buffer_size,
int buffer_mode, bool owned, ModeFlags modeflags)
: platform_write(wf), platform_read(rf), platform_seek(sf),
platform_close(cf), platform_flush(ff), mutex(false, false, false),
- buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
- mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
- eof(false), err(false) {}
+ ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode),
+ own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE),
+ read_limit(0), eof(false), err(false) {
+ adjust_buf();
+ }
// This function helps initialize the various fields of the File data
// structure after a allocating memory for it via a call to malloc.
@@ -156,6 +167,8 @@ public:
f->prev_op = FileOp::NONE;
f->read_limit = f->pos = 0;
f->eof = f->err = false;
+
+ f->adjust_buf();
}
// Buffered write of |len| bytes from |data| without the file lock.
@@ -196,9 +209,16 @@ public:
}
// Sets the internal buffer to |buffer| with buffering mode |mode|.
- // |size| is the size of |buffer|. This new |buffer| is owned by the
- // stream only if |owned| is true.
- void set_buffer(void *buffer, size_t size, bool owned);
+ // |size| is the size of |buffer|. If |size| is non-zero, but |buffer|
+ // is nullptr, then a buffer owned by this file will be allocated.
+ // Else, |buffer| will not be owned by this file.
+ //
+ // Will return zero on success, or an error value on failure. Will fail
+ // if:
+ // 1. |buffer| is not a nullptr but |size| is zero.
+ // 2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF.
+ // In both the above cases, error returned in EINVAL.
+ int set_buffer(void *buffer, size_t size, int buffer_mode);
// Closes the file stream and frees up all resources owned by it.
int close();
@@ -235,6 +255,28 @@ private:
size_t write_unlocked_lbf(const uint8_t *data, size_t len);
size_t write_unlocked_fbf(const uint8_t *data, size_t len);
size_t write_unlocked_nbf(const uint8_t *data, size_t len);
+
+ constexpr void adjust_buf() {
+ if (read_allowed() && (buf == nullptr || bufsize == 0)) {
+ // We should allow atleast one ungetc operation.
+ // This might give an impression that a buffer will be used even when
+ // the user does not want a buffer. But, that will not be the case.
+ // For reading, the buffering does not come into play. For writing, let
+ // us take up the three different kinds of buffering separately:
+ // 1. If user wants _IOFBF but gives a zero buffer, buffering still
+ // happens in the OS layer until the user flushes. So, from the user's
+ // point of view, this single byte buffer does not affect their
+ // experience.
+ // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is
+ // very similar to the _IOFBF case.
+ // 3. If user wants _IONBF, then the buffer is ignored for writing.
+ // So, all of the above cases, having a single ungetc buffer does not
+ // affect the behavior experienced by the user.
+ buf = &ungetc_buf;
+ bufsize = 1;
+ own_buf = false; // We shouldn't call free on |buf| when closing the file.
+ }
+ }
};
// The implementaiton of this function is provided by the platfrom_file
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index f8b197d984c5..61ca8ce34bbb 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -342,6 +342,32 @@ add_entrypoint_object(
)
add_entrypoint_object(
+ setbuf
+ SRCS
+ setbuf.cpp
+ HDRS
+ setbuf.h
+ DEPENDS
+ libc.include.errno
+ libc.include.stdio
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+ setvbuf
+ SRCS
+ setvbuf.cpp
+ HDRS
+ setvbuf.h
+ DEPENDS
+ libc.include.errno
+ libc.include.stdio
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
sprintf
SRCS
sprintf.cpp
diff --git a/libc/src/stdio/setbuf.cpp b/libc/src/stdio/setbuf.cpp
new file mode 100644
index 000000000000..b75963239216
--- /dev/null
+++ b/libc/src/stdio/setbuf.cpp
@@ -0,0 +1,28 @@
+//===-- Implementation of setbuf ------------------------------------------===//
+//
+// 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/setbuf.h"
+#include "src/__support/File/file.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(void, setbuf,
+ (::FILE *__restrict stream, char *__restrict buf)) {
+ int mode = _IOFBF;
+ if (buf == nullptr)
+ mode = _IONBF;
+ int err = reinterpret_cast<__llvm_libc::File *>(stream)->set_buffer(
+ buf, BUFSIZ, mode);
+ if (err != 0)
+ errno = err;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/setbuf.h b/libc/src/stdio/setbuf.h
new file mode 100644
index 000000000000..7a158ac0f173
--- /dev/null
+++ b/libc/src/stdio/setbuf.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of setbuf -------------------------*- 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_SETBUF_H
+#define LLVM_LIBC_SRC_STDIO_SETBUF_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+void setbuf(::FILE *__restrict stream, char *__restrict buf);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_SETBUF_H
diff --git a/libc/src/stdio/setvbuf.cpp b/libc/src/stdio/setvbuf.cpp
new file mode 100644
index 000000000000..162519fcca36
--- /dev/null
+++ b/libc/src/stdio/setvbuf.cpp
@@ -0,0 +1,27 @@
+//===-- Implementation of setvbuf -----------------------------------------===//
+//
+// 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/setvbuf.h"
+#include "src/__support/File/file.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, setvbuf,
+ (::FILE *__restrict stream, char *__restrict buf, int type,
+ size_t size)) {
+ int err = reinterpret_cast<__llvm_libc::File *>(stream)->set_buffer(buf, size,
+ type);
+ if (err != 0)
+ errno = err;
+ return err;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/setvbuf.h b/libc/src/stdio/setvbuf.h
new file mode 100644
index 000000000000..bceedd8b4411
--- /dev/null
+++ b/libc/src/stdio/setvbuf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of setvbuf ------------------------*- 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_SETVBUF_H
+#define LLVM_LIBC_SRC_STDIO_SETVBUF_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+int setvbuf(::FILE *__restrict stream, char *__restrict buf, int type,
+ size_t size);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_SETVBUF_H
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 904c669d63da..b453af2bb13c 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -38,6 +38,38 @@ add_libc_unittest(
)
add_libc_unittest(
+ setbuf_test
+ SUITE
+ libc_stdio_unittests
+ SRCS
+ setbuf_test.cpp
+ DEPENDS
+ libc.include.stdio
+ libc.src.stdio.fclose
+ libc.src.stdio.fopen
+ libc.src.stdio.fread
+ libc.src.stdio.fwrite
+ libc.src.stdio.setbuf
+ libc.src.stdio.ungetc
+)
+
+add_libc_unittest(
+ setvbuf_test
+ SUITE
+ libc_stdio_unittests
+ SRCS
+ setvbuf_test.cpp
+ DEPENDS
+ libc.include.errno
+ libc.include.stdio
+ libc.src.stdio.fclose
+ libc.src.stdio.fopen
+ libc.src.stdio.fread
+ libc.src.stdio.fwrite
+ libc.src.stdio.setvbuf
+)
+
+add_libc_unittest(
unlocked_fileop_test
SUITE
libc_stdio_unittests
diff --git a/libc/test/src/stdio/setbuf_test.cpp b/libc/test/src/stdio/setbuf_test.cpp
new file mode 100644
index 000000000000..0a53e221cf42
--- /dev/null
+++ b/libc/test/src/stdio/setbuf_test.cpp
@@ -0,0 +1,68 @@
+//===-- Unittests for setbuf ----------------------------------------------===//
+//
+// 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/fclose.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fread.h"
+#include "src/stdio/fwrite.h"
+#include "src/stdio/setbuf.h"
+#include "src/stdio/ungetc.h"
+#include "utils/UnitTest/Test.h"
+
+#include <stdio.h>
+
+TEST(LlvmLibcSetbufTest, DefaultBufsize) {
+ // The idea in this test is to change the buffer after opening a file and
+ // ensure that read and write work as expected.
+ constexpr char FILENAME[] = "testdata/setbuf_test_default_bufsize.test";
+ ::FILE *file = __llvm_libc::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+ char buffer[BUFSIZ];
+ __llvm_libc::setbuf(file, buffer);
+ constexpr char CONTENT[] = "abcdef";
+ constexpr size_t CONTENT_SIZE = sizeof(CONTENT);
+ ASSERT_EQ(CONTENT_SIZE, __llvm_libc::fwrite(CONTENT, 1, CONTENT_SIZE, file));
+ ASSERT_EQ(0, __llvm_libc::fclose(file));
+
+ file = __llvm_libc::fopen(FILENAME, "r");
+ __llvm_libc::setbuf(file, buffer);
+ ASSERT_FALSE(file == nullptr);
+ char data[CONTENT_SIZE];
+ ASSERT_EQ(__llvm_libc::fread(&data, 1, CONTENT_SIZE, file), CONTENT_SIZE);
+ ASSERT_STREQ(CONTENT, data);
+ ASSERT_EQ(0, __llvm_libc::fclose(file));
+}
+
+TEST(LlvmLibcSetbufTest, NullBuffer) {
+ // The idea in this test is that we set a null buffer and ensure that
+ // everything works correctly.
+ constexpr char FILENAME[] = "testdata/setbuf_test_null_buffer.test";
+ ::FILE *file = __llvm_libc::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+ __llvm_libc::setbuf(file, nullptr);
+ constexpr char CONTENT[] = "abcdef";
+ constexpr size_t CONTENT_SIZE = sizeof(CONTENT);
+ ASSERT_EQ(CONTENT_SIZE, __llvm_libc::fwrite(CONTENT, 1, CONTENT_SIZE, file));
+ ASSERT_EQ(0, __llvm_libc::fclose(file));
+
+ file = __llvm_libc::fopen(FILENAME, "r");
+ __llvm_libc::setbuf(file, nullptr);
+ ASSERT_FALSE(file == nullptr);
+ char data[CONTENT_SIZE];
+ ASSERT_EQ(__llvm_libc::fread(&data, 1, CONTENT_SIZE, file), CONTENT_SIZE);
+ ASSERT_STREQ(CONTENT, data);
+
+ // Ensure that ungetc also works.
+ char unget_char = 'z';
+ ASSERT_EQ(int(unget_char), __llvm_libc::ungetc(unget_char, file));
+ char c;
+ ASSERT_EQ(__llvm_libc::fread(&c, 1, 1, file), size_t(1));
+ ASSERT_EQ(c, unget_char);
+
+ ASSERT_EQ(0, __llvm_libc::fclose(file));
+}
diff --git a/libc/test/src/stdio/setvbuf_test.cpp b/libc/test/src/stdio/setvbuf_test.cpp
new file mode 100644
index 000000000000..3cdcc044c38e
--- /dev/null
+++ b/libc/test/src/stdio/setvbuf_test.cpp
@@ -0,0 +1,106 @@
+//===-- Unittests for setvbuf ---------------------------------------------===//
+//
+// 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/fclose.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fread.h"
+#include "src/stdio/fwrite.h"
+#include "src/stdio/setvbuf.h"
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+TEST(LlvmLibcSetvbufTest, SetNBFBuffer) {
+ // The idea in this test is that we open a file for writing and reading, and
+ // then set a NBF buffer to the write handle. Since it is NBF, the data
+ // written using the write handle should be immediately readable by the read
+ // handle.
+ constexpr char FILENAME[] = "testdata/setvbuf_nbf.test";
+
+ ::FILE *fw = __llvm_libc::fopen(FILENAME, "w");
+ ASSERT_FALSE(fw == nullptr);
+ char buffer[BUFSIZ];
+ ASSERT_EQ(__llvm_libc::setvbuf(fw, buffer, _IONBF, BUFSIZ), 0);
+
+ ::FILE *fr = __llvm_libc::fopen(FILENAME, "r");
+ ASSERT_FALSE(fr == nullptr);
+
+ constexpr char CONTENT[] = "abcdef";
+ constexpr size_t CONTENT_SIZE = sizeof(CONTENT);
+ for (size_t i = 0; i < CONTENT_SIZE; ++i) {
+ ASSERT_EQ(size_t(1), __llvm_libc::fwrite(CONTENT + i, 1, 1, fw));
+ char c;
+ ASSERT_EQ(size_t(1), __llvm_libc::fread(&c, 1, 1, fr));
+ ASSERT_EQ(c, CONTENT[i]);
+ }
+
+ ASSERT_EQ(0, __llvm_libc::fclose(fw));
+ ASSERT_EQ(0, __llvm_libc::fclose(fr));
+
+ // Make sure NBF buffer has no effect for reading.
+ fr = __llvm_libc::fopen(FILENAME, "r");
+ char data[CONTENT_SIZE];
+ ASSERT_EQ(__llvm_libc::setvbuf(fr, buffer, _IONBF, BUFSIZ), 0);
+ ASSERT_EQ(CONTENT_SIZE, __llvm_libc::fread(data, 1, CONTENT_SIZE, fr));
+ ASSERT_STREQ(CONTENT, data);
+ ASSERT_EQ(0, __llvm_libc::fclose(fr));
+}
+
+TEST(LlvmLibcSetvbufTest, SetLBFBuffer) {
+ // The idea in this test is that we open a file for writing and reading, and
+ // then set a LBF buffer to the write handle. Since it is LBF, the data
+ // written using the write handle should be available right after a '\n' is
+ // written.
+ constexpr char FILENAME[] = "testdata/setvbuf_lbf.test";
+
+ ::FILE *fw = __llvm_libc::fopen(FILENAME, "w");
+ ASSERT_FALSE(fw == nullptr);
+ char buffer[BUFSIZ];
+ ASSERT_EQ(__llvm_libc::setvbuf(fw, buffer, _IOLBF, BUFSIZ), 0);
+
+ ::FILE *fr = __llvm_libc::fopen(FILENAME, "r");
+ ASSERT_FALSE(fr == nullptr);
+
+ constexpr char CONTENT[] = "abcdef\n";
+ constexpr size_t CONTENT_SIZE = sizeof(CONTENT);
+ ASSERT_EQ(CONTENT_SIZE, __llvm_libc::fwrite(CONTENT, 1, CONTENT_SIZE, fw));
+
+ // Note that CONTENT_SIZE worth of data written also includes the
+ // null-terminator '\0'. But, since it is after the new line character,
+ // it should not be availabe for reading.
+ char data[CONTENT_SIZE];
+ ASSERT_EQ(CONTENT_SIZE - 1, __llvm_libc::fread(data, 1, CONTENT_SIZE, fr));
+ char c;
+ ASSERT_EQ(size_t(0), __llvm_libc::fread(&c, 1, 1, fr));
+
+ data[CONTENT_SIZE - 1] = '\0';
+ ASSERT_STREQ(CONTENT, data);
+
+ ASSERT_EQ(0, __llvm_libc::fclose(fw));
+ ASSERT_EQ(0, __llvm_libc::fclose(fr));
+
+ // Make sure LBF buffer has no effect for reading.
+ fr = __llvm_libc::fopen(FILENAME, "r");
+ ASSERT_EQ(__llvm_libc::setvbuf(fr, buffer, _IOLBF, BUFSIZ), 0);
+ ASSERT_EQ(CONTENT_SIZE, __llvm_libc::fread(data, 1, CONTENT_SIZE, fr));
+ ASSERT_STREQ(CONTENT, data);
+ ASSERT_EQ(0, __llvm_libc::fclose(fr));
+}
+
+TEST(LlvmLibcSetbufTest, InvalidBufferMode) {
+ constexpr char FILENAME[] = "testdata/setvbuf_invalid_bufmode.test";
+ ::FILE *f = __llvm_libc::fopen(FILENAME, "w");
+ ASSERT_FALSE(f == nullptr);
+ char buf[BUFSIZ];
+ ASSERT_NE(__llvm_libc::setvbuf(f, buf, _IOFBF + _IOLBF + _IONBF, BUFSIZ), 0);
+ ASSERT_EQ(errno, EINVAL);
+
+ errno = 0;
+ ASSERT_EQ(0, __llvm_libc::fclose(f));
+}
diff --git a/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp b/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp
index 340f79ac9d14..06f621052f15 100644
--- a/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp
+++ b/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp
@@ -94,6 +94,7 @@ bool TestGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &records) {
// We provide dummy malloc and free implementations to support the case
// when LLVM libc does to include them.
OS << "void *malloc(size_t) { return nullptr; }\n";
+ OS << "void *realloc(void *, size_t) { return nullptr; }\n";
OS << "void free(void *) {}\n";
return false;