[libc-commits] [libc] [libc] implement getwc (PR #196163)

Michael Jones via libc-commits libc-commits at lists.llvm.org
Thu May 14 10:57:47 PDT 2026


https://github.com/michaelrj-google updated https://github.com/llvm/llvm-project/pull/196163

>From f7fa1963befec76a40adf8981117da8b2226e402 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 13 May 2026 17:10:16 +0000
Subject: [PATCH 1/3] [libc] implement getwc

Add getwc function and tests. Part 7/11. All build file changes are in
part 11.

Assisted by Gemini
---
 libc/config/linux/aarch64/entrypoints.txt |   2 +-
 libc/config/linux/riscv/entrypoints.txt   |   2 +-
 libc/config/linux/x86_64/entrypoints.txt  |   2 +-
 libc/src/wchar/getwc.cpp                  |  39 +++++++
 libc/src/wchar/getwc.h                    |  27 +++++
 libc/test/src/wchar/getwc_test.cpp        | 122 ++++++++++++++++++++++
 6 files changed, 191 insertions(+), 3 deletions(-)
 create mode 100644 libc/src/wchar/getwc.cpp
 create mode 100644 libc/src/wchar/getwc.h
 create mode 100644 libc/test/src/wchar/getwc_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index e62bc67e2d5ca..811f4f26b91b1 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1257,7 +1257,7 @@ if(LLVM_LIBC_FULL_BUILD)
     # libc.src.wchar.fputwc
     # libc.src.wchar.fputws
     libc.src.wchar.fwide
-    # libc.src.wchar.getwc
+    libc.src.wchar.getwc
     # libc.src.wchar.getwchar
     # libc.src.wchar.putwc
     # libc.src.wchar.putwchar
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index d1c52dffdb6e7..f6c102016ce65 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1391,7 +1391,7 @@ if(LLVM_LIBC_FULL_BUILD)
     # libc.src.wchar.fputwc
     # libc.src.wchar.fputws
     libc.src.wchar.fwide
-    # libc.src.wchar.getwc
+    libc.src.wchar.getwc
     # libc.src.wchar.getwchar
     # libc.src.wchar.putwc
     # libc.src.wchar.putwchar
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 73b4b3fcd191f..6ea21736cd4f7 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1473,7 +1473,7 @@ if(LLVM_LIBC_FULL_BUILD)
     # libc.src.wchar.fputwc
     # libc.src.wchar.fputws
     libc.src.wchar.fwide
-    # libc.src.wchar.getwc
+    libc.src.wchar.getwc
     # libc.src.wchar.getwchar
     # libc.src.wchar.putwc
     # libc.src.wchar.putwchar
diff --git a/libc/src/wchar/getwc.cpp b/libc/src/wchar/getwc.cpp
new file mode 100644
index 0000000000000..74a95fc330a8e
--- /dev/null
+++ b/libc/src/wchar/getwc.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation for the getwc function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/wchar/getwc.h"
+#include "hdr/types/FILE.h"
+#include "hdr/types/wint_t.h"
+#include "hdr/wchar_macros.h" // For WEOF
+#include "src/__support/File/file.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(wint_t, getwc, (::FILE * stream)) {
+  LIBC_CRASH_ON_NULLPTR(stream);
+  auto *f = reinterpret_cast<File *>(stream);
+  wchar_t wc;
+  FileIOResult result = f->read(&wc, 1);
+  if (result.has_error() || result.value < 1) {
+    if (result.has_error())
+      libc_errno = result.error;
+    return WEOF;
+  }
+  return static_cast<wint_t>(wc);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/getwc.h b/libc/src/wchar/getwc.h
new file mode 100644
index 0000000000000..02d8518e0330b
--- /dev/null
+++ b/libc/src/wchar/getwc.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation header for the getwc function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_WCHAR_GETWC_H
+#define LLVM_LIBC_SRC_WCHAR_GETWC_H
+
+#include "hdr/types/FILE.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wint_t getwc(::FILE *stream);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_GETWC_H
diff --git a/libc/test/src/wchar/getwc_test.cpp b/libc/test/src/wchar/getwc_test.cpp
new file mode 100644
index 0000000000000..ba513e3eaf860
--- /dev/null
+++ b/libc/test/src/wchar/getwc_test.cpp
@@ -0,0 +1,122 @@
+//===-- Unittests for getwc -----------------------------------------------===//
+//
+// 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 "hdr/errno_macros.h"
+#include "hdr/wchar_macros.h" // For WEOF
+#include "src/stdio/fclose.h"
+#include "src/stdio/feof.h"
+#include "src/stdio/ferror.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fwrite.h"
+#include "src/wchar/fwide.h"
+#include "src/wchar/getwc.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcGetwcTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcGetwcTest, ReadValidCharacters) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("getwc_valid.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  // Write "12"
+  constexpr char CONTENT[] = "12";
+  ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file),
+            sizeof(CONTENT) - 1);
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+  // Open for reading
+  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  // Initial orientation
+  EXPECT_EQ(LIBC_NAMESPACE::fwide(file, 0), 0);
+
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'1'));
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'2'));
+
+  // Stream orientation should now be wide
+  EXPECT_GT(LIBC_NAMESPACE::fwide(file, 0), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcGetwcTest, ReadUtf8) {
+  auto FILENAME = libc_make_test_file_path(APPEND_LIBC_TEST("getwc_utf8.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  // Write "a¢€𐍈"
+  constexpr unsigned char CONTENT[] = {0x61, 0xC2, 0xA2, 0xE2, 0x82,
+                                       0xAC, 0xF0, 0x90, 0x8D, 0x88};
+  ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
+            sizeof(CONTENT));
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+  // Open for reading
+  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'a'));
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'¢'));
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'€'));
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'𐍈'));
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcGetwcTest, EndOfFile) {
+  auto FILENAME = libc_make_test_file_path(APPEND_LIBC_TEST("getwc_eof.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  // Read past EOF
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(WEOF));
+  EXPECT_NE(LIBC_NAMESPACE::feof(file), 0);
+  EXPECT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcGetwcTest, ReadError) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("getwc_readerr.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  // Try to read from write-only file
+  ASSERT_ERRNO_SUCCESS();
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(WEOF));
+  ASSERT_ERRNO_EQ(EBADF);
+  EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcGetwcTest, ByteOrientedStreamFail) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("getwc_bytemode.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w+");
+  ASSERT_FALSE(file == nullptr);
+
+  // Orient to byte mode
+  EXPECT_LT(LIBC_NAMESPACE::fwide(file, -1), 0);
+
+  // Reading should fail and set errno to EINVAL
+  EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(WEOF));
+  ASSERT_ERRNO_EQ(EINVAL);
+  EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}

