From 98c4038df36fb73601c58ccd885d1c2d3703cf6e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 8 Sep 2021 08:26:54 +0200 Subject: Factor "buffered streambuf" interface from fdbuf to bufstreambuf Also rename fdbuf to fdstreambuf for consistency. --- libbutl/bufstreambuf.cxx | 13 +++++++++ libbutl/bufstreambuf.hxx | 67 +++++++++++++++++++++++++++++++++++++++++++ libbutl/char-scanner.mxx | 16 +++++------ libbutl/char-scanner.txx | 2 +- libbutl/fdstream.cxx | 53 +++++++++++++++++----------------- libbutl/fdstream.ixx | 8 +++--- libbutl/fdstream.mxx | 44 +++++++++++----------------- libbutl/filesystem.cxx | 2 +- libbutl/manifest-rewriter.cxx | 2 +- libbutl/sha1.cxx | 12 ++++---- libbutl/sha1.mxx | 14 +++++---- libbutl/sha256.cxx | 10 +++---- libbutl/sha256.mxx | 10 ++++--- tests/fdstream/driver.cxx | 13 +++++---- 14 files changed, 169 insertions(+), 97 deletions(-) create mode 100644 libbutl/bufstreambuf.cxx create mode 100644 libbutl/bufstreambuf.hxx diff --git a/libbutl/bufstreambuf.cxx b/libbutl/bufstreambuf.cxx new file mode 100644 index 0000000..d152166 --- /dev/null +++ b/libbutl/bufstreambuf.cxx @@ -0,0 +1,13 @@ +// file : libbutl/bufstreambuf.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +namespace butl +{ + bufstreambuf:: + ~bufstreambuf () + { + // Vtable. + } +} diff --git a/libbutl/bufstreambuf.hxx b/libbutl/bufstreambuf.hxx new file mode 100644 index 0000000..a49b2d0 --- /dev/null +++ b/libbutl/bufstreambuf.hxx @@ -0,0 +1,67 @@ +// file : libbutl/bufstreambuf.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#pragma once + +#include // uint64_t +#include + +#include + +namespace butl +{ + // A buffered streambuf interface that exposes its buffer for direct scan + // and provides a notion of logical position. See fdstreambuf for background + // and motivation. + // + class LIBBUTL_SYMEXPORT bufstreambuf: public std::basic_streambuf + { + public: + using base = std::basic_streambuf; + + using int_type = base::int_type; + using traits_type = base::traits_type; + + using pos_type = base::pos_type; // std::streampos + using off_type = base::off_type; // std::streamoff + + public: + explicit + bufstreambuf (std::uint64_t pos = 0): off_ (pos) {} + + virtual + ~bufstreambuf (); + + // basic_streambuf input interface. + // + public: + + // Direct access to the get area. Use with caution. + // + using base::gptr; + using base::egptr; + using base::gbump; + + // Return the (logical) position of the next byte to be read. + // + // Note that on Windows when reading in the text mode the logical position + // may differ from the physical file descriptor position due to the CRLF + // character sequence translation. See the fdstreambuf::seekoff() + // implementation for more background on this issue. + // + std::uint64_t + tellg () const {return off_ - (egptr () - gptr ());} + + // basic_streambuf output interface. + // + public: + + // Return the (logical) position of the next byte to be written. + // + std::uint64_t + tellp () const {return off_ + (pptr () - pbase ());} + + protected: + std::uint64_t off_; + }; +} diff --git a/libbutl/char-scanner.mxx b/libbutl/char-scanner.mxx index 60994cf..27f692b 100644 --- a/libbutl/char-scanner.mxx +++ b/libbutl/char-scanner.mxx @@ -24,9 +24,8 @@ export module butl.char_scanner; import std.core; import std.io; #endif -import butl.fdstream; #else -#include +#include #endif #include @@ -59,9 +58,9 @@ LIBBUTL_MODEXPORT namespace butl // 0x0D is treated "as if" it was followed by 0x0A and multiple 0x0D // are treated as one. // - // Note also that if the stream happens to be ifdstream, then it includes - // a number of optimizations that assume nobody else is messing with the - // stream. + // Note also that if the stream happens to be bufstreambuf-based, then it + // includes a number of optimizations that assume nobody else is messing + // with the stream. // // The line and position arguments can be used to override the start line // and position in the stream (useful when re-scanning data saved with the @@ -106,8 +105,9 @@ LIBBUTL_MODEXPORT namespace butl std::uint64_t line; std::uint64_t column; - // Logical character position (see ifdstream for details on the logical - // part) if the scanned stream is ifdstream and always zero otherwise. + // Logical character position (see bufstreambuf for details on the + // logical part) if the scanned stream is bufstreambuf-based and always + // zero otherwise. // std::uint64_t position; @@ -240,7 +240,7 @@ LIBBUTL_MODEXPORT namespace butl // the hairy details; realistically, you would probably only direct-scan // ASCII fragments). // - fdbuf* buf_; // NULL if not ifdstream. + bufstreambuf* buf_; // NULL if not bufstreambuf-based. const char_type* gptr_; const char_type* egptr_; diff --git a/libbutl/char-scanner.txx b/libbutl/char-scanner.txx index 35edf42..63389f0 100644 --- a/libbutl/char-scanner.txx +++ b/libbutl/char-scanner.txx @@ -19,7 +19,7 @@ namespace butl position (p), is_ (is), val_ (std::move (v)), - buf_ (dynamic_cast (is.rdbuf ())), + buf_ (dynamic_cast (is.rdbuf ())), gptr_ (nullptr), egptr_ (nullptr), crlf_ (crlf) diff --git a/libbutl/fdstream.cxx b/libbutl/fdstream.cxx index 308d76c..eb0ec7b 100644 --- a/libbutl/fdstream.cxx +++ b/libbutl/fdstream.cxx @@ -174,7 +174,7 @@ namespace butl } #endif - // fdbuf + // fdstreambuf // // Return true if the file descriptor is in the non-blocking mode. Throw // ios::failure on the underlying OS error. @@ -195,7 +195,7 @@ namespace butl #endif } - void fdbuf:: + void fdstreambuf:: open (auto_fd&& fd, uint64_t pos) { close (); @@ -208,7 +208,7 @@ namespace butl fd_ = move (fd); } - bool fdbuf:: + bool fdstreambuf:: blocking (bool m) { // Verify that the file descriptor is open. @@ -232,7 +232,7 @@ namespace butl return !m; } - streamsize fdbuf:: + streamsize fdstreambuf:: showmanyc () { if (!is_open ()) @@ -267,7 +267,7 @@ namespace butl return 0; } - fdbuf::int_type fdbuf:: + fdstreambuf::int_type fdstreambuf:: underflow () { int_type r (traits_type::eof ()); @@ -289,7 +289,7 @@ namespace butl return r; } - bool fdbuf:: + bool fdstreambuf:: load () { // Doesn't handle blocking mode and so should not be called. @@ -306,7 +306,7 @@ namespace butl return n != 0; } - void fdbuf:: + void fdstreambuf:: seekg (uint64_t off) { // In the future we may implement the blocking behavior for a non-blocking @@ -341,7 +341,7 @@ namespace butl setg (buf_, buf_, buf_); } - fdbuf::int_type fdbuf:: + fdstreambuf::int_type fdstreambuf:: overflow (int_type c) { int_type r (traits_type::eof ()); @@ -369,7 +369,7 @@ namespace butl return r; } - int fdbuf:: + int fdstreambuf:: sync () { if (!is_open ()) @@ -394,7 +394,7 @@ namespace butl } #endif - bool fdbuf:: + bool fdstreambuf:: save () { size_t n (pptr () - pbase ()); @@ -421,7 +421,7 @@ namespace butl return true; } - streamsize fdbuf:: + streamsize fdstreambuf:: xsputn (const char_type* s, streamsize sn) { // The xsputn() function interface doesn't support the non-blocking @@ -578,13 +578,13 @@ namespace butl // // - basic_ostream::seekp(pos) -> // basic_streambuf::pubseekpos(pos, ios::out) -> - // fdbuf::seekpos(pos, ios::out) + // fdstreambuf::seekpos(pos, ios::out) // // - basic_istream::seekg(pos) -> // basic_streambuf::pubseekpos(pos, ios::in) -> - // fdbuf::seekpos(pos, ios::in) + // fdstreambuf::seekpos(pos, ios::in) // - fdbuf::pos_type fdbuf:: + fdstreambuf::pos_type fdstreambuf:: seekpos (pos_type pos, ios_base::openmode which) { // Note that the position type provides an explicit conversion to the @@ -599,21 +599,21 @@ namespace butl // // - basic_ostream::seekp(off, dir) -> // basic_streambuf::pubseekoff(off, dir, ios::out) -> - // fdbuf::seekoff(off, dir, ios::out) + // fdstreambuf::seekoff(off, dir, ios::out) // // - basic_ostream::tellp() -> // basic_streambuf::pubseekoff(0, ios::cur, ios::out) -> - // fdbuf::seekoff(0, ios::cur, ios::out) + // fdstreambuf::seekoff(0, ios::cur, ios::out) // // - basic_istream::seekg(off, dir) -> // basic_streambuf::pubseekoff(off, dir, ios::in) -> - // fdbuf::seekoff(off, dir, ios::in) + // fdstreambuf::seekoff(off, dir, ios::in) // // - basic_istream::tellg() -> // basic_streambuf::pubseekoff(0, ios::cur, ios::in) -> - // fdbuf::seekoff(0, ios::cur, ios::in) + // fdstreambuf::seekoff(0, ios::cur, ios::in) // - fdbuf::pos_type fdbuf:: + fdstreambuf::pos_type fdstreambuf:: seekoff (off_type off, ios_base::seekdir dir, ios_base::openmode which) { // The seekoff() function interface doesn't support the non-blocking @@ -837,9 +837,8 @@ namespace butl catch (const ios_base::failure&) {} } - // Underlying file descriptor is closed by fdbuf dtor with errors (if any) - // being ignored. - // + // Underlying file descriptor is closed by fdstreambuf dtor with errors + // (if any) being ignored. } void ifdstream:: @@ -888,11 +887,11 @@ namespace butl // Amend the exception mask to prevent exceptions being thrown by the C++ // IO runtime to avoid incompatibility issues due to ios_base::failure ABI // fiasco (#66145). We will not restore the mask when ios_base::failure is - // thrown by fdbuf since there is no way to "silently" restore it if the - // corresponding bits are in the error state without the exceptions() call - // throwing ios_base::failure. Not restoring exception mask on throwing - // because of badbit should probably be ok since the stream is no longer - // usable. + // thrown by fdstreambuf since there is no way to "silently" restore it if + // the corresponding bits are in the error state without the exceptions() + // call throwing ios_base::failure. Not restoring exception mask on + // throwing because of badbit should probably be ok since the stream is no + // longer usable. // if (eb != ifdstream::badbit) is.exceptions (ifdstream::badbit); diff --git a/libbutl/fdstream.ixx b/libbutl/fdstream.ixx index 4ef5b1d..9ec9e06 100644 --- a/libbutl/fdstream.ixx +++ b/libbutl/fdstream.ixx @@ -27,16 +27,16 @@ namespace butl reset (); } - // fdbuf + // fdstreambuf // - inline fdbuf:: - fdbuf (auto_fd&& fd, std::uint64_t pos) + inline fdstreambuf:: + fdstreambuf (auto_fd&& fd, std::uint64_t pos) { if (fd.get () >= 0) open (std::move (fd), pos); } - inline auto_fd fdbuf:: + inline auto_fd fdstreambuf:: release () { return std::move (fd_); diff --git a/libbutl/fdstream.mxx b/libbutl/fdstream.mxx index 9818732..0d3fd86 100644 --- a/libbutl/fdstream.mxx +++ b/libbutl/fdstream.mxx @@ -36,6 +36,7 @@ import butl.small_vector; #include #include #include +#include #endif #include @@ -142,9 +143,9 @@ LIBBUTL_MODEXPORT namespace butl // - input or output but not both (can use a union of two streams for that) // - no support for put back // - use of tell[gp]() and seek[gp]() is discouraged on Windows for - // fdstreams opened in the text mode (see fdbuf::seekoff() implementation - // for reasoning and consider using non-standard tellg() and seekg() in - // fdbuf, instead) + // fdstreams opened in the text mode (see fdstreambuf::seekoff() + // implementation for reasoning and consider using non-standard tellg() + // and seekg() in fdstreambuf, instead) // - non-blocking file descriptor is supported only by showmanyc() function // and only for pipes on Windows, in contrast to POSIX systems // - throws ios::failure in case of open(), read(), write(), close(), @@ -157,20 +158,21 @@ LIBBUTL_MODEXPORT namespace butl // - passing to constructor auto_fd with a negative file descriptor is valid // and results in the creation of an unopened object // - class LIBBUTL_SYMEXPORT fdbuf: public std::basic_streambuf + class LIBBUTL_SYMEXPORT fdstreambuf: public bufstreambuf { public: - fdbuf () = default; + fdstreambuf () = default; // Unless specified, the current read/write position is assumed to // be 0 (note: not queried). // - fdbuf (auto_fd&&, std::uint64_t pos = 0); + fdstreambuf (auto_fd&&, std::uint64_t pos = 0); - // Before we invented auto_fd into fdstreams we keept fdbuf opened on - // faulty close attempt. Now fdbuf is always closed by close() function. - // This semantics change seems to be the right one as there is no reason to - // expect fdclose() to succeed after it has already failed once. + // Before we invented auto_fd into fdstreams we keept fdstreambuf opened + // on faulty close attempt. Now fdstreambuf is always closed by close() + // function. This semantics change seems to be the right one as there is + // no reason to expect fdclose() to succeed after it has already failed + // once. // void close () {fd_.close ();} @@ -197,13 +199,7 @@ LIBBUTL_MODEXPORT namespace butl blocking (bool); public: - using base = std::basic_streambuf; - - using int_type = base::int_type; - using traits_type = base::traits_type; - - using pos_type = base::pos_type; // std::streampos - using off_type = base::off_type; // std::streamoff + using base = bufstreambuf; // basic_streambuf input interface. // @@ -222,13 +218,7 @@ LIBBUTL_MODEXPORT namespace butl // Return the (logical) position of the next byte to be read. // - // Note that on Windows when reading in the text mode the logical position - // may differ from the physical file descriptor position due to the CRLF - // character sequence translation. See the seekoff() implementation for - // more background on this issue. - // - std::uint64_t - tellg () const {return off_ - (egptr () - gptr ());} + using base::tellg; // Seek to the (logical) position as if by reading the specified number of // bytes from the beginning of the stream. Throw ios::failure on the @@ -255,8 +245,7 @@ LIBBUTL_MODEXPORT namespace butl // Return the (logical) position of the next byte to be written. // - std::uint64_t - tellp () const {return off_ + (pptr () - buf_);} + using base::tellp; // basic_streambuf positioning interface (both input/output). // @@ -273,7 +262,6 @@ LIBBUTL_MODEXPORT namespace butl private: auto_fd fd_; - std::uint64_t off_; char buf_[8192]; bool non_blocking_ = false; }; @@ -348,7 +336,7 @@ LIBBUTL_MODEXPORT namespace butl fd () const {return buf_.fd ();} protected: - fdbuf buf_; + fdstreambuf buf_; }; // iofdstream constructors and open() functions that take openmode as an diff --git a/libbutl/filesystem.cxx b/libbutl/filesystem.cxx index 787747d..3427ee9 100644 --- a/libbutl/filesystem.cxx +++ b/libbutl/filesystem.cxx @@ -1599,7 +1599,7 @@ namespace butl rm = auto_rmfile (to); - // Throws ios::failure on fdbuf read/write failures. + // Throws ios::failure on fdstreambuf read/write failures. // // Note that the eof check is important: if the stream is at eof (empty // file) then this write will fail. diff --git a/libbutl/manifest-rewriter.cxx b/libbutl/manifest-rewriter.cxx index e38d5f4..46bf239 100644 --- a/libbutl/manifest-rewriter.cxx +++ b/libbutl/manifest-rewriter.cxx @@ -64,7 +64,7 @@ namespace butl // Temporary move the descriptor into the stream. // ifdstream is (move (fd)); - fdbuf& buf (static_cast (*is.rdbuf ())); + fdstreambuf& buf (static_cast (*is.rdbuf ())); // Read suffix. // diff --git a/libbutl/sha1.cxx b/libbutl/sha1.cxx index 6a5e9db..f4a6bad 100644 --- a/libbutl/sha1.cxx +++ b/libbutl/sha1.cxx @@ -47,6 +47,8 @@ extern "C" #include #include #include + +#include #endif // Other includes. @@ -60,10 +62,8 @@ module butl.sha1; import std.core; #endif #endif - -import butl.fdstream; #else -#include +#include #endif using namespace std; @@ -91,12 +91,12 @@ namespace butl } void sha1:: - append (ifdstream& is) + append (istream& is) { - fdbuf* buf (dynamic_cast (is.rdbuf ())); + bufstreambuf* buf (dynamic_cast (is.rdbuf ())); assert (buf != nullptr); - while (is.peek () != ifdstream::traits_type::eof () && is.good ()) + while (is.peek () != istream::traits_type::eof () && is.good ()) { size_t n (buf->egptr () - buf->gptr ()); append (buf->gptr (), n); diff --git a/libbutl/sha1.mxx b/libbutl/sha1.mxx index 07c469c..f6fafc0 100644 --- a/libbutl/sha1.mxx +++ b/libbutl/sha1.mxx @@ -8,10 +8,11 @@ // C includes. #ifndef __cpp_lib_modules_ts +#include // istream #include -#include // size_t +#include // size_t #include -#include // strlen() +#include // strlen() #endif // Other includes. @@ -27,8 +28,6 @@ import std.core; LIBBUTL_MODEXPORT namespace butl { - class ifdstream; - // SHA1 checksum calculator. // // For a single chunk of data a sum can be obtained in one line, for @@ -67,11 +66,14 @@ LIBBUTL_MODEXPORT namespace butl // Append stream. // + // Note that currently the stream is expected to be bufstreambuf-based + // (e.g., ifdstream). + // void - append (ifdstream&); + append (std::istream&); explicit - sha1 (ifdstream& i): sha1 () {append (i);} + sha1 (std::istream& i): sha1 () {append (i);} // Check if any data has been hashed. // diff --git a/libbutl/sha256.cxx b/libbutl/sha256.cxx index 2528693..8a34402 100644 --- a/libbutl/sha256.cxx +++ b/libbutl/sha256.cxx @@ -34,6 +34,7 @@ extern "C" #include #include // isxdigit() +#include #include // invalid_argument #endif @@ -54,10 +55,9 @@ import std.core; #endif import butl.utility; // *case() -import butl.fdstream; #else #include -#include +#include #endif using namespace std; @@ -85,12 +85,12 @@ namespace butl } void sha256:: - append (ifdstream& is) + append (istream& is) { - fdbuf* buf (dynamic_cast (is.rdbuf ())); + bufstreambuf* buf (dynamic_cast (is.rdbuf ())); assert (buf != nullptr); - while (is.peek () != ifdstream::traits_type::eof () && is.good ()) + while (is.peek () != istream::traits_type::eof () && is.good ()) { size_t n (buf->egptr () - buf->gptr ()); append (buf->gptr (), n); diff --git a/libbutl/sha256.mxx b/libbutl/sha256.mxx index 9bc0971..d5128b1 100644 --- a/libbutl/sha256.mxx +++ b/libbutl/sha256.mxx @@ -9,6 +9,7 @@ #ifndef __cpp_lib_modules_ts #include +#include // istream #include // size_t #include #include // strlen(), memcpy() @@ -28,8 +29,6 @@ import std.core; LIBBUTL_MODEXPORT namespace butl { - class ifdstream; - // SHA256 checksum calculator. // // For a single chunk of data a sum can be obtained in one line, for @@ -101,11 +100,14 @@ LIBBUTL_MODEXPORT namespace butl // Append stream. // + // Note that currently the stream is expected to be bufstreambuf-based + // (e.g., ifdstream). + // void - append (ifdstream&); + append (std::istream&); explicit - sha256 (ifdstream& i): sha256 () {append (i);} + sha256 (std::istream& i): sha256 () {append (i);} // Check if any data has been hashed. // diff --git a/tests/fdstream/driver.cxx b/tests/fdstream/driver.cxx index 76fc64a..2309a7a 100644 --- a/tests/fdstream/driver.cxx +++ b/tests/fdstream/driver.cxx @@ -575,7 +575,8 @@ main (int argc, const char* argv[]) t.join (); } - // Test setting and getting position via the non-standard fdbuf interface. + // Test setting and getting position via the non-standard fdstreambuf + // interface. // // Seek for read. // @@ -584,7 +585,7 @@ main (int argc, const char* argv[]) ifdstream is (f); - fdbuf* buf (dynamic_cast (is.rdbuf ())); + fdstreambuf* buf (dynamic_cast (is.rdbuf ())); assert (buf != nullptr); char c; @@ -627,7 +628,7 @@ main (int argc, const char* argv[]) { ifdstream is (f, fdopen_mode::in | fdopen_mode::out); - fdbuf* buf (dynamic_cast (is.rdbuf ())); + fdstreambuf* buf (dynamic_cast (is.rdbuf ())); assert (buf != nullptr); // Read till the end of the fragment. @@ -685,7 +686,7 @@ main (int argc, const char* argv[]) assert (static_cast (is.tellg ()) == 8); - const fdbuf* buf (dynamic_cast (is.rdbuf ())); + const fdstreambuf* buf (dynamic_cast (is.rdbuf ())); assert (buf != nullptr && buf->tellg () == 8); assert (from_stream (is) == "89"); @@ -704,7 +705,7 @@ main (int argc, const char* argv[]) assert (static_cast (os.tellp ()) == 2); - const fdbuf* buf (dynamic_cast (os.rdbuf ())); + const fdstreambuf* buf (dynamic_cast (os.rdbuf ())); assert (buf != nullptr && buf->tellp () == 2); os.close (); @@ -757,7 +758,7 @@ main (int argc, const char* argv[]) assert (static_cast (is.tellg ()) == 8); - const fdbuf* buf (dynamic_cast (is.rdbuf ())); + const fdstreambuf* buf (dynamic_cast (is.rdbuf ())); assert (buf != nullptr && buf->tellp () == 8); assert (from_stream (is) == "6789"); -- cgit v1.1