<commit_before>/// HEADER
#include "import_ros.h"

/// COMPONENT
#include <csapex_ros/ros_handler.h>
#include <csapex_ros/ros_message_conversion.h>

/// PROJECT
#include <csapex/msg/io.h>
#include <csapex/factory/message_factory.h>
#include <csapex/utility/stream_interceptor.h>
#include <csapex/msg/message.h>
#include <csapex/param/parameter_factory.h>
#include <csapex/param/set_parameter.h>
#include <csapex/model/node_modifier.h>
#include <csapex/utility/register_apex_plugin.h>
#include <csapex_core_plugins/timestamp_message.h>
#include <csapex/utility/timer.h>
#include <csapex/msg/any_message.h>
#include <csapex/utility/interlude.hpp>

/// SYSTEM
#include <yaml-cpp/eventhandler.h>

CSAPEX_REGISTER_CLASS(csapex::ImportRos, csapex::Node)

using namespace csapex;

const std::string ImportRos::no_topic_("-----");

namespace {
ros::Time rosTime(u_int64_t stamp_micro_seconds) {
    ros::Time t;
    t.fromNSec(stamp_micro_seconds * 1e3);
    return t;
}
}

ImportRos::ImportRos()
    : connector_(nullptr), retries_(0), buffer_size_(1024), running_(true)
{
}

void ImportRos::setup(NodeModifier& node_modifier)
{
    input_time_ = node_modifier.addOptionalInput<connection_types::TimestampMessage>("time");
    connector_ = node_modifier.addOutput<connection_types::AnyMessage>("Something");
}

void ImportRos::setupParameters(Parameterizable& parameters)
{
    std::vector<std::string> set;
    set.push_back(no_topic_);
    parameters.addParameter(csapex::param::ParameterFactory::declareParameterStringSet("topic", set),
                            std::bind(&ImportRos::update, this));

    parameters.addParameter(csapex::param::ParameterFactory::declareTrigger("refresh"),
                            std::bind(&ImportRos::refresh, this));

    parameters.addParameter(csapex::param::ParameterFactory::declareRange("rate", 0.1, 100.0, 60.0, 0.1),
                            std::bind(&ImportRos::updateRate, this));
    parameters.addParameter(csapex::param::ParameterFactory::declareRange("queue", 0, 30, 1, 1),
                            std::bind(&ImportRos::updateSubscriber, this));
    parameters.addParameter(csapex::param::ParameterFactory::declareBool("latch", false));

    std::function<bool()> connected_condition = [&]() { return msg::isConnected(input_time_); };
    csapex::param::Parameter::Ptr buffer_p = csapex::param::ParameterFactory::declareRange("buffer/length", 0.0, 10.0, 1.0, 0.1);
    parameters.addConditionalParameter(buffer_p, connected_condition);
    csapex::param::Parameter::Ptr max_wait_p = csapex::param::ParameterFactory::declareRange("buffer/max_wait", 0.0, 10.0, 1.0, 0.1);
    parameters.addConditionalParameter(max_wait_p, connected_condition);
}

void ImportRos::setupROS()
{
    refresh();
}

void ImportRos::refresh()
{
    ROSHandler& rh = getRosHandler();

    if(rh.nh()) {
        std::string old_topic = readParameter<std::string>("topic");

        ros::master::V_TopicInfo topics;
        ros::master::getTopics(topics);

        param::SetParameter::Ptr setp = std::dynamic_pointer_cast<param::SetParameter>(getParameter("topic"));
        if(setp) {
            node_modifier_->setNoError();
            bool found = false;
            std::vector<std::string> topics_str;
            topics_str.push_back(no_topic_);
            for(ros::master::V_TopicInfo::const_iterator it = topics.begin(); it != topics.end(); ++it) {
                topics_str.push_back(it->name);
                if(it->name == old_topic) {
                    found = true;
                }
            }
            if(!found) {
                topics_str.push_back(old_topic);
            }
            setp->setSet(topics_str);

            if(old_topic != no_topic_) {
                setp->set(old_topic);
            }
            return;
        }
    }

    node_modifier_->setWarning("no ROS connection");
}

void ImportRos::update()
{
    retries_ = 5;
    if(node_modifier_->isProcessingEnabled()) {
        waitForTopic();
    }
}

void ImportRos::updateRate()
{
    setTickFrequency(readParameter<double>("rate"));
}

void ImportRos::updateSubscriber()
{
    if(!current_topic_.name.empty()) {
        current_subscriber = RosMessageConversion::instance().subscribe(current_topic_, readParameter<int>("queue"), std::bind(&ImportRos::callback, this, std::placeholders::_1));
    }
}

bool ImportRos::doSetTopic()
{
    getRosHandler().refresh();

    if(!getRosHandler().isConnected()) {
        node_modifier_->setError("no connection to ROS");
        return false;
    }

    ros::master::V_TopicInfo topics;
    ros::master::getTopics(topics);

    std::string topic = readParameter<std::string>("topic");

    if(topic == no_topic_) {
        return true;
    }

    for(ros::master::V_TopicInfo::iterator it = topics.begin(); it != topics.end(); ++it) {
        if(it->name == topic) {
            setTopic(*it);
            retries_ = 0;
            return true;
        }
    }

    std::stringstream ss;
    ss << "cannot set topic, " << topic << " doesn't exist";
    if(retries_ > 0) {
        ss << ", " << retries_ << " retries left";
    }
    ss << ".";

    node_modifier_->setWarning(ss.str());
    return false;
}

void ImportRos::processROS()
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);
    INTERLUDE("process");

    // first check if connected -> if not connected, we only use tick
    if(!msg::isConnected(input_time_)) {
        return;
    }

    // now that we are connected, check that we have a valid message
    if(!msg::hasMessage(input_time_)) {
        return;
    }

    // INPUT CONNECTED
    connection_types::TimestampMessage::ConstPtr time = msg::getMessage<connection_types::TimestampMessage>(input_time_);

    if(msgs_.empty()) {
        return;
    }

    ros::Time time_value;
    auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(time->value.time_since_epoch());
    time_value = time_value.fromNSec(nano.count());

    if(time_value == ros::Time(0)) {
        node_modifier_->setWarning("incoming time is 0, using default behaviour");
        publishLatestMessage();
        return;
    }

    if(msgs_.back()->stamp_micro_seconds == 0) {
        ainfo << msgs_.back()->stamp_micro_seconds << std::endl;
        node_modifier_->setWarning("buffered time is 0, using default behaviour");
        publishLatestMessage();
        return;
    }

    // drop old messages
    ros::Duration keep_duration(readParameter<double>("buffer/length"));
    while(!msgs_.empty() && rosTime(msgs_.front()->stamp_micro_seconds) + keep_duration < time_value) {
        msgs_.pop_front();
    }

    ros::Duration max_wait_duration(readParameter<double>("buffer/max_wait"));

    if(!msgs_.empty()) {
        if(rosTime(msgs_.front()->stamp_micro_seconds) > time_value) {
            aerr << "time stamp " << time_value << " is too old, oldest buffered is " << rosTime(msgs_.front()->stamp_micro_seconds) << std::endl;
            return;
        }

        if(rosTime(msgs_.front()->stamp_micro_seconds) + max_wait_duration < time_value) {
            // aerr << "[1] time stamp " << time_value << " is too new" << std::endl;
            // return;
        }
    }

    // wait until we have a message
    ros::Time start = ros::Time::now();
    if(!isStampCovered(time_value)) {
        ros::Rate r(10);
        while(!isStampCovered(time_value) && running_) {
            r.sleep();
            ros::spinOnce();

            if(start + max_wait_duration < ros::Time::now()) {
                break;
            }
        }
    }

    if(msgs_.empty()) {
        node_modifier_->setWarning("No messages received");
        return;
    }

    std::deque<connection_types::Message::ConstPtr>::iterator first_after = msgs_.begin();
    while(rosTime((*first_after)->stamp_micro_seconds) < time_value) {
        ++first_after;
    }

    if(first_after == msgs_.begin()) {
        msg::publish(connector_, *first_after);
        return;

    } else if(first_after == msgs_.end()) {
        assert(false);
        node_modifier_->setWarning("Should not happen.....");
        return;

    } else {
        std::deque<connection_types::Message::ConstPtr>::iterator last_before = first_after - 1;

        ros::Duration diff1 = rosTime((*first_after)->stamp_micro_seconds) - time_value;
        ros::Duration diff2 = rosTime((*last_before)->stamp_micro_seconds) - time_value;

        if(diff1 < diff2) {
            msg::publish(connector_, *first_after);
        } else {
            msg::publish(connector_, *last_before);
        }
    }
}

bool ImportRos::isStampCovered(const ros::Time &stamp)
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);
    if(msgs_.empty()) {
        return false;
    }
    return rosTime(msgs_.back()->stamp_micro_seconds) >= stamp;
}

void ImportRos::waitForTopic()
{
    INTERLUDE("wait");
    ros::WallDuration poll_wait(0.5);
    while(retries_ --> 0) {
        bool topic_exists = doSetTopic();

        if(topic_exists) {
            return;
        } else {
            ROS_WARN_STREAM("waiting for topic " << readParameter<std::string>("topic"));
            poll_wait.sleep();
        }
    }
}

bool ImportRos::canTick()
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);
    return !msg::isConnected(input_time_) && !msgs_.empty();
}

void ImportRos::tickROS()
{
    INTERLUDE("tick");

    if(retries_ > 0) {
        waitForTopic();
    }

    if(msg::isConnected(input_time_)) {
        return;
    }

    // NO INPUT CONNECTED -> ONLY KEEP CURRENT MESSAGE
    {
        INTERLUDE("pop");
        while(msgs_.size() > 1) {
            msgs_.pop_front();
        }
    }

    if(!current_topic_.name.empty()) {
        publishLatestMessage();
    }
}

void ImportRos::publishLatestMessage()
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);

    INTERLUDE("publish");

    if(msgs_.empty()) {
        INTERLUDE("wait for message");
        ros::WallRate r(10);
        while(msgs_.empty() && running_) {
            r.sleep();
            ros::spinOnce();
        }

        if(!running_) {
            return;
        }
    }

    msg::publish(connector_, msgs_.back());

    if(!readParameter<bool>("latch")) {
        msgs_.clear();
    }
}

// FIXME: this can be still called after the object is destroyed!
void ImportRos::callback(ConnectionTypeConstPtr message)
{
    connection_types::Message::ConstPtr msg = std::dynamic_pointer_cast<connection_types::Message const>(message);
    if(msg) {
        std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);

        if(!msgs_.empty() && msg->stamp_micro_seconds < msgs_.front()->stamp_micro_seconds) {
            awarn << "detected time anomaly -> reset";
            msgs_.clear();
        }

        if(!msg::isConnected(input_time_)) {
            msgs_.clear();
        }

        msgs_.push_back(msg);

        while((int) msgs_.size() > buffer_size_) {
            msgs_.pop_front();
        }
    }
}

void ImportRos::setTopic(const ros::master::TopicInfo &topic)
{
    if(topic.name == current_topic_.name) {
        return;
    }

    current_subscriber.shutdown();

    //if(RosMessageConversion::instance().isTopicTypeRegistered(topic)) {
    node_modifier_->setNoError();

    current_topic_ = topic;
    updateSubscriber();

    //    } else {
    //        node_modifier_->setError(std::string("cannot import topic of type ") + topic.datatype);
    //        return;
    //    }

}

