[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