Skip to content

Commit 133eae9

Browse files
committed
Refactor serializer::stream
1 parent e6330db commit 133eae9

3 files changed

Lines changed: 122 additions & 140 deletions

File tree

include/boost/http_proto/serializer.hpp

Lines changed: 36 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ class serializer
159159
std::size_t max_type_erase = 1024;
160160
};
161161

162-
struct stream;
162+
class stream;
163163

164164
/** Destructor
165165
*/
@@ -401,124 +401,76 @@ install_serializer_service(
401401

402402
//------------------------------------------------
403403

404-
/** The type used for caller-provided body data during
405-
serialization.
406-
407-
@code{.cpp}
408-
http_proto::serializer sr(128);
409-
410-
http_proto::request req;
411-
auto stream = sr.start_stream(req);
412-
413-
std::string_view msg = "Hello, world!";
414-
auto n = buffers::copy(
415-
stream.prepare(),
416-
buffers::make_buffer(
417-
msg.data(), msg.size()));
418-
419-
stream.commit(n);
420-
421-
auto cbs = sr.prepare().value();
422-
(void)cbs;
423-
@endcode
404+
/** Used for streaming body data during serialization.
424405
*/
425-
struct serializer::stream
406+
class serializer::stream
426407
{
427-
/** Constructor.
408+
public:
409+
/** Constructor
428410
429-
The only valid operations on default constructed
430-
streams are assignment and destruction.
411+
A default-constructed stream is considered closed.
431412
*/
432413
stream() = default;
433414

434-
/** Constructor.
435-
436-
The constructed stream will share the same
437-
serializer as `other`.
415+
/** Move constructor
438416
*/
439-
stream(stream const& other) = default;
440-
441-
/** Assignment.
417+
stream(stream&& other)
418+
: sr_(other.sr_)
419+
{
420+
other.sr_ = nullptr;
421+
}
442422

443-
The current stream will share the same serializer
444-
as `other`.
423+
/** Move assignment
445424
*/
446-
stream& operator= (
447-
stream const& other) = default;
425+
stream&
426+
operator=(stream&& other)
427+
{
428+
std::swap(sr_, other.sr_);
429+
return *this;
430+
}
448431

449-
/** A MutableBufferSequence consisting of a buffer pair.
432+
/** The buffer type returned by @ref prepare
450433
*/
451434
using buffers_type =
452435
buffers::mutable_buffer_pair;
453436

454-
/** Returns the remaining available capacity.
455-
456-
The returned value represents the available free
457-
space in the backing fixed-sized buffers used by the
458-
serializer associated with this stream.
459-
460-
The capacity is absolute and does not do any
461-
accounting for any octets required by a chunked
462-
transfer encoding.
437+
/** Returns `true` if the stream is open
463438
*/
464439
BOOST_HTTP_PROTO_DECL
465-
std::size_t
466-
capacity() const noexcept;
467-
468-
/** Return true if the stream cannot currently hold
469-
additional output data.
470-
471-
The fixed-sized buffers maintained by the associated
472-
serializer can be sufficiently full from previous
473-
calls to @ref stream::commit.
440+
bool
441+
is_open() const noexcept;
474442

475-
This function can be called to determine if the caller
476-
should drain the serializer via @ref serializer::consume calls
477-
before attempting to fill the buffer sequence
478-
returned from @ref stream::prepare.
443+
/** Returns the available capacity
479444
*/
480445
BOOST_HTTP_PROTO_DECL
481-
bool
482-
is_full() const noexcept;
483-
484-
/** Returns a MutableBufferSequence for storing
485-
serializer input. If `n` bytes are written to the
486-
buffer sequence, @ref stream::commit must be called
487-
with `n` to update the backing serializer's buffers.
446+
std::size_t
447+
capacity() const noexcept;
488448

489-
The returned buffer sequence is as wide as is
490-
possible.
449+
/** Prepares the buffer for writing
491450
492-
@exception std::length_error Thrown if the stream
493-
has insufficient capacity and a chunked transfer
494-
encoding is being used
451+
Call @ref commit to make data available
452+
to the serializer.
495453
*/
496454
BOOST_HTTP_PROTO_DECL
497455
buffers_type
498-
prepare() const;
456+
prepare() const noexcept;
499457

500-
/** Make `n` bytes available to the serializer.
458+
/** Commits data to the serializer
501459
502-
Once the buffer sequence returned from @ref stream::prepare
503-
has been filled, the input can be marked as ready
504-
for serialization by using this function.
460+
@param n Number of bytes to commit.
505461
506-
@exception std::logic_error Thrown if `commit` is
507-
called with 0.
462+
@throw std::invalid_argument if `n > capacity()`.
463+
@throw std::logic_error if `!is_open()`.
508464
*/
509465
BOOST_HTTP_PROTO_DECL
510466
void
511467
commit(std::size_t n) const;
512468

513-
/** Indicate that no more data is coming and that the
514-
body should be treated as complete.
515-
516-
@excpeption std::logic_error Thrown if the stream
517-
has been previously closed.
469+
/** Closes the stream
518470
*/
519471
BOOST_HTTP_PROTO_DECL
520472
void
521-
close() const;
473+
close() noexcept;
522474

523475
private:
524476
friend class serializer;

src/serializer.cpp

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -107,66 +107,74 @@ write_final_chunk(
107107
BOOST_ASSERT(n == 5);
108108
}
109109

110+
buffers::mutable_buffer_pair
111+
prepare_chunked(
112+
buffers::circular_buffer& cb) noexcept
113+
{
114+
return
115+
buffers::sans_suffix(
116+
buffers::sans_prefix(
117+
cb.prepare(cb.capacity()),
118+
chunk_header_len),
119+
final_chunk_len + crlf_len);
120+
}
121+
110122
//------------------------------------------------
111123

112124
class appender
113125
{
114126
buffers::circular_buffer& cb_;
115127
buffers::mutable_buffer_pair mbp_;
116-
std::size_t n_ = 0;
117128
bool is_chunked_ = false;
118129
bool more_input_ = true;
119130

120131
public:
121132
appender(
122133
buffers::circular_buffer& cb,
123-
bool is_chunked)
134+
bool is_chunked) noexcept
124135
: cb_(cb)
125-
, mbp_(cb.prepare(cb.capacity()))
136+
, mbp_(is_chunked ?
137+
prepare_chunked(cb) : cb.prepare(cb.capacity()))
126138
, is_chunked_(is_chunked)
127139
{
128140
}
129141

130142
bool
131143
is_full() const noexcept
132144
{
133-
auto remaining = cb_.capacity() - n_;
134-
if(is_chunked_)
135-
return remaining <= chunked_overhead_;
136-
137-
return remaining == 0;
145+
return buffers::size(mbp_) == 0;
138146
}
139147

140148
buffers::mutable_buffer_pair
141149
prepare() noexcept
142150
{
143-
if(is_chunked_)
144-
{
145-
return buffers::sans_suffix(
146-
buffers::sans_prefix(
147-
mbp_,
148-
chunk_header_len + n_)
149-
, final_chunk_len + crlf_len);
150-
}
151-
return buffers::sans_prefix(mbp_, n_);
151+
return mbp_;
152152
}
153153

154154
void
155155
commit(std::size_t n, bool more) noexcept
156156
{
157157
BOOST_ASSERT(more_input_);
158-
n_ += n;
158+
mbp_ = buffers::sans_prefix(mbp_, n);
159159
more_input_ = more;
160160
}
161161

162162
~appender()
163163
{
164164
if(is_chunked_)
165165
{
166-
if(n_)
166+
auto consumed = cb_.capacity() -
167+
buffers::size(mbp_) - chunked_overhead_;
168+
169+
if(consumed != 0)
167170
{
168-
write_chunk_header(mbp_, n_);
169-
cb_.commit(n_ + chunk_header_len);
171+
write_chunk_header(
172+
cb_.prepare(chunk_header_len),
173+
consumed);
174+
cb_.commit(chunk_header_len);
175+
176+
cb_.prepare(consumed);
177+
cb_.commit(consumed);
170178

171179
write_crlf(
172180
cb_.prepare(crlf_len));
@@ -182,7 +190,7 @@ class appender
182190
}
183191
else // is_chunked_ == false
184192
{
185-
cb_.commit(n_);
193+
cb_.commit(cb_.capacity() - buffers::size(mbp_));
186194
}
187195
}
188196
};
@@ -1038,11 +1046,25 @@ start_stream(
10381046

10391047
//------------------------------------------------
10401048

1049+
bool
1050+
serializer::
1051+
stream::
1052+
is_open() const noexcept
1053+
{
1054+
if(sr_ == nullptr)
1055+
return false;
1056+
1057+
return sr_->more_input_;
1058+
}
1059+
10411060
std::size_t
10421061
serializer::
10431062
stream::
10441063
capacity() const noexcept
10451064
{
1065+
if(!is_open())
1066+
return 0;
1067+
10461068
if(sr_->filter_)
10471069
return sr_->cb1_.capacity();
10481070

@@ -1057,20 +1079,15 @@ capacity() const noexcept
10571079
return 0;
10581080
}
10591081

1060-
bool
1061-
serializer::
1062-
stream::
1063-
is_full() const noexcept
1064-
{
1065-
return capacity() == 0;
1066-
}
1067-
10681082
auto
10691083
serializer::
10701084
stream::
1071-
prepare() const ->
1085+
prepare() const noexcept ->
10721086
buffers_type
10731087
{
1088+
if(!is_open())
1089+
return {};
1090+
10741091
if(sr_->filter_)
10751092
return sr_->cb1_.prepare(
10761093
sr_->cb1_.capacity());
@@ -1080,21 +1097,25 @@ prepare() const ->
10801097
sr_->cb0_.capacity());
10811098

10821099
// chunked with no filter
1083-
const auto cap = sr_->cb0_.capacity();
1084-
if(cap <= chunked_overhead_)
1085-
detail::throw_length_error();
1086-
1087-
return buffers::sans_prefix(
1088-
sr_->cb0_.prepare(
1089-
cap - crlf_len - final_chunk_len),
1090-
chunk_header_len);
1100+
return prepare_chunked(sr_->cb0_);
10911101
}
10921102

10931103
void
10941104
serializer::
10951105
stream::
10961106
commit(std::size_t n) const
10971107
{
1108+
// Precondition violation
1109+
if(!is_open())
1110+
detail::throw_logic_error();
1111+
1112+
if(n > capacity())
1113+
{
1114+
// n can't be greater than size of
1115+
// the buffers returned by prepare()
1116+
detail::throw_invalid_argument();
1117+
}
1118+
10981119
if(sr_->filter_)
10991120
return sr_->cb1_.commit(n);
11001121

@@ -1123,11 +1144,10 @@ commit(std::size_t n) const
11231144
void
11241145
serializer::
11251146
stream::
1126-
close() const
1147+
close() noexcept
11271148
{
1128-
// Precondition violation
1129-
if(!sr_->more_input_)
1130-
detail::throw_logic_error();
1149+
if(!is_open())
1150+
return; // no-op;
11311151

11321152
sr_->more_input_ = false;
11331153

0 commit comments

Comments
 (0)