2016-04-06 14:20:10 +02:00
|
|
|
/*
|
2017-07-31 15:17:29 +02:00
|
|
|
Copyright (c) 2016-2017 ZeroMQ community
|
2017-06-07 09:54:37 +02:00
|
|
|
Copyright (c) 2016 VOCA AS / Harald Nøkland
|
2016-04-06 14:20:10 +02:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to
|
|
|
|
deal in the Software without restriction, including without limitation the
|
|
|
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __ZMQ_ADDON_HPP_INCLUDED__
|
|
|
|
#define __ZMQ_ADDON_HPP_INCLUDED__
|
|
|
|
|
|
|
|
#include <zmq.hpp>
|
2018-03-07 14:11:41 +01:00
|
|
|
|
2016-04-06 14:20:10 +02:00
|
|
|
#include <deque>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <sstream>
|
2016-12-08 04:03:25 +01:00
|
|
|
#include <stdexcept>
|
2016-04-06 14:20:10 +02:00
|
|
|
|
|
|
|
namespace zmq {
|
|
|
|
|
|
|
|
#ifdef ZMQ_HAS_RVALUE_REFS
|
|
|
|
|
|
|
|
/*
|
|
|
|
This class handles multipart messaging. It is the C++ equivalent of zmsg.h,
|
|
|
|
which is part of CZMQ (the high-level C binding). Furthermore, it is a major
|
2017-06-07 09:54:37 +02:00
|
|
|
improvement compared to zmsg.hpp, which is part of the examples in the ØMQ
|
2016-04-06 14:20:10 +02:00
|
|
|
Guide. Unnecessary copying is avoided by using move semantics to efficiently
|
|
|
|
add/remove parts.
|
|
|
|
*/
|
|
|
|
class multipart_t
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
std::deque<message_t> m_parts;
|
|
|
|
|
|
|
|
public:
|
2016-12-08 14:39:24 +01:00
|
|
|
|
|
|
|
typedef std::deque<message_t>::iterator iterator;
|
|
|
|
typedef std::deque<message_t>::const_iterator const_iterator;
|
|
|
|
|
|
|
|
typedef std::deque<message_t>::reverse_iterator reverse_iterator;
|
|
|
|
typedef std::deque<message_t>::const_reverse_iterator const_reverse_iterator;
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Default constructor
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t()
|
|
|
|
{}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Construct from socket receive
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t(socket_t& socket)
|
|
|
|
{
|
|
|
|
recv(socket);
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Construct from memory block
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t(const void *src, size_t size)
|
|
|
|
{
|
|
|
|
addmem(src, size);
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Construct from string
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t(const std::string& string)
|
|
|
|
{
|
|
|
|
addstr(string);
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Construct from message part
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t(message_t&& message)
|
|
|
|
{
|
|
|
|
add(std::move(message));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Move constructor
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t(multipart_t&& other)
|
|
|
|
{
|
2016-10-23 00:27:12 +02:00
|
|
|
m_parts = std::move(other.m_parts);
|
2016-04-06 14:20:10 +02:00
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Move assignment operator
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t& operator=(multipart_t&& other)
|
|
|
|
{
|
2016-10-23 00:27:12 +02:00
|
|
|
m_parts = std::move(other.m_parts);
|
2016-04-06 14:20:10 +02:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Destructor
|
2016-04-06 14:20:10 +02:00
|
|
|
virtual ~multipart_t()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
2016-12-08 13:46:52 +01:00
|
|
|
message_t& operator[] (size_t n)
|
|
|
|
{
|
|
|
|
return m_parts[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
const message_t& operator[] (size_t n) const
|
|
|
|
{
|
|
|
|
return m_parts[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
message_t& at (size_t n)
|
|
|
|
{
|
|
|
|
return m_parts.at(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
const message_t& at (size_t n) const
|
|
|
|
{
|
|
|
|
return m_parts.at(n);
|
|
|
|
}
|
|
|
|
|
2016-12-08 14:39:24 +01:00
|
|
|
iterator begin()
|
|
|
|
{
|
|
|
|
return m_parts.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_iterator begin() const
|
|
|
|
{
|
|
|
|
return m_parts.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_iterator cbegin() const
|
|
|
|
{
|
|
|
|
return m_parts.cbegin();
|
|
|
|
}
|
|
|
|
|
|
|
|
reverse_iterator rbegin()
|
|
|
|
{
|
|
|
|
return m_parts.rbegin();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reverse_iterator rbegin() const
|
|
|
|
{
|
|
|
|
return m_parts.rbegin();
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator end()
|
|
|
|
{
|
|
|
|
return m_parts.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_iterator end() const
|
|
|
|
{
|
|
|
|
return m_parts.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_iterator cend() const
|
|
|
|
{
|
|
|
|
return m_parts.cend();
|
|
|
|
}
|
|
|
|
|
|
|
|
reverse_iterator rend()
|
|
|
|
{
|
|
|
|
return m_parts.rend();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reverse_iterator rend() const
|
|
|
|
{
|
|
|
|
return m_parts.rend();
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Delete all parts
|
2016-04-06 14:20:10 +02:00
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
m_parts.clear();
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Get number of parts
|
2016-04-06 14:20:10 +02:00
|
|
|
size_t size() const
|
|
|
|
{
|
|
|
|
return m_parts.size();
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Check if number of parts is zero
|
2016-04-06 14:20:10 +02:00
|
|
|
bool empty() const
|
|
|
|
{
|
|
|
|
return m_parts.empty();
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Receive multipart message from socket
|
2016-08-15 01:46:43 +02:00
|
|
|
bool recv(socket_t& socket, int flags = 0)
|
2016-04-06 14:20:10 +02:00
|
|
|
{
|
|
|
|
clear();
|
|
|
|
bool more = true;
|
|
|
|
while (more)
|
|
|
|
{
|
|
|
|
message_t message;
|
2016-08-15 01:46:43 +02:00
|
|
|
if (!socket.recv(&message, flags))
|
2016-04-06 14:20:10 +02:00
|
|
|
return false;
|
|
|
|
more = message.more();
|
|
|
|
add(std::move(message));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Send multipart message to socket
|
2016-08-15 01:46:43 +02:00
|
|
|
bool send(socket_t& socket, int flags = 0)
|
2016-04-06 14:20:10 +02:00
|
|
|
{
|
2016-08-15 01:46:43 +02:00
|
|
|
flags &= ~(ZMQ_SNDMORE);
|
2016-04-06 14:20:10 +02:00
|
|
|
bool more = size() > 0;
|
|
|
|
while (more)
|
|
|
|
{
|
|
|
|
message_t message = pop();
|
|
|
|
more = size() > 0;
|
2016-08-15 01:46:43 +02:00
|
|
|
if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags))
|
2016-04-06 14:20:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Concatenate other multipart to front
|
2016-04-06 14:20:10 +02:00
|
|
|
void prepend(multipart_t&& other)
|
|
|
|
{
|
|
|
|
while (!other.empty())
|
|
|
|
push(other.remove());
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Concatenate other multipart to back
|
2016-04-06 14:20:10 +02:00
|
|
|
void append(multipart_t&& other)
|
|
|
|
{
|
|
|
|
while (!other.empty())
|
|
|
|
add(other.pop());
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push memory block to front
|
2016-04-06 14:20:10 +02:00
|
|
|
void pushmem(const void *src, size_t size)
|
|
|
|
{
|
|
|
|
m_parts.push_front(message_t(src, size));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push memory block to back
|
2016-04-06 14:20:10 +02:00
|
|
|
void addmem(const void *src, size_t size)
|
|
|
|
{
|
|
|
|
m_parts.push_back(message_t(src, size));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push string to front
|
2016-04-06 14:20:10 +02:00
|
|
|
void pushstr(const std::string& string)
|
|
|
|
{
|
|
|
|
m_parts.push_front(message_t(string.data(), string.size()));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push string to back
|
2016-04-06 14:20:10 +02:00
|
|
|
void addstr(const std::string& string)
|
|
|
|
{
|
|
|
|
m_parts.push_back(message_t(string.data(), string.size()));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push type (fixed-size) to front
|
2016-04-06 14:20:10 +02:00
|
|
|
template<typename T>
|
|
|
|
void pushtyp(const T& type)
|
|
|
|
{
|
2016-10-23 00:27:12 +02:00
|
|
|
static_assert(!std::is_same<T, std::string>::value, "Use pushstr() instead of pushtyp<std::string>()");
|
2016-04-06 14:20:10 +02:00
|
|
|
m_parts.push_front(message_t(&type, sizeof(type)));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push type (fixed-size) to back
|
2016-04-06 14:20:10 +02:00
|
|
|
template<typename T>
|
|
|
|
void addtyp(const T& type)
|
|
|
|
{
|
2016-10-23 00:27:12 +02:00
|
|
|
static_assert(!std::is_same<T, std::string>::value, "Use addstr() instead of addtyp<std::string>()");
|
2016-04-06 14:20:10 +02:00
|
|
|
m_parts.push_back(message_t(&type, sizeof(type)));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push message part to front
|
2016-04-06 14:20:10 +02:00
|
|
|
void push(message_t&& message)
|
|
|
|
{
|
|
|
|
m_parts.push_front(std::move(message));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Push message part to back
|
2016-04-06 14:20:10 +02:00
|
|
|
void add(message_t&& message)
|
|
|
|
{
|
|
|
|
m_parts.push_back(std::move(message));
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Pop string from front
|
2016-04-06 14:20:10 +02:00
|
|
|
std::string popstr()
|
|
|
|
{
|
|
|
|
std::string string(m_parts.front().data<char>(), m_parts.front().size());
|
|
|
|
m_parts.pop_front();
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Pop type (fixed-size) from front
|
2016-04-06 14:20:10 +02:00
|
|
|
template<typename T>
|
|
|
|
T poptyp()
|
|
|
|
{
|
2016-10-23 00:27:12 +02:00
|
|
|
static_assert(!std::is_same<T, std::string>::value, "Use popstr() instead of poptyp<std::string>()");
|
|
|
|
if (sizeof(T) != m_parts.front().size())
|
2016-10-28 14:53:22 +02:00
|
|
|
throw std::runtime_error("Invalid type, size does not match the message size");
|
2016-04-06 14:20:10 +02:00
|
|
|
T type = *m_parts.front().data<T>();
|
|
|
|
m_parts.pop_front();
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Pop message part from front
|
2016-04-06 14:20:10 +02:00
|
|
|
message_t pop()
|
|
|
|
{
|
|
|
|
message_t message = std::move(m_parts.front());
|
|
|
|
m_parts.pop_front();
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Pop message part from back
|
2016-04-06 14:20:10 +02:00
|
|
|
message_t remove()
|
|
|
|
{
|
|
|
|
message_t message = std::move(m_parts.back());
|
|
|
|
m_parts.pop_back();
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Get pointer to a specific message part
|
2016-04-06 14:20:10 +02:00
|
|
|
const message_t* peek(size_t index) const
|
|
|
|
{
|
|
|
|
return &m_parts[index];
|
|
|
|
}
|
2018-03-07 14:11:41 +01:00
|
|
|
|
2017-06-07 09:54:37 +02:00
|
|
|
// Get a string copy of a specific message part
|
|
|
|
std::string peekstr(size_t index) const
|
|
|
|
{
|
|
|
|
std::string string(m_parts[index].data<char>(), m_parts[index].size());
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Peek type (fixed-size) from front
|
|
|
|
template<typename T>
|
2017-08-17 21:56:16 +02:00
|
|
|
T peektyp(size_t index) const
|
2017-06-07 09:54:37 +02:00
|
|
|
{
|
|
|
|
static_assert(!std::is_same<T, std::string>::value, "Use peekstr() instead of peektyp<std::string>()");
|
2017-08-17 21:03:36 +02:00
|
|
|
if(sizeof(T) != m_parts[index].size())
|
2017-06-07 09:54:37 +02:00
|
|
|
throw std::runtime_error("Invalid type, size does not match the message size");
|
|
|
|
T type = *m_parts[index].data<T>();
|
2017-08-17 21:03:36 +02:00
|
|
|
return type;
|
2017-06-07 09:54:37 +02:00
|
|
|
}
|
2016-04-06 14:20:10 +02:00
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Create multipart from type (fixed-size)
|
2016-04-06 14:20:10 +02:00
|
|
|
template<typename T>
|
|
|
|
static multipart_t create(const T& type)
|
|
|
|
{
|
|
|
|
multipart_t multipart;
|
|
|
|
multipart.addtyp(type);
|
|
|
|
return multipart;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Copy multipart
|
2016-04-06 14:20:10 +02:00
|
|
|
multipart_t clone() const
|
|
|
|
{
|
|
|
|
multipart_t multipart;
|
|
|
|
for (size_t i = 0; i < size(); i++)
|
|
|
|
multipart.addmem(m_parts[i].data(), m_parts[i].size());
|
|
|
|
return multipart;
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Dump content to string
|
2016-04-06 14:20:10 +02:00
|
|
|
std::string str() const
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
for (size_t i = 0; i < m_parts.size(); i++)
|
|
|
|
{
|
|
|
|
const unsigned char* data = m_parts[i].data<unsigned char>();
|
|
|
|
size_t size = m_parts[i].size();
|
|
|
|
|
|
|
|
// Dump the message as text or binary
|
|
|
|
bool isText = true;
|
|
|
|
for (size_t j = 0; j < size; j++)
|
|
|
|
{
|
|
|
|
if (data[j] < 32 || data[j] > 127)
|
|
|
|
{
|
|
|
|
isText = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size << "] ";
|
|
|
|
if (size >= 1000)
|
|
|
|
{
|
|
|
|
ss << "... (to big to print)";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (size_t j = 0; j < size; j++)
|
|
|
|
{
|
|
|
|
if (isText)
|
|
|
|
ss << static_cast<char>(data[j]);
|
|
|
|
else
|
|
|
|
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<short>(data[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
2016-10-23 00:27:12 +02:00
|
|
|
// Check if equal to other multipart
|
2016-04-06 14:20:10 +02:00
|
|
|
bool equal(const multipart_t* other) const
|
|
|
|
{
|
|
|
|
if (size() != other->size())
|
|
|
|
return false;
|
|
|
|
for (size_t i = 0; i < size(); i++)
|
|
|
|
if (!peek(i)->equal(other->peek(i)))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Disable implicit copying (moving is more efficient)
|
|
|
|
multipart_t(const multipart_t& other) ZMQ_DELETED_FUNCTION;
|
|
|
|
void operator=(const multipart_t& other) ZMQ_DELETED_FUNCTION;
|
2018-03-07 14:11:41 +01:00
|
|
|
}; // class multipart_t
|
2016-04-06 14:20:10 +02:00
|
|
|
|
2018-03-07 14:11:41 +01:00
|
|
|
#endif // ZMQ_HAS_RVALUE_REFS
|
2016-04-06 14:20:10 +02:00
|
|
|
|
2018-03-07 14:11:41 +01:00
|
|
|
inline std::ostream& operator<<(std::ostream& os, const multipart_t& msg)
|
|
|
|
{
|
|
|
|
return os << msg.str();
|
2016-04-06 14:20:10 +02:00
|
|
|
}
|
|
|
|
|
2018-03-07 14:11:41 +01:00
|
|
|
} // namespace zmq
|
|
|
|
|
|
|
|
#endif // __ZMQ_ADDON_HPP_INCLUDED__
|