[libc-commits] [libc] [libc] Implement fmemopen (PR #221748)

Jeong Jihyeon via libc-commits libc-commits at lists.llvm.org
Wed Sep 9 18:01:25 PDT 2026


https://github.com/JihyeonJeong129 updated https://github.com/llvm/llvm-project/pull/221748

>From ae0109e2de869da5596d1a955c8d99736f5aff22 Mon Sep 17 00:00:00 2001
From: Jihyeon Jeong <jh.jeong129 at gmail.com>
Date: Mon, 7 Sep 2026 09:27:26 +0000
Subject: [PATCH 1/3] [libc] Implement fmemopen

---
 libc/config/linux/aarch64/entrypoints.txt |   1 +
 libc/config/linux/riscv/entrypoints.txt   |   1 +
 libc/config/linux/x86_64/entrypoints.txt  |   1 +
 libc/include/stdio.yaml                   |   8 +
 libc/src/stdio/CMakeLists.txt             |  22 ++
 libc/src/stdio/fmemopen.cpp               | 172 +++++++++++++
 libc/src/stdio/fmemopen.h                 |  23 ++
 libc/test/src/stdio/CMakeLists.txt        |  30 +++
 libc/test/src/stdio/fmemopen_test.cpp     | 279 ++++++++++++++++++++++
 9 files changed, 537 insertions(+)
 create mode 100644 libc/src/stdio/fmemopen.cpp
 create mode 100644 libc/src/stdio/fmemopen.h
 create mode 100644 libc/test/src/stdio/fmemopen_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 03053ac04f743..7a894683fdfea 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1236,6 +1236,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdio.fgets
     libc.src.stdio.fileno
     libc.src.stdio.flockfile
+    libc.src.stdio.fmemopen
     libc.src.stdio.fopen
     libc.src.stdio.freopen
     libc.src.stdio.fopencookie
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 5db369a9d871d..c74521ff22995 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1423,6 +1423,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdio.fgets
     libc.src.stdio.fileno
     libc.src.stdio.flockfile
+    libc.src.stdio.fmemopen
     libc.src.stdio.fopen
     libc.src.stdio.freopen
     libc.src.stdio.fopencookie
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3cbbb7b3e9f79..3a5cb0a822473 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1436,6 +1436,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdio.fgets
     libc.src.stdio.fileno
     libc.src.stdio.flockfile
+    libc.src.stdio.fmemopen
     libc.src.stdio.fopen
     libc.src.stdio.freopen
     libc.src.stdio.fopencookie
diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml
index ef725dd4fe619..bb28dadd29b3b 100644
--- a/libc/include/stdio.yaml
+++ b/libc/include/stdio.yaml
@@ -139,6 +139,14 @@ functions:
     return_type: void
     arguments:
       - type: FILE *
+  - name: fmemopen
+    standards:
+      - posix
+    return_type: FILE *
+    arguments:
+      - type: void *__restrict
+      - type: size_t
+      - type: const char *__restrict
   - name: fopen
     standards:
       - stdc
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 3ef6e4f7b7d01..b9af9cf319d5d 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -52,6 +52,28 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
+add_entrypoint_object(
+  fmemopen
+  SRCS
+    fmemopen.cpp
+  HDRS
+    fmemopen.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.stdint_proxy
+    libc.hdr.stdio_macros
+    libc.hdr.types.FILE
+    libc.hdr.types.off_t
+    libc.hdr.types.size_t
+    libc.src.__support.CPP.limits
+    libc.src.__support.CPP.new
+    libc.src.__support.File.file
+    libc.src.__support.alloc_checker
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.null_check
+    libc.src.string.memory_utils.inline_memcpy
+)
+
 add_entrypoint_object(
   fopencookie
   SRCS
diff --git a/libc/src/stdio/fmemopen.cpp b/libc/src/stdio/fmemopen.cpp
new file mode 100644
index 0000000000000..cccb6e5e3332e
--- /dev/null
+++ b/libc/src/stdio/fmemopen.cpp
@@ -0,0 +1,172 @@
+//===-- Implementation of fmemopen ----------------------------------------===//
+//
+// 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/fmemopen.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "hdr/stdio_macros.h"
+#include "hdr/types/off_t.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/File/file.h"
+#include "src/__support/alloc-checker.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace {
+
+class MemoryFile : public File {
+  // The stdio buffer is separate from the memory used as the file's contents.
+  // In particular, ungetc and setvbuf must not modify or free that memory.
+  uint8_t stream_buffer[DEFAULT_BUFFER_SIZE];
+  uint8_t *storage;
+  size_t capacity;
+  size_t position = 0;
+  size_t end = 0;
+  bool owns_storage;
+  bool append;
+
+  static FileIOResult memory_read(File *f, void *data, size_t size) {
+    auto *mf = static_cast<MemoryFile *>(f);
+    if (size == 0 || mf->position >= mf->end)
+      return 0;
+    size_t available = mf->end - mf->position;
+    size_t count = size < available ? size : available;
+    inline_memcpy(data, mf->storage + mf->position, count);
+    mf->position += count;
+    return count;
+  }
+
+  static FileIOResult memory_write(File *f, const void *data, size_t size) {
+    auto *mf = static_cast<MemoryFile *>(f);
+    if (size == 0)
+      return 0;
+    size_t start = mf->append ? mf->end : mf->position;
+    size_t available = mf->capacity - start;
+    size_t count = size < available ? size : available;
+    if (count == 0)
+      return {0, ENOSPC};
+    inline_memcpy(mf->storage + start, data, count);
+    mf->position = start + count;
+    if (mf->position > mf->end) {
+      mf->end = mf->position;
+      // The terminator is not part of the file contents. All capacity bytes
+      // may hold data, in which case there is no room for a terminator.
+      if (mf->end < mf->capacity)
+        mf->storage[mf->end] = '\0';
+    }
+    return {count, count < size ? ENOSPC : 0};
+  }
+
+  static ErrorOr<off_t> memory_seek(File *f, off_t offset, int whence) {
+    auto *mf = static_cast<MemoryFile *>(f);
+    size_t base;
+    switch (whence) {
+    case SEEK_SET:
+      base = 0;
+      break;
+    case SEEK_CUR:
+      base = mf->position;
+      break;
+    case SEEK_END:
+      base = mf->end;
+      break;
+    default:
+      return Error(EINVAL);
+    }
+
+    size_t next;
+    if (offset < 0) {
+      // Avoid negating the minimum off_t, and compare before narrowing to
+      // size_t (which may be smaller than off_t).
+      uintmax_t distance = static_cast<uintmax_t>(-(offset + 1)) + 1;
+      if (distance > base)
+        return Error(EINVAL);
+      next = base - static_cast<size_t>(distance);
+    } else {
+      uintmax_t distance = static_cast<uintmax_t>(offset);
+      if (distance > mf->capacity - base)
+        return Error(EINVAL);
+      next = base + static_cast<size_t>(distance);
+    }
+    if (next > static_cast<uintmax_t>(cpp::numeric_limits<off_t>::max()))
+      return Error(EOVERFLOW);
+    mf->position = next;
+    return static_cast<off_t>(next);
+  }
+
+  static int memory_close(File *f) {
+    auto *mf = static_cast<MemoryFile *>(f);
+    File::remove_file(mf);
+    if (mf->owns_storage)
+      delete[] mf->storage;
+    delete mf;
+    return 0;
+  }
+
+public:
+  MemoryFile(uint8_t *storage, size_t capacity, bool owns_storage,
+             ModeFlags mode)
+      : File(&memory_write, &memory_read, &memory_seek, &memory_close,
+             stream_buffer, sizeof(stream_buffer), _IOFBF, false, mode),
+        storage(storage), capacity(capacity), owns_storage(owns_storage),
+        append(mode & static_cast<ModeFlags>(OpenMode::APPEND)) {
+    if (mode & static_cast<ModeFlags>(OpenMode::READ)) {
+      end = capacity;
+    } else if (mode & static_cast<ModeFlags>(OpenMode::WRITE)) {
+      if (capacity != 0)
+        storage[0] = '\0';
+    } else if (!owns_storage) {
+      while (end < capacity && storage[end] != '\0')
+        ++end;
+      position = end;
+    }
+  }
+};
+
+} // namespace
+
+LLVM_LIBC_FUNCTION(::FILE *, fmemopen,
+                   (void *__restrict buf, size_t max_size,
+                    const char *__restrict mode)) {
+  LIBC_CRASH_ON_NULLPTR(mode);
+  // Use the same mode parser as fopen. Binary mode has no special effect.
+  auto flags = File::mode_flags(mode);
+  if (flags == 0) {
+    libc_errno = EINVAL;
+    return nullptr;
+  }
+
+  auto *storage = static_cast<uint8_t *>(buf);
+  bool owns_storage = buf == nullptr;
+  if (owns_storage && max_size != 0) {
+    AllocChecker ac;
+    storage = new (ac) uint8_t[max_size];
+    if (!ac) {
+      libc_errno = ENOMEM;
+      return nullptr;
+    }
+  }
+
+  AllocChecker ac;
+  auto *file = new (ac) MemoryFile(storage, max_size, owns_storage, flags);
+  if (!ac) {
+    if (owns_storage)
+      delete[] storage;
+    libc_errno = ENOMEM;
+    return nullptr;
+  }
+  File::add_file(file);
+  return reinterpret_cast<::FILE *>(file);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/fmemopen.h b/libc/src/stdio/fmemopen.h
new file mode 100644
index 0000000000000..efc25f7cb93b3
--- /dev/null
+++ b/libc/src/stdio/fmemopen.h
@@ -0,0 +1,23 @@
+//===-- Implementation header of fmemopen ----------------------*- 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_FMEMOPEN_H
+#define LLVM_LIBC_SRC_STDIO_FMEMOPEN_H
+
+#include "hdr/types/FILE.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+::FILE *fmemopen(void *__restrict buf, size_t max_size,
+                 const char *__restrict mode);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_FMEMOPEN_H
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index b25219ef3ca03..36692a9145b8d 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -142,6 +142,36 @@ add_libc_test(
     libc.test.UnitTest.ErrnoCheckingTest
 )
 
+add_libc_test(
+  fmemopen_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    fmemopen_test.cpp
+  DEPENDS
+    libc.include.stdio
+    libc.hdr.stdio_macros
+    libc.hdr.types.off_t
+    libc.src.__support.CPP.limits
+    libc.src.__support.CPP.scope
+    libc.src.errno.errno
+    libc.src.stdio.clearerr
+    libc.src.stdio.fclose
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.fflush
+    libc.src.stdio.fgetc
+    libc.src.stdio.fmemopen
+    libc.src.stdio.fread
+    libc.src.stdio.fseek
+    libc.src.stdio.fseeko
+    libc.src.stdio.ftell
+    libc.src.stdio.fwrite
+    libc.src.stdio.setvbuf
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.MemoryMatcher
+)
+
 add_libc_test(
   fopencookie_test
   SUITE
diff --git a/libc/test/src/stdio/fmemopen_test.cpp b/libc/test/src/stdio/fmemopen_test.cpp
new file mode 100644
index 0000000000000..cd8e8531fe1c4
--- /dev/null
+++ b/libc/test/src/stdio/fmemopen_test.cpp
@@ -0,0 +1,279 @@
+//===-- Unittests for the fmemopen function -------------------------------===//
+//
+// 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/stdio_macros.h"
+#include "hdr/types/off_t.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/scope.h"
+#include "src/stdio/clearerr.h"
+#include "src/stdio/fclose.h"
+#include "src/stdio/feof.h"
+#include "src/stdio/ferror.h"
+#include "src/stdio/fflush.h"
+#include "src/stdio/fgetc.h"
+#include "src/stdio/fmemopen.h"
+#include "src/stdio/fread.h"
+#include "src/stdio/fseek.h"
+#include "src/stdio/fseeko.h"
+#include "src/stdio/ftell.h"
+#include "src/stdio/fwrite.h"
+#include "src/stdio/setvbuf.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/MemoryMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcFMemOpenTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using MemoryView = LIBC_NAMESPACE::testing::MemoryView;
+using LIBC_NAMESPACE::cpp::scope_exit;
+
+TEST_F(LlvmLibcFMemOpenTest, InitialPositionAndEnd) {
+  struct Mode {
+    const char *name;
+    long position;
+    long end;
+  };
+  const Mode modes[] = {
+      {"r", 0, 8}, {"rb", 0, 8}, {"r+", 0, 8}, {"rb+", 0, 8}, {"r+b", 0, 8},
+      {"w", 0, 0}, {"wb", 0, 0}, {"w+", 0, 0}, {"wb+", 0, 0}, {"w+b", 0, 0},
+      {"a", 2, 2}, {"ab", 2, 2}, {"a+", 2, 2}, {"ab+", 2, 2}, {"a+b", 2, 2},
+  };
+  for (const auto &mode : modes) {
+    char storage[] = {'A', 'B', '\0', 'x', 'x', 'x', 'x', 'x'};
+    ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), mode.name);
+    ASSERT_TRUE(f != nullptr);
+    scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+    EXPECT_EQ(mode.position, LIBC_NAMESPACE::ftell(f));
+    EXPECT_EQ(mode.name[0] == 'w' ? '\0' : 'A', storage[0]);
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+    EXPECT_EQ(mode.end, LIBC_NAMESPACE::ftell(f));
+  }
+}
+
+TEST_F(LlvmLibcFMemOpenTest, ReadIncludesNullAndStopsAtEnd) {
+  char storage[] = {'A', '\0', 'B', 'C'};
+  char output[] = {'?', '?', '?', '?', '?', '?'};
+  ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "r");
+  ASSERT_TRUE(f != nullptr);
+  scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+  EXPECT_EQ(size_t(4), LIBC_NAMESPACE::fread(output, 1, sizeof(output), f));
+  const char expected[] = {'A', '\0', 'B', 'C', '?', '?'};
+  EXPECT_MEM_EQ(MemoryView(expected, sizeof(expected)),
+                MemoryView(output, sizeof(output)));
+  EXPECT_NE(0, LIBC_NAMESPACE::feof(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::ferror(f));
+  EXPECT_EQ(size_t(0), LIBC_NAMESPACE::fread(output, 1, 1, f));
+  EXPECT_EQ(4L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+  EXPECT_EQ(0, LIBC_NAMESPACE::feof(f));
+  EXPECT_EQ(int('A'), LIBC_NAMESPACE::fgetc(f));
+}
+
+TEST_F(LlvmLibcFMemOpenTest, WriteAndOverwrite) {
+  char storage[8];
+  ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "w+");
+  ASSERT_TRUE(f != nullptr);
+  scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+  EXPECT_EQ(size_t(5), LIBC_NAMESPACE::fwrite("ABCDE", 1, 5, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+  EXPECT_STREQ("ABCDE", storage);
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 1, SEEK_SET));
+  EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("X", 1, 1, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+  EXPECT_STREQ("AXCDE", storage);
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+  EXPECT_EQ(5L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("F", 1, 1, f));
+  // Seeking flushes output and allows an update stream to switch to reading.
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+  char output[8];
+  EXPECT_EQ(size_t(6), LIBC_NAMESPACE::fread(output, 1, sizeof(output), f));
+  EXPECT_MEM_EQ(MemoryView("AXCDEF", 6), MemoryView(output, 6));
+}
+
+TEST_F(LlvmLibcFMemOpenTest, ExactCapacityAndClose) {
+  const int buffer_modes[] = {_IONBF, _IOFBF, _IOLBF};
+  for (int buffering : buffer_modes) {
+    char guarded[] = {'L', '?', '?', '?', '?', 'R'};
+    ::FILE *f = LIBC_NAMESPACE::fmemopen(guarded + 1, 4, "w");
+    ASSERT_TRUE(f != nullptr);
+    EXPECT_EQ(0, LIBC_NAMESPACE::setvbuf(f, nullptr, buffering, 16));
+    EXPECT_EQ(size_t(4), LIBC_NAMESPACE::fwrite("ABCD", 1, 4, f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f));
+    const char expected[] = {'L', 'A', 'B', 'C', 'D', 'R'};
+    EXPECT_MEM_EQ(MemoryView(expected, sizeof(expected)),
+                  MemoryView(guarded, sizeof(guarded)));
+  }
+}
+
+TEST_F(LlvmLibcFMemOpenTest, CloseFlushesPendingOutput) {
+  char storage[8];
+  ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "w");
+  ASSERT_TRUE(f != nullptr);
+  EXPECT_EQ(size_t(3), LIBC_NAMESPACE::fwrite("ABC", 1, 3, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f));
+  EXPECT_STREQ("ABC", storage);
+}
+
+TEST_F(LlvmLibcFMemOpenTest, AppendAfterSeekAndRead) {
+  const int buffer_modes[] = {_IONBF, _IOFBF};
+  for (int buffering : buffer_modes) {
+    char storage[8] = "AB";
+    ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "a+");
+    ASSERT_TRUE(f != nullptr);
+    scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+    ASSERT_EQ(0, LIBC_NAMESPACE::setvbuf(f, nullptr, buffering, 16));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+    EXPECT_EQ(int('A'), LIBC_NAMESPACE::fgetc(f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_CUR));
+    EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("X", 1, 1, f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+    EXPECT_EQ(3L, LIBC_NAMESPACE::ftell(f));
+    EXPECT_STREQ("ABX", storage);
+    // Even a position beyond the current end must not create an append hole.
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 7, SEEK_SET));
+    EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("Y", 1, 1, f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+    EXPECT_STREQ("ABXY", storage);
+  }
+}
+
+TEST_F(LlvmLibcFMemOpenTest, AppendTracksEndAcrossNullBytes) {
+  char storage[8] = "";
+  ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "a+");
+  ASSERT_TRUE(f != nullptr);
+  scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+  EXPECT_EQ(0L, LIBC_NAMESPACE::ftell(f));
+  const char first[] = {'A', '\0', 'B'};
+  EXPECT_EQ(sizeof(first), LIBC_NAMESPACE::fwrite(first, 1, sizeof(first), f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+  EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("C", 1, 1, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+  const char expected[] = {'A', '\0', 'B', 'C', '\0'};
+  EXPECT_MEM_EQ(MemoryView(expected, sizeof(expected)),
+                MemoryView(storage, sizeof(expected)));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+  EXPECT_EQ(4L, LIBC_NAMESPACE::ftell(f));
+}
+
+TEST_F(LlvmLibcFMemOpenTest, SeekBoundsAndEnd) {
+  char storage[8];
+  ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "w+");
+  ASSERT_TRUE(f != nullptr);
+  scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+  EXPECT_EQ(size_t(3), LIBC_NAMESPACE::fwrite("ABC", 1, 3, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 8, SEEK_SET));
+  EXPECT_EQ(8L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, 9, SEEK_SET));
+  ASSERT_ERRNO_EQ(EINVAL);
+  EXPECT_EQ(8L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, -1, SEEK_SET));
+  ASSERT_ERRNO_EQ(EINVAL);
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+  EXPECT_EQ(3L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 2, SEEK_END));
+  EXPECT_EQ(5L, LIBC_NAMESPACE::ftell(f));
+  // Reading past the data end must not advance the memory stream's position.
+  EXPECT_EQ(EOF, LIBC_NAMESPACE::fgetc(f));
+  EXPECT_NE(0, LIBC_NAMESPACE::feof(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::ferror(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_CUR));
+  EXPECT_EQ(5L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, -1, SEEK_END));
+  EXPECT_EQ(2L, LIBC_NAMESPACE::ftell(f));
+  const off_t offsets[] = {LIBC_NAMESPACE::cpp::numeric_limits<off_t>::min(),
+                           LIBC_NAMESPACE::cpp::numeric_limits<off_t>::max()};
+  for (off_t offset : offsets) {
+    EXPECT_NE(0, LIBC_NAMESPACE::fseeko(f, offset, SEEK_CUR));
+    ASSERT_ERRNO_EQ(EINVAL);
+    EXPECT_EQ(2L, LIBC_NAMESPACE::ftell(f));
+  }
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 6, SEEK_SET));
+  EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("Z", 1, 1, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+  EXPECT_EQ(7L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_EQ('Z', storage[6]);
+  EXPECT_EQ('\0', storage[7]);
+  EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, 0, -1));
+  ASSERT_ERRNO_EQ(EINVAL);
+  // The contents of the gap between the old end and position 6 are unspecified.
+}
+
+TEST_F(LlvmLibcFMemOpenTest, InternalBuffer) {
+  const char *update_modes[] = {"w+", "a+", "r+"};
+  for (const char *mode : update_modes) {
+    ::FILE *f = LIBC_NAMESPACE::fmemopen(nullptr, 16, mode);
+    ASSERT_TRUE(f != nullptr);
+    scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+    EXPECT_EQ(0L, LIBC_NAMESPACE::ftell(f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+    EXPECT_EQ(mode[0] == 'r' ? 16L : 0L, LIBC_NAMESPACE::ftell(f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+    EXPECT_EQ(size_t(3), LIBC_NAMESPACE::fwrite("ABC", 1, 3, f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+    char output[3];
+    EXPECT_EQ(sizeof(output),
+              LIBC_NAMESPACE::fread(output, 1, sizeof(output), f));
+    EXPECT_MEM_EQ(MemoryView("ABC", 3), MemoryView(output, 3));
+  }
+  // Non-update modes are accepted, just as with an externally supplied buffer.
+  const char *other_modes[] = {"r", "w", "a"};
+  for (const char *mode : other_modes) {
+    ::FILE *f = LIBC_NAMESPACE::fmemopen(nullptr, 4, mode);
+    ASSERT_TRUE(f != nullptr);
+    EXPECT_EQ(0L, LIBC_NAMESPACE::ftell(f));
+    EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f));
+  }
+}
+
+TEST_F(LlvmLibcFMemOpenTest, ZeroCapacity) {
+  char guard = 'X';
+  void *buffers[] = {&guard, nullptr};
+  for (void *buf : buffers) {
+    const char *modes[] = {"r+", "w+", "a+"};
+    for (const char *mode : modes) {
+      ::FILE *f = LIBC_NAMESPACE::fmemopen(buf, 0, mode);
+      ASSERT_TRUE(f != nullptr);
+      scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+      ASSERT_EQ(0, LIBC_NAMESPACE::setvbuf(f, nullptr, _IOFBF, 1));
+      EXPECT_EQ(0L, LIBC_NAMESPACE::ftell(f));
+      EXPECT_EQ(EOF, LIBC_NAMESPACE::fgetc(f));
+      EXPECT_NE(0, LIBC_NAMESPACE::feof(f));
+      EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+      EXPECT_EQ(size_t(0), LIBC_NAMESPACE::fwrite("ABC", 1, 3, f));
+      ASSERT_ERRNO_EQ(ENOSPC);
+      EXPECT_NE(0, LIBC_NAMESPACE::ferror(f));
+      EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+      EXPECT_EQ(0L, LIBC_NAMESPACE::ftell(f));
+      EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, 1, SEEK_SET));
+      ASSERT_ERRNO_EQ(EINVAL);
+      EXPECT_EQ('X', guard);
+    }
+  }
+}
+
+TEST_F(LlvmLibcFMemOpenTest, ShortWriteAndRecovery) {
+  char guarded[] = {'L', '?', '?', '?', '?', 'R'};
+  ::FILE *f = LIBC_NAMESPACE::fmemopen(guarded + 1, 4, "w+");
+  ASSERT_TRUE(f != nullptr);
+  scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+  ASSERT_EQ(0, LIBC_NAMESPACE::setvbuf(f, nullptr, _IOFBF, 1));
+  EXPECT_EQ(size_t(4), LIBC_NAMESPACE::fwrite("ABCDEF", 1, 6, f));
+  ASSERT_ERRNO_EQ(ENOSPC);
+  EXPECT_NE(0, LIBC_NAMESPACE::ferror(f));
+  EXPECT_EQ(4L, LIBC_NAMESPACE::ftell(f));
+  EXPECT_EQ(size_t(0), LIBC_NAMESPACE::fwrite("XYZ", 1, 3, f));
+  ASSERT_ERRNO_EQ(ENOSPC);
+  LIBC_NAMESPACE::clearerr(f);
+  EXPECT_EQ(0, LIBC_NAMESPACE::ferror(f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));
+  EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("X", 1, 1, f));
+  EXPECT_EQ(0, LIBC_NAMESPACE::fflush(f));
+  const char expected[] = {'L', 'X', 'B', 'C', 'D', 'R'};
+  EXPECT_MEM_EQ(MemoryView(expected, sizeof(expected)),
+                MemoryView(guarded, sizeof(guarded)));
+}

>From 46cdedb0f653232911c61629f01e8293c21336f3 Mon Sep 17 00:00:00 2001
From: Jihyeon Jeong <jh.jeong129 at gmail.com>
Date: Wed, 9 Sep 2026 17:47:46 +0000
Subject: [PATCH 2/3] [libc] Address fmemopen review comments

---
 libc/src/stdio/CMakeLists.txt         |  2 +-
 libc/src/stdio/fmemopen.cpp           | 45 ++++++++++++-------------
 libc/src/stdio/fmemopen.h             |  7 +++-
 libc/test/src/stdio/CMakeLists.txt    |  1 +
 libc/test/src/stdio/fmemopen_test.cpp | 48 ++++++++++++++++++---------
 5 files changed, 61 insertions(+), 42 deletions(-)

diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index b9af9cf319d5d..a0d4e86674b95 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -65,7 +65,7 @@ add_entrypoint_object(
     libc.hdr.types.FILE
     libc.hdr.types.off_t
     libc.hdr.types.size_t
-    libc.src.__support.CPP.limits
+    libc.src.__support.CPP.algorithm
     libc.src.__support.CPP.new
     libc.src.__support.File.file
     libc.src.__support.alloc_checker
diff --git a/libc/src/stdio/fmemopen.cpp b/libc/src/stdio/fmemopen.cpp
index cccb6e5e3332e..04fbc16adcfb7 100644
--- a/libc/src/stdio/fmemopen.cpp
+++ b/libc/src/stdio/fmemopen.cpp
@@ -1,10 +1,15 @@
-//===-- Implementation of fmemopen ----------------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // 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
+/// Implementation of fmemopen, a POSIX function.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/stdio/fmemopen.h"
 
@@ -12,7 +17,7 @@
 #include "hdr/stdint_proxy.h"
 #include "hdr/stdio_macros.h"
 #include "hdr/types/off_t.h"
-#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/algorithm.h"
 #include "src/__support/CPP/new.h"
 #include "src/__support/File/file.h"
 #include "src/__support/alloc-checker.h"
@@ -36,23 +41,24 @@ class MemoryFile : public File {
   bool append;
 
   static FileIOResult memory_read(File *f, void *data, size_t size) {
-    auto *mf = static_cast<MemoryFile *>(f);
+    auto *mf = reinterpret_cast<MemoryFile *>(f);
     if (size == 0 || mf->position >= mf->end)
       return 0;
     size_t available = mf->end - mf->position;
-    size_t count = size < available ? size : available;
+    size_t count = cpp::min(size, available);
     inline_memcpy(data, mf->storage + mf->position, count);
     mf->position += count;
     return count;
   }
 
   static FileIOResult memory_write(File *f, const void *data, size_t size) {
-    auto *mf = static_cast<MemoryFile *>(f);
+    auto *mf = reinterpret_cast<MemoryFile *>(f);
     if (size == 0)
       return 0;
     size_t start = mf->append ? mf->end : mf->position;
     size_t available = mf->capacity - start;
-    size_t count = size < available ? size : available;
+    size_t count = cpp::min(size, available);
+    // POSIX fflush and fseek specify ENOSPC for a full fmemopen buffer.
     if (count == 0)
       return {0, ENOSPC};
     inline_memcpy(mf->storage + start, data, count);
@@ -68,7 +74,7 @@ class MemoryFile : public File {
   }
 
   static ErrorOr<off_t> memory_seek(File *f, off_t offset, int whence) {
-    auto *mf = static_cast<MemoryFile *>(f);
+    auto *mf = reinterpret_cast<MemoryFile *>(f);
     size_t base;
     switch (whence) {
     case SEEK_SET:
@@ -84,28 +90,19 @@ class MemoryFile : public File {
       return Error(EINVAL);
     }
 
-    size_t next;
-    if (offset < 0) {
-      // Avoid negating the minimum off_t, and compare before narrowing to
-      // size_t (which may be smaller than off_t).
-      uintmax_t distance = static_cast<uintmax_t>(-(offset + 1)) + 1;
-      if (distance > base)
-        return Error(EINVAL);
-      next = base - static_cast<size_t>(distance);
-    } else {
-      uintmax_t distance = static_cast<uintmax_t>(offset);
-      if (distance > mf->capacity - base)
-        return Error(EINVAL);
-      next = base + static_cast<size_t>(distance);
-    }
-    if (next > static_cast<uintmax_t>(cpp::numeric_limits<off_t>::max()))
-      return Error(EOVERFLOW);
+    // The bounds fit in off_t since no object is larger than PTRDIFF_MAX, so
+    // comparing in off_t needs no negation of offset or narrowing to size_t.
+    off_t min_offset = -static_cast<off_t>(base);
+    off_t max_offset = static_cast<off_t>(mf->capacity - base);
+    if (offset < min_offset || offset > max_offset)
+      return Error(EINVAL);
+    size_t next = base + static_cast<size_t>(offset);
     mf->position = next;
     return static_cast<off_t>(next);
   }
 
   static int memory_close(File *f) {
-    auto *mf = static_cast<MemoryFile *>(f);
+    auto *mf = reinterpret_cast<MemoryFile *>(f);
     File::remove_file(mf);
     if (mf->owns_storage)
       delete[] mf->storage;
diff --git a/libc/src/stdio/fmemopen.h b/libc/src/stdio/fmemopen.h
index efc25f7cb93b3..5fe2ad4536743 100644
--- a/libc/src/stdio/fmemopen.h
+++ b/libc/src/stdio/fmemopen.h
@@ -1,10 +1,15 @@
-//===-- Implementation header of fmemopen ----------------------*- 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
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Declaration of fmemopen a POSIX function.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC_STDIO_FMEMOPEN_H
 #define LLVM_LIBC_SRC_STDIO_FMEMOPEN_H
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 36692a9145b8d..8e1e9ff056287 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -169,6 +169,7 @@ add_libc_test(
     libc.src.stdio.fwrite
     libc.src.stdio.setvbuf
     libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
     libc.test.UnitTest.MemoryMatcher
 )
 
diff --git a/libc/test/src/stdio/fmemopen_test.cpp b/libc/test/src/stdio/fmemopen_test.cpp
index cd8e8531fe1c4..1736580914a45 100644
--- a/libc/test/src/stdio/fmemopen_test.cpp
+++ b/libc/test/src/stdio/fmemopen_test.cpp
@@ -1,10 +1,15 @@
-//===-- Unittests for the fmemopen function -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // 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
+/// Unit tests for fmemopen.
+///
+//===----------------------------------------------------------------------===//
 
 #include "hdr/stdio_macros.h"
 #include "hdr/types/off_t.h"
@@ -24,12 +29,15 @@
 #include "src/stdio/fwrite.h"
 #include "src/stdio/setvbuf.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/MemoryMatcher.h"
 #include "test/UnitTest/Test.h"
 
 using LlvmLibcFMemOpenTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using MemoryView = LIBC_NAMESPACE::testing::MemoryView;
 using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
 
 TEST_F(LlvmLibcFMemOpenTest, InitialPositionAndEnd) {
   struct Mode {
@@ -165,41 +173,49 @@ TEST_F(LlvmLibcFMemOpenTest, SeekBoundsAndEnd) {
   ::FILE *f = LIBC_NAMESPACE::fmemopen(storage, sizeof(storage), "w+");
   ASSERT_TRUE(f != nullptr);
   scope_exit close([&] { EXPECT_EQ(0, LIBC_NAMESPACE::fclose(f)); });
+
   EXPECT_EQ(size_t(3), LIBC_NAMESPACE::fwrite("ABC", 1, 3, f));
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 8, SEEK_SET));
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 8, SEEK_SET), Succeeds());
   EXPECT_EQ(8L, LIBC_NAMESPACE::ftell(f));
-  EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, 9, SEEK_SET));
-  ASSERT_ERRNO_EQ(EINVAL);
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 9, SEEK_SET), Fails(EINVAL));
   EXPECT_EQ(8L, LIBC_NAMESPACE::ftell(f));
-  EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, -1, SEEK_SET));
-  ASSERT_ERRNO_EQ(EINVAL);
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, -1, SEEK_SET), Fails(EINVAL));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 0, SEEK_END), Succeeds());
   EXPECT_EQ(3L, LIBC_NAMESPACE::ftell(f));
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 2, SEEK_END));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 2, SEEK_END), Succeeds());
   EXPECT_EQ(5L, LIBC_NAMESPACE::ftell(f));
