aboutsummaryrefslogtreecommitdiff
path: root/butl/fdstream
diff options
context:
space:
mode:
Diffstat (limited to 'butl/fdstream')
-rw-r--r--butl/fdstream310
1 files changed, 259 insertions, 51 deletions
diff --git a/butl/fdstream b/butl/fdstream
index 23cc58c..bb14f25 100644
--- a/butl/fdstream
+++ b/butl/fdstream
@@ -5,24 +5,37 @@
#ifndef BUTL_FDSTREAM
#define BUTL_FDSTREAM
+#include <string>
#include <istream>
#include <ostream>
#include <cstdint> // uint16_t
#include <butl/path>
-#include <butl/filesystem> // permissions
+#include <butl/filesystem> // permissions
namespace butl
{
- // An iostream that is initialized with a file descriptor rather than
- // a file name.
+ // An [io]fstream that can be initialized with a file descriptor in addition
+ // to a file name and that also by default enables exceptions on badbit and
+ // failbit. So instead of a dance like this:
+ //
+ // ifstream ifs;
+ // ifs.exceptions (ifstream::badbit | ifstream::failbit);
+ // ifs.open (path.string ());
+ //
+ // You can simply do:
+ //
+ // ifdstream ifs (path);
//
// Notes and limitations:
//
// - char only
// - input or output but not both
// - no support for put back
- // - throws std::system_error in case of a read()/write() error
+ // - throws ios::failure in case of open()/read()/write()/close() errors
+ // - exception mask has at least badbit
+ // - after catching an exception caused by badbit the stream is no longer
+ // used
// - not movable, though can be easily supported
//
class fdbuf: public std::basic_streambuf<char>
@@ -80,81 +93,262 @@ namespace butl
char buf_[4096];
};
- // File descriptor translation mode. It has the same semantics as the
- // binary/text opening modes in std::fstream. Specifically, this is a
- // noop for POSIX systems where the two modes are the same.
+ // File stream mode.
+ //
+ // The text/binary flags have the same semantics as those in std::fstream.
+ // Specifically, this is a noop for POSIX systems where the two modes are
+ // the same.
//
- enum class fdtranslate
+ // The skip flag instructs the stream to skip to the end before closing the
+ // file descriptor. This is primarily useful when working with pipes where
+ // you may want not to "offend" the other end by closing your end before
+ // reading all the data.
+ //
+ enum class fdstream_mode: std::uint16_t
{
- text,
- binary
+ text = 0x01,
+ binary = 0x02,
+ skip = 0x04
};
+ fdstream_mode operator& (fdstream_mode, fdstream_mode);
+ fdstream_mode operator| (fdstream_mode, fdstream_mode);
+ fdstream_mode operator&= (fdstream_mode&, fdstream_mode);
+ fdstream_mode operator|= (fdstream_mode&, fdstream_mode);
+
+ // Extended (compared to ios::openmode) file open flags.
+ //
+ enum class fdopen_mode: std::uint16_t
+ {
+ in = 0x01, // Open for reading.
+ out = 0x02, // Open for writing.
+ append = 0x04, // Seek to the end of file before each write.
+ truncate = 0x08, // Discard the file contents on open.
+ create = 0x10, // Create a file if not exists.
+ exclusive = 0x20, // Fail if the file exists and the create flag is set.
+ binary = 0x40, // Set binary translation mode.
+ at_end = 0x80, // Seek to the end of stream immediately after open.
+
+ none = 0 // Usefull when build the mode incrementally.
+ };
+
+ fdopen_mode operator& (fdopen_mode, fdopen_mode);
+ fdopen_mode operator| (fdopen_mode, fdopen_mode);
+ fdopen_mode operator&= (fdopen_mode&, fdopen_mode);
+ fdopen_mode operator|= (fdopen_mode&, fdopen_mode);
+
class fdstream_base
{
protected:
fdstream_base () = default;
fdstream_base (int fd): buf_ (fd) {}
- fdstream_base (int, fdtranslate);
+ fdstream_base (int, fdstream_mode);
protected:
fdbuf buf_;
};
- // Note that the destructor may throw.
+ // iofdstream constructors and open() functions that take openmode as an
+ // argument mimic the corresponding iofstream functions in terms of the
+ // openmode mask interpretation. They throw std::invalid_argument for an
+ // invalid combination of flags (as per the standard). Note that the in and
+ // out flags are always added implicitly for ifdstream and ofdstream,
+ // respectively.
+ //
+ // iofdstream constructors and open() functions that take fdopen_mode as an
+ // argument interpret the mask literally just ignoring some flags which are
+ // meaningless in the absense of others (read more on that in the comment
+ // for fdopen()). Note that the in and out flags are always added implicitly
+ // for ifdstream and ofdstream, respectively.
+ //
+ // iofdstream constructors and open() functions that take file path as a
+ // const std::string& or const char* may throw the invalid_path exception.
+ //
+ // Passing -1 as a file descriptor is valid and results in the creation of
+ // an unopened object.
+ //
+ // Also note that open() and close() functions can be successfully called
+ // for an opened and unopened objects respectively. That is in contrast with
+ // iofstream that sets failbit in such cases.
+ //
+ // @@ Need to make sure performance is on par with fstream on both
+ // Linux and Windows.
+ //
+ // @@ Do we need to increase default buffer size? Make it customizable?
+ // Wonder what it is in libstdc++ and MSVC?
+
+ // Note that ifdstream destructor will close an open file descriptor but
+ // will ignore any errors. To detect such errors, call close() explicitly.
//
class ifdstream: fdstream_base, public std::istream
{
public:
- ifdstream (): std::istream (&buf_) {}
- ifdstream (int fd): fdstream_base (fd), std::istream (&buf_) {}
- ifdstream (int fd, fdtranslate m)
- : fdstream_base (fd, m), std::istream (&buf_) {}
+ // Create an unopened object with iostate = badbit | failbit (we cannot
+ // have the iostate as an argument since it clashes with int fd) To create
+ // an unopened object with non-default exception mask one can do:
+ //
+ // ifdstream (-1, ...);
+ //
+ ifdstream ();
+
+ explicit
+ ifdstream (int fd, iostate e = badbit | failbit);
+ ifdstream (int fd, fdstream_mode m, iostate e = badbit | failbit);
+
+ explicit
+ ifdstream (const char*,
+ openmode = in,
+ iostate e = badbit | failbit);
+
+ explicit
+ ifdstream (const std::string&,
+ openmode = in,
+ iostate e = badbit | failbit);
+
+ explicit
+ ifdstream (const path&,
+ openmode = in,
+ iostate e = badbit | failbit);
+
+ ifdstream (const char*,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ifdstream (const std::string&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ifdstream (const path&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ~ifdstream () override;
+
+ void
+ open (const char*, openmode = in);
+
+ void
+ open (const std::string&, openmode = in);
+
+ void
+ open (const path&, openmode = in);
+
+ void
+ open (const char*, fdopen_mode);
- void close () {buf_.close ();}
- void open (int fd) {buf_.open (fd);}
+ void
+ open (const std::string&, fdopen_mode);
+
+ void
+ open (const path&, fdopen_mode);
+
+ void
+ open (int fd) {buf_.open (fd); clear();}
+
+ void close ();
bool is_open () const {return buf_.is_open ();}
+
+ private:
+ bool skip_ = false;
};
- // Note that the destructor flushes the stream and may throw.
+ // Note that ofdstream requires that you explicitly call close() before
+ // destroying it. Or, more specifically, the ofdstream object should not be
+ // in the opened state by the time its destructor is called, unless it is in
+ // the "not good" state (good() == false) or the destructor is being called
+ // during the stack unwinding due to an exception being thrown
+ // (std::uncaught_exception() == true). This is enforced with assert() in
+ // the ofdstream destructor.
//
class ofdstream: fdstream_base, public std::ostream
{
public:
- ofdstream (): std::ostream (&buf_) {}
- ofdstream (int fd): fdstream_base (fd), std::ostream (&buf_) {}
- ofdstream (int fd, fdtranslate m)
- : fdstream_base (fd, m), std::ostream (&buf_) {}
+ // Create an unopened object with iostate = badbit | failbit (we cannot
+ // have the iostate as an argument since it clashes with int fd). To create
+ // an unopened object with non-default exception mask one can do:
+ //
+ // ofdstream (-1, ...);
+ //
+ ofdstream ();
- ~ofdstream () override {if (is_open () && good ()) buf_.sync ();}
+ explicit
+ ofdstream (int fd, iostate e = badbit | failbit);
+ ofdstream (int fd, fdstream_mode m, iostate e = badbit | failbit);
- void close () {flush (); buf_.close ();}
- void open (int fd) {buf_.open (fd);}
- bool is_open () const {return buf_.is_open ();}
- };
+ explicit
+ ofdstream (const char*,
+ openmode = out,
+ iostate e = badbit | failbit);
- // File open flags.
- //
- enum class fdopen_mode: std::uint16_t
- {
- in = 0x01, // Open for reading.
- out = 0x02, // Open for writing.
- append = 0x04, // Seek to the end of file before each write.
- truncate = 0x08, // Discard the file contents on open.
- create = 0x10, // Create a file if not exists.
- exclusive = 0x20, // Fail if the file exists and the create flag is set.
- binary = 0x40, // Set binary translation mode.
+ explicit
+ ofdstream (const std::string&,
+ openmode = out,
+ iostate e = badbit | failbit);
- none = 0 // Usefull when build the mode incrementally.
+ explicit
+ ofdstream (const path&,
+ openmode = out,
+ iostate e = badbit | failbit);
+
+ ofdstream (const char*,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ofdstream (const std::string&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ofdstream (const path&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ~ofdstream () override;
+
+ void
+ open (const char*, openmode = out);
+
+ void
+ open (const std::string&, openmode = out);
+
+ void
+ open (const path&, openmode = out);
+
+ void
+ open (const char*, fdopen_mode);
+
+ void
+ open (const std::string&, fdopen_mode);
+
+ void
+ open (const path&, fdopen_mode);
+
+ void
+ open (int fd) {buf_.open (fd); clear ();}
+
+ void close () {if (is_open ()) flush (); buf_.close ();}
+ bool is_open () const {return buf_.is_open ();}
};
- fdopen_mode operator& (fdopen_mode, fdopen_mode);
- fdopen_mode operator| (fdopen_mode, fdopen_mode);
- fdopen_mode operator&= (fdopen_mode&, fdopen_mode);
- fdopen_mode operator|= (fdopen_mode&, fdopen_mode);
+ // The std::getline() replacement that provides a workaround for libstdc++'s
+ // ios::failure ABI fiasco (#66145) by throwing ios::failure, as it is
+ // defined at libbutl build time (new ABI on recent distributions) rather
+ // than libstdc++ build time (still old ABI on most distributions).
+ //
+ // Notes:
+ //
+ // - This relies of ADL so if the stream is used via the std::istream
+ // interface, then std::getline() will still be used. To put it another
+ // way, this is "the best we can do" until GCC folks get their act
+ // together.
+ //
+ // - The fail and eof bits may be left cleared in the stream exception mask
+ // when the function throws because of badbit.
+ //
+ ifdstream&
+ getline (ifdstream&, std::string&, char delim = '\n');
// Open a file returning the file descriptor on success and throwing
- // std::system_error otherwise.
+ // ios:failure otherwise.
//
// The mode argument should have at least one of the in or out flags set.
// The append and truncate flags are meaningless in the absense of the out
@@ -170,6 +364,20 @@ namespace butl
// Windows permissions other than ru and wu are unlikelly to have effect.
//
int
+ fdopen (const char*,
+ fdopen_mode,
+ permissions = permissions::ru | permissions::wu |
+ permissions::rg | permissions::wg |
+ permissions::ro | permissions::wo);
+
+ int
+ fdopen (const std::string&,
+ fdopen_mode,
+ permissions = permissions::ru | permissions::wu |
+ permissions::rg | permissions::wg |
+ permissions::ro | permissions::wo);
+
+ int
fdopen (const path&,
fdopen_mode,
permissions = permissions::ru | permissions::wu |
@@ -177,17 +385,17 @@ namespace butl
permissions::ro | permissions::wo);
// Set the translation mode for the file descriptor. Return the previous
- // mode on success, throw std::system_error otherwise.
+ // mode on success, throw ios::failure otherwise.
//
- fdtranslate
- fdmode (int, fdtranslate);
+ fdstream_mode
+ fdmode (int, fdstream_mode);
// Convenience functions for setting the translation mode for standard
// streams.
//
- fdtranslate stdin_fdmode (fdtranslate);
- fdtranslate stdout_fdmode (fdtranslate);
- fdtranslate stderr_fdmode (fdtranslate);
+ fdstream_mode stdin_fdmode (fdstream_mode);
+ fdstream_mode stdout_fdmode (fdstream_mode);
+ fdstream_mode stderr_fdmode (fdstream_mode);
// Low-level, nothrow file descriptor API.
//