io::BufferedWriter

The BufferedWriter is a helper class implementing io::IWriter allowing a buffered transfer of smaller slices using an io::IWriter which provides these buffers. Using a BufferedWriter only makes sense, if the chunks of data being transferred via this channel are much smaller compared to the maximum allocation size of this channel. A typical use case would be transmission of multiple 8-byte CAN frames via UDP packets of e.g. 1400 bytes.

Properties

  • Internally the BufferedWriter manages slices of destination.maxSize() bytes and provides subslices of it to the user.

  • Call to flush() required to commit currently buffered data.

  • Memory consumption: sizeof(::io::IWriter&) + sizeof(::estd::slice<uint8_t>) + sizeof(size_t)

Public API

The public API of BufferedWriter consists of a constructor and the inherited IWriter API:

/**
 * Constructs a BufferedWriter from a given IWriter destination.
 */
explicit BufferedWriter(IWriter& destination);

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

/**
 * Allocates a slice of bytes of a given size.
 *
 * If not enough memory is available in the current buffer, it will be flushed and a
 * new allocation of destination.maxSize() bytes will be done.
 *
 * \param size  Number of bytes to allocate from this BufferedWriter.
 * \return  - Empty slice, if requested size was greater as MAX_ELEMENT_SIZE or no memory
 *            is available
 *          - Slice of bytes otherwise.
 */
::estd::slice<uint8_t> allocate(size_t size) override;

/**
 * Commits the previously allocated data. It is not guaranteed that this data is immediately
 * available to the reader as a call to flush() might be required for that.
 */
void commit() override;

/**
 * Commits the data currently written to slice allocated from destination making it available to
 * the reader.
 */
void flush() override;

Usage example

Here is an example:

 1
 2class CanFrameForwarder
 3{
 4    ::io::IReader& _canFrameInput;
 5    ::io::BufferedWriter _udpOutput;
 6
 7public:
 8    CanFrameForwarder(::io::IReader& canFrameInput, ::io::IWriter& udpOutput)
 9    : _canFrameInput(canFrameInput), _udpOutput(udpOutput)
10    {}
11
12    void operator()()
13    {
14        // Input format is: uint32_t id, payload.
15        ::estd::slice<uint8_t const> frame = _canFrameInput.peek();
16        while (frame.size() > 0)
17        {
18            // Output format is uint8_t size, uint32_t id, payload
19            ::estd::slice<uint8_t> dst = _udpOutput.allocate(frame.size() + 1);
20            if (dst.size() == 0)
21            {
22                break;
23            }
24            // Format: uint8_t payload length, uint32_t id, payload
25            ::estd::memory::take<uint8_t>(dst) = frame.size();
26            ::estd::memory::copy(dst, frame);
27            _udpOutput.commit();
28            _canFrameInput.release();
29            frame = _canFrameInput.peek();
30        }
31        _udpOutput.flush();
32    }
33};
34
35/**
36 * This usage example demonstrates how to read small input slices of bytes and accumulate them,
37 * using a BufferedWriter into a writer that support much larger allocations.
38 */
39TEST(BufferedWriter, UsageExample)
40{
41    using CanQueue = ::io::MemoryQueue<1024, 12>;
42    using UdpQueue = ::io::MemoryQueue<10240, 1400>;
43    // Create output channel.
44    UdpQueue outputQueue;
45    ::io::MemoryQueueWriter<UdpQueue> output{outputQueue};
46    ::io::BufferedWriter udpWriter{output};
47    // Create input channel.
48    CanQueue inputQueue;
49    ::io::MemoryQueueReader<CanQueue> input{inputQueue};
50
51    CanFrameForwarder canFrameForwarder{input, udpWriter};
52
53    // Call this cyclically
54    canFrameForwarder();
55}
56