+
   // Reading past the data end must not advance the memory stream's position.
   EXPECT_EQ(EOF, LIBC_NAMESPACE::fgetc(f));
   EXPECT_NE(0, LIBC_NAMESPACE::feof(f));
   EXPECT_EQ(0, LIBC_NAMESPACE::ferror(f));
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_CUR));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 0, SEEK_CUR), Succeeds());
   EXPECT_EQ(5L, LIBC_NAMESPACE::ftell(f));
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, -1, SEEK_END));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, -1, SEEK_END), Succeeds());
   EXPECT_EQ(2L, LIBC_NAMESPACE::ftell(f));
+
   const off_t offsets[] = {LIBC_NAMESPACE::cpp::numeric_limits<off_t>::min(),
                            LIBC_NAMESPACE::cpp::numeric_limits<off_t>::max()};
   for (off_t offset : offsets) {
-    EXPECT_NE(0, LIBC_NAMESPACE::fseeko(f, offset, SEEK_CUR));
-    ASSERT_ERRNO_EQ(EINVAL);
+    ASSERT_THAT(LIBC_NAMESPACE::fseeko(f, offset, SEEK_CUR), Fails(EINVAL));
     EXPECT_EQ(2L, LIBC_NAMESPACE::ftell(f));
   }
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 6, SEEK_SET));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 6, SEEK_SET), Succeeds());
   EXPECT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("Z", 1, 1, f));
