Chapter 11. IO filter classes

An input/output filter, or an IO filter, is a design pattern that originally came from several libraries that implement data compression, but is now found in other applications too. An IO filter transfers some chunk of data from an input buffer to an output buffer, using some transformation process. The input to the transformation process is a pointer to the input data, a count of how many input elements there are available, a pointer to the output buffer, and the size of the output buffer. The input buffer may not contain the entire set of data to be filtered. Typically, the input data gets presented in small portions or chunks, and gets filtered, or transformed, in small chunks.

Each transformation step can consume the entire input buffer, or some part of it, and produce a full output buffer, or fill a part of it. The transformation step updates the input pointer, the input count, the output buffer pointer, and the remaining unused output buffer size accordingly, to reflect the filtered data.

The x::iofilter class template defines an interface to a filter. The x::iofilter template takes two parameters: an input type and an output type. An x::iofilter does not necessarily convert between two same types, an implementation may convert an input sequence of one type to an input sequence of another type. An implementation must derive virtually from x::iofilter, in order to implement it.

x::basic_ostreambufiofilter virtually subclasses from std::streambuf and x::iofilter. This template implements an output std::streambuf. A sequence written to this std::streambuf gets filtered through an x::iofilter, the results gets written to another std::ostream (in the case of character-based streams). The following example implements a dummy IO filter that XOR-s the output with 1, flipping the lowest bit of each character, before writing it to another stream:

#include <iostream>
#include <x/basicstreambufiofilter.H>

class xor1 : public x::basic_ostreambufiofilter<char, char>,
             virtual public x::iofilter<char, char> {
public:
  xor1(std::ostream &o)
    : x::basic_ostreambufiofilter<char, char>(o)
  {
  }

  ~xor1() noexcept
  {
  }

  void filter()
  {
    while (avail_in && avail_out)
    {
      *next_out++ = *next_in++ ^ 1;
      --avail_in;
      --avail_out;
    }
  }
};

int main()
{
  xor1 xor1_impl(std::cout);

  std::ostream o(&xor1_impl);

  o << "Trust noone" << std::flush;
  std::cout << std::endl;
  return (0);
}
      

This example implements an stream buffer filter, from char to a char. An instance gets created, which writes the converted sequence to std::cout. Since xor1 is a subclass of a std::streambuf, a std::ostream gets instantiated which writes to this std::streambuf. The output from this example is Ustru!onnod.

x::basic_istreambufiofilter is analogous, but uses a std::istream (in the case of a character-based stream), and implements an input std::streambuf. Input is obtained by reading from some other std::istream and converting the read data using an IO filter. For example:

#include <iostream>
#include <x/basicstreambufiofilter.H>

class xor1 : public x::basic_istreambufiofilter<char, char>,
	     virtual public x::iofilter<char, char> {
public:
	xor1(std::istream &i)
		: x::basic_istreambufiofilter<char, char>(i)
	{
	}

	~xor1() noexcept
	{
	}

	void filter()
	{
		while (avail_in && avail_out)
		{
			*next_out++ = *next_in++ ^ 1;
			--avail_in;
			--avail_out;
		}
	}
};

int main()
{
	std::istringstream str("64738");
	xor1 xor1_impl(str);

	std::istream i(&xor1_impl);

	int n;

	i >> n;

	std::cout << n << std::endl;
	return (0);
}

The instance of xor1 implements a std::streambuf that reads from the given string buffer. Attaching an std::istream to the xor1 and reads from it ends up reading from the string buffer and filtering. The output from this example is 75629.

Finally, x::basic_iostreambufiofilter subclasses from x::basic_istreambufiofilter and x::basic_ostreambufiofilter, implementing a std::streambuf that could, theoretically, read and write from another input/output stream using an IO filter.

Practically, it is not generally useful. This is because implementing read/write functionality requires two IO filters: one to convert sequences when reading, and a second one to convert sequences when writing. Since IO filters must use virtual inheritance, and the same class can only be virtually inherited once, this means that an x::basic_iostreambufiofilter can be used only for converting to and from different types, which ends up instantiating two different IO filters: x::iofilter<A,B> and x::iofilter<B,A>, which requires elaborate class inheritance in order to implement it.

Given that the C++ library implements stream buffers for only two fundamental types: char and wchar_t, the only practical use of x::basic_iostreambufiofilter is for converting between narrow characters and wide characters, and there is already an implementation for that: x::ctow_ostreambuf, x::wtoc_ostreambuf, x::ctow_istreambuf, x::wtoc_istreambuf, x::ctow_iostreambuf, and x::wtoc_iostreambuf.