summaryrefslogtreecommitdiff
path: root/src/include/iodevice.h
blob: 3e3330d52058ec46833070f975aa1f95383d7094 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/**
 * @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 <string>

#include <unistd.h>  // ::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 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 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<class T> 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<class T> 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