aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/bufstreambuf.cxx13
-rw-r--r--libbutl/bufstreambuf.hxx67
-rw-r--r--libbutl/char-scanner.mxx16
-rw-r--r--libbutl/char-scanner.txx2
-rw-r--r--libbutl/fdstream.cxx53
-rw-r--r--libbutl/fdstream.ixx8
-rw-r--r--libbutl/fdstream.mxx44
-rw-r--r--libbutl/filesystem.cxx2
-rw-r--r--libbutl/manifest-rewriter.cxx2
-rw-r--r--libbutl/sha1.cxx12
-rw-r--r--libbutl/sha1.mxx14
-rw-r--r--libbutl/sha256.cxx10
-rw-r--r--libbutl/sha256.mxx10
-rw-r--r--tests/fdstream/driver.cxx13
14 files changed, 169 insertions, 97 deletions
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 <libbutl/bufstreambuf.hxx>
+
+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 <cstdint> // uint64_t
+#include <streambuf>
+
+#include <libbutl/export.hxx>
+
+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<char>
+ {
+ public:
+ using base = std::basic_streambuf<char>;
+
+ 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 <libbutl/fdstream.mxx>
+#include <libbutl/bufstreambuf.hxx>
#endif
#include <libbutl/export.hxx>
@@ -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<fdbuf*> (is.rdbuf ())),
+ buf_ (dynamic_cast<bufstreambuf*> (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 <libbutl/path.mxx>
#include <libbutl/filesystem.mxx>
#include <libbutl/small-vector.mxx>
+#include <libbutl/bufstreambuf.hxx>
#endif
#include <libbutl/export.hxx>
@@ -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<char>
+ 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<char>;
-
- 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<fdbuf&> (*is.rdbuf ()));
+ fdstreambuf& buf (static_cast<fdstreambuf&> (*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 <string>
#include <cstddef>
#include <cstdint>
+
+#include <istream>
#endif
// Other includes.
@@ -60,10 +62,8 @@ module butl.sha1;
import std.core;
#endif
#endif
-
-import butl.fdstream;
#else
-#include <libbutl/fdstream.mxx>
+#include <libbutl/bufstreambuf.hxx>
#endif
using namespace std;
@@ -91,12 +91,12 @@ namespace butl
}
void sha1::
- append (ifdstream& is)
+ append (istream& is)
{
- fdbuf* buf (dynamic_cast<fdbuf*> (is.rdbuf ()));
+ bufstreambuf* buf (dynamic_cast<bufstreambuf*> (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 <iosfwd> // istream
#include <string>
-#include <cstddef> // size_t
+#include <cstddef> // size_t
#include <cstdint>
-#include <cstring> // strlen()
+#include <cstring> // 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 <cstdint>
#include <cctype> // isxdigit()
+#include <istream>
#include <stdexcept> // invalid_argument
#endif
@@ -54,10 +55,9 @@ import std.core;
#endif
import butl.utility; // *case()
-import butl.fdstream;
#else
#include <libbutl/utility.mxx>
-#include <libbutl/fdstream.mxx>
+#include <libbutl/bufstreambuf.hxx>
#endif
using namespace std;
@@ -85,12 +85,12 @@ namespace butl
}
void sha256::
- append (ifdstream& is)
+ append (istream& is)
{
- fdbuf* buf (dynamic_cast<fdbuf*> (is.rdbuf ()));
+ bufstreambuf* buf (dynamic_cast<bufstreambuf*> (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 <string>
+#include <iosfwd> // istream
#include <cstddef> // size_t
#include <cstdint>
#include <cstring> // 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<fdbuf*> (is.rdbuf ()));
+ fdstreambuf* buf (dynamic_cast<fdstreambuf*> (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<fdbuf*> (is.rdbuf ()));
+ fdstreambuf* buf (dynamic_cast<fdstreambuf*> (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<streamoff> (is.tellg ()) == 8);
- const fdbuf* buf (dynamic_cast<const fdbuf*> (is.rdbuf ()));
+ const fdstreambuf* buf (dynamic_cast<const fdstreambuf*> (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<streamoff> (os.tellp ()) == 2);
- const fdbuf* buf (dynamic_cast<const fdbuf*> (os.rdbuf ()));
+ const fdstreambuf* buf (dynamic_cast<const fdstreambuf*> (os.rdbuf ()));
assert (buf != nullptr && buf->tellp () == 2);
os.close ();
@@ -757,7 +758,7 @@ main (int argc, const char* argv[])
assert (static_cast<streamoff> (is.tellg ()) == 8);
- const fdbuf* buf (dynamic_cast<const fdbuf*> (is.rdbuf ()));
+ const fdstreambuf* buf (dynamic_cast<const fdstreambuf*> (is.rdbuf ()));
assert (buf != nullptr && buf->tellp () == 8);
assert (from_stream (is) == "6789");