[libcxx-commits] [libcxx] 574c5cc - [libc++] Fix incorrect length check in std::basic_filebuf

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Oct 27 07:22:06 PDT 2023


Author: Louis Dionne
Date: 2023-10-27T10:21:50-04:00
New Revision: 574c5cc9d8f6fcdf3702f5f98ea07ea6a8ebdfb7

URL: https://github.com/llvm/llvm-project/commit/574c5cc9d8f6fcdf3702f5f98ea07ea6a8ebdfb7
DIFF: https://github.com/llvm/llvm-project/commit/574c5cc9d8f6fcdf3702f5f98ea07ea6a8ebdfb7.diff

LOG: [libc++] Fix incorrect length check in std::basic_filebuf

This patch fixes an ASAN-found issue in std::basic_filebuf where we'd
check the wrong size before proceeding to set our internal buffer to
the externally-provided buffer, leading to the library trying to read
from the incorrect buffer in underflow().

Thanks to Andrey Semin for the patch.

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

Added: 
    libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/buffered_reads.pass.cpp
    libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/buffered_writes.pass.cpp
    libcxx/test/std/input.output/file.streams/fstreams/types.h

Modified: 
    libcxx/include/fstream

Removed: 
    


################################################################################
diff  --git a/libcxx/include/fstream b/libcxx/include/fstream
index 024eef8a9d66980..479d8ed198860c9 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -912,7 +912,7 @@ basic_filebuf<_CharT, _Traits>::setbuf(char_type* __s, streamsize __n)
     if (!__always_noconv_)
     {
         __ibs_ = max<streamsize>(__n, sizeof(__extbuf_min_));
-        if (__s && __ibs_ >= sizeof(__extbuf_min_))
+        if (__s && __ibs_ > sizeof(__extbuf_min_))
         {
             __intbuf_ = __s;
             __owns_ib_ = false;

diff  --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/buffered_reads.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/buffered_reads.pass.cpp
new file mode 100644
index 000000000000000..699e6c20160febf
--- /dev/null
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/buffered_reads.pass.cpp
@@ -0,0 +1,227 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: c++03
+
+// <fstream>
+
+// This test checks the behavior of reading payloads of 
diff erent sizes in 
diff erent patterns.
+// In particular, it was written to exercise code paths that deal with buffering inside the fstream
+// implementation.
+//
+// For each test, we test various behaviors w.r.t. how the buffer is handled:
+// - Provide a user-managed buffer to the library. In this case, we test the following corner-cases:
+//    + A 0-sized buffer.
+//    + A buffer size greater than and smaller than the payload size, which causes multiple buffer effects.
+//      Important values are +/- 1 byte from the payload size.
+// - Let the library manage a buffer of a user-provided size 'n'. In this case, we test the following corner-cases:
+//    + A 0-sized buffer.
+//    + A buffer size greater than and smaller than the payload size, which causes multiple buffer effects.
+//      Important values are +/- 1 or 2 bytes from the payload size.
+//    + A buffer size smaller than 8 bytes. If pubsetbuf() is called with less than 8 bytes, the library will
+//      use __extbuf_min_ with 8 bytes instead of allocating anything.
+// - Let the library manage a buffer, without specifying any size. In this case, the library will use the default
+//   buffer size of 4096 bytes.
+
+#include <cassert>
+#include <codecvt>
+#include <fstream>
+#include <locale>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "../types.h"
+#include "assert_macros.h"
+#include "platform_support.h"
+#include "test_macros.h"
+
+template <class BufferPolicy>
+void test_read(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {
+  std::streamsize total_size = std::accumulate(payload_sizes.begin(), payload_sizes.end(), 0);
+  std::vector<char> data(total_size);
+  for (std::size_t i = 0; i < data.size(); ++i) {
+    data[i] = static_cast<char>(i % (1 << 8 * sizeof(char)));
+  }
+  std::string p = get_temp_file_name();
+  {
+    std::ofstream ofs;
+    ofs.open(p, std::ios::out | std::ios::binary);
+    assert(ofs.is_open());
+    ofs.write(data.data(), data.size());
+    assert(!ofs.fail());
+    // test that the user's out_buffer buffer was not modified by write()
+    for (std::streamsize j = 0; j < total_size; ++j) {
+      char exp = j % (1 << 8 * sizeof(char));
+      TEST_REQUIRE(data[j] == exp, [&] {
+        test_eprintf("failed after write() at offset %zu: got=%x, expected=%x\n", j, data[j], exp);
+      });
+    }
+    ofs.close();
+  }
+  {
+    std::ifstream ifs;
+    policy(ifs);
+    ifs.open(p, std::ios::ate | std::ios::binary);
+    assert(ifs.is_open());
+    const std::streamsize in_sz = ifs.tellg();
+    TEST_REQUIRE(in_sz == total_size, [&] { test_eprintf("out_sz = %zu, in_sz = %ld\n", total_size, in_sz); });
+    ifs.seekg(0, std::ios::beg);
+    std::size_t previously_read = 0;
+    std::vector<char> in_buffer(total_size);
+    for (const auto& payload_sz : payload_sizes) {
+      ifs.read(in_buffer.data() + previously_read, payload_sz);
+      assert(ifs);
+      for (std::streamsize j = 0; j < payload_sz; ++j) {
+        char exp = (previously_read + j) % (1 << 8 * sizeof(char));
+        TEST_REQUIRE(in_buffer[previously_read + j] == exp, [&] {
+          test_eprintf(
+              "failed after read() at offset %zu (offset %zu in chunk size %zu): got=%x, expected=%x\n",
+              previously_read + j,
+              j,
+              payload_sz,
+              in_buffer[previously_read + j],
+              exp);
+        });
+      }
+      previously_read += payload_sz;
+    }
+  }
+  std::remove(p.c_str());
+}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+template <class BufferPolicy>
+void test_read_codecvt(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {
+  std::streamsize total_size = std::accumulate(payload_sizes.begin(), payload_sizes.end(), 0);
+  std::vector<wchar_t> data(total_size);
+  for (std::size_t i = 0; i < data.size(); ++i) {
+    data[i] = static_cast<wchar_t>(i);
+  }
+  std::string p = get_temp_file_name();
+  {
+    std::wofstream ofs;
+    ofs.imbue(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));
+    ofs.open(p, std::ios::out | std::ios::binary);
+    assert(ofs.is_open());
+    ofs.write(data.data(), data.size());
+    assert(!ofs.fail());
+    // test that the user's out_buffer buffer was not modified by write()
+    for (std::streamsize j = 0; j < total_size; ++j) {
+      wchar_t exp = static_cast<wchar_t>(j);
+      TEST_REQUIRE(data[j] == exp, [&] {
+        test_eprintf("failed after write() at offset %zu: got=%x, expected=%x\n", j, data[j], exp);
+      });
+    }
+    ofs.close();
+  }
+  {
+    std::wifstream ifs;
+    ifs.imbue(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));
+    policy(ifs);
+    ifs.open(p, std::ios::in | std::ios::binary);
+    assert(ifs.is_open());
+    ifs.seekg(0, std::ios::beg);
+    std::size_t previously_read = 0;
+    std::vector<wchar_t> in_buffer(total_size);
+    for (const auto& payload_sz : payload_sizes) {
+      assert(ifs.read(in_buffer.data() + previously_read, payload_sz));
+      assert(ifs);
+      for (std::streamsize j = 0; j < payload_sz; ++j) {
+        wchar_t exp = static_cast<wchar_t>(j + previously_read);
+        TEST_REQUIRE(in_buffer[j + previously_read] == exp, [&] {
+          test_eprintf(
+              "failed after read() at offset %zu: got=%x, expected=%x\n", j, in_buffer[j + previously_read], exp);
+        });
+      }
+      previously_read += payload_sz;
+    }
+  }
+  std::remove(p.c_str());
+}
+#endif
+
+const std::vector<std::streamsize> buffer_sizes{0L, 3L, 8L, 9L, 11L};
+const std::vector<std::streamsize> io_sizes{0L, 1L, 2L, 3L, 4L, 9L, 10L, 11L, 12L, 13L, 21L, 22L, 23L};
+const std::vector<std::streamsize> io_sizes_default{
+    0L, 1L, 2L, 3L, 4L, 4094L, 4095L, 4096L, 4097L, 4098L, 8190L, 8191L, 8192L, 8193L, 8194L};
+
+// Test single read operations
+void test_1_read() {
+  // with default library buffer size: 4096b
+  for (std::streamsize x : io_sizes_default) {
+    test_read(LibraryDefaultBuffer(), {x});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    test_read_codecvt(LibraryDefaultBuffer(), {x});
+#endif
+  }
+
+  // with the library-managed buffer of given size
+  for (std::streamsize b : buffer_sizes) {
+    for (std::streamsize x : io_sizes) {
+      test_read(LibraryManagedBuffer(b), {x});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+      test_read_codecvt(LibraryManagedBuffer(b), {x});
+#endif
+    }
+  }
+
+  // with the user-managed buffer of given size
+  for (std::streamsize b : buffer_sizes) {
+    for (std::streamsize x : io_sizes) {
+      test_read(UserManagedBuffer(b), {x});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+      test_read_codecvt(UserManagedBuffer(b), {x});
+#endif
+    }
+  }
+}
+
+// Test two read operations
+void test_2_reads() {
+  // with default library buffer size: 4096b
+  for (std::streamsize a : io_sizes_default) {
+    for (std::streamsize b : io_sizes_default) {
+      test_read(LibraryDefaultBuffer(), {a, b});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+      test_read_codecvt(LibraryDefaultBuffer(), {a, b});
+#endif
+    }
+  }
+
+  // with the library-managed buffer of given size
+  for (std::streamsize buf : buffer_sizes) {
+    for (std::streamsize a : io_sizes) {
+      for (std::streamsize b : io_sizes) {
+        test_read(LibraryManagedBuffer(buf), {a, b});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+        test_read_codecvt(LibraryManagedBuffer(buf), {a, b});
+#endif
+      }
+    }
+  }
+
+  // with the user-managed buffer of given size
+  for (std::streamsize buf : buffer_sizes) {
+    for (std::streamsize a : io_sizes) {
+      for (std::streamsize b : io_sizes) {
+        test_read(UserManagedBuffer(buf), {a, b});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+        test_read_codecvt(UserManagedBuffer(buf), {a, b});
+#endif
+      }
+    }
+  }
+}
+
+int main(int, char**) {
+  test_1_read();
+  test_2_reads();
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/buffered_writes.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/buffered_writes.pass.cpp
new file mode 100644
index 000000000000000..4aeaa42c18adeb7
--- /dev/null
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/buffered_writes.pass.cpp
@@ -0,0 +1,225 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: c++03
+
+// <fstream>
+
+// This test checks the behavior of writing payloads of 
diff erent sizes in 
diff erent patterns.
+// In particular, it was written to exercise code paths that deal with buffering inside the fstream
+// implementation.
+//
+// For each test, we test various behaviors w.r.t. how the buffer is handled:
+// - Provide a user-managed buffer to the library. In this case, we test the following corner-cases:
+//    + A 0-sized buffer.
+//    + A buffer size greater than and smaller than the payload size, which causes multiple buffer effects.
+//      Important values are +/- 1 byte from the payload size.
+// - Let the library manage a buffer of a user-provided size 'n'. In this case, we test the following corner-cases:
+//    + A 0-sized buffer.
+//    + A buffer size greater than and smaller than the payload size, which causes multiple buffer effects.
+//      Important values are +/- 1 or 2 bytes from the payload size.
+//    + A buffer size smaller than 8 bytes. If pubsetbuf() is called with less than 8 bytes, the library will
+//      use __extbuf_min_ with 8 bytes instead of allocating anything.
+// - Let the library manage a buffer, without specifying any size. In this case, the library will use the default
+//   buffer size of 4096 bytes.
+
+#include <cassert>
+#include <codecvt>
+#include <fstream>
+#include <locale>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "../types.h"
+#include "assert_macros.h"
+#include "platform_support.h"
+#include "test_macros.h"
+
+template <class BufferPolicy>
+void test_write(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {
+  std::size_t previously_written = 0;
+  std::streamsize total_size     = std::accumulate(payload_sizes.begin(), payload_sizes.end(), 0);
+  std::vector<char> data(total_size);
+  for (std::size_t i = 0; i < data.size(); ++i) {
+    data[i] = static_cast<char>(i % (1 << 8 * sizeof(char)));
+  }
+  std::string p = get_temp_file_name();
+  {
+    std::ofstream ofs;
+    policy(ofs);
+    ofs.open(p, std::ios::out | std::ios::binary);
+    assert(ofs.is_open());
+    for (const auto& payload_sz : payload_sizes) {
+      ofs.write(data.data() + previously_written, payload_sz);
+      assert(!ofs.fail());
+      // test that the user's out_buffer buffer was not modified by write()
+      for (std::streamsize j = 0; j < payload_sz; ++j) {
+        char exp = (previously_written + j) % (1 << 8 * sizeof(char));
+        TEST_REQUIRE(data[previously_written + j] == exp, [&] {
+          test_eprintf(
+              "failed after write() at offset %zu (offset %zu in chunk size %zu): got=%x, expected=%x\n",
+              previously_written + j,
+              j,
+              payload_sz,
+              data[previously_written + j],
+              exp);
+        });
+      }
+      previously_written += payload_sz;
+    }
+    ofs.close();
+  }
+  { // verify contents after reading the file back
+    std::ifstream ifs(p.c_str(), std::ios::ate | std::ios::binary);
+    const std::streamsize in_sz = ifs.tellg();
+    TEST_REQUIRE(in_sz == total_size, [&] { test_eprintf("out_sz = %zu, in_sz = %ld\n", total_size, in_sz); });
+    std::vector<char> in_buffer(total_size);
+    ifs.seekg(0, std::ios::beg);
+    assert(ifs.read(in_buffer.data(), total_size));
+    for (std::size_t i = 0; i < in_buffer.size(); ++i) {
+      char exp = i % (1 << 8 * sizeof(char));
+      TEST_REQUIRE(in_buffer[i] == exp, [&] {
+        test_eprintf("failed after read() at offset %zu: got=%x, expected=%x\n", i, in_buffer[i], exp);
+      });
+    }
+  }
+  std::remove(p.c_str());
+}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+template <class BufferPolicy>
+void test_write_codecvt(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {
+  std::size_t previously_written = 0;
+  std::streamsize total_size     = std::accumulate(payload_sizes.begin(), payload_sizes.end(), 0);
+  std::vector<wchar_t> data(total_size);
+  for (std::size_t i = 0; i < data.size(); ++i) {
+    data[i] = static_cast<wchar_t>(i);
+  }
+  std::string p = get_temp_file_name();
+  {
+    std::wofstream ofs;
+    ofs.imbue(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));
+    policy(ofs);
+    ofs.open(p, std::ios::out | std::ios::binary);
+    assert(ofs.is_open());
+    for (const auto& payload_sz : payload_sizes) {
+      ofs.write(data.data() + previously_written, payload_sz);
+      assert(!ofs.fail());
+      // test that the user's out_buffer buffer was not modified by write()
+      for (std::streamsize j = 0; j < payload_sz; ++j) {
+        wchar_t exp = static_cast<wchar_t>(previously_written + j);
+        TEST_REQUIRE(data[previously_written + j] == exp, [&] {
+          test_eprintf(
+              "failed after write() at offset %zu (offset %zu in chunk size %zu): got=%x, expected=%x\n",
+              previously_written + j,
+              j,
+              payload_sz,
+              data[previously_written + j],
+              exp);
+        });
+      }
+      previously_written += payload_sz;
+    }
+    ofs.close();
+  }
+  { // verify contents after reading the file back
+    std::wifstream ifs(p.c_str(), std::ios::in | std::ios::binary);
+    ifs.imbue(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));
+    std::vector<wchar_t> in_buffer(total_size);
+    assert(ifs.read(in_buffer.data(), total_size));
+    for (std::size_t i = 0; i < in_buffer.size(); ++i) {
+      wchar_t exp = static_cast<wchar_t>(i);
+      TEST_REQUIRE(in_buffer[i] == exp, [&] {
+        test_eprintf("failed after read() at offset %zu: got=%x, expected=%x\n", i, in_buffer[i], exp);
+      });
+    }
+  }
+  std::remove(p.c_str());
+}
+#endif
+
+const std::vector<std::streamsize> buffer_sizes{0L, 3L, 8L, 9L, 11L};
+const std::vector<std::streamsize> io_sizes{0L, 1L, 2L, 3L, 4L, 9L, 10L, 11L, 12L, 13L, 21L, 22L, 23L};
+const std::vector<std::streamsize> io_sizes_default{
+    0L, 1L, 2L, 3L, 4L, 4094L, 4095L, 4096L, 4097L, 4098L, 8190L, 8191L, 8192L, 8193L, 8194L};
+
+// Test single write operations
+void test_1_write() {
+  // with default library buffer size: 4096b
+  for (std::streamsize x : io_sizes_default) {
+    test_write(LibraryDefaultBuffer(), {x});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    test_write_codecvt(LibraryDefaultBuffer(), {x});
+#endif
+  }
+
+  // with the library-managed buffer of given size
+  for (std::streamsize b : buffer_sizes) {
+    for (std::streamsize x : io_sizes) {
+      test_write(LibraryManagedBuffer(b), {x});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+      test_write_codecvt(LibraryManagedBuffer(b), {x});
+#endif
+    }
+  }
+
+  // with the user-managed buffer of given size
+  for (std::streamsize b : buffer_sizes) {
+    for (std::streamsize x : io_sizes) {
+      test_write(UserManagedBuffer(b), {x});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+      test_write_codecvt(UserManagedBuffer(b), {x});
+#endif
+    }
+  }
+}
+
+// Test two write operations
+void test_2_writes() {
+  // with default library buffer size: 4096b
+  for (std::streamsize a : io_sizes_default) {
+    for (std::streamsize b : io_sizes_default) {
+      test_write(LibraryDefaultBuffer(), {a, b});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+      test_write_codecvt(LibraryDefaultBuffer(), {a, b});
+#endif
+    }
+  }
+
+  // with the library-managed buffer of given size
+  for (std::streamsize buf : buffer_sizes) {
+    for (std::streamsize a : io_sizes) {
+      for (std::streamsize b : io_sizes) {
+        test_write(LibraryManagedBuffer(buf), {a, b});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+        test_write_codecvt(LibraryManagedBuffer(buf), {a, b});
+#endif
+      }
+    }
+  }
+
+  // with the user-managed buffer of given size
+  for (std::streamsize buf : buffer_sizes) {
+    for (std::streamsize a : io_sizes) {
+      for (std::streamsize b : io_sizes) {
+        test_write(UserManagedBuffer(buf), {a, b});
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+        test_write_codecvt(UserManagedBuffer(buf), {a, b});
+#endif
+      }
+    }
+  }
+}
+
+int main(int, char**) {
+  test_1_write();
+  test_2_writes();
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/file.streams/fstreams/types.h b/libcxx/test/std/input.output/file.streams/fstreams/types.h
new file mode 100644
index 000000000000000..b919ba20185373d
--- /dev/null
+++ b/libcxx/test/std/input.output/file.streams/fstreams/types.h
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_INPUT_OUTPUT_FILE_STREAMS_FSTREAMS_TYPES_H
+#define TEST_STD_INPUT_OUTPUT_FILE_STREAMS_FSTREAMS_TYPES_H
+
+#include <cstddef>
+#include <fstream>
+#include <vector>
+
+#include "test_macros.h"
+
+struct UserManagedBuffer {
+  UserManagedBuffer(std::size_t size) : size_(size) {}
+
+  UserManagedBuffer(UserManagedBuffer const&) = delete;
+  UserManagedBuffer(UserManagedBuffer&&)      = default;
+
+  void operator()(std::basic_ofstream<char>& stream) {
+    buffers_.emplace_back(new char[size_]);
+    stream.rdbuf()->pubsetbuf(buffers_.back(), size_);
+  }
+  void operator()(std::basic_ifstream<char>& stream) {
+    buffers_.emplace_back(new char[size_]);
+    stream.rdbuf()->pubsetbuf(buffers_.back(), size_);
+  }
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  void operator()(std::basic_ofstream<wchar_t>& stream) {
+    wbuffers_.emplace_back(new wchar_t[size_]);
+    stream.rdbuf()->pubsetbuf(wbuffers_.back(), size_);
+  }
+  void operator()(std::basic_ifstream<wchar_t>& stream) {
+    wbuffers_.emplace_back(new wchar_t[size_]);
+    stream.rdbuf()->pubsetbuf(wbuffers_.back(), size_);
+  }
+#endif
+  ~UserManagedBuffer() {
+    for (char* p : buffers_)
+      delete[] p;
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    for (wchar_t* p : wbuffers_)
+      delete[] p;
+#endif
+  }
+
+private:
+  std::size_t size_;
+  std::vector<char*> buffers_;
+  std::vector<wchar_t*> wbuffers_;
+};
+
+struct LibraryManagedBuffer {
+  LibraryManagedBuffer(std::size_t size) : size_(size) {}
+  template <class CharT>
+  void operator()(std::basic_ofstream<CharT>& stream) const {
+    stream.rdbuf()->pubsetbuf(nullptr, size_);
+  }
+
+  template <class CharT>
+  void operator()(std::basic_ifstream<CharT>& stream) const {
+    stream.rdbuf()->pubsetbuf(nullptr, size_);
+  }
+
+private:
+  std::size_t size_;
+};
+
+struct LibraryDefaultBuffer {
+  LibraryDefaultBuffer() {}
+  template <class CharT>
+  void operator()(std::basic_ofstream<CharT>&) const {}
+  template <class CharT>
+  void operator()(std::basic_ifstream<CharT>&) const {}
+};
+
+#endif // TEST_STD_INPUT_OUTPUT_FILE_STREAMS_FSTREAMS_TYPES_H


        


More information about the libcxx-commits mailing list