io::JoinReader

The JoinReader is an adapter class implementing io::IReader. It takes multiple readers and provides one unified interface to them. This is useful for connection e.g. multiple input channels with one output channel.

Properties

  • Memory consumption: sizeof(::estd::slice<::io::IReader*, N>) + sizeof(size_t) + 1

Template Parameters

JoinReader is a class template with the following parameters:

 * \tparam N Number of IReaders this JoinReader wraps.

Public API

The public API of JoinReader consists of a constructor and the inherited io::IReader API:

/**
 * Constructs a JoinReader from an array of IReader sources.
 *
 * \param sources   Slice of pointers (must not be nullptr) to IReader.
 *
 * \assert sources[0..N-1] != nullptr
 * \assert sources[0]->maxSize() == sources[1]->maxSize() ... == sources[N-1]->maxSize()
 */
explicit JoinReader(::estd::slice<IReader*, N> const& sources);

/**
 * \see ::io::IReader::maxSize()
 */
size_t maxSize() const override;

/**
 * Returns a slice to the next piece of available data.
 *
 * The strategy used is a form of round-robin, where one element is returned from each queue
 * in turn.
 * Note that the same element will keep getting returned until release() is called.
 *
 * \return - Slice of bytes if one of the underlying queues was not empty
 *         - Empty slice otherwise
 */
::estd::slice<uint8_t> peek() const override;

/**
 * \see ::io::IReader::release()
 */
void release() override;

Usage Example

The following example shows a simplified usage of JoinReader:

 1/**
 2 * Forwards all data from an IReader to an IWriter basically connecting them.
 3 * \param source    IReader acting as source of the connection.
 4 * \param destination    IWriter acting as destination of the connection.
 5 *
 6 * This function will forward one slice of data when being called. If data is available
 7 * from the source but no space is available in the destination, the source data will be kept
 8 * and retried when the function is called the next time.
 9 */
10void forwardData(::io::IReader& source, ::io::IWriter& destination)
11{
12    auto const srcData = source.peek();
13    if (srcData.size() > 0)
14    {
15        auto dstData = destination.allocate(srcData.size());
16        if (dstData.size() >= srcData.size())
17        {
18            ::estd::memory::copy(dstData, srcData);
19            destination.commit();
20            // Only release data after successful forwarding.
21            source.release();
22        }
23    }
24}
25
26/**
27 * This usage example demonstrates how to forward data from two readers to one writer. It
28 * focuses on the setup and forwarding not on how the actual data is put into the queues or
29 * read from it.
30 */
31TEST(JoinReader, UsageExample)
32{
33    using Queue = ::io::MemoryQueue<1024, 16>;
34    // Create two input queues.
35    Queue q1;
36    Queue q2;
37    io::MemoryQueueReader<Queue> r1{q1};
38    io::MemoryQueueReader<Queue> r2{q2};
39    ::io::IReader* readers[2] = {&r1, &r2};
40    ::io::JoinReader<2> r{readers};
41
42    // Create output queue
43    Queue q3;
44    io::MemoryQueueWriter<Queue> w3{q3};
45
46    // ...
47
48    // This function can be called cyclically to forward data from q1 and q2 to q3.
49    forwardData(r, w3);
50}
51