void ImportRos::setParameterState(Memento::Ptr memento)
{
    Node::setParameterState(memento);
}

void ImportRos::reset()
{
    running_ = false;
}
<commit_msg>bugfix<commit_after>/// HEADER
#include "import_ros.h"

/// COMPONENT
#include <csapex_ros/ros_handler.h>
#include <csapex_ros/ros_message_conversion.h>

/// PROJECT
#include <csapex/msg/io.h>
#include <csapex/factory/message_factory.h>
#include <csapex/utility/stream_interceptor.h>
#include <csapex/msg/message.h>
#include <csapex/param/parameter_factory.h>
#include <csapex/param/set_parameter.h>
#include <csapex/model/node_modifier.h>
#include <csapex/utility/register_apex_plugin.h>
#include <csapex_core_plugins/timestamp_message.h>
#include <csapex/utility/timer.h>
#include <csapex/msg/any_message.h>
#include <csapex/utility/interlude.hpp>

/// SYSTEM
#include <yaml-cpp/eventhandler.h>

CSAPEX_REGISTER_CLASS(csapex::ImportRos, csapex::Node)

using namespace csapex;

const std::string ImportRos::no_topic_("-----");

namespace {
ros::Time rosTime(u_int64_t stamp_micro_seconds) {
    ros::Time t;
    t.fromNSec(stamp_micro_seconds * 1e3);
    return t;
}
}

ImportRos::ImportRos()
    : connector_(nullptr), retries_(0), buffer_size_(1024), running_(true)
{
}

void ImportRos::setup(NodeModifier& node_modifier)
{
    input_time_ = node_modifier.addOptionalInput<connection_types::TimestampMessage>("time");
    connector_ = node_modifier.addOutput<connection_types::AnyMessage>("Something");
}

void ImportRos::setupParameters(Parameterizable& parameters)
{
    std::vector<std::string> set;
    set.push_back(no_topic_);
    parameters.addParameter(csapex::param::ParameterFactory::declareParameterStringSet("topic", set),
                            std::bind(&ImportRos::update, this));

    parameters.addParameter(csapex::param::ParameterFactory::declareTrigger("refresh"),
                            std::bind(&ImportRos::refresh, this));

    parameters.addParameter(csapex::param::ParameterFactory::declareRange("rate", 0.1, 100.0, 60.0, 0.1),
                            std::bind(&ImportRos::updateRate, this));
    parameters.addParameter(csapex::param::ParameterFactory::declareRange("queue", 0, 30, 1, 1),
                            std::bind(&ImportRos::updateSubscriber, this));
    parameters.addParameter(csapex::param::ParameterFactory::declareBool("latch", false));

    std::function<bool()> connected_condition = [&]() { return msg::isConnected(input_time_); };
    csapex::param::Parameter::Ptr buffer_p = csapex::param::ParameterFactory::declareRange("buffer/length", 0.0, 10.0, 1.0, 0.1);
    parameters.addConditionalParameter(buffer_p, connected_condition);
    csapex::param::Parameter::Ptr max_wait_p = csapex::param::ParameterFactory::declareRange("buffer/max_wait", 0.0, 10.0, 1.0, 0.1);
    parameters.addConditionalParameter(max_wait_p, connected_condition);
}

void ImportRos::setupROS()
{
    refresh();
}

void ImportRos::refresh()
{
    ROSHandler& rh = getRosHandler();

    if(rh.nh()) {
        std::string old_topic = readParameter<std::string>("topic");

        ros::master::V_TopicInfo topics;
        ros::master::getTopics(topics);

        param::SetParameter::Ptr setp = std::dynamic_pointer_cast<param::SetParameter>(getParameter("topic"));
        if(setp) {
            node_modifier_->setNoError();
            bool found = false;
            std::vector<std::string> topics_str;
            topics_str.push_back(no_topic_);
            for(ros::master::V_TopicInfo::const_iterator it = topics.begin(); it != topics.end(); ++it) {
                topics_str.push_back(it->name);
                if(it->name == old_topic) {
                    found = true;
                }
            }
            if(!found) {
                topics_str.push_back(old_topic);
            }
            setp->setSet(topics_str);

            if(old_topic != no_topic_) {
                setp->set(old_topic);
            }
            return;
        }
    }

    node_modifier_->setWarning("no ROS connection");
}

void ImportRos::update()
{
    retries_ = 5;
    if(node_modifier_->isProcessingEnabled()) {
        waitForTopic();
    }
}

void ImportRos::updateRate()
{
    setTickFrequency(readParameter<double>("rate"));
}

void ImportRos::updateSubscriber()
{
    if(!current_topic_.name.empty()) {
        current_subscriber = RosMessageConversion::instance().subscribe(current_topic_, readParameter<int>("queue"), std::bind(&ImportRos::callback, this, std::placeholders::_1));
    }
}

bool ImportRos::doSetTopic()
{
    getRosHandler().refresh();

    if(!getRosHandler().isConnected()) {
        node_modifier_->setError("no connection to ROS");
        return false;
    }

    ros::master::V_TopicInfo topics;
    ros::master::getTopics(topics);

    std::string topic = readParameter<std::string>("topic");

    if(topic == no_topic_) {
        return true;
    }

    for(ros::master::V_TopicInfo::iterator it = topics.begin(); it != topics.end(); ++it) {
        if(it->name == topic) {
            setTopic(*it);
            retries_ = 0;
            return true;
        }
    }

    std::stringstream ss;
    ss << "cannot set topic, " << topic << " doesn't exist";
    if(retries_ > 0) {
        ss << ", " << retries_ << " retries left";
    }
    ss << ".";

    node_modifier_->setWarning(ss.str());
    return false;
}

void ImportRos::processROS()
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);
    INTERLUDE("process");

    // first check if connected -> if not connected, we only use tick
    if(!msg::isConnected(input_time_)) {
        return;
    }

    // now that we are connected, check that we have a valid message
    if(!msg::hasMessage(input_time_)) {
        return;
    }

    // INPUT CONNECTED
    connection_types::TimestampMessage::ConstPtr time = msg::getMessage<connection_types::TimestampMessage>(input_time_);

    if(msgs_.empty()) {
        return;
    }

    ros::Time time_value;
    auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(time->value.time_since_epoch());
    time_value = time_value.fromNSec(nano.count());

    if(time_value == ros::Time(0)) {
        node_modifier_->setWarning("incoming time is 0, using default behaviour");
        publishLatestMessage();
        return;
    }

    if(msgs_.back()->stamp_micro_seconds == 0) {
        ainfo << msgs_.back()->stamp_micro_seconds << std::endl;
        node_modifier_->setWarning("buffered time is 0, using default behaviour");
        publishLatestMessage();
        return;
    }

    // drop old messages
    ros::Duration keep_duration(readParameter<double>("buffer/length"));
    while(!msgs_.empty() && rosTime(msgs_.front()->stamp_micro_seconds) + keep_duration < time_value) {
        msgs_.pop_front();
    }

    ros::Duration max_wait_duration(readParameter<double>("buffer/max_wait"));

    if(!msgs_.empty()) {
        if(rosTime(msgs_.front()->stamp_micro_seconds) > time_value) {
            aerr << "time stamp " << time_value << " is too old, oldest buffered is " << rosTime(msgs_.front()->stamp_micro_seconds) << std::endl;
            return;
        }

        if(rosTime(msgs_.front()->stamp_micro_seconds) + max_wait_duration < time_value) {
            // aerr << "[1] time stamp " << time_value << " is too new" << std::endl;
            // return;
        }
    }

    // wait until we have a message
    ros::Time start = ros::Time::now();
    if(!isStampCovered(time_value)) {
        ros::Rate r(10);
        while(!isStampCovered(time_value) && running_) {
            r.sleep();
            ros::spinOnce();

            if(start + max_wait_duration < ros::Time::now()) {
                break;
            }
        }
    }

    if(msgs_.empty()) {
        node_modifier_->setWarning("No messages received");
        return;
    }

    std::deque<connection_types::Message::ConstPtr>::iterator first_after = msgs_.begin();
    while(rosTime((*first_after)->stamp_micro_seconds) < time_value) {
        ++first_after;
        if(first_after == msgs_.end()) {
            break;
        }
    }

    if(first_after == msgs_.begin()) {
        msg::publish(connector_, *first_after);
        return;

    } else if(first_after == msgs_.end()) {
        msg::publish(connector_, msgs_.back());
        return;

    } else {
        std::deque<connection_types::Message::ConstPtr>::iterator last_before = first_after - 1;

        ros::Duration diff1 = rosTime((*first_after)->stamp_micro_seconds) - time_value;
        ros::Duration diff2 = rosTime((*last_before)->stamp_micro_seconds) - time_value;

        if(diff1 < diff2) {
            msg::publish(connector_, *first_after);
        } else {
            msg::publish(connector_, *last_before);
        }
    }
}

bool ImportRos::isStampCovered(const ros::Time &stamp)
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);
    if(msgs_.empty()) {
        return false;
    }
    return rosTime(msgs_.back()->stamp_micro_seconds) >= stamp;
}

void ImportRos::waitForTopic()
{
    INTERLUDE("wait");
    ros::WallDuration poll_wait(0.5);
    while(retries_ --> 0) {
        bool topic_exists = doSetTopic();

        if(topic_exists) {
            return;
        } else {
            ROS_WARN_STREAM("waiting for topic " << readParameter<std::string>("topic"));
            poll_wait.sleep();
        }
    }
}

bool ImportRos::canTick()
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);
    return !msg::isConnected(input_time_) && !msgs_.empty();
}

void ImportRos::tickROS()
{
    INTERLUDE("tick");

    if(retries_ > 0) {
        waitForTopic();
    }

    if(msg::isConnected(input_time_)) {
        return;
    }

    // NO INPUT CONNECTED -> ONLY KEEP CURRENT MESSAGE
    {
        INTERLUDE("pop");
        while(msgs_.size() > 1) {
            msgs_.pop_front();
        }
    }

    if(!current_topic_.name.empty()) {
        publishLatestMessage();
    }
}

void ImportRos::publishLatestMessage()
{
    std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);

    INTERLUDE("publish");

    if(msgs_.empty()) {
        INTERLUDE("wait for message");
        ros::WallRate r(10);
        while(msgs_.empty() && running_) {
            r.sleep();
            ros::spinOnce();
        }

        if(!running_) {
            return;
        }
    }

    msg::publish(connector_, msgs_.back());

    if(!readParameter<bool>("latch")) {
        msgs_.clear();
    }
}

// FIXME: this can be still called after the object is destroyed!
void ImportRos::callback(ConnectionTypeConstPtr message)
{
    connection_types::Message::ConstPtr msg = std::dynamic_pointer_cast<connection_types::Message const>(message);
    if(msg) {
        std::unique_lock<std::recursive_mutex> lock(msgs_mtx_);

        if(!msgs_.empty() && msg->stamp_micro_seconds < msgs_.front()->stamp_micro_seconds) {
            awarn << "detected time anomaly -> reset";
            msgs_.clear();
        }

        if(!msg::isConnected(input_time_)) {
            msgs_.clear();
        }

        msgs_.push_back(msg);

        while((int) msgs_.size() > buffer_size_) {
            msgs_.pop_front();
        }
    }
}

