Dynamic buffers, part 2


In the previous lesson we've learned how to read a data from a dynamic buffer and how to consume data pieces that were successfully processed. However before you could read something from the buffer, someone else should write that something there, right?

Logically dynamic buffer separated into two sequences: input and output. Input sequence represents a data which is already obtained and ready to be read, processed and consumed. Output sequence represents a data which is being currently prepared before it go into the input sequence. We've already learned how to read a data from the input sequence with dynamic buffer's data member function. Also we learned how to throw away (or consume) processed data with consume member function.

In the earlier lessons dynamic buffers were filled with data somewhere inside boost::asio::async_read_until free function. Let's see how could we do that on our own. To do that we will need two new member functions of a dynamic buffer: prepare and commit. Consider the following example:

boost::asio::streambuf streambuf;

// Allocate (or prepare) 1024 bytes for the data in the output sequence
boost::asio::mutable_buffer view = streambuf.prepare(1024);

// Receive a data into the allocated buffer
std::size_t bytes_transferred = socket.receive(view);

// Transfer bytes_transferred bytes from the output sequence into the input sequence

// Now this input data is accessible with data member function
auto data = streambuf.data();

// And consumed after it is no longer needed

So, working with functions like boost::asio::async_read_until you just have to deal with the input sequence when the output sequence is delt with inside async_read_until function. In the example above we deal with both sequences on our own. Let's take a closer look:

boost::asio::mutable_buffer view = streambuf.prepare(1024);

prepare member function allocates required amount of memory in the output sequence and returns a mutable buffer view of that allocated memory block. Now, if you want to put some data that should be read from the dynamic buffer later, you should write it via this mutable buffer view. And that's exactly what we did:

std::size_t bytes_transferred = socket.receive(view);

Now, bytes_transferred bytes were written into the input sequence. bytes_transferred value could be less than view size which is 1024 bytes in our case. So we've could continue filling the memory allocated further. When we decide that some amount of data is well-formed and ready to be processed by the consumer, we transfer that amount (bytes_transferred bytes in our case) from the output to the input sequence with commit member function:


Note that if you commit lesser amount of bytes than was prepared before with prepare member function, then the remaining amount of data in the prepared buffer will be discarded.

On the other hand, you can work with functions like boost::asio::async_write with dynamic buffers. In that case you have to prepare and commit data into the output sequence on your own, while the input sequence is delt with inside async_write function:

boost::asio::streambuf streambuf;
boost::asio::mutable_buffer view = streambuf.prepare(1024);
// Write something into the mutable buffer
// ...

boost::asio::async_write(socket, streambuf, [&] (error_code error, std::size_t bytes_transferred)
    // No need to call streambuf.consume() here
Lesson 22
Share this page:

Learning plan

A closer look on how to pass data views into Boost.Asio functions
How to operate on the underlying buffer sequence data with Boost.Asio free functions
How to read data from Boost.Asio dynamic buffers
23. Dynamic buffers, part 2
How to work with Boost.Asio dynamic buffers manually
Let's briefly summarize everything we've learned about different Boost.Asio buffers
How to deal with read and write functions properly to gain desired I/O behavior
How to deal with Boost.Asio I/O free functions: async_read, async_read_until and async_write