Correctly handle oversized compound messages in ringbuffer (#1266)

This commit is contained in:
Eddy Ashton 2020-06-09 10:25:26 +01:00 коммит произвёл GitHub
Родитель 76772060d5
Коммит 40957b4d1b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 151 добавлений и 2 удалений

Просмотреть файл

@ -255,6 +255,7 @@ if(BUILD_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/ring_buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/messaging.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/oversized.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/typed_messages.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/serializer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/hash.cpp
)

Просмотреть файл

@ -259,6 +259,7 @@ namespace oversized
auto next = underlying_writer->write_bytes(marker, bytes, write_size);
bytes += write_size;
size -= write_size;
fragment_progress->remainder -= write_size;
// While there is more to write...
while (size > 0)

Просмотреть файл

@ -311,8 +311,19 @@ namespace serializer
template <typename T, typename... Ts>
static auto deserialize_impl(const uint8_t* data, size_t size)
{
const auto next = std::make_tuple(
deserialize_value(data, size, Tag<details::remove_cvref_t<T>>{}));
using StrippedT = details::remove_cvref_t<T>;
if constexpr (
std::is_same_v<StrippedT, std::vector<uint8_t>> ||
std::is_same_v<StrippedT, ByteRange>)
{
static_assert(
sizeof...(Ts) == 0,
"Byte vectors must be the final element in message");
}
const auto next =
std::make_tuple(deserialize_value(data, size, Tag<StrippedT>{}));
if constexpr (sizeof...(Ts) == 0)
{

Просмотреть файл

@ -0,0 +1,136 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "../oversized.h"
#include <doctest/doctest.h>
enum : ringbuffer::Message
{
DEFINE_RINGBUFFER_MSG_TYPE(large_block_message),
DEFINE_RINGBUFFER_MSG_TYPE(large_compound_message),
DEFINE_RINGBUFFER_MSG_TYPE(large_complex_message),
DEFINE_RINGBUFFER_MSG_TYPE(finish),
};
DECLARE_RINGBUFFER_MESSAGE_PAYLOAD(large_block_message, std::vector<uint8_t>);
DECLARE_RINGBUFFER_MESSAGE_PAYLOAD(
large_compound_message, size_t, std::vector<uint8_t>);
DECLARE_RINGBUFFER_MESSAGE_PAYLOAD(
large_complex_message,
uint16_t,
bool,
uint32_t,
std::string,
bool,
uint16_t,
uint64_t,
std::vector<uint8_t>);
DECLARE_RINGBUFFER_MESSAGE_PAYLOAD(finish);
TEST_CASE(
"Large message reconstruction" * doctest::test_suite("typed_messages"))
{
constexpr size_t buf_size = 1 << 8;
ringbuffer::Reader rr(buf_size);
constexpr auto fragment_max = buf_size / 8;
constexpr auto total_max = buf_size / 3;
oversized::Writer writer(
std::make_unique<ringbuffer::Writer>(rr), fragment_max, total_max);
auto writer_p = &writer;
messaging::BufferProcessor bp("typed_messages");
oversized::FragmentReconstructor fr(bp.get_dispatcher());
DISPATCHER_SET_MESSAGE_HANDLER(
bp, finish, [&bp](const uint8_t* data, size_t size) {
bp.set_finished(true);
});
SUBCASE("block message")
{
bool message_seen = false;
std::vector<uint8_t> sent(fragment_max * 2);
std::iota(sent.begin(), sent.end(), 0);
DISPATCHER_SET_MESSAGE_HANDLER(
bp, large_block_message, [&](const uint8_t* data, size_t size) {
auto [body] = ringbuffer::read_message<large_block_message>(data, size);
REQUIRE(body == sent);
REQUIRE(!message_seen);
message_seen = true;
});
RINGBUFFER_WRITE_MESSAGE(large_block_message, writer_p, sent);
RINGBUFFER_WRITE_MESSAGE(finish, writer_p);
bp.run(rr);
REQUIRE(message_seen);
}
SUBCASE("compound message")
{
bool message_seen = false;
size_t sent_n = 42u;
std::vector<uint8_t> sent_body(fragment_max * 2);
std::iota(sent_body.begin(), sent_body.end(), 0);
DISPATCHER_SET_MESSAGE_HANDLER(
bp, large_compound_message, [&](const uint8_t* data, size_t size) {
auto [n, body] =
ringbuffer::read_message<large_compound_message>(data, size);
REQUIRE(n == sent_n);
REQUIRE(body == sent_body);
REQUIRE(!message_seen);
message_seen = true;
});
RINGBUFFER_WRITE_MESSAGE(
large_compound_message, writer_p, sent_n, sent_body);
RINGBUFFER_WRITE_MESSAGE(finish, writer_p);
bp.run(rr);
REQUIRE(message_seen);
}
SUBCASE("complex message")
{
bool message_seen = false;
const uint16_t a = 16;
const bool b = true;
const uint32_t c = 42;
const std::string d = "COMPLEX";
const bool e = false;
const uint16_t f = 1661;
const uint64_t g = 0xdeadbeef;
const std::vector<uint8_t> h{1, 2, 3, 4, 5};
DISPATCHER_SET_MESSAGE_HANDLER(
bp, large_complex_message, [&](const uint8_t* data, size_t size) {
auto [aa, bb, cc, dd, ee, ff, gg, hh] =
ringbuffer::read_message<large_complex_message>(data, size);
REQUIRE(a == aa);
REQUIRE(b == bb);
REQUIRE(c == cc);
REQUIRE(d == dd);
REQUIRE(e == ee);
REQUIRE(f == ff);
REQUIRE(g == gg);
REQUIRE(h == hh);
REQUIRE(!message_seen);
message_seen = true;
});
RINGBUFFER_WRITE_MESSAGE(
large_complex_message, writer_p, a, b, c, d, e, f, g, h);
RINGBUFFER_WRITE_MESSAGE(finish, writer_p);
bp.run(rr);
REQUIRE(message_seen);
}
}