void ImportRos::setTopic(const ros::master::TopicInfo &topic)
{
    if(topic.name == current_topic_.name) {
        return;
    }

    current_subscriber.shutdown();

    //if(RosMessageConversion::instance().isTopicTypeRegistered(topic)) {
    node_modifier_->setNoError();

    current_topic_ = topic;
    updateSubscriber();

    //    } else {
    //        node_modifier_->setError(std::string("cannot import topic of type ") + topic.datatype);
    //        return;
    //    }

}

void ImportRos::setParameterState(Memento::Ptr memento)
{
    Node::setParameterState(memento);
}

void ImportRos::reset()
{
    running_ = false;
}
<|endoftext|>


<commit_before>
#include "StaticFileServing.h"

#include <QStringBuilder>
#include <QUrlQuery>
#include <QList>
#include <QPair>
#include <QFile>
#include <QFileInfo>
#include <QResource>
#include <QHostInfo>
#include <bonjour/bonjourserviceregister.h>
#include <bonjour/bonjourrecord.h>

StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent)
	:  QObject   (parent)
	, _hyperion(hyperion)
	, _baseUrl (baseUrl)
	, _cgi(hyperion, baseUrl, this)
	, _log(Logger::getInstance("WEBSERVER"))
{
	Q_INIT_RESOURCE(WebConfig);

	_mimeDb = new QMimeDatabase;

	_server = new QtHttpServer (this);
	_server->setServerName (QStringLiteral ("Hyperion WebConfig"));

	connect (_server, &QtHttpServer::started,           this, &StaticFileServing::onServerStarted);
	connect (_server, &QtHttpServer::stopped,           this, &StaticFileServing::onServerStopped);
	connect (_server, &QtHttpServer::error,             this, &StaticFileServing::onServerError);
	connect (_server, &QtHttpServer::requestNeedsReply, this, &StaticFileServing::onRequestNeedsReply);

	_server->start (port);

}

StaticFileServing::~StaticFileServing ()
{
	_server->stop ();
}

void StaticFileServing::onServerStarted (quint16 port)
{
	Info(_log, "started on port %d name \"%s\"", port ,_server->getServerName().toStdString().c_str());

        const std::string mDNSDescr = ( _server->getServerName().toStdString()
                                        + "@" +
                                        QHostInfo::localHostName().toStdString()
                                        );

	BonjourServiceRegister *bonjourRegister_http = new BonjourServiceRegister();
	bonjourRegister_http->registerService(
		BonjourRecord(mDNSDescr.c_str(), "_http._tcp", QString()),
		port
		);
	Debug(_log, "Web Config mDNS responder started");
}

void StaticFileServing::onServerStopped () {
	Info(_log, "stopped %s", _server->getServerName().toStdString().c_str());
}

void StaticFileServing::onServerError (QString msg)
{
	Error(_log, "%s", msg.toStdString().c_str());
}

static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage)
{
	reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
	reply->appendRawData (errorMessage.toLocal8Bit ());
}

void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply)
{
	QString command = request->getCommand ();
	if (command == QStringLiteral ("GET") || command == QStringLiteral ("POST"))
	{
		QString path = request->getUrl ().path ();
		QStringList uri_parts = path.split('/', QString::SkipEmptyParts);

		// special uri handling for server commands
		if ( ! uri_parts.empty() && uri_parts.at(0) == "cgi"  )
		{
			uri_parts.removeAt(0);
			try
			{
				if (command == QStringLiteral ("POST"))
				{
					QString postData = request->getRawData();
					uri_parts.append(postData.split('&', QString::SkipEmptyParts));
				}
				_cgi.exec(uri_parts, request, reply);
			}
			catch(...)
			{
				printErrorToReply (reply, "script failed (" % path % ")");
			}
			return;
		}
		Q_INIT_RESOURCE(WebConfig);

		QFileInfo info(_baseUrl % "/" % path);
		if ( path == "/" || path.isEmpty() || ! info.exists() )
		{
			path = "index.html";
		}
		else if (info.isDir() && path.endsWith("/") )
		{
			path += "index.html";
		}
		else if (info.isDir() && ! path.endsWith("/") )
		{
			path += "/index.html";
		}

		// get static files
		QFile file(_baseUrl % "/" % path);
		if (file.exists())
		{
			QMimeType mime = _mimeDb->mimeTypeForFile (file.fileName ());
			if (file.open (QFile::ReadOnly)) {
				QByteArray data = file.readAll ();
				reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
				reply->appendRawData (data);
				file.close ();
			}
			else
			{
				printErrorToReply (reply, "Requested file " % path % " couldn't be open for reading !");
			}
		}
		else
		{
			printErrorToReply (reply, "Requested file " % path % " couldn't be found !");
		}
	}
	else
	{
		printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on web server !");
	}
}

<commit_msg>implement 404 for webserver - this is a quick hack, should be refactored later (#378)<commit_after>
#include "StaticFileServing.h"

#include <QStringBuilder>
#include <QUrlQuery>
#include <QList>
#include <QPair>
#include <QFile>
#include <QFileInfo>
#include <QResource>
#include <QHostInfo>
#include <bonjour/bonjourserviceregister.h>
#include <bonjour/bonjourrecord.h>

StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent)
	:  QObject   (parent)
	, _hyperion(hyperion)
	, _baseUrl (baseUrl)
	, _cgi(hyperion, baseUrl, this)
	, _log(Logger::getInstance("WEBSERVER"))
{
	Q_INIT_RESOURCE(WebConfig);

	_mimeDb = new QMimeDatabase;

	_server = new QtHttpServer (this);
	_server->setServerName (QStringLiteral ("Hyperion WebConfig"));

	connect (_server, &QtHttpServer::started,           this, &StaticFileServing::onServerStarted);
	connect (_server, &QtHttpServer::stopped,           this, &StaticFileServing::onServerStopped);
	connect (_server, &QtHttpServer::error,             this, &StaticFileServing::onServerError);
	connect (_server, &QtHttpServer::requestNeedsReply, this, &StaticFileServing::onRequestNeedsReply);

	_server->start (port);

}

StaticFileServing::~StaticFileServing ()
{
	_server->stop ();
}

void StaticFileServing::onServerStarted (quint16 port)
{
	Info(_log, "started on port %d name \"%s\"", port ,_server->getServerName().toStdString().c_str());

        const std::string mDNSDescr = ( _server->getServerName().toStdString()
                                        + "@" +
                                        QHostInfo::localHostName().toStdString()
                                        );

	BonjourServiceRegister *bonjourRegister_http = new BonjourServiceRegister();
	bonjourRegister_http->registerService(
		BonjourRecord(mDNSDescr.c_str(), "_http._tcp", QString()),
		port
		);
	Debug(_log, "Web Config mDNS responder started");
}

void StaticFileServing::onServerStopped () {
	Info(_log, "stopped %s", _server->getServerName().toStdString().c_str());
}

void StaticFileServing::onServerError (QString msg)
{
	Error(_log, "%s", msg.toStdString().c_str());
}

static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage)
{
	reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
	reply->appendRawData (errorMessage.toLocal8Bit ());
}

static inline void printError404ToReply (QtHttpReply * reply, QString errorMessage)
{
	reply->setStatusCode(QtHttpReply::NotFound);
	reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
	reply->appendRawData (errorMessage.toLocal8Bit ());
}

void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply)
{
	QString command = request->getCommand ();
	if (command == QStringLiteral ("GET") || command == QStringLiteral ("POST"))
	{
		QString path = request->getUrl ().path ();
		QStringList uri_parts = path.split('/', QString::SkipEmptyParts);

		// special uri handling for server commands
		if ( ! uri_parts.empty() && uri_parts.at(0) == "cgi"  )
		{
			uri_parts.removeAt(0);
			try
			{
				if (command == QStringLiteral ("POST"))
				{
					QString postData = request->getRawData();
					uri_parts.append(postData.split('&', QString::SkipEmptyParts));
				}
				_cgi.exec(uri_parts, request, reply);
			}
			catch(...)
			{
				printErrorToReply (reply, "script failed (" % path % ")");
			}
			return;
		}
		Q_INIT_RESOURCE(WebConfig);

		QFileInfo info(_baseUrl % "/" % path);
		if ( path == "/" || path.isEmpty()  )
		{
			path = "index.html";
		}
		else if (info.isDir() && path.endsWith("/") )
		{
			path += "index.html";
		}
		else if (info.isDir() && ! path.endsWith("/") )
		{
			path += "/index.html";
		}

		// get static files
		QFile file(_baseUrl % "/" % path);
		if (file.exists())
		{
			QMimeType mime = _mimeDb->mimeTypeForFile (file.fileName ());
			if (file.open (QFile::ReadOnly)) {
				QByteArray data = file.readAll ();
				reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
				reply->appendRawData (data);
				file.close ();
			}
			else
			{
				printErrorToReply (reply, "Requested file " % path % " couldn't be open for reading !");
			}
		}
		else
		{
			printError404ToReply (reply, "404 Not Found\n" % path % " couldn't be found !");
		}
	}
	else
	{
		printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on web server !");
	}
}

<|endoftext|>


<commit_before>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkCanvas.h"
#include "SkGraphics.h"
#include "SkTypeface.h"

// GM to stress the GPU font cache

const char* gFamilyNames[] = {
    "sans-serif", "serif", "monospace"
};

const SkTypeface::Style gStyles[] = {
    SkTypeface::kNormal, SkTypeface::kItalic
};

const SkScalar gTextSizes[] = {
    12, 14, 16, 18, 20, 22, 24, 26, 28, 30
};

#define TYPEFACE_COUNT (SK_ARRAY_COUNT(gFamilyNames)*SK_ARRAY_COUNT(gStyles))

static SkScalar draw_string(SkCanvas* canvas, const SkString& text, SkScalar x,
                           SkScalar y, const SkPaint& paint) {
    canvas->drawText(text.c_str(), text.size(), x, y, paint);
    return x + paint.measureText(text.c_str(), text.size());
}

class FontCacheGM : public skiagm::GM {
public:
    FontCacheGM() {
        for (size_t i = 0; i < TYPEFACE_COUNT; ++i) {
            fTypefaces[i] = NULL;
        }
    }

    virtual ~FontCacheGM() {
        for (size_t i = 0; i < TYPEFACE_COUNT; ++i) {
            SkSafeUnref(fTypefaces[i]);
        }
    }

protected:
    virtual SkString onShortName() SK_OVERRIDE {
        return fName;
    }

    virtual SkISize onISize() SK_OVERRIDE {
        return SkISize::Make(640, 320);
    }

    virtual void onOnceBeforeDraw() SK_OVERRIDE {
        int typefaceCount = 0;
        for (size_t i = 0; i < SK_ARRAY_COUNT(gFamilyNames); ++i) {
            for (size_t j = 0; j < SK_ARRAY_COUNT(gStyles); ++j) {
                fTypefaces[typefaceCount++] = SkTypeface::CreateFromName(gFamilyNames[i],
                                                                          gStyles[j]);
            }
        }
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkScalar y = 32;
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);
        paint.setSubpixelText(true);

        SkString text("Ham");