>From 64c33427550716b7c404955ef8da01da931e8db0 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 13 May 2026 21:42:06 +0000
Subject: [PATCH 2/3] address comments, update test

---
 libc/src/wchar/getwc.cpp           | 3 ++-
 libc/src/wchar/getwc.h             | 3 ++-
 libc/test/src/wchar/CMakeLists.txt | 4 +++-
 libc/test/src/wchar/getwc_test.cpp | 3 +++
 4 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/libc/src/wchar/getwc.cpp b/libc/src/wchar/getwc.cpp
index 74a95fc330a8e..d444672e8b315 100644
--- a/libc/src/wchar/getwc.cpp
+++ b/libc/src/wchar/getwc.cpp
@@ -7,7 +7,8 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// This file contains the implementation for the getwc function.
+/// This file contains the implementation for the getwc function, which
+/// reads a single character from the provided stream.
 ///
 //===----------------------------------------------------------------------===//
 
diff --git a/libc/src/wchar/getwc.h b/libc/src/wchar/getwc.h
index 02d8518e0330b..2217bfab9fa2a 100644
--- a/libc/src/wchar/getwc.h
+++ b/libc/src/wchar/getwc.h
@@ -7,7 +7,8 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// This file contains the implementation header for the getwc function.
+/// This file contains the prototype function for the getwc function, which
+/// reads a single character from the provided stream.
 ///
 //===----------------------------------------------------------------------===//
 
diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt
index dd8a3fd4b735c..186ca0e72d8b0 100644
--- a/libc/test/src/wchar/CMakeLists.txt
+++ b/libc/test/src/wchar/CMakeLists.txt
@@ -648,7 +648,9 @@ add_libc_test(
   SRCS
     getwc_test.cpp
   DEPENDS
-    libc.include.stdio
+    libc.hdr.errno_macros
+    libc.hdr.stdint_proxy
+    libc.hdr.wchar_macros
     libc.src.stdio.fopen
     libc.src.stdio.fclose
     libc.src.stdio.fwrite
diff --git a/libc/test/src/wchar/getwc_test.cpp b/libc/test/src/wchar/getwc_test.cpp
index ba513e3eaf860..deb3c7654b663 100644
--- a/libc/test/src/wchar/getwc_test.cpp
+++ b/libc/test/src/wchar/getwc_test.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "hdr/errno_macros.h"
+#include "hdr/stdint_proxy.h"
 #include "hdr/wchar_macros.h" // For WEOF
 #include "src/stdio/fclose.h"
 #include "src/stdio/feof.h"
@@ -66,8 +67,10 @@ TEST_F(LlvmLibcGetwcTest, ReadUtf8) {
 
   EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'a'));
   EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'¢'));
+#if WINT_MAX > 0xFFFF
   EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'€'));
   EXPECT_EQ(LIBC_NAMESPACE::getwc(file), static_cast<wint_t>(L'𐍈'));
+#endif
 
   ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
 }

>From e0fb6a8f4033a2a6cea1ebe28db885d01bd4f126 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Thu, 14 May 2026 17:57:16 +0000
Subject: [PATCH 3/3] fix comment to address comment

---
 libc/src/wchar/getwc.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/wchar/getwc.cpp b/libc/src/wchar/getwc.cpp
index d444672e8b315..0798d6eb1d645 100644
--- a/libc/src/wchar/getwc.cpp
+++ b/libc/src/wchar/getwc.cpp
@@ -8,7 +8,7 @@
 ///
 /// \file
 /// This file contains the implementation for the getwc function, which
-/// reads a single character from the provided stream.
+/// reads a single wide character from the provided stream.
 ///
 //===----------------------------------------------------------------------===//
 



More information about the libc-commits mailing list