[libc-commits] [libc] 4eea884 - [libc] Add implementation of setbuf and setvbuf.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Fri Nov 4 00:15:23 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-11-04T07:15:13Z
New Revision: 4eea884959498b283335f18fc7899ba022bcb881

URL: https://github.com/llvm/llvm-project/commit/4eea884959498b283335f18fc7899ba022bcb881
DIFF: https://github.com/llvm/llvm-project/commit/4eea884959498b283335f18fc7899ba022bcb881.diff

LOG: [libc] Add implementation of setbuf and setvbuf.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D137356

Added: 
    libc/src/stdio/setbuf.cpp
    libc/src/stdio/setbuf.h
    libc/src/stdio/setvbuf.cpp
    libc/src/stdio/setvbuf.h
    libc/test/src/stdio/setbuf_test.cpp
    libc/test/src/stdio/setvbuf_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/stdc.td
    libc/src/__support/File/file.cpp
    libc/src/__support/File/file.h
    libc/src/stdio/CMakeLists.txt
    libc/test/src/stdio/CMakeLists.txt
    libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 17f2c994c12fb..2c4867f48b1b9 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 69a3ac1e26d8b..64ee9b7c45399 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -613,6 +613,16 @@ def StdC : StandardSpec<"stdc"> {
               RetValSpec<IntType>,
               [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>,

diff  --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index 352b1a4d24005..9129f68b521d4 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 7ea780d94f555..e08508bcb1d83 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -71,6 +71,11 @@ class File {
 
   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 @@ class File {
   };
 
 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 @@ class File {
 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 @@ class File {
     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 @@ class File {
   }
 
   // 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 @@ class File {
   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 
diff erent 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 f8b197d984c52..61ca8ce34bbba 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -341,6 +341,32 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
+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

diff  --git a/libc/src/stdio/setbuf.cpp b/libc/src/stdio/setbuf.cpp
new file mode 100644
index 0000000000000..b75963239216b
--- /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 0000000000000..7a158ac0f173e
--- /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 0000000000000..162519fcca36b
--- /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 0000000000000..bceedd8b44113
--- /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 904c669d63da6..b453af2bb13c5 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -37,6 +37,38 @@ add_libc_unittest(
     libc.src.stdio.ungetc
 )
 
+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

diff  --git a/libc/test/src/stdio/setbuf_test.cpp b/libc/test/src/stdio/setbuf_test.cpp
new file mode 100644
index 0000000000000..0a53e221cf425
--- /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 0000000000000..3cdcc044c38e1
--- /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 340f79ac9d14d..06f621052f154 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;


        


More information about the libc-commits mailing list