Skip to content

Commit 5620864

Browse files
committed
Refactor serializer
Improvements: * User-provided buffers are now type-erased and used directly, rather than being copied into a flat buffer. This enables efficient handling of buffer sequences with many elements. * Chunk encoding now occurs within the main buffers, allowing serialization of multiple chunks. This also improves buffer consumption efficiency by reducing the total number of buffers. * The number of internal states maintained by the serializer has been reduced. This lowers memory overhead and simplifies reasoning about the implementation.
1 parent 5427839 commit 5620864

File tree

7 files changed

+693
-596
lines changed

7 files changed

+693
-596
lines changed

doc/local-playbook.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ content:
1414

1515
ui:
1616
bundle:
17-
url: https://github.com/boostorg/website-v2-docs/releases/download/ui-master/ui-bundle.zip
17+
url: https://github.com/boostorg/website-v2-docs/releases/download/ui-develop/ui-bundle.zip
1818
snapshot: true
1919

2020
antora:

include/boost/http_proto/detail/impl/array_of_buffers.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ reset(std::size_t n)
124124
BOOST_ASSERT(n <= capacity());
125125
p_ = o_;
126126
n_ = n;
127-
for( auto p = p_; p < p_ + n; ++p )
128-
*p = value_type();
129127
}
130128

131129
} // detail

include/boost/http_proto/serializer.hpp

Lines changed: 139 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@
1717
#include <boost/http_proto/detail/header.hpp>
1818
#include <boost/http_proto/detail/workspace.hpp>
1919
#include <boost/http_proto/source.hpp>
20+
2021
#include <boost/buffers/circular_buffer.hpp>
2122
#include <boost/buffers/const_buffer_span.hpp>
2223
#include <boost/buffers/range.hpp>
2324
#include <boost/buffers/type_traits.hpp>
2425
#include <boost/system/result.hpp>
25-
#include <cstdint>
26+
2627
#include <memory>
28+
#include <numeric>
2729
#include <type_traits>
2830
#include <utility>
2931

