Null stream

From Mailutils
Revision as of 12:42, 22 December 2016 by Gray (talk | contribs) (→‎Null stream)
Jump to navigationJump to search

Null stream

A null stream is similar to the system /dev/null device. It is not connected to any particular physical storage. Any data written to such stream are irrevocably lost. Reading from such a stream returns fixed data, depending on the mode of the stream.

An instance of the null stream is created using the following function:

int mu_nullstream_create (mu_stream_t *pstr, int mode)

It creates an instance of the null stream and returns it in the memory location pointed to by pstr. The mode argument specifies the access mode for this stream. It can be a binary or of the following values:

MU_STREAM_READ
Stream is opened for reading.
MU_STREAM_WRITE
Stream is opened for writing.

The returned stream is always seekable, so the MU_STREAM_SEEK mode is implied.

Any other bits set in the mode argument are silently ignored.

Writing to an instance of a null stream and seeking in such streams always succeeds. If the stream was created with the MU_STREAM_READ on, any reads from it normally behave as if it were connected to an endless source of zero bytes, i.e. each call to:

mu_stream_read (str, buf, size, &n);

results in filling buf with size zeroes. This is similar to reading from the system /dev/zero device.

Note: The actual implementation of null streams has nothing to do with /dev/null, or /dev/zero. We refer to these devices only to illustrate the behavior of null streams.

This is the default behavior when reading. It can be altered using the MU_IOCTL_NULLSTREAM ioctl.

The MU_IOCTL_NULLSTREAM ioctl

This ioctl controls various parameters of a null stream. Synopsis:

mu_stream_ioctl (stream, MU_IOCTL_NULLSTREAM, opcode, arg);

where opcode is the operation code specific to null streams and arg is the operation argument. Supported operation codes and their arguments are discussed below in this document.

MU_IOCTL_NULLSTREAM_SET_PATTERN

This ioctl sets a read pattern. The argument is a pointer to struct mu_nullstream_pattern, defined as:

struct mu_nullstream_pattern
{
  char *pattern;   /* Return pattern */
  size_t size;     /* Number of bytes in pattern */
};

The pattern member points to size bytes of data which are returned cyclically at each read. For example, suppose that str is a null stream instance, and consider the following code:

  struct mu_nullstream_pattern pat;
  char buf[16];
  size_t n;

  pat.pattern = "01234567";
  pat.size = 8;
  mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM, MU_IOCTL_NULLSTREAM_SET_PATTERN, &pat);

  mu_stream_read (str, buf, sizeof (buf), &n);

Then, after the call to mu_stream_read with the size argument of 16, we will receive "0123456701234567".

Similarly, the following code:

  mu_stream_seek (str, 3, MU_SEEK_SET, NULL);
  mu_stream_read (str, buf, sizeof (buf), &n);

will yield "3456701234567012".

The default behavior corresponds to the following initialization:

  pat.pattern = "";
  pat.size = 1;
  mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM, MU_IOCTL_NULLSTREAM_SET_PATTERN, &pat);

Calling the MU_IOCTL_NULLSTREAM_SET_PATTERN with a NULL argument causes all subsequent reads from that stream to return EOF, e.g. the following code:

  mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM, MU_IOCTL_NULLSTREAM_SET_PATTERN, NULL);
  rc = mu_stream_read (str, buf, sizeof (buf), &n);

will return rc = 0 and n = 0.

MU_IOCTL_NULLSTREAM_SET_PATCLASS

Sets read pattern in terms of C character classes The argument is a pointer to an integer containing a bitwise OR of the desired character classes from mailutils/cctype.h. For example, the following code:

  int class = MU_CTYPE_DIGIT|MU_CTYPE_XLETR;
  mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM, MU_IOCTL_NULLSTREAM_SET_PATCLASS, &class);

initializes the read pattern to the following string: 0123456789ABCDEFabcdef

Two ioctls are provided to control the size of a null stream available to seek and read operations.

MU_IOCTL_NULLSTREAM_SETSIZE

Limits the addressable size of a null stream. The argument is a pointer to mu_off_t object specifying the new size. The example below limits the stream size to 32 bytes:

  mu_off_t limit = 32;
  mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM, MU_IOCTL_NULLSTREAM_SETSIZE, &limit);

Another way to set the null stream size is via the mu_stream_truncate function:

 mu_stream_truncate (str, 32);

Setting the stream size to 0 causes all subsequent reads from that stream to return EOF. The similar effect has the MU_IOCTL_NULLSTREAM_SET_PATTERN ioctl with the NULL argument.

MU_IOCTL_NULLSTREAM_CLRSIZE

Cancels the size limitation imposed by a previous MU_IOCTL_NULLSTREAM_SETSIZE ioctl or a call to mu_stream_truncate. The argument must be NULL.

Null stream usage

Due to their nature, null streams are not among the most used stream flavors. They are mainly useful for testing or measuring purposes.

The mhn utility from GNU Mailutils MH suite gives a nice example of using a null stream instance to compute the actual (decoded) size of a part of a MIME message. In the example below we present a simplified version of this code. It defines the function decoded_size:

mu_off_t decoded_size (mu_stream_t mime_str, const char *encoding)

This function returns size of the decoded input strem. Arguments are:

mime_str
A stream obtained from the MIME part.
encoding
Encoding type, as obtained from the Content-Transfer-Encoding MIME header.
 1mu_off_t
 2decoded_size (mu_stream_t mime_str, const char *encoding)
 3{
 4  int rc;           /* Result code */
 5  mu_stream_t fstr  /* Filter stream */
 6  mu_stream_stat_buffer stat; /* Statistics buffer */
 7  mu_stream_t null; /* Null stream */
 8
 9  rc = mu_filter_create (&fstr, mime_str, encoding, MU_FILTER_DECODE, MU_STREAM_READ);
10  if (rc)
11    abort ();
12
13  mu_nullstream_create (&null, MU_STREAM_WRITE);
14
15  mu_stream_set_stat (null, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT), stat);
16  rc = mu_stream_copy (null, fstr, 0, NULL);
17  if (rc)
18    abort ();
19
20  mu_stream_destroy (&null);
21  mu_stream_destroy (&fstr);
22  return stat[MU_STREAM_STAT_OUT];
23}

Line 9 creates a filter stream for decoding the message part.

Line 13 creates an instance of the null stream which is to act as a receiver.

Line 15 instructs the stream to keep track of the bytes output (i.e. written) to the stream.

In line 16 all data are copied from fstr to null.

Lines 20-21 destroy both streams: they are not needed any more.

After that, the MU_STREAM_STAT_OUTth element in stat contains the number of bytes written, i.e. the decoded stream size. This value is returned to the caller in line 22.