/** * @file iodevice.h * @brief Declaration of the IODevice class. * @author Andreas Aardal Hanssen * @date 2002, 2003 */ #ifndef iodevice_h_included #define iodevice_h_included #include "convert.h" // BincStream #include #include // ::write const char CMS_END_OF_LINE[4] = {0x0d, '\n', 0x00, 0x00}; namespace Binc { /** * @class IODevice * @brief The IODevice class provides a framework for reading and writing to device. * * Implement new devices by inheriting this class and overloading all * virtual methods. * * service() returns the service that the specific device is used * for. Two values are "log" and "client". * * @sa IOFactory, MultilogDevice, SyslogDevice, StdIODevice, SSLDevice */ class IODevice { public: /// Standard options for an IODevice. enum Flags { None = 0, FlushesOnEndl = 1 << 0, HasInputLimit = 1 << 1, HasOutputLimit = 1 << 2, IsEnabled = 1 << 3, HasTimeout = 1 << 4 }; /// Errors from when an operation returned false. enum class Error { Unknown, Timeout }; /** * Constructs an invalid IODevice. * * Instances of IODevice perform no operations, and all boolean * functions always return false. This constructor is only useful * if called from a subclass that reimplements all virtual methods. */ IODevice(int f = 0); /// Destructs an IODevice; does nothing. virtual ~IODevice(); /// Clears all data in the input and output buffers. void clear(); /** * Sets one or more flags. * @param f A bitwise OR of flags from the Flags enum. */ void setFlags(unsigned int f); /*! * Clears one or more flags. * @param f A bitwise OR of flags from the Flags enum. */ void clearFlags(unsigned int f); /** * Sets the maximum allowed input buffer size. If this size is * non-zero and exceeded, reading from the device will fail. This * functionality is used to prevent clients from forcing this class * to consume so much memory that the program crashes. * * Setting the max input buffer size to 0 disables the input size * limit. * * @param max The maximum input buffer size in bytes. */ void setMaxInputBufferSize(unsigned int max); /** * Sets the maximum allowed output buffer size. If this size is * non-zero and exceeded, flush() is called implicitly. * * Setting the max output buffer size to 0 disables the output size * limit. This is generally discouraged. * * As a contrast to setMaxInputBufferSize(), this function is used * to bundle up consequent write calls, allowing more efficient use * of the underlying device as larger blocks of data are written at * a time. * * @param max The maximum output buffer size in bytes. */ void setMaxOutputBufferSize(unsigned int max); /** * Sets the device's internal timeout in seconds. This timeout is * used both when waiting for data to read and for waiting for the * ability to write. * * If this timeout is exceeded, the read or write function that * triggered the timeout will fail. * * Setting the timeout to 0 disables the timeout. * * @param t The timeout in seconds. * @sa getTimeout() */ void setTimeout(unsigned int t); /** * Returns the timeout in seconds, or 0 if there is no timeout. * * @sa setTimeout() */ unsigned int getTimeout() const; enum class LogLevel { ErrorLevel, InfoLevel, WarningLevel, DebugLevel }; /** * Sets the output level for the following write operations on this * device. * * The output level is a number which gives the following write * operations a priority. You can use setOutputLevelLimit() to * filter the write operations valid for different operating modes. * This enables you to have certain write operations ignored. * * For instance, if the output level is set to 0, then "Hello" is * written, and the output level is set to 1, followed by writing * "Daisy", the output level limit value will decide whether only * "Hello" is written, or if also "Daisy" is written. * * A low value of the level gives higher priority, and a high level * will give low priority. The default value is 0, and write * operations that are done with output level 0 are never ignored. * * @param level The output level * @sa getOutputLevel(), setOutputLevelLimit() */ void setOutputLevel(LogLevel level); /** * Returns the current output level. * * @sa setOutputLevel() */ LogLevel getOutputLevel() const; /** * Sets the current output level limit. Write operations with a * level higher than the output level limit are ignored. * * @param level The output level limit * @sa setOutputLevel() */ void setOutputLevelLimit(LogLevel level); /** * Returns the current output level limit. * * @sa setOutputLevelLimit() */ LogLevel getOutputLevelLimit() const; /** * Returns the number of bytes that have been read from this device * since it was created. */ unsigned int getReadCount() const; /** * Returns the number of bytes that have been written to this * device since it was created. */ unsigned int getWriteCount() const; /** * Calling this function enables the built-in protocol dumping feature in * the device. All input and output to this device will be dumped to a file * in /tmp. */ void enableProtocolDumping(); /** * Writes data to the device. Depending on the value of the max * output buffer size, the data may not be written immediately. * * @sa setMaxOutputBufferSize() */ template IODevice &operator<<(const T &source); /** * Writes data to the device. This function specializes on standard * ostream derivatives, such as std::endl. */ IODevice &operator<<(std::ostream &(*source)(std::ostream &)); /** * Returns true if data can be read from the device; otherwise * returns false. */ virtual bool canRead() const; /** * Reads data from the device, and stores this in a string. Returns * true on success; otherwise returns false. * * @param dest The incoming data is stored in this string. * @param max No more than this number of bytes is read from the * device. */ bool readStr(std::string *dest, unsigned int max = 0); /** * Reads exactly one byte from the device and stores this in a * char. Returns true on success; otherwise returns false. * * @param dest The incoming byte is stored in this char. */ bool readChar(char *dest = nullptr); /*! FIXME: add docs */ void unreadChar(char c); /*! FIXME: add docs */ void unreadStr(const std::string &s); /** * Reads characters from the device, until and including one * certain character is found. All read characters are discarded. * * This function can be used to skip to the beginning of a line, * with the terminating character being '\n'. * * @param The certain character. */ bool skipTo(char c); /** * Flushes the output buffer. Writes all data in the output buffer * to the device. */ bool flush(); /** * Returns the type of error that most recently occurred. */ Error getLastError() const; /** * Returns a human readable description of the error that most * recently occurred. If no known error has occurred, this method * returns "Unknown error". */ std::string getLastErrorString() const; /** * Returns the type of service provided by this device. Two valid * return values are "client" and "log". */ virtual std::string service() const; protected: /** * Waits until data can be written to the device. If the timeout is * 0, this function waits indefinitely. Otherwise, it waits until * the timeout has expired. * * If this function returns true, data can be written to the * device; otherwise, getLastError() must be checked to determine * whether a timeout occurred or whether an error with the device * prevents further writing. */ virtual bool waitForWrite() const; /** * Waits until data can be read from the device. * * \sa waitForWrite() */ virtual bool waitForRead() const; /// Types of results from a write. enum WriteResult { WriteWait = 0, WriteDone = 1 << 0, WriteError = 1 << 1 }; /** * Writes as much data as possible to the device. If some but not * all data was written, returns WriteWait. If all data was * written, returns WriteDone. If an error occurred, returns * WriteError. */ virtual WriteResult write(); /** * Reads data from the device, and stores it in the input buffer. * Returns true on success; otherwise returns false. * * This method will fail if there is no more data available, if a * timeout occurred or if an error with the device prevents more * data from being read. * * The number of bytes read from the device is undefined. */ virtual bool fillInputBuffer(); BincStream inputBuffer; BincStream outputBuffer; unsigned int flags; unsigned int maxInputBufferSize; unsigned int maxOutputBufferSize; unsigned int timeout; size_t readCount; size_t writeCount; LogLevel outputLevel; LogLevel outputLevelLimit; mutable Error error; mutable std::string errorString; int dumpfd; }; template IODevice &IODevice::operator<<(const T &source) { if ((flags & IsEnabled) && outputLevel <= outputLevelLimit) { outputBuffer << source; if (dumpfd) { BincStream ss; ss << source; ::write(dumpfd, ss.str().c_str(), ss.getSize()); } if (flags & HasInputLimit) { if (outputBuffer.getSize() > maxOutputBufferSize) flush(); } } return *this; } } #endif