        // draw some initial text to partially fill the GPU cache
        for (size_t i = 0; i < 2; ++i) {
            paint.setTypeface(fTypefaces[i]);
            SkScalar x = 20;

            for (size_t j = 0; j < SK_ARRAY_COUNT(gTextSizes); ++j) {
                paint.setTextSize(gTextSizes[j]);
                x = draw_string(canvas, text, x, y, paint) + 19;
            }
            y += 32;
        }

        // force a flush
        canvas->flush();

        // draw again, and more to overflow the cache
        for (size_t i = 0; i < TYPEFACE_COUNT; ++i) {
            paint.setTypeface(fTypefaces[i]);
            SkScalar x = 20;

            for (size_t j = 0; j < SK_ARRAY_COUNT(gTextSizes); ++j) {
                paint.setTextSize(gTextSizes[j]);
                x = draw_string(canvas, text, x, y, paint) + 19;
            }
            y += 32;
        }

    }

    virtual uint32_t onGetFlags() const SK_OVERRIDE {
        // this GM is meant only for the GPU
        return kGPUOnly_Flag;
    }

private:
    SkTypeface* fTypefaces[TYPEFACE_COUNT];
    SkString fName;
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

DEF_GM( return SkNEW(FontCacheGM); )

<commit_msg>Give the new font cache GM a name<commit_after>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkCanvas.h"
#include "SkGraphics.h"
#include "SkTypeface.h"

// GM to stress the GPU font cache

const char* gFamilyNames[] = {
    "sans-serif", "serif", "monospace"
};

const SkTypeface::Style gStyles[] = {
    SkTypeface::kNormal, SkTypeface::kItalic
};

const SkScalar gTextSizes[] = {
    12, 14, 16, 18, 20, 22, 24, 26, 28, 30
};

#define TYPEFACE_COUNT (SK_ARRAY_COUNT(gFamilyNames)*SK_ARRAY_COUNT(gStyles))

static SkScalar draw_string(SkCanvas* canvas, const SkString& text, SkScalar x,
                           SkScalar y, const SkPaint& paint) {
    canvas->drawText(text.c_str(), text.size(), x, y, paint);
    return x + paint.measureText(text.c_str(), text.size());
}

class FontCacheGM : public skiagm::GM {
public:
    FontCacheGM() {
        for (size_t i = 0; i < TYPEFACE_COUNT; ++i) {
            fTypefaces[i] = NULL;
        }
    }

    virtual ~FontCacheGM() {
        for (size_t i = 0; i < TYPEFACE_COUNT; ++i) {
            SkSafeUnref(fTypefaces[i]);
        }
    }

protected:
    virtual SkString onShortName() SK_OVERRIDE {
        return SkString("fontcache");
    }

    virtual SkISize onISize() SK_OVERRIDE {
        return SkISize::Make(640, 320);
    }

    virtual void onOnceBeforeDraw() SK_OVERRIDE {
        int typefaceCount = 0;
        for (size_t i = 0; i < SK_ARRAY_COUNT(gFamilyNames); ++i) {
            for (size_t j = 0; j < SK_ARRAY_COUNT(gStyles); ++j) {
                fTypefaces[typefaceCount++] = SkTypeface::CreateFromName(gFamilyNames[i],
                                                                          gStyles[j]);
            }
        }
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkScalar y = 32;
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);
        paint.setSubpixelText(true);

        SkString text("Ham");

        // draw some initial text to partially fill the GPU cache
        for (size_t i = 0; i < 2; ++i) {
            paint.setTypeface(fTypefaces[i]);
            SkScalar x = 20;

            for (size_t j = 0; j < SK_ARRAY_COUNT(gTextSizes); ++j) {
                paint.setTextSize(gTextSizes[j]);
                x = draw_string(canvas, text, x, y, paint) + 19;
            }
            y += 32;
        }

        // force a flush
        canvas->flush();

        // draw again, and more to overflow the cache
        for (size_t i = 0; i < TYPEFACE_COUNT; ++i) {
            paint.setTypeface(fTypefaces[i]);
            SkScalar x = 20;

            for (size_t j = 0; j < SK_ARRAY_COUNT(gTextSizes); ++j) {
                paint.setTextSize(gTextSizes[j]);
                x = draw_string(canvas, text, x, y, paint) + 19;
            }
            y += 32;
        }

    }

    virtual uint32_t onGetFlags() const SK_OVERRIDE {
        // this GM is meant only for the GPU
        return kGPUOnly_Flag;
    }

private:
    SkTypeface* fTypefaces[TYPEFACE_COUNT];
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

DEF_GM( return SkNEW(FontCacheGM); )

<|endoftext|>


<commit_before>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gm.h"

#include "SkColorPriv.h"
#include "SkGeometry.h"
#include "SkShader.h"

#define WIRE_FRAME_WIDTH    1.1f

static void tesselate(const SkPath& src, SkPath* dst) {
    SkPath::Iter iter(src, true);
    SkPoint pts[4];
    SkPath::Verb verb;
    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
        switch (verb) {
            case SkPath::kMove_Verb:
                dst->moveTo(pts[0]);
                break;
            case SkPath::kLine_Verb:
                dst->lineTo(pts[1]);
                break;
            case SkPath::kQuad_Verb: {
                SkPoint p;
                for (int i = 1; i <= 8; ++i) {
                    SkEvalQuadAt(pts, i / 8.0f, &p, NULL);
                    dst->lineTo(p);
                }
            } break;
            case SkPath::kCubic_Verb: {
                SkPoint p;
                for (int i = 1; i <= 8; ++i) {
                    SkEvalCubicAt(pts, i / 8.0f, &p, NULL, NULL);
                    dst->lineTo(p);
                }
            } break;
        }
    }
}

static void setFade(SkPaint* paint, bool showGL) {
    paint->setAlpha(showGL ? 0x66 : 0xFF);
}

static void setGLFrame(SkPaint* paint) {
    paint->setColor(0xFFFF0000);
    paint->setStyle(SkPaint::kStroke_Style);
    paint->setAntiAlias(true);
    paint->setStrokeWidth(WIRE_FRAME_WIDTH);
}

static void show_mesh(SkCanvas* canvas, const SkRect& r) {
    SkPaint paint;
    setGLFrame(&paint);

    canvas->drawRect(r, paint);
    canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
}

static void drawLine(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1,
                     const SkPaint& paint) {
    canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, paint);
}

static void show_mesh(SkCanvas* canvas, const SkPoint pts[],
                      const uint16_t indices[], int count) {
    SkPaint paint;
    setGLFrame(&paint);

    for (int i = 0; i < count - 2; ++i) {
        drawLine(canvas, pts[indices[i]], pts[indices[i+1]], paint);
        drawLine(canvas, pts[indices[i+1]], pts[indices[i+2]], paint);
        drawLine(canvas, pts[indices[i+2]], pts[indices[i]], paint);
    }
}

static void show_glframe(SkCanvas* canvas, const SkPath& path) {
    SkPaint paint;
    setGLFrame(&paint);
    canvas->drawPath(path, paint);
}

static void show_mesh_between(SkCanvas* canvas, const SkPath& p0, const SkPath& p1) {
    SkPath d0, d1;
    tesselate(p0, &d0);
    tesselate(p1, &d1);

    SkPoint pts0[256*2], pts1[256];
    int count = d0.getPoints(pts0, SK_ARRAY_COUNT(pts0));
    int count1 = d1.getPoints(pts1, SK_ARRAY_COUNT(pts1));
    SkASSERT(count == count1);
    memcpy(&pts0[count], pts1, count * sizeof(SkPoint));

    uint16_t indices[256*6];
    uint16_t* ndx = indices;
    for (int i = 0; i < count; ++i) {
        *ndx++ = i;
        *ndx++ = i + count;
    }
    *ndx++ = 0;

    show_mesh(canvas, pts0, indices, ndx - indices);
}

static void show_fan(SkCanvas* canvas, const SkPath& path, SkScalar cx, SkScalar cy) {
    SkPaint paint;
    setGLFrame(&paint);

    canvas->drawPath(path, paint);

    SkPoint pts[256];
    int count = path.getPoints(pts, SK_ARRAY_COUNT(pts));
    for (int i = 0; i < count; ++i) {
        canvas->drawLine(pts[i].fX, pts[i].fY, cx, cy, paint);
    }
}

///////////////////////////////////////////////////////////////////////////////

typedef void (*DrawProc)(SkCanvas* canvas, bool showGL, int flags);

static void draw_line(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);

    if (showGL) {
        setGLFrame(&paint);
    }
    canvas->drawLine(50, 50, 400, 100, paint);
    paint.setColor(SK_ColorBLACK);

    canvas->rotate(40);
    setFade(&paint, showGL);
    paint.setStrokeWidth(40);
    canvas->drawLine(100, 50, 450, 50, paint);
    if (showGL) {
        show_mesh(canvas, SkRect::MakeLTRB(100, 50-20, 450, 50+20));
    }
}

static void draw_rect(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);

    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);

    setFade(&paint, showGL);
    canvas->drawRect(r, paint);
    if (showGL) {
        show_mesh(canvas, r);
    }

    canvas->translate(320, 0);

    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(25);
    canvas->drawRect(r, paint);
    if (showGL) {
        SkScalar rad = paint.getStrokeWidth() / 2;
        SkPoint pts[8];
        r.outset(rad, rad);
        r.toQuad(&pts[0]);
        r.inset(rad*2, rad*2);
        r.toQuad(&pts[4]);

        const uint16_t indices[] = {
            0, 4, 1, 5, 2, 6, 3, 7, 0, 4
        };
        show_mesh(canvas, pts, indices, SK_ARRAY_COUNT(indices));
    }
}

static void draw_oval(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);

    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);

    setFade(&paint, showGL);
    canvas->drawOval(r, paint);
    if (showGL) {
        switch (flags) {
            case 0: {
                SkPath path;
                path.addOval(r);
                show_glframe(canvas, path);
            } break;
            case 1:
            case 3: {
                SkPath src, dst;
                src.addOval(r);
                tesselate(src, &dst);
                show_fan(canvas, dst, r.centerX(), r.centerY());
            } break;
            case 2: {
                SkPaint p(paint);
                show_mesh(canvas, r);
                setGLFrame(&p);
                paint.setStyle(SkPaint::kFill_Style);
                canvas->drawCircle(r.centerX(), r.centerY(), 3, p);
            } break;
        }
    }

    canvas->translate(320, 0);

    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(25);
    canvas->drawOval(r, paint);
    if (showGL) {
        switch (flags) {
            case 0: {
                SkPath path;
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                path.addOval(r);
                r.inset(rad*2, rad*2);
                path.addOval(r);
                show_glframe(canvas, path);
            } break;
            case 1: {
                SkPath path0, path1;
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                path0.addOval(r);
                r.inset(rad*2, rad*2);
                path1.addOval(r);
                show_mesh_between(canvas, path0, path1);
            } break;
            case 2: {
                SkPath path;
                path.addOval(r);
                show_glframe(canvas, path);
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                show_mesh(canvas, r);
            } break;
            case 3: {
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                SkPaint paint;
                paint.setAlpha(0x33);
                canvas->drawRect(r, paint);
                show_mesh(canvas, r);
            } break;
        }
    }
}