-  EXPECT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_END));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 0, SEEK_END), Succeeds());
   EXPECT_EQ(7L, LIBC_NAMESPACE::ftell(f));
   EXPECT_EQ('Z', storage[6]);
   EXPECT_EQ('\0', storage[7]);
-  EXPECT_NE(0, LIBC_NAMESPACE::fseek(f, 0, -1));
-  ASSERT_ERRNO_EQ(EINVAL);
+
+  ASSERT_THAT(LIBC_NAMESPACE::fseek(f, 0, -1), Fails(EINVAL));
   // The contents of the gap between the old end and position 6 are unspecified.
 }
 

>From 8759fa9307d973fee6dbaecf718b2a7852e6619b Mon Sep 17 00:00:00 2001
From: Jihyeon Jeong <jh.jeong129 at gmail.com>
Date: Thu, 10 Sep 2026 01:00:51 +0000
Subject: [PATCH 3/3] [libc] Make fmemopen seek bounds const

---
 libc/src/stdio/fmemopen.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/src/stdio/fmemopen.cpp b/libc/src/stdio/fmemopen.cpp
index 04fbc16adcfb7..d88426452fd2a 100644
--- a/libc/src/stdio/fmemopen.cpp
+++ b/libc/src/stdio/fmemopen.cpp
@@ -92,8 +92,8 @@ class MemoryFile : public File {
 
     // The bounds fit in off_t since no object is larger than PTRDIFF_MAX, so
     // comparing in off_t needs no negation of offset or narrowing to size_t.
-    off_t min_offset = -static_cast<off_t>(base);
-    off_t max_offset = static_cast<off_t>(mf->capacity - base);
+    const off_t min_offset = -static_cast<off_t>(base);
+    const off_t max_offset = static_cast<off_t>(mf->capacity - base);
     if (offset < min_offset || offset > max_offset)
       return Error(EINVAL);
     size_t next = base + static_cast<size_t>(offset);



More information about the libc-commits mailing list