forked from boostorg/beast
Fix and refactor buffers_cat:
Elements of zero size are correctly skipped during iteration. * Tidy up tests * Increase code coverage * Remove move special members * Correct behavior for default constructed iterators
This commit is contained in:
1 parent
ca728e4022
commit
719fe66b77
4 files changed
+350
-380
No files matched your search
+216
-257
@@ -10,6 +10,8 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/core/buffers_cat.hpp>
|
||||
|
||||
#include "buffer_test.hpp"
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/beast/core/buffers_prefix.hpp>
|
||||
#include <boost/beast/core/buffers_suffix.hpp>
|
||||
@@ -26,213 +28,17 @@ namespace beast {
|
||||
class buffers_cat_test : public unit_test::suite
|
||||
{
|
||||
public:
|
||||
template<class Iterator>
|
||||
static
|
||||
std::reverse_iterator<Iterator>
|
||||
make_reverse_iterator(Iterator i)
|
||||
{
|
||||
return std::reverse_iterator<Iterator>(i);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
static
|
||||
std::size_t
|
||||
bsize1(ConstBufferSequence const& bs)
|
||||
buffers_length(
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
using net::buffer_size;
|
||||
std::size_t n = 0;
|
||||
for(auto it = bs.begin(); it != bs.end(); ++it)
|
||||
n += buffer_size(*it);
|
||||
return n;
|
||||
return std::distance(
|
||||
net::buffer_sequence_begin(buffers),
|
||||
net::buffer_sequence_end(buffers));
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
static
|
||||
std::size_t
|
||||
bsize2(ConstBufferSequence const& bs)
|
||||
{
|
||||
using net::buffer_size;
|
||||
std::size_t n = 0;
|
||||
for(auto it = bs.begin(); it != bs.end(); it++)
|
||||
n += buffer_size(*it);
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
static
|
||||
std::size_t
|
||||
bsize3(ConstBufferSequence const& bs)
|
||||
{
|
||||
using net::buffer_size;
|
||||
std::size_t n = 0;
|
||||
for(auto it = bs.end(); it != bs.begin();)
|
||||
n += buffer_size(*--it);
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
static
|
||||
std::size_t
|
||||
bsize4(ConstBufferSequence const& bs)
|
||||
{
|
||||
using net::buffer_size;
|
||||
std::size_t n = 0;
|
||||
for(auto it = bs.end(); it != bs.begin();)
|
||||
{
|
||||
it--;
|
||||
n += buffer_size(*it);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void testBufferCat()
|
||||
{
|
||||
using net::buffer_size;
|
||||
using net::const_buffer;
|
||||
char buf[10];
|
||||
std::list<const_buffer> b1;
|
||||
std::vector<const_buffer> b2{
|
||||
const_buffer{buf+0, 1},
|
||||
const_buffer{buf+1, 2}};
|
||||
std::list<const_buffer> b3;
|
||||
std::array<const_buffer, 3> b4{{
|
||||
const_buffer{buf+3, 1},
|
||||
const_buffer{buf+4, 2},
|
||||
const_buffer{buf+6, 3}}};
|
||||
std::list<const_buffer> b5{
|
||||
const_buffer{buf+9, 1}};
|
||||
std::list<const_buffer> b6;
|
||||
auto bs = buffers_cat(
|
||||
b1, b2, b3, b4, b5, b6);
|
||||
BEAST_EXPECT(buffer_size(bs) == 10);
|
||||
BEAST_EXPECT(bsize1(bs) == 10);
|
||||
BEAST_EXPECT(bsize2(bs) == 10);
|
||||
BEAST_EXPECT(bsize3(bs) == 10);
|
||||
BEAST_EXPECT(bsize4(bs) == 10);
|
||||
std::vector<const_buffer> v;
|
||||
for(auto iter = make_reverse_iterator(bs.end());
|
||||
iter != make_reverse_iterator(bs.begin()); ++iter)
|
||||
v.emplace_back(*iter);
|
||||
BEAST_EXPECT(buffer_size(bs) == 10);
|
||||
decltype(bs) bs2(bs);
|
||||
auto bs3(std::move(bs));
|
||||
{
|
||||
net::streambuf sb1, sb2;
|
||||
BEAST_EXPECT(buffer_size(buffers_cat(
|
||||
sb1.prepare(5), sb2.prepare(7))) == 12);
|
||||
sb1.commit(5);
|
||||
sb2.commit(7);
|
||||
BEAST_EXPECT(buffer_size(buffers_cat(
|
||||
sb1.data(), sb2.data())) == 12);
|
||||
}
|
||||
for(auto it = bs.begin(); it != bs.end(); ++it)
|
||||
{
|
||||
decltype(bs)::const_iterator copy;
|
||||
copy = it;
|
||||
BEAST_EXPECT(copy == it);
|
||||
auto copy2 = copy;
|
||||
BEAST_EXPECT(copy2 == it);
|
||||
}
|
||||
}
|
||||
|
||||
void testIterators()
|
||||
{
|
||||
using net::buffer_size;
|
||||
using net::const_buffer;
|
||||
char buf[9];
|
||||
std::vector<const_buffer> b1{
|
||||
const_buffer{buf+0, 1},
|
||||
const_buffer{buf+1, 2}};
|
||||
std::array<const_buffer, 3> b2{{
|
||||
const_buffer{buf+3, 1},
|
||||
const_buffer{buf+4, 2},
|
||||
const_buffer{buf+6, 3}}};
|
||||
auto bs = buffers_cat(b1, b2);
|
||||
for(int n = 0;
|
||||
n <= std::distance(bs.begin(), bs.end()); ++n)
|
||||
{
|
||||
auto it = std::next(bs.begin(), n);
|
||||
decltype(it) it2(std::move(it));
|
||||
it = std::move(it2);
|
||||
auto pit = ⁢
|
||||
it = std::move(*pit);
|
||||
}
|
||||
try
|
||||
{
|
||||
std::size_t n = 0;
|
||||
for(auto it = bs.begin(); n < 100; ++it)
|
||||
++n;
|
||||
fail();
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
|
||||
// decrement iterator
|
||||
/* VFALCO
|
||||
This causes a mysterious "uninitialized variable"
|
||||
warning related to this function (see comment)
|
||||
https://code.woboq.org/qt5/include/c++/6.3.1/bits/stl_iterator.h.html#159
|
||||
*/
|
||||
#if 0
|
||||
{
|
||||
auto const rbegin =
|
||||
make_reverse_iterator(bs.end());
|
||||
auto const rend =
|
||||
make_reverse_iterator(bs.begin());
|
||||
std::size_t n = 0;
|
||||
for(auto it = rbegin; it != rend; ++it)
|
||||
n += buffer_size(*it);
|
||||
BEAST_EXPECT(n == 9);
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
std::size_t n = 0;
|
||||
for(auto it = bs.end(); n < 100; --it)
|
||||
++n;
|
||||
fail();
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
buffer_size(*bs.end());
|
||||
fail();
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
auto bs2 = bs;
|
||||
BEAST_EXPECT(bs.begin() != bs2.begin());
|
||||
BEAST_EXPECT(bs.end() != bs2.end());
|
||||
decltype(bs)::const_iterator it;
|
||||
decltype(bs2)::const_iterator it2;
|
||||
BEAST_EXPECT(it == it2);
|
||||
|
||||
// decrement begin() should throw
|
||||
try
|
||||
{
|
||||
--bs.begin();
|
||||
fail();
|
||||
}
|
||||
catch(std::logic_error const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
testDefaultIterators()
|
||||
{
|
||||
@@ -275,6 +81,209 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBufferSequence()
|
||||
{
|
||||
string_view s = "Hello, world!";
|
||||
net::const_buffer b1(s.data(), 6);
|
||||
net::const_buffer b2(
|
||||
s.data() + b1.size(), s.size() - b1.size());
|
||||
test_buffer_sequence(
|
||||
*this, buffers_cat(b1, b2));
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void
|
||||
checkException(F&& f)
|
||||
{
|
||||
try
|
||||
{
|
||||
f();
|
||||
fail("missing exception", __FILE__, __LINE__);
|
||||
}
|
||||
catch(std::logic_error const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fail("wrong exception", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testExceptions()
|
||||
{
|
||||
net::const_buffer b1{"He", 2};
|
||||
net::const_buffer b2{"llo,", 4};
|
||||
net::const_buffer b3{" world!", 7};
|
||||
|
||||
auto const b = beast::buffers_cat(b1, b2, b3);
|
||||
|
||||
// Dereferencing a default-constructed iterator
|
||||
checkException(
|
||||
[]
|
||||
{
|
||||
*(decltype(b)::const_iterator{});
|
||||
});
|
||||
|
||||
// Incrementing a default-constructed iterator
|
||||
checkException(
|
||||
[]
|
||||
{
|
||||
++(decltype(b)::const_iterator{});
|
||||
});
|
||||
|
||||
// Decrementing a default-constructed iterator
|
||||
checkException(
|
||||
[]
|
||||
{
|
||||
--(decltype(b)::const_iterator{});
|
||||
});
|
||||
|
||||
// Decrementing an iterator to the beginning
|
||||
checkException(
|
||||
[&b]
|
||||
{
|
||||
--b.begin();
|
||||
});
|
||||
|
||||
// Dereferencing an iterator to the end
|
||||
checkException(
|
||||
[&b]
|
||||
{
|
||||
*b.end();
|
||||
});
|
||||
|
||||
// Incrementing an iterator to the end
|
||||
checkException(
|
||||
[&b]
|
||||
{
|
||||
++b.end();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
testEmpty()
|
||||
{
|
||||
using net::buffer_size;
|
||||
|
||||
struct empty_sequence
|
||||
{
|
||||
using value_type = net::const_buffer;
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
const_iterator
|
||||
begin() const noexcept
|
||||
{
|
||||
return &v_;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const noexcept
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
private:
|
||||
value_type v_;
|
||||
};
|
||||
|
||||
{
|
||||
net::const_buffer b0{};
|
||||
net::const_buffer b1{"He", 2};
|
||||
net::const_buffer b2{"llo,", 4};
|
||||
net::const_buffer b3{" world!", 7};
|
||||
|
||||
{
|
||||
auto const b = beast::buffers_cat(b0, b0);
|
||||
BEAST_EXPECT(buffer_size(b) == 0);
|
||||
BEAST_EXPECT(buffers_length(b) == 0);
|
||||
}
|
||||
{
|
||||
auto const b = beast::buffers_cat(b0, b0, b0, b0);
|
||||
BEAST_EXPECT(buffer_size(b) == 0);
|
||||
BEAST_EXPECT(buffers_length(b) == 0);
|
||||
}
|
||||
{
|
||||
auto const b = beast::buffers_cat(b1, b2, b3);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 3);
|
||||
test_buffer_sequence(*this, b);
|
||||
}
|
||||
{
|
||||
auto const b = beast::buffers_cat(b0, b1, b2, b3);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 3);
|
||||
test_buffer_sequence(*this, b);
|
||||
}
|
||||
{
|
||||
auto const b = beast::buffers_cat(b1, b0, b2, b3);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 3);
|
||||
test_buffer_sequence(*this, b);
|
||||
}
|
||||
{
|
||||
auto const b = beast::buffers_cat(b1, b2, b0, b3);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 3);
|
||||
test_buffer_sequence(*this, b);
|
||||
}
|
||||
{
|
||||
auto const b = beast::buffers_cat(b1, b2, b3, b0);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 3);
|
||||
test_buffer_sequence(*this, b);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto e1 = net::const_buffer{};
|
||||
auto b1 = std::array<net::const_buffer, 3>{{
|
||||
e1,
|
||||
net::const_buffer{"He", 2},
|
||||
net::const_buffer{"l", 1} }};
|
||||
auto b2 = std::array<net::const_buffer, 3>{{
|
||||
net::const_buffer{"lo", 2},
|
||||
e1,
|
||||
net::const_buffer{", ", 2} }};
|
||||
auto b3 = std::array<net::const_buffer, 3>{{
|
||||
net::const_buffer{"w", 1},
|
||||
net::const_buffer{"orld!", 5},
|
||||
e1 }};
|
||||
{
|
||||
auto const b = beast::buffers_cat(
|
||||
e1, b1, e1, b2, e1, b3, e1);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto e1 = net::const_buffer{};
|
||||
auto e2 = empty_sequence{};
|
||||
auto b1 = std::array<net::const_buffer, 3>{{
|
||||
e1,
|
||||
net::const_buffer{"He", 2},
|
||||
net::const_buffer{"l", 1} }};
|
||||
auto b2 = std::array<net::const_buffer, 3>{{
|
||||
net::const_buffer{"lo", 2},
|
||||
e1,
|
||||
net::const_buffer{", ", 2} }};
|
||||
auto b3 = std::array<net::const_buffer, 3>{
|
||||
net::const_buffer{"w", 1},
|
||||
net::const_buffer{"orld!", 5},
|
||||
e1
|
||||
};
|
||||
{
|
||||
auto const b = beast::buffers_cat(
|
||||
e2, b1, e2, b2, e2, b3, e2);
|
||||
BEAST_EXPECT(beast::buffers_to_string(b) == "Hello, world!");
|
||||
BEAST_EXPECT(buffers_length(b) == 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testGccWarning1()
|
||||
{
|
||||
@@ -307,70 +316,20 @@ public:
|
||||
char out[64];
|
||||
const_buffer buffers("Hello, world!", 13);
|
||||
std::size_t i = 3;
|
||||
buffers_suffix<const_buffer> cb{buffers};
|
||||
buffers_suffix<net::const_buffer> cb{buffers};
|
||||
cb.consume(i);
|
||||
buffer_copy(
|
||||
buffer(out),
|
||||
net::buffer_copy(
|
||||
net::buffer(out),
|
||||
buffers_cat(buffers_prefix(i, buffers), cb));
|
||||
}
|
||||
|
||||
void
|
||||
test_empty_buffer_sequences()
|
||||
{
|
||||
using net::buffer_size;
|
||||
using net::const_buffer;
|
||||
std::vector<const_buffer> v1;
|
||||
std::vector<const_buffer> v2;
|
||||
BEAST_EXPECT(buffer_size(buffers_cat(v1, v2)) == 0);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
using net::const_buffer;
|
||||
using net::const_buffer;
|
||||
using net::mutable_buffer;
|
||||
struct user_defined : mutable_buffer
|
||||
{
|
||||
};
|
||||
|
||||
// Check is_all_const_buffer_sequence
|
||||
BOOST_STATIC_ASSERT(
|
||||
detail::is_all_const_buffer_sequence<const_buffer>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
detail::is_all_const_buffer_sequence<const_buffer, const_buffer>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
detail::is_all_const_buffer_sequence<mutable_buffer, mutable_buffer>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
detail::is_all_const_buffer_sequence<const_buffer, mutable_buffer>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
! detail::is_all_const_buffer_sequence<const_buffer, mutable_buffer, int>::value);
|
||||
|
||||
// Ensure that concatenating mutable buffer
|
||||
// sequences results in a mutable buffer sequence
|
||||
BOOST_STATIC_ASSERT(std::is_same<
|
||||
mutable_buffer,
|
||||
decltype(buffers_cat(
|
||||
std::declval<mutable_buffer>(),
|
||||
std::declval<mutable_buffer>(),
|
||||
std::declval<std::vector<mutable_buffer>>()
|
||||
))::value_type>::value);
|
||||
|
||||
// Ensure that concatenating mixed buffer
|
||||
// sequences results in a const buffer sequence.
|
||||
BOOST_STATIC_ASSERT(std::is_same<
|
||||
const_buffer,
|
||||
decltype(buffers_cat(
|
||||
std::declval<mutable_buffer>(),
|
||||
std::declval<user_defined>(),
|
||||
std::declval<const_buffer>()
|
||||
))::value_type>::value);
|
||||
|
||||
testBufferCat();
|
||||
testIterators();
|
||||
testDefaultIterators();
|
||||
testBufferSequence();
|
||||
testExceptions();
|
||||
testEmpty();
|
||||
testGccWarning1();
|
||||
testGccWarning2();
|
||||
test_empty_buffer_sequences();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user