#include "SkImageDecoder.h"
static void draw_image(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setFilterBitmap(true);
    setFade(&paint, showGL);

    SkBitmap bm;
    SkImageDecoder::DecodeFile("/skimages/startrek.png", &bm);
    SkRect r = SkRect::MakeWH(bm.width(), bm.height());

    canvas->save();
    canvas->translate(30, 30);
    canvas->scale(0.8f, 0.8f);
    canvas->drawBitmap(bm, 0, 0, &paint);
    if (showGL) {
        show_mesh(canvas, r);
    }
    canvas->restore();

    canvas->translate(210, 290);
    canvas->rotate(-35);
    canvas->drawBitmap(bm, 0, 0, &paint);
    if (showGL) {
        show_mesh(canvas, r);
    }
}

static void draw_text(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);
    const char text[] = "Graphics at Google";
    size_t len = strlen(text);
    setFade(&paint, showGL);

    canvas->translate(40, 50);
    for (int i = 0; i < 10; ++i) {
        paint.setTextSize(12 + i * 3);
        canvas->drawText(text, len, 0, 0, paint);
        if (showGL) {
            SkRect bounds[256];
            SkScalar widths[256];
            int count = paint.getTextWidths(text, len, widths, bounds);
            SkScalar adv = 0;
            for (int j = 0; j < count; ++j) {
                bounds[j].offset(adv, 0);
                show_mesh(canvas, bounds[j]);
                adv += widths[j];
            }
        }
        canvas->translate(0, paint.getTextSize() * 3 / 2);
    }
}

static const struct {
    DrawProc    fProc;
    const char* fName;
} gRec[] = {
    {   draw_line,  "Lines" },
    {   draw_rect,  "Rects" },
    {   draw_oval,  "Ovals" },
    {   draw_image, "Images" },
    {   draw_text,  "Text" },
};

class TalkGM : public skiagm::GM {
    DrawProc fProc;
    SkString fName;
    bool     fShowGL;
    int      fFlags;

public:
    TalkGM(int index, bool showGL, int flags = 0) {
        fProc = gRec[index].fProc;
        fName.set(gRec[index].fName);
        if (showGL) {
            fName.append("-gl");
        }
        fShowGL = showGL;
        fFlags = flags;
    }

protected:
    virtual SkString onShortName() {
        return fName;
    }

    virtual SkISize onISize() {
        return SkISize::Make(640, 480);
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkRect dst = SkRect::MakeWH(canvas->getDevice()->width(), canvas->getDevice()->height());
        SkRect src = SkRect::MakeWH(640, 480);
        SkMatrix matrix;
        matrix.setRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);

        canvas->concat(matrix);
        fProc(canvas, fShowGL, fFlags);
    }

private:
    typedef skiagm::GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

#define GM_CONCAT(X,Y) GM_CONCAT_IMPL(X,Y)
#define GM_CONCAT_IMPL(X,Y) X##Y

#define FACTORY_NAME  GM_CONCAT(Factory, __LINE__)
#define REGISTRY_NAME  GM_CONCAT(gReg, __LINE__)

#define ADD_GM(Class, args)    \
    static skiagm::GM* FACTORY_NAME(void*) { return new Class args; } \
    static skiagm::GMRegistry REGISTRY_NAME(FACTORY_NAME);

ADD_GM(TalkGM, (0, false))
ADD_GM(TalkGM, (0, true))
ADD_GM(TalkGM, (1, false))
ADD_GM(TalkGM, (1, true))
ADD_GM(TalkGM, (2, false))
ADD_GM(TalkGM, (2, true))
ADD_GM(TalkGM, (2, true, 1))
ADD_GM(TalkGM, (2, true, 2))
ADD_GM(TalkGM, (2, true, 3))
ADD_GM(TalkGM, (3, false))
ADD_GM(TalkGM, (3, true))
ADD_GM(TalkGM, (4, false))
ADD_GM(TalkGM, (4, true))

//static GM* MyFactory(void*) { return new TalkGM(0, false); }
//static GMRegistry reg(MyFactory);

<commit_msg>enable lcdtext<commit_after>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gm.h"

#include "SkColorPriv.h"
#include "SkGeometry.h"
#include "SkShader.h"

#define WIRE_FRAME_WIDTH    1.1f

static void tesselate(const SkPath& src, SkPath* dst) {
    SkPath::Iter iter(src, true);
    SkPoint pts[4];
    SkPath::Verb verb;
    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
        switch (verb) {
            case SkPath::kMove_Verb:
                dst->moveTo(pts[0]);
                break;
            case SkPath::kLine_Verb:
                dst->lineTo(pts[1]);
                break;
            case SkPath::kQuad_Verb: {
                SkPoint p;
                for (int i = 1; i <= 8; ++i) {
                    SkEvalQuadAt(pts, i / 8.0f, &p, NULL);
                    dst->lineTo(p);
                }
            } break;
            case SkPath::kCubic_Verb: {
                SkPoint p;
                for (int i = 1; i <= 8; ++i) {
                    SkEvalCubicAt(pts, i / 8.0f, &p, NULL, NULL);
                    dst->lineTo(p);
                }
            } break;
        }
    }
}

static void setFade(SkPaint* paint, bool showGL) {
    paint->setAlpha(showGL ? 0x66 : 0xFF);
}

static void setGLFrame(SkPaint* paint) {
    paint->setColor(0xFFFF0000);
    paint->setStyle(SkPaint::kStroke_Style);
    paint->setAntiAlias(true);
    paint->setStrokeWidth(WIRE_FRAME_WIDTH);
}

static void show_mesh(SkCanvas* canvas, const SkRect& r) {
    SkPaint paint;
    setGLFrame(&paint);

    canvas->drawRect(r, paint);
    canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
}

static void drawLine(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1,
                     const SkPaint& paint) {
    canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, paint);
}

static void show_mesh(SkCanvas* canvas, const SkPoint pts[],
                      const uint16_t indices[], int count) {
    SkPaint paint;
    setGLFrame(&paint);

    for (int i = 0; i < count - 2; ++i) {
        drawLine(canvas, pts[indices[i]], pts[indices[i+1]], paint);
        drawLine(canvas, pts[indices[i+1]], pts[indices[i+2]], paint);
        drawLine(canvas, pts[indices[i+2]], pts[indices[i]], paint);
    }
}

static void show_glframe(SkCanvas* canvas, const SkPath& path) {
    SkPaint paint;
    setGLFrame(&paint);
    canvas->drawPath(path, paint);
}

static void show_mesh_between(SkCanvas* canvas, const SkPath& p0, const SkPath& p1) {
    SkPath d0, d1;
    tesselate(p0, &d0);
    tesselate(p1, &d1);

    SkPoint pts0[256*2], pts1[256];
    int count = d0.getPoints(pts0, SK_ARRAY_COUNT(pts0));
    int count1 = d1.getPoints(pts1, SK_ARRAY_COUNT(pts1));
    SkASSERT(count == count1);
    memcpy(&pts0[count], pts1, count * sizeof(SkPoint));

    uint16_t indices[256*6];
    uint16_t* ndx = indices;
    for (int i = 0; i < count; ++i) {
        *ndx++ = i;
        *ndx++ = i + count;
    }
    *ndx++ = 0;

    show_mesh(canvas, pts0, indices, ndx - indices);
}

static void show_fan(SkCanvas* canvas, const SkPath& path, SkScalar cx, SkScalar cy) {
    SkPaint paint;
    setGLFrame(&paint);

    canvas->drawPath(path, paint);

    SkPoint pts[256];
    int count = path.getPoints(pts, SK_ARRAY_COUNT(pts));
    for (int i = 0; i < count; ++i) {
        canvas->drawLine(pts[i].fX, pts[i].fY, cx, cy, paint);
    }
}

///////////////////////////////////////////////////////////////////////////////

typedef void (*DrawProc)(SkCanvas* canvas, bool showGL, int flags);

static void draw_line(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);

    if (showGL) {
        setGLFrame(&paint);
    }
    canvas->drawLine(50, 50, 400, 100, paint);
    paint.setColor(SK_ColorBLACK);

    canvas->rotate(40);
    setFade(&paint, showGL);
    paint.setStrokeWidth(40);
    canvas->drawLine(100, 50, 450, 50, paint);
    if (showGL) {
        show_mesh(canvas, SkRect::MakeLTRB(100, 50-20, 450, 50+20));
    }
}

static void draw_rect(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);

    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);

    setFade(&paint, showGL);
    canvas->drawRect(r, paint);
    if (showGL) {
        show_mesh(canvas, r);
    }

    canvas->translate(320, 0);

    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(25);
    canvas->drawRect(r, paint);
    if (showGL) {
        SkScalar rad = paint.getStrokeWidth() / 2;
        SkPoint pts[8];
        r.outset(rad, rad);
        r.toQuad(&pts[0]);
        r.inset(rad*2, rad*2);
        r.toQuad(&pts[4]);

        const uint16_t indices[] = {
            0, 4, 1, 5, 2, 6, 3, 7, 0, 4
        };
        show_mesh(canvas, pts, indices, SK_ARRAY_COUNT(indices));
    }
}

static void draw_oval(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);

    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);

    setFade(&paint, showGL);
    canvas->drawOval(r, paint);
    if (showGL) {
        switch (flags) {
            case 0: {
                SkPath path;
                path.addOval(r);
                show_glframe(canvas, path);
            } break;
            case 1:
            case 3: {
                SkPath src, dst;
                src.addOval(r);
                tesselate(src, &dst);
                show_fan(canvas, dst, r.centerX(), r.centerY());
            } break;
            case 2: {
                SkPaint p(paint);
                show_mesh(canvas, r);
                setGLFrame(&p);
                paint.setStyle(SkPaint::kFill_Style);
                canvas->drawCircle(r.centerX(), r.centerY(), 3, p);
            } break;
        }
    }

    canvas->translate(320, 0);

    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(25);
    canvas->drawOval(r, paint);
    if (showGL) {
        switch (flags) {
            case 0: {
                SkPath path;
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                path.addOval(r);
                r.inset(rad*2, rad*2);
                path.addOval(r);
                show_glframe(canvas, path);
            } break;
            case 1: {
                SkPath path0, path1;
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                path0.addOval(r);
                r.inset(rad*2, rad*2);
                path1.addOval(r);
                show_mesh_between(canvas, path0, path1);
            } break;
            case 2: {
                SkPath path;
                path.addOval(r);
                show_glframe(canvas, path);
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                show_mesh(canvas, r);
            } break;
            case 3: {
                SkScalar rad = paint.getStrokeWidth() / 2;
                r.outset(rad, rad);
                SkPaint paint;
                paint.setAlpha(0x33);
                canvas->drawRect(r, paint);
                show_mesh(canvas, r);
            } break;
        }
    }
}

#include "SkImageDecoder.h"
static void draw_image(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setFilterBitmap(true);
    setFade(&paint, showGL);

    SkBitmap bm;
    SkImageDecoder::DecodeFile("/skimages/startrek.png", &bm);
    SkRect r = SkRect::MakeWH(bm.width(), bm.height());

    canvas->save();
    canvas->translate(30, 30);
    canvas->scale(0.8f, 0.8f);
    canvas->drawBitmap(bm, 0, 0, &paint);
    if (showGL) {
        show_mesh(canvas, r);
    }
    canvas->restore();

    canvas->translate(210, 290);
    canvas->rotate(-35);
    canvas->drawBitmap(bm, 0, 0, &paint);
    if (showGL) {
        show_mesh(canvas, r);
    }
}

