/* SPDX-License-Identifier: MPL-2.0 */
 
#include "precompiled.hpp"
#include <string.h>
 
#include "macros.hpp"
#include "dish.hpp"
#include "err.hpp"
 
zmq::dish_t::dish_t (class ctx_t *parent_, uint32_t tid_, int sid_) :
    socket_base_t (parent_, tid_, sid_, true), _has_message (false)
{
    options.type = ZMQ_DISH;
 
    //  When socket is being closed down we don't want to wait till pending
    //  subscription commands are sent to the wire.
    options.linger.store (0);
 
    const int rc = _message.init ();
    errno_assert (rc == 0);
}
 
zmq::dish_t::~dish_t ()
{
    const int rc = _message.close ();
    errno_assert (rc == 0);
}
 
void zmq::dish_t::xattach_pipe (pipe_t *pipe_,
                                bool subscribe_to_all_,
                                bool locally_initiated_)
{
    LIBZMQ_UNUSED (subscribe_to_all_);
    LIBZMQ_UNUSED (locally_initiated_);
 
    zmq_assert (pipe_);
    _fq.attach (pipe_);
    _dist.attach (pipe_);
 
    //  Send all the cached subscriptions to the new upstream peer.
    send_subscriptions (pipe_);
}
 
void zmq::dish_t::xread_activated (pipe_t *pipe_)
{
    _fq.activated (pipe_);
}
 
void zmq::dish_t::xwrite_activated (pipe_t *pipe_)
{
    _dist.activated (pipe_);
}
 
void zmq::dish_t::xpipe_terminated (pipe_t *pipe_)
{
    _fq.pipe_terminated (pipe_);
    _dist.pipe_terminated (pipe_);
}
 
void zmq::dish_t::xhiccuped (pipe_t *pipe_)
{
    //  Send all the cached subscriptions to the hiccuped pipe.
    send_subscriptions (pipe_);
}
 
int zmq::dish_t::xjoin (const char *group_)
{
    const std::string group = std::string (group_);
 
    if (group.length () > ZMQ_GROUP_MAX_LENGTH) {
        errno = EINVAL;
        return -1;
    }
 
    //  User cannot join same group twice
    if (!_subscriptions.insert (group).second) {
        errno = EINVAL;
        return -1;
    }
 
    msg_t msg;
    int rc = msg.init_join ();
    errno_assert (rc == 0);
 
    rc = msg.set_group (group_);
    errno_assert (rc == 0);
 
    int err = 0;
    rc = _dist.send_to_all (&msg);
    if (rc != 0)
        err = errno;
    const int rc2 = msg.close ();
    errno_assert (rc2 == 0);
    if (rc != 0)
        errno = err;
    return rc;
}
 
int zmq::dish_t::xleave (const char *group_)
{
    const std::string group = std::string (group_);
 
    if (group.length () > ZMQ_GROUP_MAX_LENGTH) {
        errno = EINVAL;
        return -1;
    }
 
    if (0 == _subscriptions.erase (group)) {
        errno = EINVAL;
        return -1;
    }
 
    msg_t msg;
    int rc = msg.init_leave ();
    errno_assert (rc == 0);
 
    rc = msg.set_group (group_);
    errno_assert (rc == 0);
 
    int err = 0;
    rc = _dist.send_to_all (&msg);
    if (rc != 0)
        err = errno;
    const int rc2 = msg.close ();
    errno_assert (rc2 == 0);
    if (rc != 0)
        errno = err;
    return rc;
}
 
int zmq::dish_t::xsend (msg_t *msg_)
{
    LIBZMQ_UNUSED (msg_);
    errno = ENOTSUP;
    return -1;
}
 
bool zmq::dish_t::xhas_out ()
{
    //  Subscription can be added/removed anytime.
    return true;
}
 
int zmq::dish_t::xrecv (msg_t *msg_)
{
    //  If there's already a message prepared by a previous call to zmq_poll,
    //  return it straight ahead.
    if (_has_message) {
        const int rc = msg_->move (_message);
        errno_assert (rc == 0);
        _has_message = false;
        return 0;
    }
 
    return xxrecv (msg_);
}
 
