[libcxx-commits] [libcxx] [libc++] Optimize filebuf::seekoff (PR #211788)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 27 08:09:22 PDT 2026


================
@@ -6,64 +6,316 @@
 //
 //===----------------------------------------------------------------------===//
 
+// FILE_DEPENDENCIES: seekoff.dat
+
 // <fstream>
 
 // pos_type seekoff(off_type off, ios_base::seekdir way,
 //                  ios_base::openmode which = ios_base::in | ios_base::out);
 // pos_type seekpos(pos_type sp,
 //                  ios_base::openmode which = ios_base::in | ios_base::out);
 
-#include <fstream>
 #include <cassert>
+#include <fstream>
 
+#include "make_string.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    {
-        char buf[10];
-        typedef std::filebuf::pos_type pos_type;
-        std::filebuf f;
-        f.pubsetbuf(buf, sizeof(buf));
-        assert(f.open("seekoff.dat", std::ios_base::in | std::ios_base::out
-                                                       | std::ios_base::trunc) != 0);
-        assert(f.is_open());
-        f.sputn("abcdefghijklmnopqrstuvwxyz", 26);
-        pos_type p = f.pubseekoff(-15, std::ios_base::cur);
-        assert(p == 11);
-        assert(f.sgetc() == 'l');
-        f.pubseekoff(0, std::ios_base::beg);
-        assert(f.sgetc() == 'a');
-        f.pubseekoff(-1, std::ios_base::end);
-        assert(f.sgetc() == 'z');
-        assert(f.pubseekpos(p) == p);
-        assert(f.sgetc() == 'l');
-    }
-    std::remove("seekoff.dat");
+template <class CharT>
+void reopen(std::basic_filebuf<CharT>& fb) {
+  fb.close();
+  fb.open("seekoff.dat", std::ios::in);
+}
+
+template <class CharT>
+void test() {
+  using filebuf = std::basic_filebuf<CharT>;
+
+  { // seek with file closed
+    filebuf fb;
+    assert(fb.pubseekoff(0, std::ios::beg) == -1);
+  }
+
+  {   // seek stream from begin
+    { // unbuffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.sgetc() == '1');
+
+      reopen(fb);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.sgetc() == '2');
+
+      reopen(fb);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+
+      assert(fb.sgetc() == '1'); // prime the buffer
+
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.sgetc() == CharT('1'));
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.sgetc() == CharT('2'));
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered, tiny buffer
+      filebuf fb;
+      CharT buffer[5];
+      fb.pubsetbuf(buffer, 5);
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.sgetc() == CharT('1'));
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.sgetc() == CharT('2'));
+      assert(fb.pubseekoff(8, std::ios::beg) == 8);
+      assert(fb.sgetc() == CharT('9'));
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, neither read from nor written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+    }
+    { // input/output stream, written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out | std::ios::trunc));
+      fb.sputn(MAKE_CSTRING(CharT, "1234567890"), 10);
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+    }
+    { // input/output stream, read from
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      assert(fb.pubseekoff(0, std::ios::beg) == 0); // Go to the start, so we can read something
+
+      CharT buffer[11];
+      buffer[10] = '\0';
+      assert(fb.sgetn(buffer, 10) == 10);
+      assert(buffer == MAKE_STRING(CharT, "1234567890"));
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+    }
+  }
+  {   // seek stream from current position
+    { // unbuffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      // negative offsets, from begin
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+
+      // negative offsets, from begin
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered, tiny buffer
+      filebuf fb;
+      CharT buffer[5];
+      fb.pubsetbuf(buffer, 5);
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+
+      // negative offsets, from begin
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, neither read from nor written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+    }
+    { // input/output stream, written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out | std::ios::trunc));
+      fb.sputn(MAKE_CSTRING(CharT, "1234567890"), 10);
+
+      // negative offsets; we've written and the cursor is at the end of the file, so these succeed
+      assert(fb.pubseekoff(-1, std::ios::cur) == 9);
+      assert(fb.pubseekoff(-2, std::ios::cur) == 7);
+
+      // except this one, we didn't write this much
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 7);
+      assert(fb.pubseekoff(1, std::ios::cur) == 8);
+      assert(fb.pubseekoff(10, std::ios::cur) == 18);
+    }
+    { // input/output stream, read from
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      assert(fb.pubseekoff(0, std::ios::beg) == 0); // Go to the start, so we can read something
+
+      CharT buffer[11];
+      buffer[10] = '\0';
+      assert(fb.sgetn(buffer, 10) == 10);
+      assert(buffer == MAKE_STRING(CharT, "1234567890"));
 
+      // negative offsets; we've read and the cursor is at the end of the file, so these succeed
+      assert(fb.pubseekoff(-1, std::ios::cur) == 9);
+      assert(fb.pubseekoff(-2, std::ios::cur) == 7);
+
+      // except this one, we didn't read this much
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 7);
+      assert(fb.pubseekoff(1, std::ios::cur) == 8);
+      assert(fb.pubseekoff(10, std::ios::cur) == 18);
+    }
+  }
+  {   // seek stream end
+    { // unbuffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered, tiny buffer
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      CharT buffer[5];
+      fb.pubsetbuf(buffer, 5);
+      assert(fb.sgetc() == '1');
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, neither read from nor written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out | std::ios::trunc));
+      fb.sputn(MAKE_CSTRING(CharT, "1234567890"), 10);
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, read from
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      assert(fb.pubseekoff(0, std::ios::beg) == 0); // Go to the start, so we can read something
+
+      CharT buffer[11];
+      buffer[10] = '\0';
+      assert(fb.sgetn(buffer, 10) == 10);
+      assert(buffer == MAKE_STRING(CharT, "1234567890"));
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+  }
+}
+
+int main(int, char**) {
+  test<char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-    {
-        wchar_t buf[10];
-        typedef std::filebuf::pos_type pos_type;
-        std::wfilebuf f;
-        f.pubsetbuf(buf, sizeof(buf)/sizeof(buf[0]));
-        assert(f.open("seekoff.dat", std::ios_base::in | std::ios_base::out
-                                                       | std::ios_base::trunc) != 0);
-        assert(f.is_open());
-        f.sputn(L"abcdefghijklmnopqrstuvwxyz", 26);
-        LIBCPP_ASSERT(buf[0] == L'v');
-        pos_type p = f.pubseekoff(-15, std::ios_base::cur);
-        assert(p == 11);
-        assert(f.sgetc() == L'l');
-        f.pubseekoff(0, std::ios_base::beg);
-        assert(f.sgetc() == L'a');
-        f.pubseekoff(-1, std::ios_base::end);
-        assert(f.sgetc() == L'z');
-        assert(f.pubseekpos(p) == p);
-        assert(f.sgetc() == L'l');
-    }
-    std::remove("seekoff.dat");
+  test<wchar_t>();
 #endif
 
+  // TODO: test with different codecvt facets (e.g. where always_noconv() is false, encoding() == 0/-1)
----------------
ldionne wrote:

Since we're touching code that hasn't changed in 15 years, I think we should include this test coverage in this patch.

https://github.com/llvm/llvm-project/pull/211788


More information about the libcxx-commits mailing list