static void draw_text(SkCanvas* canvas, bool showGL, int flags) {
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setLCDRenderText(true);
    const char text[] = "Graphics at Google";
    size_t len = strlen(text);
    setFade(&paint, showGL);

    canvas->translate(40, 50);
    for (int i = 0; i < 10; ++i) {
        paint.setTextSize(12 + i * 3);
        canvas->drawText(text, len, 0, 0, paint);
        if (showGL) {
            SkRect bounds[256];
            SkScalar widths[256];
            int count = paint.getTextWidths(text, len, widths, bounds);
            SkScalar adv = 0;
            for (int j = 0; j < count; ++j) {
                bounds[j].offset(adv, 0);
                show_mesh(canvas, bounds[j]);
                adv += widths[j];
            }
        }
        canvas->translate(0, paint.getTextSize() * 3 / 2);
    }
}

static const struct {
    DrawProc    fProc;
    const char* fName;
} gRec[] = {
    {   draw_line,  "Lines" },
    {   draw_rect,  "Rects" },
    {   draw_oval,  "Ovals" },
    {   draw_image, "Images" },
    {   draw_text,  "Text" },
};

class TalkGM : public skiagm::GM {
    DrawProc fProc;
    SkString fName;
    bool     fShowGL;
    int      fFlags;

public:
    TalkGM(int index, bool showGL, int flags = 0) {
        fProc = gRec[index].fProc;
        fName.set(gRec[index].fName);
        if (showGL) {
            fName.append("-gl");
        }
        fShowGL = showGL;
        fFlags = flags;
    }

protected:
    virtual SkString onShortName() {
        return fName;
    }

    virtual SkISize onISize() {
        return SkISize::Make(640, 480);
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkRect dst = SkRect::MakeWH(canvas->getDevice()->width(), canvas->getDevice()->height());
        SkRect src = SkRect::MakeWH(640, 480);
        SkMatrix matrix;
        matrix.setRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);

        canvas->concat(matrix);
        fProc(canvas, fShowGL, fFlags);
    }

private:
    typedef skiagm::GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

#define GM_CONCAT(X,Y) GM_CONCAT_IMPL(X,Y)
#define GM_CONCAT_IMPL(X,Y) X##Y

#define FACTORY_NAME  GM_CONCAT(Factory, __LINE__)
#define REGISTRY_NAME  GM_CONCAT(gReg, __LINE__)

#define ADD_GM(Class, args)    \
    static skiagm::GM* FACTORY_NAME(void*) { return new Class args; } \
    static skiagm::GMRegistry REGISTRY_NAME(FACTORY_NAME);

ADD_GM(TalkGM, (0, false))
ADD_GM(TalkGM, (0, true))
ADD_GM(TalkGM, (1, false))
ADD_GM(TalkGM, (1, true))
ADD_GM(TalkGM, (2, false))
ADD_GM(TalkGM, (2, true))
ADD_GM(TalkGM, (2, true, 1))
ADD_GM(TalkGM, (2, true, 2))
ADD_GM(TalkGM, (2, true, 3))
ADD_GM(TalkGM, (3, false))
ADD_GM(TalkGM, (3, true))
ADD_GM(TalkGM, (4, false))
ADD_GM(TalkGM, (4, true))

//static GM* MyFactory(void*) { return new TalkGM(0, false); }
//static GMRegistry reg(MyFactory);

<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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 "lingvo/core/ops/record_yielder.h"

#include <gtest/gtest.h>
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/io/record_writer.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow/core/platform/env.h"
#include "lingvo/core/ops/input_common.h"

