<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "VirtualNetwork.h"
#include "VirtualNetworkPool.h"

#include "NebulaLog.h"
#include "RangedLeases.h"
#include "FixedLeases.h"

#include "AuthManager.h"
#include "UserPool.h"

/* ************************************************************************** */
/* Virtual Network :: Constructor/Destructor                                  */
/* ************************************************************************** */

VirtualNetwork::VirtualNetwork(int uid,
                               string _user_name,
                               VirtualNetworkTemplate *_vn_template):
                PoolObjectSQL(-1,"",uid,table),
                user_name(_user_name),
                bridge(""),
                type(UNINITIALIZED),
                leases(0)
{
    if (_vn_template != 0)
    {
        vn_template = _vn_template;
    }
    else
    {
        vn_template = new VirtualNetworkTemplate;
    }
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

VirtualNetwork::~VirtualNetwork()
{
    if (leases != 0)
    {
        delete leases;
    }

    if (vn_template != 0)
    {
        delete vn_template;
    }
}

/* ************************************************************************** */
/* Virtual Network :: Database Access Functions                               */
/* ************************************************************************** */

const char * VirtualNetwork::table        = "network_pool";

const char * VirtualNetwork::db_names     = "oid, name, body, uid";

const char * VirtualNetwork::db_bootstrap = "CREATE TABLE IF NOT EXISTS"
    " network_pool (oid INTEGER PRIMARY KEY, name VARCHAR(256),"
    " body TEXT, uid INTEGER, public INTEGER, UNIQUE(name,uid))";

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::select(SqlDB * db)
{
    int             rc;

    rc = PoolObjectSQL::select(db);

    if ( rc != 0 )
    {
        return rc;
    }

    return select_leases(db);
}

/* -------------------------------------------------------------------------- */

int VirtualNetwork::select(SqlDB * db, const string& name, int uid)
{
    int             rc;

    rc = PoolObjectSQL::select(db,name,uid);

    if ( rc != 0 )
    {
        return rc;
    }

    return select_leases(db);
}

/* -------------------------------------------------------------------------- */

int VirtualNetwork::select_leases(SqlDB * db)
{
    ostringstream   oss;
    ostringstream   ose;

    string          network_address;

    unsigned int default_size = VirtualNetworkPool::default_size();
    unsigned int mac_prefix   = VirtualNetworkPool::mac_prefix();

    //Get the leases
    if (type == RANGED)
    {
        string  nclass = "";
        int     size = 0;

        // retrieve specific information from the template
        get_template_attribute("NETWORK_ADDRESS",network_address);

        if (network_address.empty())
        {
            goto error_addr;
        }

        get_template_attribute("NETWORK_SIZE",nclass);

        if ( nclass == "B" || nclass == "b" )
        {
            size = 65534;
        }
        else if ( nclass == "C" || nclass == "c" )
        {
            size = 254;
        }
        else if (!nclass.empty()) //Assume it's a number
        {
            istringstream iss(nclass);
            iss >> size;
        }

        if (size == 0)
        {
            size = default_size;
        }

        leases = new RangedLeases(db,
                                  oid,
                                  size,
                                  mac_prefix,
                                  network_address);
    }
    else if(type == FIXED)
    {
        leases = new  FixedLeases(db,
                                  oid,
                                  mac_prefix);
    }
    else
    {
        goto error_type;
    }

    if (leases == 0)
    {
        goto error_leases;
    }

    return leases->select(db);

error_leases:
    ose << "Error getting Virtual Network leases nid: " << oid;
    goto error_common;

error_type:
    ose << "Wrong type of Virtual Network: " << type;
    goto error_common;

error_addr:
    ose << "Network address is not defined nid: " << oid;

error_common:
    NebulaLog::log("VNM", Log::ERROR, ose);
    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::insert(SqlDB * db, string& error_str)
{
    ostringstream   ose;
    int             rc;

    string          pub;
    string          s_type;

    unsigned int default_size = VirtualNetworkPool::default_size();
    unsigned int mac_prefix   = VirtualNetworkPool::mac_prefix();

    //--------------------------------------------------------------------------
    // VirtualNetwork Attributes from the template
    //--------------------------------------------------------------------------

    // ------------ TYPE ----------------------
    get_template_attribute("TYPE",s_type);

    transform(s_type.begin(),s_type.end(),s_type.begin(),(int(*)(int))toupper);

    if (s_type == "RANGED")
    {
        type = VirtualNetwork::RANGED;
    }
    else if ( s_type == "FIXED")
    {
        type = VirtualNetwork::FIXED;
    }
    else if ( s_type.empty() )
    {
        goto error_type_defined;
    }
    else
    {
        goto error_wrong_type;
    }

    // ------------ NAME ----------------------

    get_template_attribute("NAME",name);

    if (name.empty())
    {
        goto error_name;
    }

    // ------------ BRIDGE --------------------

    get_template_attribute("BRIDGE",bridge);

    if (bridge.empty())
    {
        goto error_bridge;
    }

    // ------------ PUBLIC --------------------

    get_template_attribute("PUBLIC", pub);

    transform (pub.begin(), pub.end(), pub.begin(), (int(*)(int))toupper);

    public_vnet = (pub == "YES");

    vn_template->erase("PUBLIC");

    //--------------------------------------------------------------------------
    // Get the leases
    //--------------------------------------------------------------------------
    if (type == VirtualNetwork::RANGED)
    {
        string nclass = "";
        string naddr  = "";
        int    size   = 0;

        // retrieve specific information from template
        get_template_attribute("NETWORK_ADDRESS",naddr);

        if (naddr.empty())
        {
            goto error_addr;
        }

        get_template_attribute("NETWORK_SIZE",nclass);

        if ( nclass == "B" || nclass == "b"  )
        {
            size = 65534;
        }
        else if ( nclass == "C" || nclass == "c"  )
        {
            size = 254;
        }
        else if (!nclass.empty())//Assume its a number
        {
            istringstream iss(nclass);

            iss >> size;
        }

        if (size == 0)
        {
            SingleAttribute * attribute;
            ostringstream     oss;

            oss << default_size;

            attribute = new SingleAttribute("NETWORK_SIZE",oss.str());
            vn_template->set(attribute);

            size = default_size;
        }

        leases = new RangedLeases(db,
                                  oid,
                                  size,
                                  mac_prefix,
                                  naddr);
    }
    else // VirtualNetwork::FIXED
    {
        vector<const Attribute *>   vector_leases;

        get_template_attribute("LEASES",vector_leases);

        leases = new FixedLeases(db,
                                 oid,
                                 mac_prefix,
                                 vector_leases);
    }

    if (leases == 0)
    {
        goto error_null_leases;
    }

    //--------------------------------------------------------------------------
    // Insert the Virtual Network
    //--------------------------------------------------------------------------
    rc = insert_replace(db, false);

    if ( rc != 0 )
    {
        goto error_update;
    }

    return 0;

error_type_defined:
    ose << "No TYPE in template for Virtual Network.";
    goto error_common;

error_wrong_type:
    ose << "Wrong type \""<< s_type <<"\" in template for Virtual Network.";
    goto error_common;

error_name:
    ose << "No NAME in template for Virtual Network.";
    goto error_common;

error_bridge:
    ose << "No BRIDGE in template for Virtual Network.";
    goto error_common;

error_update:
    ose << "Can not update Virtual Network.";
    goto error_common;

error_addr:
    ose << "No NETWORK_ADDRESS in template for Virtual Network.";
    goto error_common;

error_null_leases:
    ose << "Error getting Virtual Network leases.";

error_common:
    error_str = ose.str();
    NebulaLog::log("VNM", Log::ERROR, ose);
    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::insert_replace(SqlDB *db, bool replace)
{
    ostringstream   oss;
    int             rc;

    string xml_body;

    char * sql_name;
    char * sql_xml;

    sql_name = db->escape_str(name.c_str());

    if ( sql_name == 0 )
    {
        goto error_name;
    }

    sql_xml = db->escape_str(to_xml(xml_body).c_str());

    if ( sql_xml == 0 )
    {
        goto error_body;
    }

    // Construct the SQL statement to Insert or Replace
    if(replace)
    {
        oss << "REPLACE";
    }
    else
    {
        oss << "INSERT";
    }

    oss << " INTO " << table << " (" << db_names << ") VALUES ("
        <<          oid         << ","
        << "'" <<   sql_name    << "',"
        << "'" <<   sql_xml     << "',"
        <<          uid         << ","
        <<          public_vnet << ")";

    rc = db->exec(oss);

    db->free_str(sql_name);
    db->free_str(sql_xml);

    return rc;

error_body:
    db->free_str(sql_name);
error_name:
    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::drop(SqlDB * db)
{
    int rc;

    rc = PoolObjectSQL::drop(db);

    if ( rc == 0 && leases != 0 )
    {
        rc += leases->drop(db);
    }

    return rc;
}

/* ************************************************************************** */
/* Virtual Network :: Misc                                                    */
/* ************************************************************************** */

ostream& operator<<(ostream& os, VirtualNetwork& vn)
{
    string vnet_xml;

    os << vn.to_xml_extended(vnet_xml,true);

    return os;
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string& VirtualNetwork::to_xml(string& xml) const
{
    return to_xml_extended(xml,false);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string& VirtualNetwork::to_xml_extended(string& xml, bool extended) const
{
    ostringstream os;

    string template_xml;
    string leases_xml;

    // Total leases is the number of used leases.
    int total_leases = 0;

    if (leases != 0)
    {
        total_leases = leases->n_used;
    }

    os <<
        "<VNET>" <<
            "<ID>"          << oid          << "</ID>"          <<
            "<UID>"         << uid          << "</UID>"         <<
            "<USERNAME>"    << user_name    << "</USERNAME>"    <<
            "<NAME>"        << name         << "</NAME>"        <<
            "<TYPE>"        << type         << "</TYPE>"        <<
            "<BRIDGE>"      << bridge       << "</BRIDGE>"      <<
            "<PUBLIC>"      << public_vnet  << "</PUBLIC>"      <<
            "<TOTAL_LEASES>"<< total_leases << "</TOTAL_LEASES>"<<
            vn_template->to_xml(template_xml);

    if (extended && leases != 0)
    {
        os << leases->to_xml(leases_xml);
    }

    os << "</VNET>";

    xml = os.str();

    return xml;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::from_xml(const string &xml_str)
{
    vector<xmlNodePtr> content;

    int rc = 0;
    int int_type;

    // Initialize the internal XML object
    update_from_str(xml_str);

    // Get class base attributes
    rc += xpath(oid,        "/VNET/ID",         -1);
    rc += xpath(uid,        "/VNET/UID",        -1);
    rc += xpath(user_name,  "/VNET/USERNAME",   "not_found");
    rc += xpath(name,       "/VNET/NAME",       "not_found");
    rc += xpath(int_type,   "/VNET/TYPE",       -1);
    rc += xpath(bridge,     "/VNET/BRIDGE",     "not_found");
    rc += xpath(public_vnet,"/VNET/PUBLIC",     0);

    type = static_cast<NetworkType>( int_type );

    // Get associated classes
    ObjectXML::get_nodes("/VNET/TEMPLATE", content);

    if( content.size() < 1 )
    {
        return -1;
    }

    // Virtual Network template
    rc += vn_template->from_xml_node( content[0] );

    if (rc != 0)
    {
        return -1;
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::nic_attribute(VectorAttribute *nic, int vid)
{
    int rc;

    string  model;
    string  ip;
    string  mac;

    ostringstream  vnid;

    map<string,string> new_nic;

    model   = nic->vector_value("MODEL");
    ip      = nic->vector_value("IP");
    vnid   << oid;

    //--------------------------------------------------------------------------
    //                       GET NETWORK LEASE
    //--------------------------------------------------------------------------

    if (ip.empty())
    {
        rc = leases->get(vid,ip,mac);
    }
    else
    {
        rc = leases->set(vid,ip,mac);
    }

    if ( rc != 0 )
    {
        return -1;
    }

    //--------------------------------------------------------------------------
    //                       NEW NIC ATTRIBUTES
    //--------------------------------------------------------------------------

    new_nic.insert(make_pair("NETWORK"   ,name));
    new_nic.insert(make_pair("MAC"       ,mac));
    new_nic.insert(make_pair("BRIDGE"    ,bridge));
    new_nic.insert(make_pair("NETWORK_ID",vnid.str()));
    new_nic.insert(make_pair("IP"        ,ip));

    if (!model.empty())
    {
        new_nic.insert(make_pair("MODEL",model));
    }

    nic->replace(new_nic);

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::add_leases(VirtualNetworkTemplate * leases_template,
                               string&                  error_msg)
{
    vector<const Attribute *> vector_leases;

    leases_template->get("LEASES", vector_leases);

    return leases->add_leases(vector_leases, error_msg);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::remove_leases(VirtualNetworkTemplate * leases_template,
                                  string&                  error_msg)
{
    vector<const Attribute *> vector_leases;

    leases_template->get("LEASES", vector_leases);

    return leases->remove_leases(vector_leases, error_msg);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
<commit_msg>Feature #523: Small bug introduced in ff73aefc6f46a8713b9ad771bd40cd1fe898f3ee<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "VirtualNetwork.h"
#include "VirtualNetworkPool.h"

#include "NebulaLog.h"
#include "RangedLeases.h"
#include "FixedLeases.h"

#include "AuthManager.h"
#include "UserPool.h"

/* ************************************************************************** */
/* Virtual Network :: Constructor/Destructor                                  */
/* ************************************************************************** */

VirtualNetwork::VirtualNetwork(int uid,
                               string _user_name,
                               VirtualNetworkTemplate *_vn_template):
                PoolObjectSQL(-1,"",uid,table),
                user_name(_user_name),
                bridge(""),
                type(UNINITIALIZED),
                leases(0)
{
    if (_vn_template != 0)
    {
        vn_template = _vn_template;
    }
    else
    {
        vn_template = new VirtualNetworkTemplate;
    }
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

VirtualNetwork::~VirtualNetwork()
{
    if (leases != 0)
    {
        delete leases;
    }

    if (vn_template != 0)
    {
        delete vn_template;
    }
}

/* ************************************************************************** */
/* Virtual Network :: Database Access Functions                               */
/* ************************************************************************** */

const char * VirtualNetwork::table        = "network_pool";

const char * VirtualNetwork::db_names     = "oid, name, body, uid, public";

const char * VirtualNetwork::db_bootstrap = "CREATE TABLE IF NOT EXISTS"
    " network_pool (oid INTEGER PRIMARY KEY, name VARCHAR(256),"
    " body TEXT, uid INTEGER, public INTEGER, UNIQUE(name,uid))";

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::select(SqlDB * db)
{
    int             rc;

    rc = PoolObjectSQL::select(db);

    if ( rc != 0 )
    {
        return rc;
    }

    return select_leases(db);
}

/* -------------------------------------------------------------------------- */

int VirtualNetwork::select(SqlDB * db, const string& name, int uid)
{
    int             rc;

    rc = PoolObjectSQL::select(db,name,uid);

    if ( rc != 0 )
    {
        return rc;
    }

    return select_leases(db);
}

/* -------------------------------------------------------------------------- */

int VirtualNetwork::select_leases(SqlDB * db)
{
    ostringstream   oss;
    ostringstream   ose;

    string          network_address;

    unsigned int default_size = VirtualNetworkPool::default_size();
    unsigned int mac_prefix   = VirtualNetworkPool::mac_prefix();

    //Get the leases
    if (type == RANGED)
    {
        string  nclass = "";
        int     size = 0;

        // retrieve specific information from the template
        get_template_attribute("NETWORK_ADDRESS",network_address);

        if (network_address.empty())
        {
            goto error_addr;
        }

        get_template_attribute("NETWORK_SIZE",nclass);

        if ( nclass == "B" || nclass == "b" )
        {
            size = 65534;
        }
        else if ( nclass == "C" || nclass == "c" )
        {
            size = 254;
        }
        else if (!nclass.empty()) //Assume it's a number
        {
            istringstream iss(nclass);
            iss >> size;
        }

        if (size == 0)
        {
            size = default_size;
        }

        leases = new RangedLeases(db,
                                  oid,
                                  size,
                                  mac_prefix,
                                  network_address);
    }
    else if(type == FIXED)
    {
        leases = new  FixedLeases(db,
                                  oid,
                                  mac_prefix);
    }
    else
    {
        goto error_type;
    }

    if (leases == 0)
    {
        goto error_leases;
    }

    return leases->select(db);

error_leases:
    ose << "Error getting Virtual Network leases nid: " << oid;
    goto error_common;

error_type:
    ose << "Wrong type of Virtual Network: " << type;
    goto error_common;

error_addr:
    ose << "Network address is not defined nid: " << oid;

error_common:
    NebulaLog::log("VNM", Log::ERROR, ose);
    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::insert(SqlDB * db, string& error_str)
{
    ostringstream   ose;
    int             rc;

    string          pub;
    string          s_type;

    unsigned int default_size = VirtualNetworkPool::default_size();
    unsigned int mac_prefix   = VirtualNetworkPool::mac_prefix();

    //--------------------------------------------------------------------------
    // VirtualNetwork Attributes from the template
    //--------------------------------------------------------------------------

    // ------------ TYPE ----------------------
    get_template_attribute("TYPE",s_type);

    transform(s_type.begin(),s_type.end(),s_type.begin(),(int(*)(int))toupper);

    if (s_type == "RANGED")
    {
        type = VirtualNetwork::RANGED;
    }
    else if ( s_type == "FIXED")
    {
        type = VirtualNetwork::FIXED;
    }
    else if ( s_type.empty() )
    {
        goto error_type_defined;
    }
    else
    {
        goto error_wrong_type;
    }

    // ------------ NAME ----------------------

    get_template_attribute("NAME",name);

    if (name.empty())
    {
        goto error_name;
    }

    // ------------ BRIDGE --------------------

    get_template_attribute("BRIDGE",bridge);

    if (bridge.empty())
    {
        goto error_bridge;
    }

    // ------------ PUBLIC --------------------

    get_template_attribute("PUBLIC", pub);

    transform (pub.begin(), pub.end(), pub.begin(), (int(*)(int))toupper);

    public_vnet = (pub == "YES");

    vn_template->erase("PUBLIC");

    //--------------------------------------------------------------------------
    // Get the leases
    //--------------------------------------------------------------------------
    if (type == VirtualNetwork::RANGED)
    {
        string nclass = "";
        string naddr  = "";
        int    size   = 0;

        // retrieve specific information from template
        get_template_attribute("NETWORK_ADDRESS",naddr);

        if (naddr.empty())
        {
            goto error_addr;
        }

        get_template_attribute("NETWORK_SIZE",nclass);

        if ( nclass == "B" || nclass == "b"  )
        {
            size = 65534;
        }
        else if ( nclass == "C" || nclass == "c"  )
        {
            size = 254;
        }
        else if (!nclass.empty())//Assume its a number
        {
            istringstream iss(nclass);

            iss >> size;
        }

        if (size == 0)
        {
            SingleAttribute * attribute;
            ostringstream     oss;

            oss << default_size;

            attribute = new SingleAttribute("NETWORK_SIZE",oss.str());
            vn_template->set(attribute);

            size = default_size;
        }

        leases = new RangedLeases(db,
                                  oid,
                                  size,
                                  mac_prefix,
                                  naddr);
    }
    else // VirtualNetwork::FIXED
    {
        vector<const Attribute *>   vector_leases;

        get_template_attribute("LEASES",vector_leases);

        leases = new FixedLeases(db,
                                 oid,
                                 mac_prefix,
                                 vector_leases);
    }

    if (leases == 0)
    {
        goto error_null_leases;
    }

    //--------------------------------------------------------------------------
    // Insert the Virtual Network
    //--------------------------------------------------------------------------
    rc = insert_replace(db, false);

    if ( rc != 0 )
    {
        goto error_update;
    }

    return 0;

error_type_defined:
    ose << "No TYPE in template for Virtual Network.";
    goto error_common;

error_wrong_type:
    ose << "Wrong type \""<< s_type <<"\" in template for Virtual Network.";
    goto error_common;

error_name:
    ose << "No NAME in template for Virtual Network.";
    goto error_common;

error_bridge:
    ose << "No BRIDGE in template for Virtual Network.";
    goto error_common;

error_update:
    ose << "Can not update Virtual Network.";
    goto error_common;

error_addr:
    ose << "No NETWORK_ADDRESS in template for Virtual Network.";
    goto error_common;

error_null_leases:
    ose << "Error getting Virtual Network leases.";

error_common:
    error_str = ose.str();
    NebulaLog::log("VNM", Log::ERROR, ose);
    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::insert_replace(SqlDB *db, bool replace)
{
    ostringstream   oss;
    int             rc;

    string xml_body;

    char * sql_name;
    char * sql_xml;

    sql_name = db->escape_str(name.c_str());

    if ( sql_name == 0 )
    {
        goto error_name;
    }

    sql_xml = db->escape_str(to_xml(xml_body).c_str());

    if ( sql_xml == 0 )
    {
        goto error_body;
    }

    // Construct the SQL statement to Insert or Replace
    if(replace)
    {
        oss << "REPLACE";
    }
    else
    {
        oss << "INSERT";
    }

    oss << " INTO " << table << " (" << db_names << ") VALUES ("
        <<          oid         << ","
        << "'" <<   sql_name    << "',"
        << "'" <<   sql_xml     << "',"
        <<          uid         << ","
        <<          public_vnet << ")";

    rc = db->exec(oss);

    db->free_str(sql_name);
    db->free_str(sql_xml);

    return rc;

error_body:
    db->free_str(sql_name);
error_name:
    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::drop(SqlDB * db)
{
    int rc;

    rc = PoolObjectSQL::drop(db);

    if ( rc == 0 && leases != 0 )
    {
        rc += leases->drop(db);
    }

    return rc;
}

/* ************************************************************************** */
/* Virtual Network :: Misc                                                    */
/* ************************************************************************** */

ostream& operator<<(ostream& os, VirtualNetwork& vn)
{
    string vnet_xml;

    os << vn.to_xml_extended(vnet_xml,true);

    return os;
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string& VirtualNetwork::to_xml(string& xml) const
{
    return to_xml_extended(xml,false);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string& VirtualNetwork::to_xml_extended(string& xml, bool extended) const
{
    ostringstream os;

    string template_xml;
    string leases_xml;

    // Total leases is the number of used leases.
    int total_leases = 0;

    if (leases != 0)
    {
        total_leases = leases->n_used;
    }

    os <<
        "<VNET>" <<
            "<ID>"          << oid          << "</ID>"          <<
            "<UID>"         << uid          << "</UID>"         <<
            "<USERNAME>"    << user_name    << "</USERNAME>"    <<
            "<NAME>"        << name         << "</NAME>"        <<
            "<TYPE>"        << type         << "</TYPE>"        <<
            "<BRIDGE>"      << bridge       << "</BRIDGE>"      <<
            "<PUBLIC>"      << public_vnet  << "</PUBLIC>"      <<
            "<TOTAL_LEASES>"<< total_leases << "</TOTAL_LEASES>"<<
            vn_template->to_xml(template_xml);

    if (extended && leases != 0)
    {
        os << leases->to_xml(leases_xml);
    }

    os << "</VNET>";

    xml = os.str();

    return xml;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::from_xml(const string &xml_str)
{
    vector<xmlNodePtr> content;

    int rc = 0;
    int int_type;

    // Initialize the internal XML object
    update_from_str(xml_str);

    // Get class base attributes
    rc += xpath(oid,        "/VNET/ID",         -1);
    rc += xpath(uid,        "/VNET/UID",        -1);
    rc += xpath(user_name,  "/VNET/USERNAME",   "not_found");
    rc += xpath(name,       "/VNET/NAME",       "not_found");
    rc += xpath(int_type,   "/VNET/TYPE",       -1);
    rc += xpath(bridge,     "/VNET/BRIDGE",     "not_found");
    rc += xpath(public_vnet,"/VNET/PUBLIC",     0);

    type = static_cast<NetworkType>( int_type );

    // Get associated classes
    ObjectXML::get_nodes("/VNET/TEMPLATE", content);

    if( content.size() < 1 )
    {
        return -1;
    }

    // Virtual Network template
    rc += vn_template->from_xml_node( content[0] );

    if (rc != 0)
    {
        return -1;
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::nic_attribute(VectorAttribute *nic, int vid)
{
    int rc;

    string  model;
    string  ip;
    string  mac;

    ostringstream  vnid;

    map<string,string> new_nic;

    model   = nic->vector_value("MODEL");
    ip      = nic->vector_value("IP");
    vnid   << oid;

    //--------------------------------------------------------------------------
    //                       GET NETWORK LEASE
    //--------------------------------------------------------------------------

    if (ip.empty())
    {
        rc = leases->get(vid,ip,mac);
    }
    else
    {
        rc = leases->set(vid,ip,mac);
    }

    if ( rc != 0 )
    {
        return -1;
    }

    //--------------------------------------------------------------------------
    //                       NEW NIC ATTRIBUTES
    //--------------------------------------------------------------------------

    new_nic.insert(make_pair("NETWORK"   ,name));
    new_nic.insert(make_pair("MAC"       ,mac));
    new_nic.insert(make_pair("BRIDGE"    ,bridge));
    new_nic.insert(make_pair("NETWORK_ID",vnid.str()));
    new_nic.insert(make_pair("IP"        ,ip));

    if (!model.empty())
    {
        new_nic.insert(make_pair("MODEL",model));
    }

    nic->replace(new_nic);

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::add_leases(VirtualNetworkTemplate * leases_template,
                               string&                  error_msg)
{
    vector<const Attribute *> vector_leases;

    leases_template->get("LEASES", vector_leases);

    return leases->add_leases(vector_leases, error_msg);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int VirtualNetwork::remove_leases(VirtualNetworkTemplate * leases_template,
                                  string&                  error_msg)
{
    vector<const Attribute *> vector_leases;

    leases_template->get("LEASES", vector_leases);

    return leases->remove_leases(vector_leases, error_msg);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
<|endoftext|>


<commit_before>/*
* Copyright (c) 2020 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "printgraph.h"
#include <set>
#include <map>
#include <list>
#include <algorithm>
#include <cstring>
#include <climits>

static std::string mangleNode(VSNodeRef *node, const VSAPI *vsapi) {
    return "n" + std::to_string(reinterpret_cast<uintptr_t>(node)) + "_" + std::to_string(vsapi->getNodeIndex(node));
}

static std::string mangleFrame(VSNodeRef *node, int level, const VSAPI *vsapi) {
    return "s" + std::to_string(reinterpret_cast<uintptr_t>(vsapi->getNodeCreationFunctionArguments(node, level)));
}

static int getMaxLevel(VSNodeRef *node, const VSAPI *vsapi) {
    for (int i = 0; i < INT_MAX; i++) {
        if (!vsapi->getNodeCreationFunctionArguments(node, i))
            return i - 1;
    }
    return 0;
}

static int getMinRealLevel(VSNodeRef *node, const VSAPI *vsapi) {
    int level = 0;
    while (vsapi->getNodeCreationFunctionArguments(node, level) && vsapi->getNodeCreationFunctionName(node, level) && !strcmp(vsapi->getNodeCreationFunctionName(node, level), ""))
        level++;
    return level;
}

static std::string printVSMap(const VSMap *args, int maxPrintLength, const VSAPI *vsapi) {
    int numKeys = vsapi->mapNumKeys(args);
    std::string setArgsStr;
    for (int i = 0; i < numKeys; i++) {
        const char *key = vsapi->mapGetKey(args, i);
        int numElems = vsapi->mapNumElements(args, key);

        setArgsStr += "\\n";
        setArgsStr += key;
        setArgsStr += "=";

        switch (vsapi->mapGetType(args, key)) {
            case ptInt:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++)
                    setArgsStr += (j ? ", " : "") + std::to_string(vsapi->mapGetInt(args, key, j, nullptr));
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptFloat:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++)
                    setArgsStr += (j ? ", " : "") + std::to_string(vsapi->mapGetFloat(args, key, j, nullptr));
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptData:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++)
                    setArgsStr += std::string(j ? ", " : "") + (vsapi->mapGetDataType(args, key, j, nullptr) == dtUtf8 ? vsapi->mapGetData(args, key, j, nullptr) : ("[binary data " + std::to_string(vsapi->mapGetDataSize(args, key, j, nullptr)) + " bytes]"));
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptVideoNode:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    const VSVideoInfo *vi = vsapi->getVideoInfo(ref);
                    char formatName[32];
                    vsapi->getVideoFormatName(&vi->format, formatName);
                    setArgsStr += (j ? ", [" : " [") + std::string(formatName) + ":" + std::to_string(vi->width) + "x" + std::to_string(vi->height) + "]";
                    vsapi->freeNode(ref);
                }
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptAudioNode:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    const VSAudioInfo *ai = vsapi->getAudioInfo(ref);
                    char formatName[32];
                    vsapi->getAudioFormatName(&ai->format, formatName);
                    setArgsStr += (j ? ", [" : " [") + std::string(formatName) + ":" + std::to_string(ai->sampleRate) + ":" + std::to_string(ai->format.channelLayout) + "]";
                    vsapi->freeNode(ref);
                }
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            default:
                setArgsStr += "<" + std::to_string(numElems) + ">";
        }
    }
    return setArgsStr;
}

static void printNodeGraphHelper(std::set<std::string> &lines, std::map<std::string, std::set<std::string>> &nodes, std::set<VSNodeRef *> &visited, VSNodeRef *node, const VSAPI *vsapi) {
    if (!visited.insert(node).second)
        return;

    int maxLevel = getMaxLevel(node, vsapi);
    int minRealLevel = getMinRealLevel(node, vsapi);

    std::string setArgsStr = printVSMap(vsapi->getNodeCreationFunctionArguments(node, minRealLevel), 5, vsapi);

    std::string thisNode = mangleNode(node, vsapi);
    std::string thisFrame = mangleFrame(node, 0, vsapi);
    std::string baseFrame = mangleFrame(node, maxLevel, vsapi);

    if (minRealLevel != 0) {
        nodes[baseFrame].insert("label=\"" + std::string(vsapi->getNodeCreationFunctionName(node, minRealLevel)) + setArgsStr + "\"");
        nodes[baseFrame].insert(thisFrame + " [label=\"" + std::string(vsapi->getNodeCreationFunctionName(node, minRealLevel)) + "\", shape=octagon]");
    } else {
        nodes[baseFrame].insert(thisFrame + " [label=\"" + std::string(vsapi->getNodeCreationFunctionName(node, minRealLevel)) + setArgsStr + "\", shape=box]");
    }

    nodes[baseFrame].insert(thisNode + " [label=\"" + std::string(vsapi->getNodeName(node)) + "#" + std::to_string(vsapi->getNodeIndex(node)) + "\", shape=oval]");
    lines.insert(thisFrame + " -> " + thisNode);

    const VSMap *args = vsapi->getNodeCreationFunctionArguments(node, 0);
    int numKeys = vsapi->mapNumKeys(args);
    for (int i = 0; i < numKeys; i++) {
        const char *key = vsapi->mapGetKey(args, i);
        int numElems = vsapi->mapNumElements(args, key);
        switch (vsapi->mapGetType(args, key)) {
            case ptVideoNode:
            case ptAudioNode:
                for (int j = 0; j < numElems; j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    lines.insert(mangleNode(ref, vsapi) +  " -> " + thisFrame);
                    printNodeGraphHelper(lines, nodes, visited, ref, vsapi);
                    vsapi->freeNode(ref);
                }
                break;
            default:
                break;
        }
    }
}

std::string printFullNodeGraph(VSNodeRef *node, const VSAPI *vsapi) {
    std::map<std::string, std::set<std::string>> nodes;
    std::set<std::string> lines;
    std::set<VSNodeRef *> visited;
    std::string s = "digraph {\n";
    printNodeGraphHelper(lines, nodes, visited, node, vsapi);
    for (const auto &iter : nodes) {
        if (iter.second.size() > 1) {
            s += "  subgraph cluster_" +iter.first  + " {\n";
            for (const auto &iter2 : iter.second)
                s += "    " + iter2 + "\n";
            s += "  }\n";
        } else if (iter.second.size() <= 2) {
            for (const auto &iter2 : iter.second)
                s += "  " + iter2 + "\n";
        }
    }

    for (const auto &iter : lines)
        s += "  " + iter + "\n";
    s += "}";
    return s;
}

struct NodeTimeRecord {
    std::string filterName;
    int filterMode;
    int64_t nanoSeconds;

    bool operator<(const NodeTimeRecord &other) {
        return nanoSeconds > other.nanoSeconds;
    }
};

static void printNodeTimesHelper(std::list<NodeTimeRecord> &lines, std::set<VSNodeRef *> &visited, VSNodeRef *node, const VSAPI *vsapi) {
    if (!visited.insert(node).second)
        return;

    int minRealLevel = getMinRealLevel(node, vsapi);

    lines.push_back(NodeTimeRecord{ vsapi->getNodeCreationFunctionName(node, minRealLevel), vsapi->getNodeFilterMode(node), vsapi->getNodeFilterTime(node) } );

    const VSMap *args = vsapi->getNodeCreationFunctionArguments(node, 0);
    int numKeys = vsapi->mapNumKeys(args);
    for (int i = 0; i < numKeys; i++) {
        const char *key = vsapi->mapGetKey(args, i);
        int numElems = vsapi->mapNumElements(args, key);
        switch (vsapi->mapGetType(args, key)) {
            case ptVideoNode:
            case ptAudioNode:
                for (int j = 0; j < numElems; j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    printNodeTimesHelper(lines, visited, ref, vsapi);
                    vsapi->freeNode(ref);
                }
                break;
            default:
                break;
        }
    }
}

static std::string extendStringRight(const std::string &s, size_t length) {
    if (s.length() >= length)
        return s;
    else
        return s + std::string(length - s.length(), ' ');
}

static std::string extendStringLeft(const std::string &s, size_t length) {
    if (s.length() >= length)
        return s;
    else
        return std::string(length - s.length(), ' ') + s;
}

static std::string printWithTwoDecimals(double d) {
    char buffer[32];
    snprintf(buffer, sizeof(buffer), "%.2f", d);
    return buffer;
}

static std::string filterModeToString(int fm) {
    if (fm == fmParallel)
        return "parallel";
    else if (fm == fmParallelRequests)
        return "parreq";
    else if (fm == fmUnordered)
        return "unordered";
    else if (fm == fmSerial)
        return "serial";
    else
        return "unordered";
}

std::string printNodeTimes(VSNodeRef *node, double processingTime, const VSAPI *vsapi) {
    std::list<NodeTimeRecord> lines;
    std::set<VSNodeRef *> visited;
    std::string s;

    printNodeTimesHelper(lines, visited, node, vsapi);

    lines.sort();

    s += extendStringRight("Filtername", 20) + " " + extendStringRight("Filter mode", 10) + " " + extendStringLeft("Time (%)", 10) + "% " + extendStringLeft("Time (s)", 10) + "s\n";

    for (const auto & it : lines)
        s += extendStringRight(it.filterName, 20) + " " + extendStringRight(filterModeToString(it.filterMode), 10) + " " + extendStringLeft(printWithTwoDecimals((it.nanoSeconds) / (processingTime * 10000000)), 10) + " " + extendStringLeft(printWithTwoDecimals(it.nanoSeconds / 1000000000.), 10) + "\n";

    return s;
}<commit_msg>Don't duplicate the unit<commit_after>/*
* Copyright (c) 2020 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "printgraph.h"
#include <set>
#include <map>
#include <list>
#include <algorithm>
#include <cstring>
#include <climits>

static std::string mangleNode(VSNodeRef *node, const VSAPI *vsapi) {
    return "n" + std::to_string(reinterpret_cast<uintptr_t>(node)) + "_" + std::to_string(vsapi->getNodeIndex(node));
}

static std::string mangleFrame(VSNodeRef *node, int level, const VSAPI *vsapi) {
    return "s" + std::to_string(reinterpret_cast<uintptr_t>(vsapi->getNodeCreationFunctionArguments(node, level)));
}

static int getMaxLevel(VSNodeRef *node, const VSAPI *vsapi) {
    for (int i = 0; i < INT_MAX; i++) {
        if (!vsapi->getNodeCreationFunctionArguments(node, i))
            return i - 1;
    }
    return 0;
}

static int getMinRealLevel(VSNodeRef *node, const VSAPI *vsapi) {
    int level = 0;
    while (vsapi->getNodeCreationFunctionArguments(node, level) && vsapi->getNodeCreationFunctionName(node, level) && !strcmp(vsapi->getNodeCreationFunctionName(node, level), ""))
        level++;
    return level;
}

static std::string printVSMap(const VSMap *args, int maxPrintLength, const VSAPI *vsapi) {
    int numKeys = vsapi->mapNumKeys(args);
    std::string setArgsStr;
    for (int i = 0; i < numKeys; i++) {
        const char *key = vsapi->mapGetKey(args, i);
        int numElems = vsapi->mapNumElements(args, key);

        setArgsStr += "\\n";
        setArgsStr += key;
        setArgsStr += "=";

        switch (vsapi->mapGetType(args, key)) {
            case ptInt:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++)
                    setArgsStr += (j ? ", " : "") + std::to_string(vsapi->mapGetInt(args, key, j, nullptr));
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptFloat:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++)
                    setArgsStr += (j ? ", " : "") + std::to_string(vsapi->mapGetFloat(args, key, j, nullptr));
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptData:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++)
                    setArgsStr += std::string(j ? ", " : "") + (vsapi->mapGetDataType(args, key, j, nullptr) == dtUtf8 ? vsapi->mapGetData(args, key, j, nullptr) : ("[binary data " + std::to_string(vsapi->mapGetDataSize(args, key, j, nullptr)) + " bytes]"));
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptVideoNode:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    const VSVideoInfo *vi = vsapi->getVideoInfo(ref);
                    char formatName[32];
                    vsapi->getVideoFormatName(&vi->format, formatName);
                    setArgsStr += (j ? ", [" : " [") + std::string(formatName) + ":" + std::to_string(vi->width) + "x" + std::to_string(vi->height) + "]";
                    vsapi->freeNode(ref);
                }
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            case ptAudioNode:
                for (int j = 0; j < std::min(maxPrintLength, numElems); j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    const VSAudioInfo *ai = vsapi->getAudioInfo(ref);
                    char formatName[32];
                    vsapi->getAudioFormatName(&ai->format, formatName);
                    setArgsStr += (j ? ", [" : " [") + std::string(formatName) + ":" + std::to_string(ai->sampleRate) + ":" + std::to_string(ai->format.channelLayout) + "]";
                    vsapi->freeNode(ref);
                }
                if (numElems > maxPrintLength)
                    setArgsStr += ", <" + std::to_string(numElems - maxPrintLength) + ">";
                break;
            default:
                setArgsStr += "<" + std::to_string(numElems) + ">";
        }
    }
    return setArgsStr;
}

static void printNodeGraphHelper(std::set<std::string> &lines, std::map<std::string, std::set<std::string>> &nodes, std::set<VSNodeRef *> &visited, VSNodeRef *node, const VSAPI *vsapi) {
    if (!visited.insert(node).second)
        return;

    int maxLevel = getMaxLevel(node, vsapi);
    int minRealLevel = getMinRealLevel(node, vsapi);

    std::string setArgsStr = printVSMap(vsapi->getNodeCreationFunctionArguments(node, minRealLevel), 5, vsapi);

    std::string thisNode = mangleNode(node, vsapi);
    std::string thisFrame = mangleFrame(node, 0, vsapi);
    std::string baseFrame = mangleFrame(node, maxLevel, vsapi);

    if (minRealLevel != 0) {
        nodes[baseFrame].insert("label=\"" + std::string(vsapi->getNodeCreationFunctionName(node, minRealLevel)) + setArgsStr + "\"");
        nodes[baseFrame].insert(thisFrame + " [label=\"" + std::string(vsapi->getNodeCreationFunctionName(node, minRealLevel)) + "\", shape=octagon]");
    } else {
        nodes[baseFrame].insert(thisFrame + " [label=\"" + std::string(vsapi->getNodeCreationFunctionName(node, minRealLevel)) + setArgsStr + "\", shape=box]");
    }

    nodes[baseFrame].insert(thisNode + " [label=\"" + std::string(vsapi->getNodeName(node)) + "#" + std::to_string(vsapi->getNodeIndex(node)) + "\", shape=oval]");
    lines.insert(thisFrame + " -> " + thisNode);

    const VSMap *args = vsapi->getNodeCreationFunctionArguments(node, 0);
    int numKeys = vsapi->mapNumKeys(args);
    for (int i = 0; i < numKeys; i++) {
        const char *key = vsapi->mapGetKey(args, i);
        int numElems = vsapi->mapNumElements(args, key);
        switch (vsapi->mapGetType(args, key)) {
            case ptVideoNode:
            case ptAudioNode:
                for (int j = 0; j < numElems; j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    lines.insert(mangleNode(ref, vsapi) +  " -> " + thisFrame);
                    printNodeGraphHelper(lines, nodes, visited, ref, vsapi);
                    vsapi->freeNode(ref);
                }
                break;
            default:
                break;
        }
    }
}

std::string printFullNodeGraph(VSNodeRef *node, const VSAPI *vsapi) {
    std::map<std::string, std::set<std::string>> nodes;
    std::set<std::string> lines;
    std::set<VSNodeRef *> visited;
    std::string s = "digraph {\n";
    printNodeGraphHelper(lines, nodes, visited, node, vsapi);
    for (const auto &iter : nodes) {
        if (iter.second.size() > 1) {
            s += "  subgraph cluster_" +iter.first  + " {\n";
            for (const auto &iter2 : iter.second)
                s += "    " + iter2 + "\n";
            s += "  }\n";
        } else if (iter.second.size() <= 2) {
            for (const auto &iter2 : iter.second)
                s += "  " + iter2 + "\n";
        }
    }

    for (const auto &iter : lines)
        s += "  " + iter + "\n";
    s += "}";
    return s;
}

struct NodeTimeRecord {
    std::string filterName;
    int filterMode;
    int64_t nanoSeconds;

    bool operator<(const NodeTimeRecord &other) {
        return nanoSeconds > other.nanoSeconds;
    }
};

static void printNodeTimesHelper(std::list<NodeTimeRecord> &lines, std::set<VSNodeRef *> &visited, VSNodeRef *node, const VSAPI *vsapi) {
    if (!visited.insert(node).second)
        return;

    int minRealLevel = getMinRealLevel(node, vsapi);

    lines.push_back(NodeTimeRecord{ vsapi->getNodeCreationFunctionName(node, minRealLevel), vsapi->getNodeFilterMode(node), vsapi->getNodeFilterTime(node) } );

    const VSMap *args = vsapi->getNodeCreationFunctionArguments(node, 0);
    int numKeys = vsapi->mapNumKeys(args);
    for (int i = 0; i < numKeys; i++) {
        const char *key = vsapi->mapGetKey(args, i);
        int numElems = vsapi->mapNumElements(args, key);
        switch (vsapi->mapGetType(args, key)) {
            case ptVideoNode:
            case ptAudioNode:
                for (int j = 0; j < numElems; j++) {
                    VSNodeRef *ref = vsapi->mapGetNode(args, key, j, nullptr);
                    printNodeTimesHelper(lines, visited, ref, vsapi);
                    vsapi->freeNode(ref);
                }
                break;
            default:
                break;
        }
    }
}

static std::string extendStringRight(const std::string &s, size_t length) {
    if (s.length() >= length)
        return s;
    else
        return s + std::string(length - s.length(), ' ');
}

static std::string extendStringLeft(const std::string &s, size_t length) {
    if (s.length() >= length)
        return s;
    else
        return std::string(length - s.length(), ' ') + s;
}

static std::string printWithTwoDecimals(double d) {
    char buffer[32];
    snprintf(buffer, sizeof(buffer), "%.2f", d);
    return buffer;
}

static std::string filterModeToString(int fm) {
    if (fm == fmParallel)
        return "parallel";
    else if (fm == fmParallelRequests)
        return "parreq";
    else if (fm == fmUnordered)
        return "unordered";
    else if (fm == fmSerial)
        return "serial";
    else
        return "unordered";
}

std::string printNodeTimes(VSNodeRef *node, double processingTime, const VSAPI *vsapi) {
    std::list<NodeTimeRecord> lines;
    std::set<VSNodeRef *> visited;
    std::string s;

    printNodeTimesHelper(lines, visited, node, vsapi);

    lines.sort();

    s += extendStringRight("Filtername", 20) + " " + extendStringRight("Filter mode", 10) + " " + extendStringLeft("Time (%)", 10) + " " + extendStringLeft("Time (s)", 10) + "\n";

    for (const auto & it : lines)
        s += extendStringRight(it.filterName, 20) + " " + extendStringRight(filterModeToString(it.filterMode), 10) + " " + extendStringLeft(printWithTwoDecimals((it.nanoSeconds) / (processingTime * 10000000)), 10) + " " + extendStringLeft(printWithTwoDecimals(it.nanoSeconds / 1000000000.), 10) + "\n";

    return s;
}<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
// Copyright (C) 2006-2010 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/Plate/IndexPage.h>

#include <boost/shared_array.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
namespace fs = boost::filesystem;

// ----------------------------------------------------------------------
//                            INDEX PAGE
// ----------------------------------------------------------------------

vw::platefile::IndexPage::IndexPage(int level, int base_col, int base_row,
                                    int page_width, int page_height) :
  m_level(level), m_base_col(base_col), m_base_row(base_row),
  m_page_width(page_width), m_page_height(page_height) {

  // Set the size of the sparse table.
  m_sparse_table.resize(page_width*page_height);

  vw_out(VerboseDebugMessage, "platefile::index") << "Opening index page [ "
                                                  << m_base_col << " " << m_base_row
                                                  << " @ " << m_level << " ]\n";
}

vw::platefile::IndexPage::~IndexPage() {
  vw_out(VerboseDebugMessage, "platefile::index") << "Closing index page [ "
                                                  << m_base_col << " " << m_base_row
                                                  << " @ " << m_level << " ]\n";
}

void vw::platefile::IndexPage::serialize(std::ostream& ostr) {
  // Part 1: Write out the page size
  ostr.write((char*)(&m_page_width), sizeof(m_page_width));
  ostr.write((char*)(&m_page_height), sizeof(m_page_height));

  // Part 2: Write the sparsetable metadata
  m_sparse_table.write_metadata(&ostr);

  // Part 3: Write sparse entries
  for (google::sparsetable<multi_value_type>::nonempty_iterator it = m_sparse_table.nonempty_begin();
       it != m_sparse_table.nonempty_end(); ++it) {

    // Iterate over transaction_id list.
    int32 transaction_list_size = (*it).size();
    ostr.write((char*)(&transaction_list_size), sizeof(transaction_list_size));

    multi_value_type::iterator transaction_iter = (*it).begin();
    while (transaction_iter != (*it).end()) {

      // Save the transaction id
      int32 t_id = (*transaction_iter).first;
      ostr.write((char*)(&t_id), sizeof(t_id));

      // Save the size of each protobuf, and then serialize it to disk.
      uint16 protobuf_size = (*transaction_iter).second.ByteSize();
      boost::shared_array<uint8> protobuf_bytes( new uint8[protobuf_size] );
      (*transaction_iter).second.SerializeToArray(protobuf_bytes.get(), protobuf_size);
      ostr.write((char*)(&protobuf_size), sizeof(protobuf_size));
      ostr.write((char*)(protobuf_bytes.get()), protobuf_size);

      ++transaction_iter;
    }
  }
}

void vw::platefile::IndexPage::deserialize(std::istream& istr) {
  // Part 1: Read the page size
  istr.read((char*)(&m_page_width), sizeof(m_page_width));
  istr.read((char*)(&m_page_height), sizeof(m_page_height));

  // Part 2: Read the sparsetable metadata
  m_sparse_table.read_metadata(&istr);

  // Part 3: Read sparse entries
  for (google::sparsetable<multi_value_type>::nonempty_iterator it = m_sparse_table.nonempty_begin();
       it != m_sparse_table.nonempty_end(); ++it) {

    // Iterate over transaction_id list.
    int32 transaction_list_size;
    istr.read((char*)(&transaction_list_size), sizeof(transaction_list_size));

    new (&(*it)) multi_value_type();
    for (int tid = 0; tid < transaction_list_size; ++tid) {

      // Read the transaction id
      int32 t_id;
      istr.read((char*)(&t_id), sizeof(t_id));

      // Read the size (in bytes) of this protobuffer and then read
      // the protobuffer and deserialize it.
      uint16 protobuf_size;
      istr.read((char*)(&protobuf_size), sizeof(protobuf_size));
      boost::shared_array<uint8> protobuf_bytes( new uint8[protobuf_size] );
      istr.read((char*)(protobuf_bytes.get()), protobuf_size);
      IndexRecord rec;
      if (!rec.ParseFromArray(protobuf_bytes.get(), protobuf_size))
        vw_throw(IOErr() << "An error occurred while parsing an IndexEntry.");

      (*it).push_back(std::pair<int32, IndexRecord>(t_id, rec));
    }
  }
}

// ----------------------- ACCESSORS  ----------------------

void vw::platefile::IndexPage::set(TileHeader const& header, IndexRecord const& record) {

  VW_ASSERT( header.col() >= 0 && header.row() >= 0,
             TileNotFoundErr() << "IndexPage::get() failed.  Column and row indices must be positive.");

  int32 page_col = header.col() % m_page_width;
  int32 page_row = header.row() % m_page_height;

  std::pair<int32, IndexRecord> p(header.transaction_id(), record);
  int elmnt = page_row*m_page_width + page_col;
  if (m_sparse_table.test(elmnt)) {

    // Add to existing entry.
    multi_value_type *entries = m_sparse_table[page_row*m_page_width + page_col].operator&();

    // We need to keep this list sorted in decreasing order of
    // transaction ID, do a simple insertion sort here.
    multi_value_type::iterator it = entries->begin();
    while (it != entries->end() && (*it).first >= int64(header.transaction_id()) ) {

      // Handle the case where we replace an entry
      if ( (*it).first == int64(header.transaction_id()) ) {
        (*it).second = record;
        return;
      }

      // Otherwise, we search forward in the list.
      ++it;
    }

    // If we reach this point, we are either at the end of the list,
    // or we have found the correct position.  Either way, we call
    // insert().
    entries->insert(it, p);

  } else {

    // Create a new entry
    multi_value_type l;
    l.push_front(p);
    m_sparse_table[elmnt] = l;

  }
}

/// Return the IndexRecord for a the given transaction_id at
/// this location.  By default this routine returns the record with
/// the greatest transaction id that is less than or equal to the
/// requested transaction_id.
///
/// Exceptions to the above behavior:
///
///   - A transaction_id == -1 will return the most recent
///   transaction id.
///
///   - Setting exact_match to true forces an exact transaction_id
///   match.
///
vw::platefile::IndexRecord vw::platefile::IndexPage::get(int col, int row,
                                                         int transaction_id,
                                                         bool exact_match) const {

  VW_ASSERT( col >= 0 && row >= 0,
             TileNotFoundErr() << "IndexPage::get() failed.  Column and row indices must be positive.");

  // Compute page_col and row
  int32 page_col = col % m_page_width;
  int32 page_row = row % m_page_height;

  // Interate over entries.
  multi_value_type const& entries = m_sparse_table[page_row*m_page_width + page_col];
  multi_value_type::const_iterator it = entries.begin();

  // A transaction ID of -1 indicates that we should return the most
  // recent tile (which is the first entry in the list, since it is
  // sorted from most recent to least recent), regardless of its
  // transaction id.
  if (transaction_id == -1 && it != entries.end())
    return (*it).second;

  // Otherwise, we search through the entries in the list, looking for
  // the requested t_id.  Note: this search is O(n), so it can be slow
  // if there are a lot of entries and the entry you are looking for
  // is near the end.  However, most pages will contain very few
  // entries, and for those with many entries (i.e. tiles near the
  // root of the mosaic), you will rarely search for old tiles.
  while (it != entries.end()) {
    if (exact_match) {
      if ((*it).first == transaction_id)
        return (*it).second;
    } else {
      if ((*it).first <= transaction_id)
        return (*it).second;
    }
    ++it;
  }

  // If we reach this point, then there are no entries before
  // the given transaction_id, so we return an empty (and invalid) record.
  vw_throw(TileNotFoundErr() << "Tiles exist at this location, "
           << "but none before transaction_id = "  << transaction_id << "\n");
  return IndexRecord(); // never reached
}

void vw::platefile::IndexPage::append_if_in_region( std::list<vw::platefile::TileHeader> &results,
                                                    multi_value_type const& candidates,
                                                    int col, int row, BBox2i const& region,
                                                    int min_num_matches) const {

  // Check to see if the tile is in the specified region.
  Vector2i loc( m_base_col + col, m_base_row + row);
  if ( region.contains( loc ) && int(candidates.size()) >= min_num_matches ) {
    TileHeader hdr;
    hdr.set_col( m_base_col + col );
    hdr.set_row( m_base_row + row );
    hdr.set_level(m_level);

    // Return the transaction ID of the first result in the list.
    hdr.set_transaction_id(candidates.begin()->first);
    results.push_back(hdr);
  }
}

/// Returns a list of valid tiles in this IndexPage.  Returns a list
/// of TileHeaders with col/row/level and transaction_id of the most
/// recent tile at each valid location.  Note: there may be other
/// tiles in the transaction range at this col/row/level, but
/// search_by_region() only returns the first one.
std::list<vw::platefile::TileHeader>
vw::platefile::IndexPage::search_by_region(vw::BBox2i const& region,
                                           int start_transaction_id,
                                           int end_transaction_id,
                                           int min_num_matches,
                                           bool fetch_one_additional_entry) const {
  std::list<TileHeader> results;

  for (int row = 0; row < m_page_height; ++row) {
    for (int col = 0; col < m_page_width; ++col) {
      if (m_sparse_table.test(row*m_page_width + col)) {

        // Iterate over entries.
        multi_value_type const& entries = m_sparse_table[row*m_page_width + col];
        multi_value_type::const_iterator it = entries.begin();
        multi_value_type candidates;

        if (start_transaction_id == -1 && end_transaction_id == -1) {

          // If the user has specified a transaction range of [-1, -1],
          // then we only return the last valid tile.
          if (entries.size() > 0)
            candidates.push_back( *(entries.begin()) );

        } else {

          // std::ostringstream ostr;
          // ostr << "Accumulating entries for tile ["
          //      << (m_base_col + col) << " " << (m_base_row + row) <<  " @ "
          //      << m_level << "] with " << entries.size() << " entries -- ";

          // Search through the entries in the list, looking entries
          // that match the requested transaction_id range.  Note: this
          // search is O(n), so it can be slow if there are a lot of
          // entries and the entry you are looking for is near the end.
          // However, most pages will contain very few entries, and for
          // those with many entries (i.e. tiles near the root of the
          // mosaic), you will most likely be searching for recently
          // added tiles, which are sorted to the beginning.
          while (it != entries.end() && it->first >= start_transaction_id) {
            if ( it->first >= start_transaction_id && it->first <= end_transaction_id ) {
              //              ostr << it->first << " ";
              candidates.push_back(*it);
            }
            ++it;
          }

          // For snapshotting, we need to fetch one additional entry
          // outside of the specified range.  This next tile
          // represents the "top" tile in the mosaic for entries that
          // may not have been part of the last snapshot.
          if (fetch_one_additional_entry && it != entries.end()) {
            //ostr << it->first << " ";
            candidates.push_back(*it);
          }

          // For debugging
          // if (candidates.size() != 0)// && m_level == 10)
          //   std::cout << ostr.str() << "   ( " << candidates.size() << " )\n";
        }

        // Do the region check.
        append_if_in_region( results, candidates, col, row, region, min_num_matches );
      }
    }
  }

  return results;
}

std::list<vw::platefile::TileHeader>
vw::platefile::IndexPage::search_by_location(int col, int row,
                                             int start_transaction_id,
                                             int end_transaction_id,
                                             bool fetch_one_additional_entry) const {

  int32 page_col = col % m_page_width;
  int32 page_row = row % m_page_height;

  // Basic bounds checking
  VW_ASSERT(page_col >= 0 && page_col < m_page_width && page_row >= 0 && page_row < m_page_height,
            TileNotFoundErr() << "IndexPage::read_headers() failed.  Invalid index ["
            << page_col << " " << page_row << "]");

  // Check first to make sure that there are actually tiles at this location.
  if (!m_sparse_table.test(page_row*m_page_width + page_col))
    vw_throw(TileNotFoundErr() << "No tiles were found at this location.\n");

  // If there are, then we apply the transaction_id filters to select the requested ones.
  std::list<vw::platefile::TileHeader> results;
  multi_value_type const& entries = m_sparse_table[page_row*m_page_width + page_col];
  multi_value_type::const_iterator it = entries.begin();
  while (it != entries.end() && it->first >= start_transaction_id) {
    if (it->first >= start_transaction_id && it->first <= end_transaction_id) {
      TileHeader hdr;
      hdr.set_col( m_base_col + page_col );
      hdr.set_row( m_base_row + page_row );
      hdr.set_level(m_level);
      hdr.set_transaction_id(it->first);
      results.push_back(hdr);
    }
    ++it;
  }

  // For snapshotting, we need to fetch one additional entry
  // outside of the specified range.  This next tile
  // represents the "top" tile in the mosaic for entries that
  // may not have been part of the last snapshot.
  if (fetch_one_additional_entry && it != entries.end()) {
    TileHeader hdr;
    hdr.set_col( m_base_col + page_col );
    hdr.set_row( m_base_row + page_row );
    hdr.set_level(m_level);
    hdr.set_transaction_id(it->first);
    results.push_back(hdr);
  }

  return results;
}
<commit_msg>Added support for transaction ids of -1 to search_by_location()<commit_after>// __BEGIN_LICENSE__
// Copyright (C) 2006-2010 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/Plate/IndexPage.h>

#include <boost/shared_array.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
namespace fs = boost::filesystem;

// ----------------------------------------------------------------------
//                            INDEX PAGE
// ----------------------------------------------------------------------

vw::platefile::IndexPage::IndexPage(int level, int base_col, int base_row,
                                    int page_width, int page_height) :
  m_level(level), m_base_col(base_col), m_base_row(base_row),
  m_page_width(page_width), m_page_height(page_height) {

  // Set the size of the sparse table.
  m_sparse_table.resize(page_width*page_height);

  vw_out(VerboseDebugMessage, "platefile::index") << "Opening index page [ "
                                                  << m_base_col << " " << m_base_row
                                                  << " @ " << m_level << " ]\n";
}

vw::platefile::IndexPage::~IndexPage() {
  vw_out(VerboseDebugMessage, "platefile::index") << "Closing index page [ "
                                                  << m_base_col << " " << m_base_row
                                                  << " @ " << m_level << " ]\n";
}

void vw::platefile::IndexPage::serialize(std::ostream& ostr) {
  // Part 1: Write out the page size
  ostr.write((char*)(&m_page_width), sizeof(m_page_width));
  ostr.write((char*)(&m_page_height), sizeof(m_page_height));

  // Part 2: Write the sparsetable metadata
  m_sparse_table.write_metadata(&ostr);

  // Part 3: Write sparse entries
  for (google::sparsetable<multi_value_type>::nonempty_iterator it = m_sparse_table.nonempty_begin();
       it != m_sparse_table.nonempty_end(); ++it) {

    // Iterate over transaction_id list.
    int32 transaction_list_size = (*it).size();
    ostr.write((char*)(&transaction_list_size), sizeof(transaction_list_size));

    multi_value_type::iterator transaction_iter = (*it).begin();
    while (transaction_iter != (*it).end()) {

      // Save the transaction id
      int32 t_id = (*transaction_iter).first;
      ostr.write((char*)(&t_id), sizeof(t_id));

      // Save the size of each protobuf, and then serialize it to disk.
      uint16 protobuf_size = (*transaction_iter).second.ByteSize();
      boost::shared_array<uint8> protobuf_bytes( new uint8[protobuf_size] );
      (*transaction_iter).second.SerializeToArray(protobuf_bytes.get(), protobuf_size);
      ostr.write((char*)(&protobuf_size), sizeof(protobuf_size));
      ostr.write((char*)(protobuf_bytes.get()), protobuf_size);

      ++transaction_iter;
    }
  }
}

void vw::platefile::IndexPage::deserialize(std::istream& istr) {
  // Part 1: Read the page size
  istr.read((char*)(&m_page_width), sizeof(m_page_width));
  istr.read((char*)(&m_page_height), sizeof(m_page_height));

  // Part 2: Read the sparsetable metadata
  m_sparse_table.read_metadata(&istr);

  // Part 3: Read sparse entries
  for (google::sparsetable<multi_value_type>::nonempty_iterator it = m_sparse_table.nonempty_begin();
       it != m_sparse_table.nonempty_end(); ++it) {

    // Iterate over transaction_id list.
    int32 transaction_list_size;
    istr.read((char*)(&transaction_list_size), sizeof(transaction_list_size));

    new (&(*it)) multi_value_type();
    for (int tid = 0; tid < transaction_list_size; ++tid) {

      // Read the transaction id
      int32 t_id;
      istr.read((char*)(&t_id), sizeof(t_id));

      // Read the size (in bytes) of this protobuffer and then read
      // the protobuffer and deserialize it.
      uint16 protobuf_size;
      istr.read((char*)(&protobuf_size), sizeof(protobuf_size));
      boost::shared_array<uint8> protobuf_bytes( new uint8[protobuf_size] );
      istr.read((char*)(protobuf_bytes.get()), protobuf_size);
      IndexRecord rec;
      if (!rec.ParseFromArray(protobuf_bytes.get(), protobuf_size))
        vw_throw(IOErr() << "An error occurred while parsing an IndexEntry.");

      (*it).push_back(std::pair<int32, IndexRecord>(t_id, rec));
    }
  }
}

// ----------------------- ACCESSORS  ----------------------

void vw::platefile::IndexPage::set(TileHeader const& header, IndexRecord const& record) {

  VW_ASSERT( header.col() >= 0 && header.row() >= 0,
             TileNotFoundErr() << "IndexPage::get() failed.  Column and row indices must be positive.");

  int32 page_col = header.col() % m_page_width;
  int32 page_row = header.row() % m_page_height;

  std::pair<int32, IndexRecord> p(header.transaction_id(), record);
  int elmnt = page_row*m_page_width + page_col;
  if (m_sparse_table.test(elmnt)) {

    // Add to existing entry.
    multi_value_type *entries = m_sparse_table[page_row*m_page_width + page_col].operator&();

    // We need to keep this list sorted in decreasing order of
    // transaction ID, do a simple insertion sort here.
    multi_value_type::iterator it = entries->begin();
    while (it != entries->end() && (*it).first >= int64(header.transaction_id()) ) {

      // Handle the case where we replace an entry
      if ( (*it).first == int64(header.transaction_id()) ) {
        (*it).second = record;
        return;
      }

      // Otherwise, we search forward in the list.
      ++it;
    }

    // If we reach this point, we are either at the end of the list,
    // or we have found the correct position.  Either way, we call
    // insert().
    entries->insert(it, p);

  } else {

    // Create a new entry
    multi_value_type l;
    l.push_front(p);
    m_sparse_table[elmnt] = l;

  }
}

/// Return the IndexRecord for a the given transaction_id at
/// this location.  By default this routine returns the record with
/// the greatest transaction id that is less than or equal to the
/// requested transaction_id.
///
/// Exceptions to the above behavior:
///
///   - A transaction_id == -1 will return the most recent
///   transaction id.
///
///   - Setting exact_match to true forces an exact transaction_id
///   match.
///
vw::platefile::IndexRecord vw::platefile::IndexPage::get(int col, int row,
                                                         int transaction_id,
                                                         bool exact_match) const {

  VW_ASSERT( col >= 0 && row >= 0,
             TileNotFoundErr() << "IndexPage::get() failed.  Column and row indices must be positive.");

  // Compute page_col and row
  int32 page_col = col % m_page_width;
  int32 page_row = row % m_page_height;

  // Interate over entries.
  multi_value_type const& entries = m_sparse_table[page_row*m_page_width + page_col];
  multi_value_type::const_iterator it = entries.begin();

  // A transaction ID of -1 indicates that we should return the most
  // recent tile (which is the first entry in the list, since it is
  // sorted from most recent to least recent), regardless of its
  // transaction id.
  if (transaction_id == -1 && it != entries.end())
    return (*it).second;

  // Otherwise, we search through the entries in the list, looking for
  // the requested t_id.  Note: this search is O(n), so it can be slow
  // if there are a lot of entries and the entry you are looking for
  // is near the end.  However, most pages will contain very few
  // entries, and for those with many entries (i.e. tiles near the
  // root of the mosaic), you will rarely search for old tiles.
  while (it != entries.end()) {
    if (exact_match) {
      if ((*it).first == transaction_id)
        return (*it).second;
    } else {
      if ((*it).first <= transaction_id)
        return (*it).second;
    }
    ++it;
  }

  // If we reach this point, then there are no entries before
  // the given transaction_id, so we return an empty (and invalid) record.
  vw_throw(TileNotFoundErr() << "Tiles exist at this location, "
           << "but none before transaction_id = "  << transaction_id << "\n");
  return IndexRecord(); // never reached
}

void vw::platefile::IndexPage::append_if_in_region( std::list<vw::platefile::TileHeader> &results,
                                                    multi_value_type const& candidates,
                                                    int col, int row, BBox2i const& region,
                                                    int min_num_matches) const {

  // Check to see if the tile is in the specified region.
  Vector2i loc( m_base_col + col, m_base_row + row);
  if ( region.contains( loc ) && int(candidates.size()) >= min_num_matches ) {
    TileHeader hdr;
    hdr.set_col( m_base_col + col );
    hdr.set_row( m_base_row + row );
    hdr.set_level(m_level);

    // Return the transaction ID of the first result in the list.
    hdr.set_transaction_id(candidates.begin()->first);
    results.push_back(hdr);
  }
}

/// Returns a list of valid tiles in this IndexPage.  Returns a list
/// of TileHeaders with col/row/level and transaction_id of the most
/// recent tile at each valid location.  Note: there may be other
/// tiles in the transaction range at this col/row/level, but
/// search_by_region() only returns the first one.
std::list<vw::platefile::TileHeader>
vw::platefile::IndexPage::search_by_region(vw::BBox2i const& region,
                                           int start_transaction_id,
                                           int end_transaction_id,
                                           int min_num_matches,
                                           bool fetch_one_additional_entry) const {
  std::list<TileHeader> results;

  for (int row = 0; row < m_page_height; ++row) {
    for (int col = 0; col < m_page_width; ++col) {
      if (m_sparse_table.test(row*m_page_width + col)) {

        // Iterate over entries.
        multi_value_type const& entries = m_sparse_table[row*m_page_width + col];
        multi_value_type::const_iterator it = entries.begin();
        multi_value_type candidates;

        if (start_transaction_id == -1 && end_transaction_id == -1) {

          // If the user has specified a transaction range of [-1, -1],
          // then we only return the last valid tile.
          if (entries.size() > 0)
            candidates.push_back( *(entries.begin()) );

        } else {

          // std::ostringstream ostr;
          // ostr << "Accumulating entries for tile ["
          //      << (m_base_col + col) << " " << (m_base_row + row) <<  " @ "
          //      << m_level << "] with " << entries.size() << " entries -- ";

          // Search through the entries in the list, looking entries
          // that match the requested transaction_id range.  Note: this
          // search is O(n), so it can be slow if there are a lot of
          // entries and the entry you are looking for is near the end.
          // However, most pages will contain very few entries, and for
          // those with many entries (i.e. tiles near the root of the
          // mosaic), you will most likely be searching for recently
          // added tiles, which are sorted to the beginning.
          while (it != entries.end() && it->first >= start_transaction_id) {
            if ( it->first >= start_transaction_id && it->first <= end_transaction_id ) {
              //              ostr << it->first << " ";
              candidates.push_back(*it);
            }
            ++it;
          }

          // For snapshotting, we need to fetch one additional entry
          // outside of the specified range.  This next tile
          // represents the "top" tile in the mosaic for entries that
          // may not have been part of the last snapshot.
          if (fetch_one_additional_entry && it != entries.end()) {
            //ostr << it->first << " ";
            candidates.push_back(*it);
          }

          // For debugging
          // if (candidates.size() != 0)// && m_level == 10)
          //   std::cout << ostr.str() << "   ( " << candidates.size() << " )\n";
        }

        // Do the region check.
        append_if_in_region( results, candidates, col, row, region, min_num_matches );
      }
    }
  }

  return results;
}

std::list<vw::platefile::TileHeader>
vw::platefile::IndexPage::search_by_location(int col, int row,
                                             int start_transaction_id,
                                             int end_transaction_id,
                                             bool fetch_one_additional_entry) const {

  int32 page_col = col % m_page_width;
  int32 page_row = row % m_page_height;

  // Basic bounds checking
  VW_ASSERT(page_col >= 0 && page_col < m_page_width && page_row >= 0 && page_row < m_page_height,
            TileNotFoundErr() << "IndexPage::read_headers() failed.  Invalid index ["
            << page_col << " " << page_row << "]");

  // Check first to make sure that there are actually tiles at this location.
  if (!m_sparse_table.test(page_row*m_page_width + page_col))
    vw_throw(TileNotFoundErr() << "No tiles were found at this location.\n");

  // If there are, then we apply the transaction_id filters to select the requested ones.
  std::list<vw::platefile::TileHeader> results;
  multi_value_type const& entries = m_sparse_table[page_row*m_page_width + page_col];
  multi_value_type::const_iterator it = entries.begin();

  if (start_transaction_id == -1 && end_transaction_id == -1) {

    if (it != entries.end() ) {

      // If the user has specified a transaction range of [-1, -1],
      // then we only return the last valid tile.
      TileHeader hdr;
      hdr.set_col( m_base_col + page_col );
      hdr.set_row( m_base_row + page_row );
      hdr.set_level(m_level);
      hdr.set_transaction_id(it->first);
      results.push_back(hdr);
    } else {
      vw_throw(TileNotFoundErr() << "No tiles were found at this location.\n");
    }

  } else {

    while (it != entries.end() && it->first >= start_transaction_id) {
      if (it->first >= start_transaction_id && it->first <= end_transaction_id) {
        TileHeader hdr;
        hdr.set_col( m_base_col + page_col );
        hdr.set_row( m_base_row + page_row );
        hdr.set_level(m_level);
        hdr.set_transaction_id(it->first);
        results.push_back(hdr);
      }
      ++it;
    }

    // For snapshotting, we need to fetch one additional entry
    // outside of the specified range.  This next tile
    // represents the "top" tile in the mosaic for entries that
    // may not have been part of the last snapshot.
    if (fetch_one_additional_entry && it != entries.end()) {
      TileHeader hdr;
      hdr.set_col( m_base_col + page_col );
      hdr.set_row( m_base_row + page_row );
      hdr.set_level(m_level);
      hdr.set_transaction_id(it->first);
      results.push_back(hdr);
    }

  }

  return results;
}
<|endoftext|>