int zmq::dish_t::xxrecv (msg_t *msg_)
{
    do {
        //  Get a message using fair queueing algorithm.
        const int rc = _fq.recv (msg_);
 
        //  If there's no message available, return immediately.
        //  The same when error occurs.
        if (rc != 0)
            return -1;
 
        //  Skip non matching messages
    } while (0 == _subscriptions.count (std::string (msg_->group ())));
 
    //  Found a matching message
    return 0;
}
 
bool zmq::dish_t::xhas_in ()
{
    //  If there's already a message prepared by a previous call to zmq_poll,
    //  return straight ahead.
    if (_has_message)
        return true;
 
    const int rc = xxrecv (&_message);
    if (rc != 0) {
        errno_assert (errno == EAGAIN);
        return false;
    }
 
    //  Matching message found
    _has_message = true;
    return true;
}
 
void zmq::dish_t::send_subscriptions (pipe_t *pipe_)
{
    for (subscriptions_t::iterator it = _subscriptions.begin (),
                                   end = _subscriptions.end ();
         it != end; ++it) {
        msg_t msg;
        int rc = msg.init_join ();
        errno_assert (rc == 0);
 
        rc = msg.set_group (it->c_str ());
        errno_assert (rc == 0);
 
        //  Send it to the pipe.
        pipe_->write (&msg);
    }
 
    pipe_->flush ();
}
 
zmq::dish_session_t::dish_session_t (io_thread_t *io_thread_,
                                     bool connect_,
                                     socket_base_t *socket_,
                                     const options_t &options_,
                                     address_t *addr_) :
    session_base_t (io_thread_, connect_, socket_, options_, addr_),
    _state (group)
{
}
 
zmq::dish_session_t::~dish_session_t ()
{
}
 
int zmq::dish_session_t::push_msg (msg_t *msg_)
{
    if (_state == group) {
        if ((msg_->flags () & msg_t::more) != msg_t::more) {
            errno = EFAULT;
            return -1;
        }
 
        if (msg_->size () > ZMQ_GROUP_MAX_LENGTH) {
            errno = EFAULT;
            return -1;
        }
 
        _group_msg = *msg_;
        _state = body;
 
        const int rc = msg_->init ();
        errno_assert (rc == 0);
        return 0;
    }
    const char *group_setting = msg_->group ();
    int rc;
    if (group_setting[0] != 0)
        goto has_group;
 
    //  Set the message group
    rc = msg_->set_group (static_cast<char *> (_group_msg.data ()),
                          _group_msg.size ());
    errno_assert (rc == 0);
 
    //  We set the group, so we don't need the group_msg anymore
    rc = _group_msg.close ();
    errno_assert (rc == 0);
has_group:
    //  Thread safe socket doesn't support multipart messages
    if ((msg_->flags () & msg_t::more) == msg_t::more) {
        errno = EFAULT;
        return -1;
    }
 
    //  Push message to dish socket
    rc = session_base_t::push_msg (msg_);
 
    if (rc == 0)
        _state = group;
 
    return rc;
}
 
int zmq::dish_session_t::pull_msg (msg_t *msg_)
{
    int rc = session_base_t::pull_msg (msg_);
 
    if (rc != 0)
        return rc;
 
    if (!msg_->is_join () && !msg_->is_leave ())
        return rc;
 
    const int group_length = static_cast<int> (strlen (msg_->group ()));
 
    msg_t command;
    int offset;
 
    if (msg_->is_join ()) {
        rc = command.init_size (group_length + 5);
        errno_assert (rc == 0);
        offset = 5;
        memcpy (command.data (), "\4JOIN", 5);
    } else {
        rc = command.init_size (group_length + 6);
        errno_assert (rc == 0);
        offset = 6;
        memcpy (command.data (), "\5LEAVE", 6);
    }
 
    command.set_flags (msg_t::command);
    char *command_data = static_cast<char *> (command.data ());
 
    //  Copy the group
    memcpy (command_data + offset, msg_->group (), group_length);
 
    //  Close the join message
    rc = msg_->close ();
    errno_assert (rc == 0);
 
    *msg_ = command;
 
    return 0;
}
 
void zmq::dish_session_t::reset ()
{
    session_base_t::reset ();
    _state = group;
}

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: _group_msg.