namespace tensorflow {
namespace lingvo {

void GeneratePlainTextTestData(const string& prefix, int n, int m) {
  for (int i = 0; i < n; ++i) {
    std::unique_ptr<WritableFile> file;
    TF_CHECK_OK(Env::Default()->NewWritableFile(
        io::JoinPath("/tmp", strings::StrCat(prefix, ".", i)),
        &file));
    for (int j = 0; j < m; ++j) {
      TF_CHECK_OK(file->Append(strings::Printf("%010d\n", m * i + j)));
    }
  }
}

TEST(RecordYielderTest, PlainTextYielderBasicTest) {
  const int N = 10;
  const int M = 1000;
  GeneratePlainTextTestData("basic", N, M);
  RecordYielder::Options opts;
  opts.file_pattern =
      strings::StrCat("text:", io::JoinPath("/tmp", "basic.*"));
  opts.seed = 301;
  opts.bufsize = 2000;
  opts.parallelism = 1;

  RecordYielder* yielder = RecordYielder::New(opts);
  std::vector<string> vals;
  Rope v;
  for (int i = 0; i < N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
    VLOG(1) << i << " " << v;
    vals.emplace_back(string(v));
  }
  std::sort(vals.begin(), vals.end());
  auto new_end = std::unique(vals.begin(), vals.end());

  // One epoch should have no duplicates.
  ASSERT_EQ(new_end, vals.end());

  // Iterates another two epochs.
  for (int i = 0; i < 2 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  // Iterates another 34 epochs.
  for (int i = 0; i < 2 * 17 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  EXPECT_EQ(yielder->current_epoch(), 37);
  yielder->Close();
}

void GenerateTfRecordTestData(const string& prefix, int n, int m) {
  for (int i = 0; i < n; ++i) {
    std::unique_ptr<WritableFile> file;
    TF_CHECK_OK(Env::Default()->NewWritableFile(
        io::JoinPath("/tmp", strings::StrCat(prefix, ".", i)),
        &file));
    io::RecordWriter writer(file.get());
    for (int j = 0; j < m; ++j) {
      TF_CHECK_OK(writer.WriteRecord(strings::Printf("%010d", m * i + j)));
    }
  }
}

TEST(RecordYielderTest, TfRecordYielderBasicTest) {
  const int N = 10;
  const int M = 1000;
  GenerateTfRecordTestData("basic", N, M);
  RecordYielder::Options opts;
  opts.file_pattern =
      strings::StrCat("tfrecord:", io::JoinPath("/tmp", "basic.*"));
  opts.seed = 301;
  opts.bufsize = 2000;
  opts.parallelism = 1;

  RecordYielder* yielder = RecordYielder::New(opts);
  std::vector<string> vals;
  Rope v;
  for (int i = 0; i < N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
    VLOG(1) << i << " " << v;
    vals.emplace_back(string(v));
  }
  std::sort(vals.begin(), vals.end());
  auto new_end = std::unique(vals.begin(), vals.end());

  // One epoch should have no duplicates.
  ASSERT_EQ(new_end, vals.end());

  // Iterates another two epochs.
  for (int i = 0; i < 2 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  // Iterates another 34 epochs.
  for (int i = 0; i < 2 * 17 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  EXPECT_EQ(yielder->current_epoch(), 37);
  yielder->Close();
}

int NumMatches(const std::vector<Rope>& vals1,
               const std::vector<Rope>& vals2) {
  CHECK_EQ(vals1.size(), vals2.size());
  int num_matches = 0;
  for (int i = 0; i < vals1.size(); ++i) {
    num_matches += vals1[i] == vals2[i];
  }
  return num_matches;
}

TEST(RecordYielderTest, ShufflesShard) {
  const int M = 32;
  GenerateTfRecordTestData("oneshard", 1 /* num_shards */, M);

  RecordYielder::Options opts;
  opts.file_pattern = strings::StrCat(
      "tfrecord:", io::JoinPath("/tmp", "oneshard.0"));
  opts.bufsize = M;
  opts.parallelism = 1;

  // Subsequent epochs should be yielded in different orders.
  std::vector<Rope> epoch1, epoch2;
  {
    opts.seed = 301;
    auto yielder = RecordYielder::New(opts);
    for (int i = 0; i < M; ++i) {
      Rope v;
      TF_CHECK_OK(yielder->Yield(&v));
      epoch1.push_back(v);
    }
    for (int i = 0; i < M; ++i) {
      Rope v;
      TF_CHECK_OK(yielder->Yield(&v));
      epoch2.push_back(v);
    }
    yielder->Close();
    EXPECT_LT(NumMatches(epoch1, epoch2), M);
  }

  // Ordering should change if the seed changes.
  std::vector<Rope> epoch1_different_seed;
  {
    opts.seed = 103;
    auto yielder = RecordYielder::New(opts);
    for (int i = 0; i < M; ++i) {
      Rope v;
      TF_CHECK_OK(yielder->Yield(&v));
      epoch1_different_seed.push_back(v);
    }
    yielder->Close();
    EXPECT_LT(NumMatches(epoch1, epoch1_different_seed), M);
  }
}

TEST(RecordYielder, Error) {
  RecordYielder::Options opts;
  opts.file_pattern = strings::StrCat(
      "tfrecord:", io::JoinPath("/tmp", "nothing.*"));
  auto yielder = RecordYielder::New(opts);
  Rope v;
  EXPECT_TRUE(errors::IsNotFound(yielder->Yield(&v)));
  yielder->Close();
}

TEST(RecordYielder, MatchFilesFromMultiplePatterns) {
  const int N = 2;
  const int M = 32;
  GenerateTfRecordTestData("twoshard", N /* num_shards */,
                           M /* record per shard */);
  RecordYielder::Options opts;
  const string path0 = io::JoinPath("/tmp", "twoshard.0");
  const string path1 = io::JoinPath("/tmp", "twoshard.1");
  opts.file_pattern = strings::StrCat("tfrecord:", path0, ",", path1);
  opts.bufsize = M;
  opts.parallelism = 1;
  std::vector<Rope> epoch;
  auto yielder = RecordYielder::New(opts);
  Rope v;
  for (int i = 0; i < N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
    epoch.emplace_back(string(v));
  }
  auto new_end = std::unique(epoch.begin(), epoch.end());
  // If we iterated through both shards (rather than 1 shard twice), there
  // should be no duplicates, and we should be at the end of the first epoch.
  EXPECT_EQ(new_end, epoch.end());
  EXPECT_EQ(yielder->current_epoch(), 1);
  TF_CHECK_OK(yielder->Yield(&v));
  EXPECT_EQ(yielder->current_epoch(), 2);
  yielder->Close();
}

}  // namespace lingvo
}  // namespace tensorflow
<commit_msg>Fix flaky record_yielder_test.<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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 "lingvo/core/ops/record_yielder.h"

#include <gtest/gtest.h>
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/io/record_writer.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow/core/platform/env.h"
#include "lingvo/core/ops/input_common.h"

namespace tensorflow {
namespace lingvo {

void GeneratePlainTextTestData(const string& prefix, int n, int m) {
  for (int i = 0; i < n; ++i) {
    std::unique_ptr<WritableFile> file;
    TF_CHECK_OK(Env::Default()->NewWritableFile(
        io::JoinPath("/tmp", strings::StrCat(prefix, ".", i)),
        &file));
    for (int j = 0; j < m; ++j) {
      TF_CHECK_OK(file->Append(strings::Printf("%010d\n", m * i + j)));
    }
  }
}

TEST(RecordYielderTest, PlainTextYielderBasicTest) {
  const int N = 10;
  const int M = 1000;
  GeneratePlainTextTestData("basic", N, M);
  RecordYielder::Options opts;
  opts.file_pattern =
      strings::StrCat("text:", io::JoinPath("/tmp", "basic.*"));
  opts.seed = 301;
  opts.bufsize = 2000;
  opts.parallelism = 1;

  RecordYielder* yielder = RecordYielder::New(opts);
  std::vector<string> vals;
  Rope v;
  for (int i = 0; i < N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
    VLOG(1) << i << " " << v;
    vals.emplace_back(string(v));
  }
  std::sort(vals.begin(), vals.end());
  auto new_end = std::unique(vals.begin(), vals.end());

  // One epoch should have no duplicates.
  ASSERT_EQ(new_end, vals.end());

  // Iterates another two epochs.
  for (int i = 0; i < 2 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  // Iterates another 34 epochs.
  for (int i = 0; i < 2 * 17 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  // End of the 37th epoch | start of the 38th epoch.
  EXPECT_TRUE(yielder->current_epoch() == 37 || yielder->current_epoch() == 38);
  yielder->Close();
}

void GenerateTfRecordTestData(const string& prefix, int n, int m) {
  for (int i = 0; i < n; ++i) {
    std::unique_ptr<WritableFile> file;
    TF_CHECK_OK(Env::Default()->NewWritableFile(
        io::JoinPath("/tmp", strings::StrCat(prefix, ".", i)),
        &file));
    io::RecordWriter writer(file.get());
    for (int j = 0; j < m; ++j) {
      TF_CHECK_OK(writer.WriteRecord(strings::Printf("%010d", m * i + j)));
    }
  }
}

TEST(RecordYielderTest, TfRecordYielderBasicTest) {
  const int N = 10;
  const int M = 1000;
  GenerateTfRecordTestData("basic", N, M);
  RecordYielder::Options opts;
  opts.file_pattern =
      strings::StrCat("tfrecord:", io::JoinPath("/tmp", "basic.*"));
  opts.seed = 301;
  opts.bufsize = 2000;
  opts.parallelism = 1;

  RecordYielder* yielder = RecordYielder::New(opts);
  std::vector<string> vals;
  Rope v;
  for (int i = 0; i < N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
    VLOG(1) << i << " " << v;
    vals.emplace_back(string(v));
  }
  std::sort(vals.begin(), vals.end());
  auto new_end = std::unique(vals.begin(), vals.end());

  // One epoch should have no duplicates.
  ASSERT_EQ(new_end, vals.end());

  // Iterates another two epochs.
  for (int i = 0; i < 2 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  // Iterates another 34 epochs.
  for (int i = 0; i < 2 * 17 * N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
  }

  // End of the 37th epoch | start of the 38th epoch.
  EXPECT_TRUE(yielder->current_epoch() == 37 || yielder->current_epoch() == 38);
  yielder->Close();
}

int NumMatches(const std::vector<Rope>& vals1,
               const std::vector<Rope>& vals2) {
  CHECK_EQ(vals1.size(), vals2.size());
  int num_matches = 0;
  for (int i = 0; i < vals1.size(); ++i) {
    num_matches += vals1[i] == vals2[i];
  }
  return num_matches;
}

TEST(RecordYielderTest, ShufflesShard) {
  const int M = 32;
  GenerateTfRecordTestData("oneshard", 1 /* num_shards */, M);

  RecordYielder::Options opts;
  opts.file_pattern = strings::StrCat(
      "tfrecord:", io::JoinPath("/tmp", "oneshard.0"));
  opts.bufsize = M;
  opts.parallelism = 1;

  // Subsequent epochs should be yielded in different orders.
  std::vector<Rope> epoch1, epoch2;
  {
    opts.seed = 301;
    auto yielder = RecordYielder::New(opts);
    for (int i = 0; i < M; ++i) {
      Rope v;
      TF_CHECK_OK(yielder->Yield(&v));
      epoch1.push_back(v);
    }
    for (int i = 0; i < M; ++i) {
      Rope v;
      TF_CHECK_OK(yielder->Yield(&v));
      epoch2.push_back(v);
    }
    yielder->Close();
    EXPECT_LT(NumMatches(epoch1, epoch2), M);
  }

  // Ordering should change if the seed changes.
  std::vector<Rope> epoch1_different_seed;
  {
    opts.seed = 103;
    auto yielder = RecordYielder::New(opts);
    for (int i = 0; i < M; ++i) {
      Rope v;
      TF_CHECK_OK(yielder->Yield(&v));
      epoch1_different_seed.push_back(v);
    }
    yielder->Close();
    EXPECT_LT(NumMatches(epoch1, epoch1_different_seed), M);
  }
}

TEST(RecordYielder, Error) {
  RecordYielder::Options opts;
  opts.file_pattern = strings::StrCat(
      "tfrecord:", io::JoinPath("/tmp", "nothing.*"));
  auto yielder = RecordYielder::New(opts);
  Rope v;
  EXPECT_TRUE(errors::IsNotFound(yielder->Yield(&v)));
  yielder->Close();
}

TEST(RecordYielder, MatchFilesFromMultiplePatterns) {
  const int N = 2;
  const int M = 32;
  GenerateTfRecordTestData("twoshard", N /* num_shards */,
                           M /* record per shard */);
  RecordYielder::Options opts;
  const string path0 = io::JoinPath("/tmp", "twoshard.0");
  const string path1 = io::JoinPath("/tmp", "twoshard.1");
  opts.file_pattern = strings::StrCat("tfrecord:", path0, ",", path1);
  opts.bufsize = M;
  opts.parallelism = 1;
  std::vector<Rope> epoch;
  auto yielder = RecordYielder::New(opts);
  Rope v;
  for (int i = 0; i < N * M; ++i) {
    TF_CHECK_OK(yielder->Yield(&v));
    epoch.emplace_back(string(v));
  }
  auto new_end = std::unique(epoch.begin(), epoch.end());
  // If we iterated through both shards (rather than 1 shard twice), there
  // should be no duplicates, and we should be at the end of the first epoch.
  EXPECT_EQ(new_end, epoch.end());
  // End of the 1st epoch | start of the 2nd epoch.
  EXPECT_TRUE(yielder->current_epoch() == 1 || yielder->current_epoch() == 2);
  TF_CHECK_OK(yielder->Yield(&v));
  // End of the 2st epoch | start of the 3nd epoch.
  EXPECT_TRUE(yielder->current_epoch() == 2 || yielder->current_epoch() == 3);
  yielder->Close();
}

}  // namespace lingvo
}  // namespace tensorflow
<|endoftext|>


<commit_before>#include "dirlistjob.h"
#include <gio/gio.h>
#include "fileinfo_p.h"
#include "gioptrs.h"
#include <QDebug>

namespace Fm {

DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr<const HashSet>& cutFilesHashSet):
    dir_path{path}, flags{_flags}, cutFilesHashSet_{cutFilesHashSet} {
}

void DirListJob::exec() {
    GErrorPtr err;
    GFileInfoPtr dir_inf;
    GFilePtr dir_gfile = dir_path.gfile();
    // FIXME: these are hacks for search:/// URI implemented by libfm which contains some bugs
    bool isFileSearch = dir_path.hasUriScheme("search");
    if(isFileSearch) {
        // NOTE: The GFile instance changes its URI during file enumeration (bad design).
        // So we create a copy here to avoid channging the gfile stored in dir_path.
        // FIXME: later we should refactor file search and remove this dirty hack.
        dir_gfile = GFilePtr{g_file_dup(dir_gfile.get())};
    }
_retry:
    err.reset();
    dir_inf = GFileInfoPtr{
        g_file_query_info(dir_gfile.get(), defaultGFileInfoQueryAttribs,
                          G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
        false
    };
    if(!dir_inf) {
        ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
        if(act == ErrorAction::RETRY) {
            err.reset();
            goto _retry;
        }
        return;
    }

    if(g_file_info_get_file_type(dir_inf.get()) != G_FILE_TYPE_DIRECTORY) {
        auto path_str = dir_path.toString();
        err = GErrorPtr{
                G_IO_ERROR,
                G_IO_ERROR_NOT_DIRECTORY,
                tr("The specified directory '%1' is not valid").arg(QString::fromUtf8(path_str.get()))
        };
        emitError(err, ErrorSeverity::CRITICAL);
        return;
    }
    else {
        std::lock_guard<std::mutex> lock{mutex_};
        dir_fi = std::make_shared<FileInfo>(dir_inf, dir_path);
    }

    FileInfoList foundFiles;
    /* check if FS is R/O and set attr. into inf */
    // FIXME:  _fm_file_info_job_update_fs_readonly(gf, inf, nullptr, nullptr);
    err.reset();
    GFileEnumeratorPtr enu = GFileEnumeratorPtr{
            g_file_enumerate_children(dir_gfile.get(), defaultGFileInfoQueryAttribs,
                                      G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
            false
    };
    if(enu) {
        // qDebug() << "START LISTING:" << dir_path.toString().get();
        while(!isCancelled()) {
            err.reset();
            GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
            if(inf) {
#if 0
                FmPath* dir, *sub;
                GFile* child;
                if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) {
                    /* FIXME: handle symlinks */
                    if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) {
                        g_object_unref(inf);
                        continue;
                    }
                }
#endif
                // virtual folders may return children not within them
                // For example: the search:/// URI implemented by libfm might return files from different folders during enumeration.
                // So here we call g_file_enumerator_get_container() to get the real parent path rather than simply using dir_path.
                // This is not the behaviour of gio, but the extensions by libfm might do this.
                // FIXME: after we port these vfs implementation from libfm, we can redesign this.
                FilePath realParentPath = FilePath{g_file_enumerator_get_container(enu.get()), true};
                if(isFileSearch) { // this is a file sarch job (search:/// URI)
                    // FIXME: redesign file search and remove this dirty hack
                    // the libfm implementation of search:/// URI returns a customized GFile implementation that does not behave normally.
                    // let's get its actual URI and re-create a normal gio GFile instance from it.
                    realParentPath = FilePath::fromUri(realParentPath.uri().get());
                }
#if 0
                if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY)
                    /* for dir: check if its FS is R/O and set attr. into inf */
                {
                    _fm_file_info_job_update_fs_readonly(child, inf, nullptr, nullptr);
                }
                fi = fm_file_info_new_from_g_file_data(child, inf, sub);
#endif
                auto fileInfo = std::make_shared<FileInfo>(inf, FilePath(), realParentPath);
                if(emit_files_found) {
                    // Q_EMIT filesFound();
                }

                if(cutFilesHashSet_
                        && cutFilesHashSet_->count(fileInfo->path().hash()) > 0) {
                    fileInfo->bindCutFiles(cutFilesHashSet_);
                }

                foundFiles.push_back(std::move(fileInfo));
            }
            else {
                if(err) {
                    ErrorAction act = emitError(err, ErrorSeverity::MILD);
                    /* ErrorAction::RETRY is not supported. */
                    if(act == ErrorAction::ABORT) {
                        cancel();
                    }
                }
                /* otherwise it's EOL */
                break;
            }
        }
        err.reset();
        g_file_enumerator_close(enu.get(), cancellable().get(), &err);
    }
    else {
        emitError(err, err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_CANCELLED
                       ? ErrorSeverity::MILD // may happen at Folder::reload()
                       : ErrorSeverity::CRITICAL);
    }

    // qDebug() << "END LISTING:" << dir_path.toString().get();
    if(!foundFiles.empty()) {
        std::lock_guard<std::mutex> lock{mutex_};
        files_.swap(foundFiles);
    }
}

#if 0
//FIXME: incremental..

static gboolean emit_found_files(gpointer user_data) {
    /* this callback is called from the main thread */
    FmDirListJob* job = FM_DIR_LIST_JOB(user_data);
    /* g_print("emit_found_files: %d\n", g_slist_length(job->files_to_add)); */

    if(g_source_is_destroyed(g_main_current_source())) {
        return FALSE;
    }
    g_signal_emit(job, signals[FILES_FOUND], 0, job->files_to_add);
    g_slist_free_full(job->files_to_add, (GDestroyNotify)fm_file_info_unref);
    job->files_to_add = nullptr;
    job->delay_add_files_handler = 0;
    return FALSE;
}

static gpointer queue_add_file(FmJob* fmjob, gpointer user_data) {
    FmDirListJob* job = FM_DIR_LIST_JOB(fmjob);
    FmFileInfo* file = FM_FILE_INFO(user_data);
    /* this callback is called from the main thread */
    /* g_print("queue_add_file: %s\n", fm_file_info_get_disp_name(file)); */
    job->files_to_add = g_slist_prepend(job->files_to_add, fm_file_info_ref(file));
    if(job->delay_add_files_handler == 0)
        job->delay_add_files_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW,
                                       1, emit_found_files, g_object_ref(job), g_object_unref);
    return nullptr;
}

void fm_dir_list_job_add_found_file(FmDirListJob* job, FmFileInfo* file) {
    fm_file_info_list_push_tail(job->files, file);
    if(G_UNLIKELY(job->emit_files_found)) {
        fm_job_call_main_thread(FM_JOB(job), queue_add_file, file);
    }
}
#endif

} // namespace Fm
<commit_msg>Set info query cancellation error to mild in dir list job<commit_after>#include "dirlistjob.h"
#include <gio/gio.h>
#include "fileinfo_p.h"
#include "gioptrs.h"
#include <QDebug>

namespace Fm {

DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr<const HashSet>& cutFilesHashSet):
    dir_path{path}, flags{_flags}, cutFilesHashSet_{cutFilesHashSet} {
}

void DirListJob::exec() {
    GErrorPtr err;
    GFileInfoPtr dir_inf;
    GFilePtr dir_gfile = dir_path.gfile();
    // FIXME: these are hacks for search:/// URI implemented by libfm which contains some bugs
    bool isFileSearch = dir_path.hasUriScheme("search");
    if(isFileSearch) {
        // NOTE: The GFile instance changes its URI during file enumeration (bad design).
        // So we create a copy here to avoid channging the gfile stored in dir_path.
        // FIXME: later we should refactor file search and remove this dirty hack.
        dir_gfile = GFilePtr{g_file_dup(dir_gfile.get())};
    }
_retry:
    err.reset();
    dir_inf = GFileInfoPtr{
        g_file_query_info(dir_gfile.get(), defaultGFileInfoQueryAttribs,
                          G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
        false
    };
    if(!dir_inf) {
        ErrorAction act = emitError(err, err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_CANCELLED
                                         ? ErrorSeverity::MILD // may happen with MTP
                                         : ErrorSeverity::MODERATE);
        if(act == ErrorAction::RETRY) {
            err.reset();
            goto _retry;
        }
        return;
    }

    if(g_file_info_get_file_type(dir_inf.get()) != G_FILE_TYPE_DIRECTORY) {
        auto path_str = dir_path.toString();
        err = GErrorPtr{
                G_IO_ERROR,
                G_IO_ERROR_NOT_DIRECTORY,
                tr("The specified directory '%1' is not valid").arg(QString::fromUtf8(path_str.get()))
        };
        emitError(err, ErrorSeverity::CRITICAL);
        return;
    }
    else {
        std::lock_guard<std::mutex> lock{mutex_};
        dir_fi = std::make_shared<FileInfo>(dir_inf, dir_path);
    }

    FileInfoList foundFiles;
    /* check if FS is R/O and set attr. into inf */
    // FIXME:  _fm_file_info_job_update_fs_readonly(gf, inf, nullptr, nullptr);
    err.reset();
    GFileEnumeratorPtr enu = GFileEnumeratorPtr{
            g_file_enumerate_children(dir_gfile.get(), defaultGFileInfoQueryAttribs,
                                      G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
            false
    };
    if(enu) {
        // qDebug() << "START LISTING:" << dir_path.toString().get();
        while(!isCancelled()) {
            err.reset();
            GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
            if(inf) {
#if 0
                FmPath* dir, *sub;
                GFile* child;
                if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) {
                    /* FIXME: handle symlinks */
                    if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) {
                        g_object_unref(inf);
                        continue;
                    }
                }
#endif
                // virtual folders may return children not within them
                // For example: the search:/// URI implemented by libfm might return files from different folders during enumeration.
                // So here we call g_file_enumerator_get_container() to get the real parent path rather than simply using dir_path.
                // This is not the behaviour of gio, but the extensions by libfm might do this.
                // FIXME: after we port these vfs implementation from libfm, we can redesign this.
                FilePath realParentPath = FilePath{g_file_enumerator_get_container(enu.get()), true};
                if(isFileSearch) { // this is a file sarch job (search:/// URI)
                    // FIXME: redesign file search and remove this dirty hack
                    // the libfm implementation of search:/// URI returns a customized GFile implementation that does not behave normally.
                    // let's get its actual URI and re-create a normal gio GFile instance from it.
                    realParentPath = FilePath::fromUri(realParentPath.uri().get());
                }
#if 0
                if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY)
                    /* for dir: check if its FS is R/O and set attr. into inf */
                {
                    _fm_file_info_job_update_fs_readonly(child, inf, nullptr, nullptr);
                }
                fi = fm_file_info_new_from_g_file_data(child, inf, sub);
#endif
                auto fileInfo = std::make_shared<FileInfo>(inf, FilePath(), realParentPath);
                if(emit_files_found) {
                    // Q_EMIT filesFound();
                }

                if(cutFilesHashSet_
                        && cutFilesHashSet_->count(fileInfo->path().hash()) > 0) {
                    fileInfo->bindCutFiles(cutFilesHashSet_);
                }

                foundFiles.push_back(std::move(fileInfo));
            }
            else {
                if(err) {
                    ErrorAction act = emitError(err, ErrorSeverity::MILD);
                    /* ErrorAction::RETRY is not supported. */
                    if(act == ErrorAction::ABORT) {
                        cancel();
                    }
                }
                /* otherwise it's EOL */
                break;
            }
        }
        err.reset();
        g_file_enumerator_close(enu.get(), cancellable().get(), &err);
    }
    else {
        emitError(err, err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_CANCELLED
                       ? ErrorSeverity::MILD // may happen at Folder::reload()
                       : ErrorSeverity::CRITICAL);
    }

    // qDebug() << "END LISTING:" << dir_path.toString().get();
    if(!foundFiles.empty()) {
        std::lock_guard<std::mutex> lock{mutex_};
        files_.swap(foundFiles);
    }
}

#if 0
//FIXME: incremental..

static gboolean emit_found_files(gpointer user_data) {
    /* this callback is called from the main thread */
    FmDirListJob* job = FM_DIR_LIST_JOB(user_data);
    /* g_print("emit_found_files: %d\n", g_slist_length(job->files_to_add)); */

    if(g_source_is_destroyed(g_main_current_source())) {
        return FALSE;
    }
    g_signal_emit(job, signals[FILES_FOUND], 0, job->files_to_add);
    g_slist_free_full(job->files_to_add, (GDestroyNotify)fm_file_info_unref);
    job->files_to_add = nullptr;
    job->delay_add_files_handler = 0;
    return FALSE;
}

static gpointer queue_add_file(FmJob* fmjob, gpointer user_data) {
    FmDirListJob* job = FM_DIR_LIST_JOB(fmjob);
    FmFileInfo* file = FM_FILE_INFO(user_data);
    /* this callback is called from the main thread */
    /* g_print("queue_add_file: %s\n", fm_file_info_get_disp_name(file)); */
    job->files_to_add = g_slist_prepend(job->files_to_add, fm_file_info_ref(file));
    if(job->delay_add_files_handler == 0)
        job->delay_add_files_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW,
                                       1, emit_found_files, g_object_ref(job), g_object_unref);
    return nullptr;
}

void fm_dir_list_job_add_found_file(FmDirListJob* job, FmFileInfo* file) {
    fm_file_info_list_push_tail(job->files, file);
    if(G_UNLIKELY(job->emit_files_found)) {
        fm_job_call_main_thread(FM_JOB(job), queue_add_file, file);
    }
}
#endif

} // namespace Fm
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2013 Danny Y., Rapptz

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef SOL_OBJECT_HPP
#define SOL_OBJECT_HPP

#include "reference.hpp"
#include "stack.hpp"

namespace sol {
class object : public reference {
public:
    object(lua_State* L, int index = -1): reference(L, index) {}
    object() = default;

