<commit_before>#include "Client.h"
#include "core/global.h"
#include "core/msgtypes.h"
#include "clientagent/ClientMessages.h"
#include "clientagent/ClientAgent.h"
using namespace std;
using dclass::Class;

Client::Client(ConfigNode, ClientAgent* client_agent) :
    m_client_agent(client_agent)
{
    m_channel = m_client_agent->m_ct.alloc_channel();
    if(!m_channel) {
        m_log = m_client_agent->log();
        send_disconnect(CLIENT_DISCONNECT_GENERIC, "Client capacity reached");
        return;
    }
    m_allocated_channel = m_channel;

    stringstream name;
    name << "Client (" << m_allocated_channel << ")";
    m_log = new LogCategory("client", name.str());
    set_con_name(name.str());

    subscribe_channel(m_channel);
    subscribe_channel(BCHAN_CLIENTS);
}

Client::~Client()
{
    // We need to be holding our own lock, just in case another thread is busy
    // doing some last-microsecond cleanup.
    lock_guard<recursive_mutex> lock(m_client_lock);

    assert(!m_pending_interests.size());

    delete m_log;
}

void Client::annihilate()
{
    lock_guard<recursive_mutex> lock(m_client_lock);
    if(is_terminated()) {
        return;
    }

    // Unsubscribe from all channels first so the DELETE messages aren't sent back to us.
    unsubscribe_all();
    m_client_agent->m_ct.free_channel(m_allocated_channel);

    // Delete all session objects
    while(m_session_objects.size() > 0) {
        doid_t do_id = *m_session_objects.begin();
        m_session_objects.erase(do_id);
        m_log->debug() << "Client exited, deleting session object with id " << do_id << ".\n";
        DatagramPtr dg = Datagram::create(do_id, m_channel, STATESERVER_OBJECT_DELETE_RAM);
        dg->add_doid(do_id);
        route_datagram(dg);
    }

    // Note that finish() normally results in the InterestOperation deleting
    // itself from m_pending_interests, so we have to be VERY careful with this
    // for loop. Using (it++) ensures that 'it' is advanced BEFORE finish() is
    // called; doing so after means the iterator is invalid!
    for(auto it = m_pending_interests.begin(); it != m_pending_interests.end();) {
        (it++)->second->finish();
    }

    // Tell the MD this client is gone
    terminate();
}

// log_event sends an event to the EventLogger
void Client::log_event(LoggedEvent &event)
{
    stringstream ss;
    ss << "Client:" << m_allocated_channel;
    event.add("sender", ss.str());

    g_eventsender.send(event);
}

// lookup_object returns the class of the object with a do_id.
// If that object is not visible to the client, nullptr will be returned instead.
const Class *Client::lookup_object(doid_t do_id)
{
    // First see if it's an UberDOG:
    if(g_uberdogs.find(do_id) != g_uberdogs.end()) {
        return g_uberdogs[do_id].dcc;
    }

    // Next, check the object cache, but this client only knows about it
    // if it occurs in m_seen_objects or m_owned_objects:
    if(m_owned_objects.find(do_id) != m_owned_objects.end() ||
       m_seen_objects.find(do_id) != m_seen_objects.end()) {
        if(m_visible_objects.find(do_id) != m_visible_objects.end()) {
            return m_visible_objects[do_id].dcc;
        }
    }

    // Hey we also know about it if its a declared object!
    else if(m_declared_objects.find(do_id) != m_declared_objects.end()) {
        return m_declared_objects[do_id].dcc;
    }

    // We're at the end of our rope; we have no clue what this object is.
    return nullptr;
}

// lookup_interests returns a list of all the interests that a parent-zone pair is visible to.
list<Interest> Client::lookup_interests(doid_t parent_id, zone_t zone_id)
{
    list<Interest> interests;
    for(const auto& it : m_interests) {
        if(parent_id == it.second.parent && (it.second.zones.find(zone_id) != it.second.zones.end())) {
            interests.push_back(it.second);
        }
    }
    return interests;
}

// build_interest will build an interest from a datagram. It is expected that the datagram
// iterator is positioned such that next item to be read is the interest_id.
void Client::build_interest(DatagramIterator &dgi, bool multiple, Interest &out)
{
    uint16_t interest_id = dgi.read_uint16();
    doid_t parent = dgi.read_doid();

    out.id = interest_id;
    out.parent = parent;

    uint16_t count = 1;
    if(multiple) {
        count = dgi.read_uint16();
    }

    // TODO: We shouldn't have to do this ourselves, figure out where else we're doing
    //       something wrong.
    out.zones.rehash((unsigned int)(ceil(count / out.zones.max_load_factor())));

    for(int x = 0; x < count; ++x) {
        zone_t zone = dgi.read_zone();
        out.zones.insert(out.zones.end(), zone);
    }
}

