Merge branch 'master' into fix_out_of_files_in_encoding

This commit is contained in:
Isaac Connor 2021-05-17 16:32:58 -04:00
commit ccc4d4af54
16 changed files with 183 additions and 169 deletions

View File

@ -3,7 +3,8 @@ target_compile_options(zm-warning-interface
-Wall
-Wextra
-Wimplicit-fallthrough
-Wno-unused-parameter)
-Wno-unused-parameter
-Wvla)
if(ENABLE_WERROR)
target_compile_options(zm-warning-interface

View File

@ -7,7 +7,8 @@ target_compile_options(zm-warning-interface
-Wno-cast-function-type
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,11>:-Wno-clobbered>
-Wno-unused-parameter
-Woverloaded-virtual)
-Woverloaded-virtual
-Wvla)
if(ENABLE_WERROR)
target_compile_options(zm-warning-interface

View File

@ -36,7 +36,7 @@
int ZM::CommsBase::readV(int iovcnt, /* const void *, int, */ ...) {
va_list arg_ptr;
iovec iov[iovcnt];
std::vector<iovec> iov(iovcnt);
va_start(arg_ptr, iovcnt);
for (int i = 0; i < iovcnt; i++) {
@ -45,7 +45,7 @@ int ZM::CommsBase::readV(int iovcnt, /* const void *, int, */ ...) {
}
va_end(arg_ptr);
int nBytes = ::readv(mRd, iov, iovcnt);
int nBytes = ::readv(mRd, iov.data(), iovcnt);
if (nBytes < 0) {
Debug(1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno));
}
@ -54,7 +54,7 @@ int ZM::CommsBase::readV(int iovcnt, /* const void *, int, */ ...) {
int ZM::CommsBase::writeV(int iovcnt, /* const void *, int, */ ...) {
va_list arg_ptr;
iovec iov[iovcnt];
std::vector<iovec> iov(iovcnt);
va_start(arg_ptr, iovcnt);
for (int i = 0; i < iovcnt; i++) {
@ -63,7 +63,7 @@ int ZM::CommsBase::writeV(int iovcnt, /* const void *, int, */ ...) {
}
va_end(arg_ptr);
ssize_t nBytes = ::writev(mWd, iov, iovcnt);
ssize_t nBytes = ::writev(mWd, iov.data(), iovcnt);
if (nBytes < 0) {
Debug(1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno));
}

View File

@ -243,27 +243,27 @@ class Socket : public CommsBase {
return nBytes;
}
virtual int recv(std::string &msg) const {
char buffer[msg.capacity()];
int nBytes = 0;
if ((nBytes = ::recv(mSd, buffer, sizeof(buffer), 0)) < 0) {
Debug(1, "Recv of %zd bytes max to string on sd %d failed: %s", sizeof(buffer), mSd, strerror(errno));
virtual ssize_t recv(std::string &msg) const {
std::vector<char> buffer(msg.capacity());
ssize_t nBytes;
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
Debug(1, "Recv of %zd bytes max to string on sd %d failed: %s", msg.size(), mSd, strerror(errno));
return nBytes;
}
buffer[nBytes] = '\0';
msg = buffer;
msg = {buffer.begin(), buffer.begin() + nBytes};
return nBytes;
}
virtual int recv(std::string &msg, size_t maxLen) const {
char buffer[maxLen];
int nBytes = 0;
if ((nBytes = ::recv(mSd, buffer, sizeof(buffer), 0)) < 0) {
virtual ssize_t recv(std::string &msg, size_t maxLen) const {
std::vector<char> buffer(maxLen);
ssize_t nBytes;
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
Debug(1, "Recv of %zd bytes max to string on sd %d failed: %s", maxLen, mSd, strerror(errno));
return nBytes;
}
buffer[nBytes] = '\0';
msg = buffer;
msg = {buffer.begin(), buffer.begin() + nBytes};
return nBytes;
}

View File

@ -2458,9 +2458,9 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) {
} // end foreach coordinate in the polygon
}
/* RGB32 compatible: complete */
// Polygon filling is based on the Scan-line Polygon filling algorithm
void Image::Fill(Rgb colour, int density, const Polygon &polygon) {
if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) {
if (!(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32)) {
Panic("Attempt to fill image with unexpected colours %d", colours);
}
@ -2468,119 +2468,91 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) {
colour = rgb_convert(colour, subpixelorder);
size_t n_coords = polygon.GetVertices().size();
int n_global_edges = 0;
Edge global_edges[n_coords];
std::vector<PolygonFill::Edge> global_edges;
global_edges.reserve(n_coords);
for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) {
const Vector2 &p1 = polygon.GetVertices()[i];
const Vector2 &p2 = polygon.GetVertices()[j];
int x1 = p1.x_;
int x2 = p2.x_;
int y1 = p1.y_;
int y2 = p2.y_;
//Debug( 9, "x1:%d,y1:%d x2:%d,y2:%d", x1, y1, x2, y2 );
if ( y1 == y2 )
// Do not add horizontal edges to the global edge table.
if (p1.y_ == p2.y_)
continue;
double dx = x2 - x1;
double dy = y2 - y1;
Vector2 d = p2 - p1;
global_edges[n_global_edges].min_y = y1<y2?y1:y2;
global_edges[n_global_edges].max_y = y1<y2?y2:y1;
global_edges[n_global_edges].min_x = y1<y2?x1:x2;
global_edges[n_global_edges]._1_m = dx/dy;
n_global_edges++;
}
std::sort(global_edges, global_edges + n_global_edges, Edge::CompareYX);
global_edges.emplace_back(std::min(p1.y_, p2.y_),
std::max(p1.y_, p2.y_),
p1.y_ < p2.y_ ? p1.x_ : p2.x_,
d.x_ / static_cast<double>(d.y_));
#ifndef ZM_DBG_OFF
if ( logLevel() >= Logger::DEBUG9 ) {
for ( int i = 0; i < n_global_edges; i++ ) {
Debug(9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f",
i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m);
}
}
#endif
std::sort(global_edges.begin(), global_edges.end(), PolygonFill::Edge::CompareYX);
int n_active_edges = 0;
Edge active_edges[n_global_edges];
int y = global_edges[0].min_y;
do {
for ( int i = 0; i < n_global_edges; i++ ) {
if ( global_edges[i].min_y == y ) {
Debug(9, "Moving global edge");
active_edges[n_active_edges++] = global_edges[i];
if ( i < (n_global_edges-1) ) {
memmove(&global_edges[i], &global_edges[i + 1], sizeof(*global_edges) * (n_global_edges - i - 1));
i--;
}
n_global_edges--;
std::vector<PolygonFill::Edge> active_edges;
active_edges.reserve(global_edges.size());
int32 scan_line = global_edges[0].min_y;
while (!global_edges.empty() || !active_edges.empty()) {
// Deactivate edges with max_y < current scan line
for (auto it = active_edges.begin(); it != active_edges.end();) {
if (scan_line >= it->max_y) {
it = active_edges.erase(it);
} else {
break;
it->min_x += it->_1_m;
++it;
}
}
std::sort(active_edges, active_edges + n_active_edges, Edge::CompareX);
#ifndef ZM_DBG_OFF
if ( logLevel() >= Logger::DEBUG9 ) {
for ( int i = 0; i < n_active_edges; i++ ) {
Debug(9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f",
y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m );
// Activate edges with min_y == current scan line
for (auto it = global_edges.begin(); it != global_edges.end();) {
if (it->min_y == scan_line) {
active_edges.emplace_back(*it);
it = global_edges.erase(it);
} else {
++it;
}
}
#endif
if ( !(y%density) ) {
//Debug( 9, "%d", y );
for ( int i = 0; i < n_active_edges; ) {
int lo_x = int(round(active_edges[i++].min_x));
int hi_x = int(round(active_edges[i++].min_x));
if ( colours == ZM_COLOUR_GRAY8 ) {
unsigned char *p = &buffer[(y*width)+lo_x];
for ( int x = lo_x; x <= hi_x; x++, p++) {
if ( !(x%density) ) {
//Debug( 9, " %d", x );
std::sort(active_edges.begin(), active_edges.end(), PolygonFill::Edge::CompareX);
if (!(scan_line % density)) {
for (auto it = active_edges.begin(); it != active_edges.end(); ++it) {
int32 lo_x = static_cast<int32>(it->min_x);
int32 hi_x = static_cast<int32>(std::next(it)->min_x);
if (colours == ZM_COLOUR_GRAY8) {
uint8 *p = &buffer[(scan_line * width) + lo_x];
for (int32 x = lo_x; x <= hi_x; x++, p++) {
if (!(x % density)) {
*p = colour;
}
}
} else if ( colours == ZM_COLOUR_RGB24 ) {
unsigned char *p = &buffer[colours*((y*width)+lo_x)];
for ( int x = lo_x; x <= hi_x; x++, p += 3) {
if ( !(x%density) ) {
RED_PTR_RGBA(p) = RED_VAL_RGBA(colour);
GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour);
BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour);
}
}
} else if( colours == ZM_COLOUR_RGB32 ) {
Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2];
for ( int x = lo_x; x <= hi_x; x++, p++) {
if ( !(x%density) ) {
/* Fast, copies the entire pixel in a single pass */
*p = colour;
}
}
}
}
}
y++;
for ( int i = n_active_edges-1; i >= 0; i-- ) {
if ( y >= active_edges[i].max_y ) {
// Or >= as per sheets
Debug(9, "Deleting active_edge");
if ( i < (n_active_edges-1) ) {
//memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) );
memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) );
}
n_active_edges--;
} else {
active_edges[i].min_x += active_edges[i]._1_m;
}
}
} while ( n_global_edges || n_active_edges );
}
} else if (colours == ZM_COLOUR_RGB24) {
constexpr uint8 bytesPerPixel = 3;
uint8 *ptr = &buffer[((scan_line * width) + lo_x) * bytesPerPixel];
void Image::Fill(Rgb colour, const Polygon &polygon) {
Fill(colour, 1, polygon);
for (int32 x = lo_x; x <= hi_x; x++, ptr += bytesPerPixel) {
if (!(x % density)) {
RED_PTR_RGBA(ptr) = RED_VAL_RGBA(colour);
GREEN_PTR_RGBA(ptr) = GREEN_VAL_RGBA(colour);
BLUE_PTR_RGBA(ptr) = BLUE_VAL_RGBA(colour);
}
}
} else if (colours == ZM_COLOUR_RGB32) {
constexpr uint8 bytesPerPixel = 4;
Rgb *ptr = reinterpret_cast<Rgb *>(&buffer[((scan_line * width) + lo_x) * bytesPerPixel]);
for (int32 x = lo_x; x <= hi_x; x++, ptr++) {
if (!(x % density)) {
*ptr = colour;
}
}
}
}
}
scan_line++;
}
}
void Image::Rotate(int angle) {

View File

@ -278,8 +278,8 @@ class Image {
void Fill( Rgb colour, const Box *limits=0 );
void Fill( Rgb colour, int density, const Box *limits=0 );
void Outline( Rgb colour, const Polygon &polygon );
void Fill( Rgb colour, const Polygon &polygon );
void Fill( Rgb colour, int density, const Polygon &polygon );
void Fill(Rgb colour, const Polygon &polygon) { Fill(colour, 1, polygon); };
void Fill(Rgb colour, int density, const Polygon &polygon);
void Rotate( int angle );
void Flip( bool leftright );
@ -292,6 +292,31 @@ class Image {
void Deinterlace_4Field(const Image* next_image, unsigned int threshold);
};
// Scan-line polygon fill algorithm
namespace PolygonFill {
class Edge {
public:
Edge() = default;
Edge(int32 min_y, int32 max_y, double min_x, double _1_m) : min_y(min_y), max_y(max_y), min_x(min_x), _1_m(_1_m) {}
static bool CompareYX(const Edge &e1, const Edge &e2) {
if (e1.min_y == e2.min_y)
return e1.min_x < e2.min_x;
return e1.min_y < e2.min_y;
}
static bool CompareX(const Edge &e1, const Edge &e2) {
return e1.min_x < e2.min_x;
}
public:
int32 min_y;
int32 max_y;
double min_x;
double _1_m;
};
}
#endif // ZM_IMAGE_H
/* Blend functions */

View File

@ -381,7 +381,11 @@ LocalCamera::LocalCamera(
} else {
if ( capture ) {
Info("Selected capture palette: %s (0x%02hhx%02hhx%02hhx%02hhx)",
palette_desc, (palette>>24)&0xff, (palette>>16)&0xff, (palette>>8)&0xff, (palette)&0xff);
palette_desc,
static_cast<uint8>((palette >> 24) & 0xff),
static_cast<uint8>((palette >> 16) & 0xff),
static_cast<uint8>((palette >> 8) & 0xff),
static_cast<uint8>((palette) & 0xff));
}
}
}
@ -438,8 +442,10 @@ LocalCamera::LocalCamera(
} else {
if ( capture ) {
#if HAVE_LIBSWSCALE
Info("No direct match for the selected palette (0x%02hhx%02hhx%02hhx%02hhx) and target colorspace (%02u). Format conversion is required, performance penalty expected",
(capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff), colours);
Info(
"No direct match for the selected palette (%d) and target colorspace (%02u). Format conversion is required, performance penalty expected",
capturePixFormat,
colours);
#else
Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected");
#endif
@ -463,13 +469,11 @@ LocalCamera::LocalCamera(
if ( capture ) {
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
if ( !sws_isSupportedInput(capturePixFormat) ) {
Error("swscale does not support the used capture format: 0x%02hhx%02hhx%02hhx%02hhx",
(capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff));
Error("swscale does not support the used capture format: %d", capturePixFormat);
conversion_type = 2; /* Try ZM format conversions */
}
if ( !sws_isSupportedOutput(imagePixFormat) ) {
Error("swscale does not support the target format: 0x%02hhx%02hhx%02hhx%02hhx",
(imagePixFormat>>24)&0xff,((imagePixFormat>>16)&0xff),((imagePixFormat>>8)&0xff),((imagePixFormat)&0xff));
Error("swscale does not support the target format: 0x%d", imagePixFormat);
conversion_type = 2; /* Try ZM format conversions */
}
#endif
@ -1215,10 +1219,10 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
Debug(3, "Got format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %d",
fmt_desc[nIndex],
(fmt_fcc[nIndex]>>24)&0xff,
(fmt_fcc[nIndex]>>16)&0xff,
(fmt_fcc[nIndex]>>8)&0xff,
(fmt_fcc[nIndex])&0xff,
static_cast<uint8>((fmt_fcc[nIndex] >> 24) & 0xff),
static_cast<uint8>((fmt_fcc[nIndex] >> 16) & 0xff),
static_cast<uint8>((fmt_fcc[nIndex] >> 8) & 0xff),
static_cast<uint8>((fmt_fcc[nIndex]) & 0xff),
nIndex);
/* Proceed to the next index */
@ -1248,13 +1252,23 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
for ( unsigned int j=0; j < nIndex; j++ ) {
if ( preferedformats[i] == fmt_fcc[j] ) {
Debug(6, "Choosing format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %u",
fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j);
fmt_desc[j],
static_cast<uint8>(fmt_fcc[j] & 0xff),
static_cast<uint8>((fmt_fcc[j] >> 8) & 0xff),
static_cast<uint8>((fmt_fcc[j] >> 16) & 0xff),
static_cast<uint8>((fmt_fcc[j] >> 24) & 0xff),
j);
/* Found a format! */
nIndexUsed = j;
break;
} else {
Debug(6, "No match for format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %u",
fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j);
fmt_desc[j],
static_cast<uint8>(fmt_fcc[j] & 0xff),
static_cast<uint8>((fmt_fcc[j] >> 8) & 0xff),
static_cast<uint8>((fmt_fcc[j] >> 16) & 0xff),
static_cast<uint8>((fmt_fcc[j] >> 24) & 0xff),
j);
}
}
}

View File

@ -529,15 +529,17 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const
if (level <= mDatabaseLevel) {
if (zmDbConnected) {
int syslogSize = syslogEnd-syslogStart;
char escapedString[(syslogSize*2)+1];
mysql_real_escape_string(&dbconn, escapedString, syslogStart, syslogSize);
std::string escapedString;
escapedString.reserve((syslogSize * 2) + 1);
mysql_real_escape_string(&dbconn, &escapedString[0], syslogStart, syslogSize);
escapedString.resize(std::strlen(escapedString.c_str()));
std::string sql_string = stringtf(
"INSERT INTO `Logs` "
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
" VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString.c_str(), file, line
);
dbQueue.push(std::move(sql_string));
} else {

View File

@ -1855,7 +1855,7 @@ bool Monitor::Analyse() {
/* try to stay behind the decoder. */
if (decoding_enabled) {
while (!snap->image and !snap->decoded and !zm_terminate and !analysis_thread->Stopped()) {
while ((!snap->image or deinterlacing_value) and !snap->decoded and !zm_terminate and !analysis_thread->Stopped()) {
// Need to wait for the decoder thread.
Debug(1, "Waiting for decode");
packet_lock->wait();

View File

@ -42,10 +42,8 @@ ZMPacket::ZMPacket() :
pts(0),
decoded(0)
{
Debug(1, "ZMPacket");
av_init_packet(&packet);
packet.size = 0; // So we can detect whether it has been filled.
Debug(1, "ZMPacket");
}
ZMPacket::ZMPacket(Image *i, const timeval &tv) :

View File

@ -23,23 +23,6 @@
#include "zm_box.h"
#include <vector>
struct Edge {
int min_y;
int max_y;
double min_x;
double _1_m;
static bool CompareYX(const Edge &e1, const Edge &e2) {
if (e1.min_y == e2.min_y)
return e1.min_x < e2.min_x;
return e1.min_y < e2.min_y;
}
static bool CompareX(const Edge &e1, const Edge &e2) {
return e1.min_x < e2.min_x;
}
};
// This class represents convex or concave non-self-intersecting polygons.
class Polygon {
public:

View File

@ -131,9 +131,9 @@ std::string Authenticator::computeDigestResponse(const std::string &method, cons
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
// The "response" field is computed as:
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
size_t md5len = 16;
unsigned char md5buf[md5len];
char md5HexBuf[md5len*2+1];
constexpr size_t md5len = 16;
uint8 md5buf[md5len];
char md5HexBuf[md5len * 2 + 1];
// Step 1: md5(<username>:<realm>:<password>)
std::string ha1Data = username() + ":" + realm() + ":" + password();

View File

@ -236,8 +236,8 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
}
char auth_key[512] = "";
char auth_md5[32+1] = "";
size_t md5len = 16;
unsigned char md5sum[md5len];
constexpr size_t md5len = 16;
uint8 md5sum[md5len];
const char * hex = "0123456789abcdef";
while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) {

View File

@ -96,7 +96,7 @@ constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp) {
template<class T>
constexpr const T &clamp(const T &v, const T &lo, const T &hi) {
return clamp(v, lo, hi, std::less<T>{});
return ZM::clamp(v, lo, hi, std::less<T>{});
}
}

View File

@ -348,7 +348,6 @@ bool VideoStore::open() {
#else
avcodec_copy_context(video_out_stream->codec, video_out_ctx);
#endif
video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q;
if (monitor->GetOptVideoWriter() == Monitor::PASSTHROUGH) {
// Only set orientation if doing passthrough, otherwise the frame image will be rotated

View File

@ -224,15 +224,16 @@ TEST_CASE("ZM::UdpUnixSocket send/recv") {
ZM::UdpUnixSocket srv_socket;
ZM::UdpUnixSocket client_socket;
SECTION("send/recv byte buffer") {
std::array<char, 3> msg = {'a', 'b', 'c'};
std::array<char, msg.size()> rcv{};
SECTION("send/recv on unbound socket") {
SECTION("on unbound socket") {
REQUIRE(client_socket.send(msg.data(), msg.size()) == -1);
REQUIRE(srv_socket.recv(rcv.data(), rcv.size()) == -1);
}
SECTION("send/recv") {
SECTION("on bound socket") {
REQUIRE(srv_socket.bind(sock_path.c_str()) == true);
REQUIRE(srv_socket.isOpen() == true);
@ -242,6 +243,24 @@ TEST_CASE("ZM::UdpUnixSocket send/recv") {
REQUIRE(client_socket.send(msg.data(), msg.size()) == msg.size());
REQUIRE(srv_socket.recv(rcv.data(), rcv.size()) == msg.size());
REQUIRE(rcv == msg);
}
}
SECTION("send/recv string") {
std::string msg = "abc";
std::string rcv;
rcv.reserve(msg.length());
REQUIRE(srv_socket.bind(sock_path.c_str()) == true);
REQUIRE(srv_socket.isOpen() == true);
REQUIRE(client_socket.connect(sock_path.c_str()) == true);
REQUIRE(client_socket.isConnected() == true);
REQUIRE(client_socket.send(msg) == static_cast<ssize_t>(msg.size()));
REQUIRE(srv_socket.recv(rcv) == static_cast<ssize_t>(msg.size()));
REQUIRE(rcv == msg);
}
}