I need to do async_read_until
, after which I want to call async_read_some
using the same boost::asio::streambuf
that was used for async_read_until
.
The reason why I want to use the same boost::asio::streambuf
is that after async_read_until
there might be some data left in the buffer. When reading via async_read_some
I want to get not only the new data that is available but also the data that might have been left after async_read_until
.
However, async_read_some
does not have an overload for boost::asio::streambuf
. How can I solve this problem?
Here is an example of the code I need to execute. Note that read_some can be called multiple times.
class io
{
//...
public:
void read_until()
{
boost::asio::async_read_until(m_socket, m_buffer, "\n",
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
{
read_some();
});
}
void read_some()
{
// I need use async_read_some here into m_buffer
// ...
// read_some() can be called again from completion handler
}
private:
boost::asio::streambuf m_buffer;
boost::asio::ip::tcp::socket m_socket;
};
I read that you can use prepare/commit/consume, but I don't fully understand how to use these methods correctly. Can someone give an example and explain what exactly these methods do and how to use them correctly?
UPDATE
Please answer the following questions:
Q. Is it true that asio::streambuf
consists of at least two buffers? A read buffer and a write buffer?
Not necessarily. It might be, or it might not be. asio::streambuf
bridges two worls:
the Asio concept DynamicBuffer
(v1) which defines the prepare
, commit
, and consume
interface in terms of single buffers.
the iostreams
std::streambuf
which similarly describes an interface like¹
Note, here too there's a logical "input" and "output area" which can be from the same buffer (as in e.g. std::basic_stringbuf
or std::basic_spanbuf
That said, currently Asio's streambuf
happens to always keep a single buffer in the storage model: is it safe to use boost::asio::streambuf as both an istream and an array as string_view?
Q. If so, is it true that prepare
allocates space in the write buffer?
It reserves enough space for the output area. If necessary, it will (re)allocate.
Q. If so, is it true that commit
copies data from the write buffer to the read buffer?
No. It merely manipulates some pointers to indicate the new extents of available input/output areas.
Q. *Is it true that consume
removes data from the read buffer?
It will typically again just update some pointers, though implementations might shrink the allocated storage to fit. You can infer that implementations have this leeway from the invalidation specifications in the concept:
expression | assertion/note pre/post conditions |
---|---|
x.consume(n) | Removes n bytes from beginning of the input sequence. If n is greater than the size of the input sequence, the entire input sequence is removed. All constant or mutable buffer sequences previously obtained using data() or prepare() are invalidated. |
Q. Is it true that if the read buffer contained data "my_data", and then prepare(n)
, async_read_some
and commit
are called, then after commit
the read buffer will contain "my_data"+[new_data_from_async_read_some]?
Logically, yes. To be more precisely, you might refer to "get area" instead of "read buffer", matching the iostreams
jargon, and avoiding the implication that data is copied during commit
.
¹ image originally from https://cppreference.com/w/cpp/io/basic_streambuf.html where it is currently broken for some reason?