Skip to content

Commit

Permalink
Simplify basic_socket<T>'s buffer management
Browse files Browse the repository at this point in the history
Now we read all the tokens without interruptions
(i.e. `std::copy_n`/`set_buffer` magic).

And we only do the trickery with `field_name` token if we get
`error_insufficient_data` value/token.

It should be much faster too.
  • Loading branch information
vinipsmaker committed Aug 5, 2016
1 parent 52af247 commit dc1dfee
Showing 1 changed file with 21 additions and 47 deletions.
68 changes: 21 additions & 47 deletions include/boost/http/socket-inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,22 +620,24 @@ ::on_async_read_message(Handler handler, String *method, String *path,

used_size += bytes_transferred;
if (expecting_field) {
/* We complicate field management to avoid allocations. The field name
MUST occupy the initial bytes on the buffer. The bytes that
immediately follow should be the field value (i.e. remove any `skip`
that appears between name and value). */
parser.set_buffer(asio::buffer(buffer + field_name_size,
used_size - field_name_size));
} else {
parser.set_buffer(asio::buffer(buffer, used_size));
}

auto nparsed = 0;
bool skip_parser_next = false;
std::size_t nparsed = expecting_field ? field_name_size : 0;
std::size_t field_name_begin = 0;
int flags = 0;

/* Buffer management is simplified by making `error_insufficient_data` the
only way to break out of this loop (on non-error paths). */
do {
if (!skip_parser_next)
parser.next();
skip_parser_next = false;
parser.next();
switch (parser.code()) {
case token::code::error_insufficient_data:
// break of for loop completely
Expand All @@ -653,19 +655,6 @@ ::on_async_read_message(Handler handler, String *method, String *path,
return;
}
case token::code::skip:
if (expecting_field) {
auto buf_view = asio::buffer_cast<char*>(buffer);
auto skip_size = parser.token_size();
parser.next();
skip_parser_next = true;
std::copy_n(buf_view + field_name_size + skip_size,
used_size - field_name_size - skip_size,
buf_view + field_name_size);
used_size -= skip_size;
parser.set_buffer(asio::buffer(buffer + field_name_size,
used_size - field_name_size));
}

break;
case token::code::method:
use_trailers = false;
Expand Down Expand Up @@ -696,33 +685,17 @@ ::on_async_read_message(Handler handler, String *method, String *path,
BOOST_HTTP_DETAIL_UNREACHABLE("unimplemented not exposed feature");
break;
case token::code::field_name:
/* Here we complicate field management to avoid allocations. The
field name MUST occupy the initial bytes on the buffer. The bytes
that immediately follow should be the field value.
The `expecting_field` and `field_name_size` variables help to
orchestrate that. While `expecting_field == true`, `nparsed` has
no defined meaning.
I'm NOT removing the "skip bytes" for performance reasons, as
they almost always are only 2. The reason I remove them is to
simplify layout management (I don't need an extra index to
indicate where field value starts and this complex control flow
is largely simplified too). This `rotate` algorithm may very well
degrade performance. */
{
auto buf_view = asio::buffer_cast<char*>(buffer);
std::copy_n(buf_view + nparsed, used_size - nparsed, buf_view);
used_size -= nparsed;
nparsed = 0;
parser.set_buffer(asio::buffer(buffer, used_size));
field_name_begin = nparsed;
field_name_size = parser.token_size();
expecting_field = true;

for (std::size_t i = 0 ; i != field_name_size ; ++i) {
auto &ch = buf_view[i];
auto &ch = buf_view[field_name_begin + i];
ch = std::tolower(ch);
}

expecting_field = true;
}
break;
case token::code::field_value:
Expand All @@ -731,7 +704,8 @@ ::on_async_read_message(Handler handler, String *method, String *path,
typedef typename Message::headers_type::mapped_type ValueT;

auto buf_view = asio::buffer_cast<char*>(buffer);
auto name = string_ref(buf_view, field_name_size);
auto name = string_ref(buf_view + field_name_begin,
field_name_size);
auto value = parser.value<token::field_value>();

if (modern_http || (name != "expect" && name != "upgrade")) {
Expand Down Expand Up @@ -761,13 +735,7 @@ ::on_async_read_message(Handler handler, String *method, String *path,
ValueT(value.data(), value.size()));
}

std::copy_n(buf_view + field_name_size,
used_size - field_name_size,
buf_view);
used_size -= field_name_size;
parser.set_buffer(asio::buffer(buffer, used_size));
expecting_field = false;
nparsed = 0;
}
break;
case token::code::end_of_headers:
Expand Down Expand Up @@ -814,9 +782,15 @@ ::on_async_read_message(Handler handler, String *method, String *path,
auto buf_view = asio::buffer_cast<char*>(buffer);
std::copy_n(buf_view + nparsed, used_size - nparsed, buf_view);
used_size -= nparsed;
nparsed = 0;
parser.set_buffer(asio::buffer(buffer, used_size));
} else if(nparsed != 0) {
auto buf_view = asio::buffer_cast<char*>(buffer);
std::copy_n(buf_view + field_name_begin, field_name_size, buf_view);
std::copy_n(buf_view + nparsed, used_size - nparsed,
buf_view + field_name_size);
used_size -= nparsed;
used_size += field_name_size;
}
nparsed = 0;

if (target == READY && flags & READY) {
handler(system::error_code{});
Expand Down

0 comments on commit dc1dfee

Please sign in to comment.