    template<typename T>
    T as() const {
        push();
        type_assert(state(), -1, type_of<T>());
        return stack::get<T>(state());
    }

    template<typename T>
    bool is() const {
        push();
        auto expected = type_of<T>();
        auto actual = lua_type(state(), -1);
        return (static_cast<int>(expected) == actual) || (expected == type::poly);
    }
};

inline bool operator==(const object& lhs, const nil_t&) {
    return lhs.is<nil_t>();
}

inline bool operator==(const nil_t&, const object& rhs) {
    return rhs.is<nil_t>();
}

inline bool operator!=(const object& lhs, const nil_t&) {
    return !lhs.is<nil_t>();
}

inline bool operator!=(const nil_t&, const object& rhs) {
    return !rhs.is<nil_t>();
}
} // sol

#endif // SOL_OBJECT_HPP<commit_msg>Add explicit operator bool for sol::object for even easier nil checking<commit_after>// The MIT License (MIT)

// Copyright (c) 2013 Danny Y., Rapptz

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef SOL_OBJECT_HPP
#define SOL_OBJECT_HPP

#include "reference.hpp"
#include "stack.hpp"

namespace sol {
class object : public reference {
public:
    object(lua_State* L, int index = -1): reference(L, index) {}
    object() = default;

    template<typename T>
    T as() const {
        push();
        type_assert(state(), -1, type_of<T>());
        return stack::get<T>(state());
    }

    template<typename T>
    bool is() const {
        push();
        auto expected = type_of<T>();
        auto actual = lua_type(state(), -1);
        return (static_cast<int>(expected) == actual) || (expected == type::poly);
    }

    explicit operator bool() const {
        return is<nil_t>();
    }
};

inline bool operator==(const object& lhs, const nil_t&) {
    return lhs.is<nil_t>();
}

inline bool operator==(const nil_t&, const object& rhs) {
    return rhs.is<nil_t>();
}

inline bool operator!=(const object& lhs, const nil_t&) {
    return !lhs.is<nil_t>();
}

inline bool operator!=(const nil_t&, const object& rhs) {
    return !rhs.is<nil_t>();
}
} // sol

#endif // SOL_OBJECT_HPP<|endoftext|>