Buffers

RESTinio supports a plenty types of buffers that contain data forming a response body. .. The s .. has a capability to receive not only string buffers but also .. constant and custom buffers. Message builders has body setters methods (set_body(), append_body(), append_chunk()) with an argument of a type writable_item_t (see buffers.hpp for more details). writable_item_t is a wrapper for different types of buffers. writable_item_t wrapps two categories of buffer entities: trivial buffers – they can be eventually described as {void* data, size_t size}, and send files – see File support (sendfile).

Here we’ll cover trivial buffers details.

Const buffer

Const buffers are intended for cases when the data is defined as a constant char sequence and its lifetime is guaranteed to be long enough (for example a c-strings defined globally).

To make the usage of const buffers safer writable_item_t constructors don’t accept pointer and size params directly, and to instantiate a writable_item_t object that refers to const buffers a helper const_buffer_t class must be used. There is a helper function const_buffer() that helps to create const_buffer_t.

namespace restinio
{
    inline constexpr const_buffer_t const_buffer( const void * str, std::size_t size ) noexcept;
    inline const_buffer_t const_buffer( const char * str ) noexcept;
}

Let’s have a look at a clarifying example:

// Request handler:
[]( restinio::request_handle_t req ){
  // Create response builder.
  auro resp = req->create_response();

  const char * resp = "0123456789 ...";

  // Set response part as const buffer.
  resp.set_body( restinio::const_buffer( resp ) ); // OK, size will be calculated with std::strlen().
  resp.set_body( restinio::const_buffer( resp, 4 ) ); // OK, size will be 4.

  // When not using restinio::const_buffer() helper function
  // char* will be treated as a parameter for std::string constructor.
  // See next section.
  resp.set_body( resp ); // OK, but std::string will be actually used.

  const std::string temp{ "watch the lifetime, please" };

  // Using a temporary source for const buffer.
  resp.set_body( restinio::const_buffer( temp.data(), temp.size() ) ); // BAD!

  // ...
}

Types with datasizeable interface

RESTinio support any arbitrary type T as a buffer if it fills the following contract:

  • T must have const member function data() returning a pointer to the begining of the buffer data.
  • T must have const member function size() returning a size of a buffer in bytes.
  • T must be move constructible.

For example support of std::string and fmt::basic_memory_buffer<char,1> is done this way.

For this writable_item_t has a special template constructor to initialize itself with this kind of trivial buffer.

Note: there is an important limitation. The size of such type T must be not greater than maximum size of (std::string, std::shared_ptr<U>, fmt::basic_memory_buffer<char,1>).

An example:

// Request handler:
[]( restinio::request_handle_t req ){
  // Create response builder.
  auro resp = req->create_response();

  const std::string body{ "Hello" };

  // Though using a temporary source directly is OK.
  resp.set_body( body ); // OK, will create a copy of the string.
  resp.append_body( std::move( body ) ); // OK, will move the string.

  // ...
}

Support for fmt::basic_memory_buffer<char,1>

Since v.0.6.3 there is a support for fmt::basic_memory_buffer<char,1>:

// Request handler:
[]( restinio::request_handle_t req ){
  // Create response builder.
  auro resp = req->create_response();

  // The buffer for building response body.
  fmt::basic_memory_buffer<char, 1u> resp_body;
  // Building the body...
  fmt::format_to(resp_body, "...", ...);
  ...
  fmt::format_to(resp_body, "...", ...);

  resp.append_body( std::move(resp_body) ); // Move the body to the response.

  // ...
}

A shared pointer to a type with datasizeable interface

If a constraint on size for a given datasizeable interface cannot be fulfilled one can use shared data-sizeable option.

Simply use a shared_pointer to an object with data-size interface. std::shared_ptr<Buf> where Buf has data() and size() methods returning void* (or convertible to it) and size_t (or convertible to). In this case, the type is not constrained to have a move contructor. For example if there exist lots of parallel requests that must be served with the same response (or partly the same, so there are an identical parts) it might be helpful to use a cache in a form of std::shared_ptr<std::string> to eleminate mutlipple copies of the same data.