3032
namespace boost {
3133
namespace http_proto {
3234

3335
#ifndef BOOST_HTTP_PROTO_DOCS
34-
class request;
35-
class response;
36-
class request_view;
37-
class response_view;
3836
class message_view_base;
3937
namespace detail {
4038
class filter;
@@ -219,36 +217,14 @@ class serializer
219217
void
220218
consume(std::size_t n);
221219

222-
/** Applies deflate compression to the current message
223-
224-
After @ref reset is called, compression is not
225-
applied to the next message.
226-
227-
Must be called before any calls to @ref start.
228-
*/
229-
BOOST_HTTP_PROTO_DECL
230-
void
231-
use_deflate_encoding();
232-
233-
/** Applies gzip compression to the current message
220+
private:
221+
class const_buf_gen_base;
234222

235-
After @ref reset is called, compression is not
236-
applied to the next message.
223+
template<class>
224+
class const_buf_gen;
237225

238-
Must be called before any calls to @ref start.
239-
*/
240-
BOOST_HTTP_PROTO_DECL
241-
void
242-
use_gzip_encoding();
243-
244-
private:
245-
static void copy(
246-
buffers::const_buffer*,
247-
buffers::const_buffer const*,
248-
std::size_t n) noexcept;
249-
auto
250-
make_array(std::size_t n) ->
251-
detail::array_of_const_buffers;
226+
detail::array_of_const_buffers
227+
make_array(std::size_t n);
252228

253229
template<
254230
class Source,
@@ -279,10 +255,24 @@ class serializer
279255
ws_, std::forward<Args>(args)...);
280256
}
281257

282-
BOOST_HTTP_PROTO_DECL void start_init(message_view_base const&);
283-
BOOST_HTTP_PROTO_DECL void start_empty(message_view_base const&);
284-
BOOST_HTTP_PROTO_DECL void start_buffers(message_view_base const&);
285-
BOOST_HTTP_PROTO_DECL void start_source(message_view_base const&, source*);
258+
BOOST_HTTP_PROTO_DECL
259+
void
260+
start_init(
261+
message_view_base const&);
262+
263+
BOOST_HTTP_PROTO_DECL
264+
void
265+
start_empty(
266+
message_view_base const&);
267+
268+
BOOST_HTTP_PROTO_DECL
269+
void start_buffers(
270+
message_view_base const&);
271+
272+
BOOST_HTTP_PROTO_DECL
273+
void
274+
start_source(
275+
message_view_base const&, source*);
286276

287277
enum class style
288278
{
@@ -292,68 +282,25 @@ class serializer
292282
stream
293283
};
294284

295-
// chunked-body = *chunk
296-
// last-chunk
297-
// trailer-section
298-
// CRLF
299-
300-
static
301-
constexpr
302-
std::size_t
303-
crlf_len_ = 2;
304-
305-
// chunk = chunk-size [ chunk-ext ] CRLF
306-
// chunk-data CRLF
307-
static
308-
constexpr
309-
std::size_t
310-
chunk_header_len_ =
311-
16 + // 16 hex digits => 64 bit number
312-
crlf_len_;
313-
314-
// last-chunk = 1*("0") [ chunk-ext ] CRLF
315-
static
316-
constexpr
317-
std::size_t
318-
last_chunk_len_ =
319-
1 + // "0"
320-
crlf_len_ +
321-
crlf_len_; // chunked-body termination requires an extra CRLF
322-
323-
static
324-
constexpr
325-
std::size_t
326-
chunked_overhead_ =
327-
chunk_header_len_ +
328-
crlf_len_ + // closing chunk data
329-
last_chunk_len_;
330-
331-
detail::workspace ws_;
332-
detail::array_of_const_buffers buf_;
333-
detail::filter* filter_ = nullptr;
334-
source* src_;
335285
context& ctx_;
336-
buffers::circular_buffer tmp0_;
337-
buffers::circular_buffer tmp1_;
338-
detail::array_of_const_buffers prepped_;
339-
340-
buffers::mutable_buffer chunk_header_;
341-
buffers::mutable_buffer chunk_close_;
342-
buffers::mutable_buffer last_chunk_;
286+
detail::workspace ws_;
343287

344-
buffers::circular_buffer* in_ = nullptr;
345-
buffers::circular_buffer* out_ = nullptr;
288+
const_buf_gen_base* buf_gen_;
289+
detail::filter* filter_;
290+
source* source_;
346291

347-
buffers::const_buffer* hp_; // header
292+
buffers::circular_buffer cb0_;
293+
buffers::circular_buffer cb1_;
294+
detail::array_of_const_buffers prepped_;
295+
buffers::const_buffer tmp_;
348296

349297
style st_;
350-
bool more_;
298+
bool more_input_;
351299
bool is_done_;
352300
bool is_header_done_;
353301
bool is_chunked_;
354-
bool is_expect_continue_;
355-
bool is_compressed_ = false;
356-
bool filter_done_ = false;
302+
bool needs_exp100_continue_;
303+
bool filter_done_;
357304
};
358305

359306
//------------------------------------------------
@@ -422,17 +369,6 @@ struct serializer::stream
422369
std::size_t
423370
capacity() const noexcept;
424371