// add_interest will start a new interest operation and retrieve all the objects an interest
// from the server, subscribing to each zone in the interest.  If the interest already
// exists, the interest will be updated with the new zones passed in by the argument.
void Client::add_interest(Interest &i, uint32_t context, channel_t caller)
{
    unordered_set<zone_t> new_zones;

    for(const auto& it : i.zones) {
        if(lookup_interests(i.parent, it).empty()) {
            new_zones.insert(it);<commit_msg>Client: Iterate with consistent types over dgi count<commit_after>#include "Client.h"
#include "core/global.h"
#include "core/msgtypes.h"
#include "clientagent/ClientMessages.h"
#include "clientagent/ClientAgent.h"
using namespace std;
using dclass::Class;

Client::Client(ConfigNode, ClientAgent* client_agent) :
    m_client_agent(client_agent)
{
    m_channel = m_client_agent->m_ct.alloc_channel();
    if(!m_channel) {
        m_log = m_client_agent->log();
        send_disconnect(CLIENT_DISCONNECT_GENERIC, "Client capacity reached");
        return;
    }
    m_allocated_channel = m_channel;

    stringstream name;
    name << "Client (" << m_allocated_channel << ")";
    m_log = new LogCategory("client", name.str());
    set_con_name(name.str());

    subscribe_channel(m_channel);
    subscribe_channel(BCHAN_CLIENTS);
}

Client::~Client()
{
    // We need to be holding our own lock, just in case another thread is busy
    // doing some last-microsecond cleanup.
    lock_guard<recursive_mutex> lock(m_client_lock);

    assert(!m_pending_interests.size());

    delete m_log;
}

void Client::annihilate()
{
    lock_guard<recursive_mutex> lock(m_client_lock);
    if(is_terminated()) {
        return;
    }

    // Unsubscribe from all channels first so the DELETE messages aren't sent back to us.
    unsubscribe_all();
    m_client_agent->m_ct.free_channel(m_allocated_channel);

    // Delete all session objects
    while(m_session_objects.size() > 0) {
        doid_t do_id = *m_session_objects.begin();
        m_session_objects.erase(do_id);
        m_log->debug() << "Client exited, deleting session object with id " << do_id << ".\n";
        DatagramPtr dg = Datagram::create(do_id, m_channel, STATESERVER_OBJECT_DELETE_RAM);
        dg->add_doid(do_id);
        route_datagram(dg);
    }

    // Note that finish() normally results in the InterestOperation deleting
    // itself from m_pending_interests, so we have to be VERY careful with this
    // for loop. Using (it++) ensures that 'it' is advanced BEFORE finish() is
    // called; doing so after means the iterator is invalid!
    for(auto it = m_pending_interests.begin(); it != m_pending_interests.end();) {
        (it++)->second->finish();
    }

    // Tell the MD this client is gone
    terminate();
}

// log_event sends an event to the EventLogger
void Client::log_event(LoggedEvent &event)
{
    stringstream ss;
    ss << "Client:" << m_allocated_channel;
    event.add("sender", ss.str());

    g_eventsender.send(event);
}

// lookup_object returns the class of the object with a do_id.
// If that object is not visible to the client, nullptr will be returned instead.
const Class *Client::lookup_object(doid_t do_id)
{
    // First see if it's an UberDOG:
    if(g_uberdogs.find(do_id) != g_uberdogs.end()) {
        return g_uberdogs[do_id].dcc;
    }

    // Next, check the object cache, but this client only knows about it
    // if it occurs in m_seen_objects or m_owned_objects:
    if(m_owned_objects.find(do_id) != m_owned_objects.end() ||
       m_seen_objects.find(do_id) != m_seen_objects.end()) {
        if(m_visible_objects.find(do_id) != m_visible_objects.end()) {
            return m_visible_objects[do_id].dcc;
        }
    }

    // Hey we also know about it if its a declared object!
    else if(m_declared_objects.find(do_id) != m_declared_objects.end()) {
        return m_declared_objects[do_id].dcc;
    }

    // We're at the end of our rope; we have no clue what this object is.
    return nullptr;
}

// lookup_interests returns a list of all the interests that a parent-zone pair is visible to.
list<Interest> Client::lookup_interests(doid_t parent_id, zone_t zone_id)
{
    list<Interest> interests;
    for(const auto& it : m_interests) {
        if(parent_id == it.second.parent && (it.second.zones.find(zone_id) != it.second.zones.end())) {
            interests.push_back(it.second);
        }
    }
    return interests;
}

// build_interest will build an interest from a datagram. It is expected that the datagram
// iterator is positioned such that next item to be read is the interest_id.
void Client::build_interest(DatagramIterator &dgi, bool multiple, Interest &out)
{
    uint16_t interest_id = dgi.read_uint16();
    doid_t parent = dgi.read_doid();

    out.id = interest_id;
    out.parent = parent;

    uint16_t count = 1;
    if(multiple) {
        count = dgi.read_uint16();
    }

    // TODO: We shouldn't have to do this ourselves, figure out where else we're doing
    //       something wrong.
    out.zones.rehash((unsigned int)(ceil(count / out.zones.max_load_factor())));

    for(uint16_t x{}; x < count; ++x) {
        zone_t zone = dgi.read_zone();
        out.zones.insert(out.zones.end(), zone);
    }
}

// add_interest will start a new interest operation and retrieve all the objects an interest
// from the server, subscribing to each zone in the interest.  If the interest already
// exists, the interest will be updated with the new zones passed in by the argument.
void Client::add_interest(Interest &i, uint32_t context, channel_t caller)
{
    unordered_set<zone_t> new_zones;

    for(const auto& it : i.zones) {
        if(lookup_interests(i.parent, it).empty()) {
            new_zones.insert(it);
{'fraction_non_alphanumeric': 0.11293087031041707, 'fraction_numerical': 0.002570938868786898, 'mean_word_length': 3.308039376538146, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1432160', 'n_tokens_mistral': 3441, 'n_tokens_neox': 3355, 'n_words': 927}