425-
/** Returns the number of octets serialized by this
426-
stream.
427-
428-
The associated serializer stores stream output in its
429-
internal buffers. The stream returns the size of this
430-
output.
431-
*/
432-
BOOST_HTTP_PROTO_DECL
433-
std::size_t
434-
size() const noexcept;
435-
436372
/** Return true if the stream cannot currently hold
437373
additional output data.
438374
@@ -503,28 +439,116 @@ struct serializer::stream
503439

504440
//---------------------------------------------------------
505441

442+
class serializer::const_buf_gen_base
443+
{
444+
public:
445+
// Next non-empty buffer
446+
virtual
447+
buffers::const_buffer
448+
next() = 0;
449+
450+
// Total size of remaining buffers
451+
virtual
452+
std::size_t
453+
remaining_size() const = 0;
454+
455+
// Count of non-empty remaining buffers
456+
virtual
457+
std::size_t
458+
remaining_count() const = 0;
459+
460+
// Returns true when there is no buffer or
461+
// the remaining buffers are empty
462+
virtual
463+
bool
464+
is_empty() const = 0;
465+
};
466+
467+
template<class ConstBufferSequence>
468+
class serializer::const_buf_gen
469+
: public const_buf_gen_base
470+
{
471+
using it_t = decltype(buffers::begin(
472+
std::declval<ConstBufferSequence>()));
473+
474+
using const_buffer =
475+
buffers::const_buffer;
476+
477+
ConstBufferSequence cbs_;
478+
it_t current_;
479+
public:
480+
explicit
481+
const_buf_gen(ConstBufferSequence cbs)
482+
: cbs_(std::move(cbs))
483+
, current_(buffers::begin(cbs_))
484+
{
485+
}
486+
487+
buffers::const_buffer
488+
next() override
489+
{
490+
while(current_ != buffers::end(cbs_))
491+
{
492+
auto buf = static_cast<
493+
const_buffer>(*current_++);
494+
if(buf.size() != 0)
495+
return buf;
496+
}
497+
return {};
498+
}
499+
500+
std::size_t
501+
remaining_size() const override
502+
{
503+
return std::accumulate(
504+
current_,
505+
buffers::end(cbs_),
506+
std::size_t{},
507+
[](std::size_t sum, const_buffer cb) {
508+
return sum + cb.size(); });
509+
}
510+
511+
std::size_t
512+
remaining_count() const override
513+
{
514+
return std::count_if(
515+
current_,
516+
buffers::end(cbs_),
517+
[](const_buffer cb) {
518+
return cb.size() != 0; });
519+
}
520+
521+
bool
522+
is_empty() const override
523+
{
524+
return std::all_of(
525+
current_,
526+
buffers::end(cbs_),
527+
[](const_buffer cb) {
528+
return cb.size() == 0; });
529+
}
530+
};
531+
532+
//---------------------------------------------------------
533+
506534
template<
507535
class ConstBufferSequence,
508536
class>
509537
void
510538
serializer::
511539
start(
512540
message_view_base const& m,
513-
ConstBufferSequence&& body)
541+
ConstBufferSequence&& cbs)
514542
{
515-
start_init(m);
516-
auto const& bs =
517-
ws_.emplace<ConstBufferSequence>(
518-
std::forward<ConstBufferSequence>(body));
519-
520-
std::size_t n = std::distance(
521-
buffers::begin(bs),
522-
buffers::end(bs));
543+
static_assert(buffers::is_const_buffer_sequence<
544+
ConstBufferSequence>::value,
545+
"ConstBufferSequence type requirements not met");
523546

524-
buf_ = make_array(n);
525-
auto p = buf_.data();
526-
for(buffers::const_buffer b : buffers::range(bs))
527-
*p++ = b;
547+
start_init(m);
548+
buf_gen_ = &ws_.emplace<
549+
const_buf_gen<typename std::decay<
550+
ConstBufferSequence>::type>>(
551+
std::forward<ConstBufferSequence>(cbs));
528552

529553
start_buffers(m);
530554
}
@@ -553,20 +577,6 @@ start(
553577
return src;
554578
}
555579

556-
//------------------------------------------------
557-
558-
inline
559-
auto
560-
serializer::
561-
make_array(std::size_t n) ->
562-
detail::array_of_const_buffers
563-
{
564-
return {
565-
ws_.push_array(n,
566-
buffers::const_buffer{}),
567-
n };
568-
}
569-
570580
} // http_proto
571581
} // boost
572582

src/detail/impl/filter.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ process_impl(
4141
auto ib = *it_i++;
4242
for(;;)
4343
{
44-
// empty buffers may be passed, and this is
44+
// empty input buffer may be passed, and this is
4545
// intentional and valid.
4646
results rs = process_impl(ob, ib, more);
4747

@@ -56,7 +56,7 @@ process_impl(
5656
ob = buffers::sans_prefix(ob, rs.out_bytes);
5757
ib = buffers::sans_prefix(ib, rs.in_bytes);
5858

59-
if( ob.size() == 0 )
59+
while( ob.size() == 0 )
6060
{
6161
if( it_o == buffers::end(out) )
6262
return rv;

0 commit comments

Comments
 (0)