<commit_before>#include <stdarg.h>

#include <list>

#include "errors.hpp"
#include <boost/bind.hpp>
#include <boost/tokenizer.hpp>

#include "arch/runtime/coroutines.hpp"
#include "containers/scoped.hpp"
#include "logger.hpp"
#include "perfmon/core.hpp"

/* Constructor and destructor register and deregister the perfmon. */
perfmon_t::perfmon_t() {
}

perfmon_t::~perfmon_t() {
}

struct stats_collection_context_t : public home_thread_mixin_t {
    stats_collection_context_t(rwi_lock_t *constituents_lock, size_t size)
        : contexts(new void *[size]), lock_sentry(constituents_lock) { }

    ~stats_collection_context_t() {
        delete[] contexts;
    }

    void **contexts;
private:
    rwi_lock_t::read_acq_t lock_sentry;
};

perfmon_collection_t::perfmon_collection_t() {
}

void *perfmon_collection_t::begin_stats() {
    stats_collection_context_t *ctx;
    {
        on_thread_t thread_switcher(home_thread());
        ctx = new stats_collection_context_t(&constituents_access, constituents.size());
    }

    size_t i = 0;
    for (perfmon_membership_t *p = constituents.head(); p; p = constituents.next(p), ++i) {
        ctx->contexts[i] = p->get()->begin_stats();
    }
    return ctx;
}

void perfmon_collection_t::visit_stats(void *_context) {
    stats_collection_context_t *ctx = reinterpret_cast<stats_collection_context_t*>(_context);
    size_t i = 0;
    for (perfmon_membership_t *p = constituents.head(); p; p = constituents.next(p), ++i) {
        p->get()->visit_stats(ctx->contexts[i]);
    }
}

perfmon_result_t * perfmon_collection_t::end_stats(void *_context) {
    stats_collection_context_t *ctx = reinterpret_cast<stats_collection_context_t*>(_context);

    perfmon_result_t *map;
    perfmon_result_t::alloc_map_result(&map);

    size_t i = 0;
    for (perfmon_membership_t *p = constituents.head(); p; p = constituents.next(p), ++i) {
        perfmon_result_t * stat = p->get()->end_stats(ctx->contexts[i]);
        if (p->splice()) {
            stat->splice_into(map);
            delete stat; // `stat` is empty now, we can delete it safely
        } else {
            map->insert(p->name, stat);
        }
    }

    {
        on_thread_t thread_switcher(home_thread());
        delete ctx; // cleans up, unlocks
    }

    return map;
}

void perfmon_collection_t::add(perfmon_membership_t *perfmon) {
    scoped_ptr_t<on_thread_t> thread_switcher;
    if (coroutines_have_been_initialized()) {
        thread_switcher.init(new on_thread_t(home_thread()));
    }

    rwi_lock_t::write_acq_t write_acq(&constituents_access);
    constituents.push_back(perfmon);
}

void perfmon_collection_t::remove(perfmon_membership_t *perfmon) {
    scoped_ptr_t<on_thread_t> thread_switcher;
    if (coroutines_have_been_initialized()) {
        thread_switcher.init(new on_thread_t(home_thread()));
    }

    rwi_lock_t::write_acq_t write_acq(&constituents_access);
    constituents.remove(perfmon);
}

perfmon_membership_t::perfmon_membership_t(perfmon_collection_t *_parent, perfmon_t *_perfmon, const char *_name, bool _own_the_perfmon)
    : name(_name != NULL ? _name : ""), parent(_parent), perfmon(_perfmon), own_the_perfmon(_own_the_perfmon)
{
    parent->add(this);
}

perfmon_membership_t::perfmon_membership_t(perfmon_collection_t *_parent, perfmon_t *_perfmon, const std::string &_name, bool _own_the_perfmon)
    : name(_name), parent(_parent), perfmon(_perfmon), own_the_perfmon(_own_the_perfmon)
{
    parent->add(this);
}

perfmon_membership_t::~perfmon_membership_t() {
    parent->remove(this);
    if (own_the_perfmon)
        delete perfmon;
}

perfmon_t *perfmon_membership_t::get() {
    return perfmon;
}

bool perfmon_membership_t::splice() {
    return name.length() == 0;
}

perfmon_multi_membership_t::perfmon_multi_membership_t(perfmon_collection_t *collection, perfmon_t *perfmon, const char *name, ...) {
    // Create membership for the first provided perfmon first
    memberships.push_back(new perfmon_membership_t(collection, perfmon, name));

    va_list args;
    va_start(args, name);

    // Now go through varargs list until we read NULL
    while ((perfmon = va_arg(args, perfmon_t *)) != NULL) {
        name = va_arg(args, const char *);
        memberships.push_back(new perfmon_membership_t(collection, perfmon, name));
    }

    va_end(args);
}

perfmon_multi_membership_t::~perfmon_multi_membership_t() {
    for (std::vector<perfmon_membership_t*>::const_iterator it = memberships.begin(); it != memberships.end(); ++it) {
        delete *it;
    }
}

perfmon_result_t::perfmon_result_t() {
    type = type_value;
}

perfmon_result_t::perfmon_result_t(const perfmon_result_t &copyee)
    : type(copyee.type), value_(copyee.value_), map_() {
    for (perfmon_result_t::internal_map_t::const_iterator it = copyee.map_.begin(); it != copyee.map_.end(); ++it) {
        perfmon_result_t *subcopy = new perfmon_result_t(*it->second);
        map_.insert(std::pair<std::string, perfmon_result_t *>(it->first, subcopy));
    }
}

perfmon_result_t::perfmon_result_t(const std::string &s) {
    type = type_value;
    value_ = s;
}

perfmon_result_t::perfmon_result_t(const std::map<std::string, perfmon_result_t *> &m) {
    type = type_map;
    map_ = m;
}

perfmon_result_t::~perfmon_result_t() {
    if (type == type_map) {
        clear_map();
    }
    rassert(map_.empty());
}

void perfmon_result_t::clear_map() {
    for (perfmon_result_t::internal_map_t::iterator it = map_.begin(); it != map_.end(); ++it) {
        delete it->second;
    }
    map_.clear();
}

void perfmon_result_t::erase(perfmon_result_t::iterator it) {
    delete it->second;
    map_.erase(it);
}

perfmon_result_t perfmon_result_t::make_string() {
    return perfmon_result_t(std::string());
}

void perfmon_result_t::alloc_string_result(perfmon_result_t **out) {
    *out = new perfmon_result_t(std::string());
}

perfmon_result_t perfmon_result_t::make_map() {
    return perfmon_result_t(perfmon_result_t::internal_map_t());
}

void perfmon_result_t::alloc_map_result(perfmon_result_t **out) {
    *out = new perfmon_result_t(perfmon_result_t::internal_map_t());
}

std::string *perfmon_result_t::get_string() {
    rassert(type == type_value);
    return &value_;
}

const std::string *perfmon_result_t::get_string() const {
    rassert(type == type_value);
    return &value_;
}

perfmon_result_t::internal_map_t *perfmon_result_t::get_map() {
    rassert(type == type_map);
    return &map_;
}

const perfmon_result_t::internal_map_t *perfmon_result_t::get_map() const {
    rassert(type == type_map);
    return &map_;
}

size_t perfmon_result_t::get_map_size() const {
    rassert(type == type_map);
    return map_.size();
}

bool perfmon_result_t::is_string() const {
    return type == type_value;
}

bool perfmon_result_t::is_map() const {
    return type == type_map;
}

perfmon_result_t::perfmon_result_type_t perfmon_result_t::get_type() const {
    return type;
}

void perfmon_result_t::reset_type(perfmon_result_type_t new_type) {
    value_.clear();
    clear_map();
    type = new_type;
}

std::pair<perfmon_result_t::iterator, bool> perfmon_result_t::insert(const std::string &name, perfmon_result_t *val) {
    std::string s(name);
    perfmon_result_t::internal_map_t *map = get_map();
    rassert(map->count(name) == 0);
    return map->insert(std::pair<std::string, perfmon_result_t *>(s, val));
}

perfmon_result_t::iterator perfmon_result_t::begin() {
    return map_.begin();
}

perfmon_result_t::iterator perfmon_result_t::end() {
    return map_.end();
}

perfmon_result_t::const_iterator perfmon_result_t::begin() const {
    return map_.begin();
}

perfmon_result_t::const_iterator perfmon_result_t::end() const {
    return map_.end();
}

void perfmon_result_t::splice_into(perfmon_result_t *map) {
    rassert(type == type_map);

    // Transfer all elements from the internal map to the passed map.
    // Unfortunately we can't use here std::map::insert(InputIterator first, InputIterator last),
    // because that way we can overwrite an entry in the target map and thus leak a
    // perfmon_result_t value.
    for (const_iterator it = begin(); it != end(); ++it) {
        map->insert(it->first, it->second);
    }
    map_.clear();
}

/* Construct a filter from a set of paths.  Paths are of the form foo/bar/baz,
   where each of those can be a regular expression.  They act a lot like XPath
   expressions, but for perfmon_t objects.  */
perfmon_filter_t::perfmon_filter_t(const std::set<std::string> &paths) {
    typedef boost::escaped_list_separator<char> separator_t;
    typedef boost::tokenizer<separator_t> tokenizer_t;
    separator_t slashes("\\", "/", "");

    for (std::set<std::string>::const_iterator
             str = paths.begin(); str != paths.end(); ++str) {
        regexps.push_back(std::vector<scoped_regex_t *>());
        std::vector<scoped_regex_t *> *path = &regexps.back();
        try {
            tokenizer_t t(*str, slashes);
            for (tokenizer_t::const_iterator it = t.begin(); it != t.end(); ++it) {
                path->push_back(new scoped_regex_t("^"+(*it)+"$"));
            }
        } catch (const boost::escaped_list_error &e) {
            logINF("Error: Could not parse %s (%s), skipping.",
                   sanitize_for_logger(*str).c_str(), e.what());
            continue; //Skip this path
        }
    }
}

perfmon_filter_t::~perfmon_filter_t() {
    for (std::vector<std::vector<scoped_regex_t *> >::const_iterator
             it = regexps.begin(); it != regexps.end(); ++it) {
        for (std::vector<scoped_regex_t *>::const_iterator
                 regexp = it->begin(); regexp != it->end(); ++regexp) {
            delete *regexp;
        }
    }
}

/* Filter a [perfmon_result_t].  [depth] is how deep we are in the paths that
   the [perfmon_filter_t] was constructed from, and [active] is the set of paths
   that are still active (i.e. that haven't failed a match yet).  This should
   only be called by [filter]. */
perfmon_result_t *perfmon_filter_t::subfilter(
    perfmon_result_t *p, size_t depth, std::vector<bool> active) const {
    bool keep_this_perfmon = true;
    if (p->is_string()) {
        std::string *str = p->get_string();
        for (size_t i = 0; i < regexps.size(); ++i) {
            if (!active[i]) continue;
            if (depth >= regexps[i].size()) return p;
            if (depth == regexps[i].size() && regexps[i][depth]->matches(*str)) return p;
        }
        keep_this_perfmon = false;
    } else if (p->is_map()) {
        std::list<perfmon_result_t::iterator> to_delete;
        for (perfmon_result_t::iterator it = p->begin(); it != p->end(); ++it) {
            std::vector<bool> subactive = active;
            bool some_subpath = false;
            for (size_t i = 0; i < regexps.size(); ++i) {
                if (!active[i]) continue;
                if (depth >= regexps[i].size()) return p;
                subactive[i] = regexps[i][depth]->matches(it->first);
                some_subpath |= subactive[i];
            }
            if (some_subpath) it->second = subfilter(it->second, depth+1, subactive);
            if (!some_subpath || !it->second) to_delete.push_back(it);
        }
        for (std::list<perfmon_result_t::iterator>::const_iterator
                 it = to_delete.begin(); it != to_delete.end(); ++it) {
            p->erase(*it);
        }
        if (p->get_map()->empty()) keep_this_perfmon = false;
    }

    if (!keep_this_perfmon && depth > 0) { //Never delete topmost node
        delete p;
        return 0;
    } else {
        return p;
    }
}

void perfmon_filter_t::filter(perfmon_result_t *p) const {
    subfilter(p, 0, std::vector<bool>(regexps.size(), true));
}

perfmon_collection_t &get_global_perfmon_collection() {
    // Getter function so that we can be sure that `collection` is initialized
    // before it is needed, as advised by the C++ FAQ. Otherwise, a `perfmon_t`
    // might be initialized before `collection` was initialized.

    // FIXME: probably use "new" to create the perfmon_collection_t. For more info check out C++ FAQ Lite answer 10.16.
    static perfmon_collection_t collection;
    return collection;
}

<commit_msg>Minor cosmetic improvements.<commit_after>#include <stdarg.h>

#include <list>

#include "errors.hpp"
#include <boost/bind.hpp>
#include <boost/tokenizer.hpp>

#include "arch/runtime/coroutines.hpp"
#include "containers/scoped.hpp"
#include "logger.hpp"
#include "perfmon/core.hpp"

/* Constructor and destructor register and deregister the perfmon. */
perfmon_t::perfmon_t() {
}

perfmon_t::~perfmon_t() {
}

struct stats_collection_context_t : public home_thread_mixin_t {
    stats_collection_context_t(rwi_lock_t *constituents_lock, size_t size)
        : contexts(new void *[size]), lock_sentry(constituents_lock) { }

    ~stats_collection_context_t() {
        delete[] contexts;
    }

    void **contexts;
private:
    rwi_lock_t::read_acq_t lock_sentry;
};

perfmon_collection_t::perfmon_collection_t() {
}

void *perfmon_collection_t::begin_stats() {
    stats_collection_context_t *ctx;
    {
        on_thread_t thread_switcher(home_thread());
        ctx = new stats_collection_context_t(&constituents_access, constituents.size());
    }

    size_t i = 0;
    for (perfmon_membership_t *p = constituents.head(); p; p = constituents.next(p), ++i) {
        ctx->contexts[i] = p->get()->begin_stats();
    }
    return ctx;
}

void perfmon_collection_t::visit_stats(void *_context) {
    stats_collection_context_t *ctx = reinterpret_cast<stats_collection_context_t*>(_context);
    size_t i = 0;
    for (perfmon_membership_t *p = constituents.head(); p; p = constituents.next(p), ++i) {
        p->get()->visit_stats(ctx->contexts[i]);
    }
}

perfmon_result_t * perfmon_collection_t::end_stats(void *_context) {
    stats_collection_context_t *ctx = reinterpret_cast<stats_collection_context_t*>(_context);

    perfmon_result_t *map;
    perfmon_result_t::alloc_map_result(&map);

    size_t i = 0;
    for (perfmon_membership_t *p = constituents.head(); p; p = constituents.next(p), ++i) {
        perfmon_result_t * stat = p->get()->end_stats(ctx->contexts[i]);
        if (p->splice()) {
            stat->splice_into(map);
            delete stat; // `stat` is empty now, we can delete it safely
        } else {
            map->insert(p->name, stat);
        }
    }

    {
        on_thread_t thread_switcher(home_thread());
        delete ctx; // cleans up, unlocks
    }

    return map;
}

void perfmon_collection_t::add(perfmon_membership_t *perfmon) {
    scoped_ptr_t<on_thread_t> thread_switcher;
    if (coroutines_have_been_initialized()) {
        thread_switcher.init(new on_thread_t(home_thread()));
    }

    rwi_lock_t::write_acq_t write_acq(&constituents_access);
    constituents.push_back(perfmon);
}

void perfmon_collection_t::remove(perfmon_membership_t *perfmon) {
    scoped_ptr_t<on_thread_t> thread_switcher;
    if (coroutines_have_been_initialized()) {
        thread_switcher.init(new on_thread_t(home_thread()));
    }

    rwi_lock_t::write_acq_t write_acq(&constituents_access);
    constituents.remove(perfmon);
}

perfmon_membership_t::perfmon_membership_t(perfmon_collection_t *_parent, perfmon_t *_perfmon, const char *_name, bool _own_the_perfmon)
    : name(_name != NULL ? _name : ""), parent(_parent), perfmon(_perfmon), own_the_perfmon(_own_the_perfmon)
{
    parent->add(this);
}

perfmon_membership_t::perfmon_membership_t(perfmon_collection_t *_parent, perfmon_t *_perfmon, const std::string &_name, bool _own_the_perfmon)
    : name(_name), parent(_parent), perfmon(_perfmon), own_the_perfmon(_own_the_perfmon)
{
    parent->add(this);
}

perfmon_membership_t::~perfmon_membership_t() {
    parent->remove(this);
    if (own_the_perfmon)
        delete perfmon;
}

perfmon_t *perfmon_membership_t::get() {
    return perfmon;
}

bool perfmon_membership_t::splice() {
    return name.length() == 0;
}

perfmon_multi_membership_t::perfmon_multi_membership_t(perfmon_collection_t *collection, perfmon_t *perfmon, const char *name, ...) {
    // Create membership for the first provided perfmon first
    memberships.push_back(new perfmon_membership_t(collection, perfmon, name));

    va_list args;
    va_start(args, name);

    // Now go through varargs list until we read NULL
    while ((perfmon = va_arg(args, perfmon_t *)) != NULL) {
        name = va_arg(args, const char *);
        memberships.push_back(new perfmon_membership_t(collection, perfmon, name));
    }

    va_end(args);
}

perfmon_multi_membership_t::~perfmon_multi_membership_t() {
    for (std::vector<perfmon_membership_t*>::const_iterator it = memberships.begin(); it != memberships.end(); ++it) {
        delete *it;
    }
}

perfmon_result_t::perfmon_result_t() {
    type = type_value;
}

perfmon_result_t::perfmon_result_t(const perfmon_result_t &copyee)
    : type(copyee.type), value_(copyee.value_), map_() {
    for (perfmon_result_t::internal_map_t::const_iterator it = copyee.map_.begin(); it != copyee.map_.end(); ++it) {
        perfmon_result_t *subcopy = new perfmon_result_t(*it->second);
        map_.insert(std::pair<std::string, perfmon_result_t *>(it->first, subcopy));
    }
}

perfmon_result_t::perfmon_result_t(const std::string &s) {
    type = type_value;
    value_ = s;
}

perfmon_result_t::perfmon_result_t(const std::map<std::string, perfmon_result_t *> &m) {
    type = type_map;
    map_ = m;
}

perfmon_result_t::~perfmon_result_t() {
    if (type == type_map) {
        clear_map();
    }
    rassert(map_.empty());
}

void perfmon_result_t::clear_map() {
    for (perfmon_result_t::internal_map_t::iterator it = map_.begin(); it != map_.end(); ++it) {
        delete it->second;
    }
    map_.clear();
}

void perfmon_result_t::erase(perfmon_result_t::iterator it) {
    delete it->second;
    map_.erase(it);
}

perfmon_result_t perfmon_result_t::make_string() {
    return perfmon_result_t(std::string());
}

void perfmon_result_t::alloc_string_result(perfmon_result_t **out) {
    *out = new perfmon_result_t(std::string());
}

perfmon_result_t perfmon_result_t::make_map() {
    return perfmon_result_t(perfmon_result_t::internal_map_t());
}

void perfmon_result_t::alloc_map_result(perfmon_result_t **out) {
    *out = new perfmon_result_t(perfmon_result_t::internal_map_t());
}

std::string *perfmon_result_t::get_string() {
    rassert(type == type_value);
    return &value_;
}

const std::string *perfmon_result_t::get_string() const {
    rassert(type == type_value);
    return &value_;
}

perfmon_result_t::internal_map_t *perfmon_result_t::get_map() {
    rassert(type == type_map);
    return &map_;
}

const perfmon_result_t::internal_map_t *perfmon_result_t::get_map() const {
    rassert(type == type_map);
    return &map_;
}

size_t perfmon_result_t::get_map_size() const {
    rassert(type == type_map);
    return map_.size();
}

bool perfmon_result_t::is_string() const {
    return type == type_value;
}

bool perfmon_result_t::is_map() const {
    return type == type_map;
}

perfmon_result_t::perfmon_result_type_t perfmon_result_t::get_type() const {
    return type;
}

void perfmon_result_t::reset_type(perfmon_result_type_t new_type) {
    value_.clear();
    clear_map();
    type = new_type;
}

std::pair<perfmon_result_t::iterator, bool> perfmon_result_t::insert(const std::string &name, perfmon_result_t *val) {
    std::string s(name);
    perfmon_result_t::internal_map_t *map = get_map();
    rassert(map->count(name) == 0);
    return map->insert(std::pair<std::string, perfmon_result_t *>(s, val));
}

perfmon_result_t::iterator perfmon_result_t::begin() {
    return map_.begin();
}

perfmon_result_t::iterator perfmon_result_t::end() {
    return map_.end();
}

perfmon_result_t::const_iterator perfmon_result_t::begin() const {
    return map_.begin();
}

perfmon_result_t::const_iterator perfmon_result_t::end() const {
    return map_.end();
}

void perfmon_result_t::splice_into(perfmon_result_t *map) {
    rassert(type == type_map);

    // Transfer all elements from the internal map to the passed map.
    // Unfortunately we can't use here std::map::insert(InputIterator first, InputIterator last),
    // because that way we can overwrite an entry in the target map and thus leak a
    // perfmon_result_t value.
    for (const_iterator it = begin(); it != end(); ++it) {
        map->insert(it->first, it->second);
    }
    map_.clear();
}

/* Construct a filter from a set of paths.  Paths are of the form foo/bar/baz,
   where each of those can be a regular expression.  They act a lot like XPath
   expressions, but for perfmon_t objects.  */
perfmon_filter_t::perfmon_filter_t(const std::set<std::string> &paths) {
    typedef boost::escaped_list_separator<char> separator_t;
    typedef boost::tokenizer<separator_t> tokenizer_t;
    separator_t slashes("\\", "/", "");

    for (std::set<std::string>::const_iterator
             str = paths.begin(); str != paths.end(); ++str) {
        regexps.push_back(std::vector<scoped_regex_t *>());
        std::vector<scoped_regex_t *> *path = &regexps.back();
        try {
            tokenizer_t t(*str, slashes);
            for (tokenizer_t::const_iterator it = t.begin(); it != t.end(); ++it) {
                path->push_back(new scoped_regex_t("^"+(*it)+"$"));
            }
        } catch (const boost::escaped_list_error &e) {
            logINF("Error: Could not parse %s (%s), skipping.",
                   sanitize_for_logger(*str).c_str(), e.what());
            continue; //Skip this path
        }
    }
}

perfmon_filter_t::~perfmon_filter_t() {
    for (std::vector<std::vector<scoped_regex_t *> >::const_iterator
             it = regexps.begin(); it != regexps.end(); ++it) {
        for (std::vector<scoped_regex_t *>::const_iterator
                 regexp = it->begin(); regexp != it->end(); ++regexp) {
            delete *regexp;
        }
    }
}

/* Filter a [perfmon_result_t].  [depth] is how deep we are in the paths that
   the [perfmon_filter_t] was constructed from, and [active] is the set of paths
   that are still active (i.e. that haven't failed a match yet).  This should
   only be called by [filter]. */
perfmon_result_t *perfmon_filter_t::subfilter(
    perfmon_result_t *p, size_t depth, std::vector<bool> active) const {
    bool keep_this_perfmon = true;
    if (p->is_string()) {
        std::string *str = p->get_string();
        for (size_t i = 0; i < regexps.size(); ++i) {
            if (!active[i]) continue;
            if (depth >= regexps[i].size()) return p;
            if (depth == regexps[i].size() && regexps[i][depth]->matches(*str)) return p;
        }
        keep_this_perfmon = false;
    } else if (p->is_map()) {
        std::list<perfmon_result_t::iterator> to_delete;
        for (perfmon_result_t::iterator it = p->begin(); it != p->end(); ++it) {
            std::vector<bool> subactive = active;
            bool some_subpath = false;
            for (size_t i = 0; i < regexps.size(); ++i) {
                if (!active[i]) continue;
                if (depth >= regexps[i].size()) return p;
                subactive[i] = regexps[i][depth]->matches(it->first);
                some_subpath |= subactive[i];
            }
            if (some_subpath) it->second = subfilter(it->second, depth + 1, subactive);
            if (!some_subpath || !it->second) to_delete.push_back(it);
        }
        for (std::list<perfmon_result_t::iterator>::const_iterator
                 it = to_delete.begin(); it != to_delete.end(); ++it) {
            p->erase(*it);
        }
        if (p->get_map()->empty()) keep_this_perfmon = false;
    }

    if (!keep_this_perfmon && depth > 0) { //Never delete topmost node
        delete p;
        return 0;
    } else {
        return p;
    }
}

void perfmon_filter_t::filter(perfmon_result_t *p) const {
    subfilter(p, 0, std::vector<bool>(regexps.size(), true));
}

perfmon_collection_t &get_global_perfmon_collection() {
    // Getter function so that we can be sure that `collection` is initialized
    // before it is needed, as advised by the C++ FAQ. Otherwise, a `perfmon_t`
    // might be initialized before `collection` was initialized.

    // FIXME: probably use "new" to create the perfmon_collection_t. For more info check out C++ FAQ Lite answer 10.16.
    static perfmon_collection_t collection;
    return collection;
}

<|endoftext|>


<commit_before>#ifndef _TEST_H_
#define _TEST_H_

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <cmath>

#include "utils.hpp"
#include "debug.hpp"
#include "constants.hpp"

static const u8 *global_keys_pressed = 0;

typedef struct
{
    float red;
    float green;
    float blue;
    float alpha;
} color_t;

static color_t background_color;

typedef struct
{
    double x;
    double y;
    double speed;

    SDL_Texture *image_path;
    int w;
    int h;
} ball_t;

static ball_t global_ball;

void test_initialize(SDL_Renderer *renderer, const u8 *keys_pressed)
{
    global_ball.image_path = IMG_LoadTexture(renderer, BALL_IMG_PATH);
    SDL_QueryTexture(global_ball.image_path, 0, 0, &global_ball.w, &global_ball.h);
    global_ball.speed = BALL_SPEED;

    debug_initialize_text(renderer);

    global_keys_pressed = keys_pressed;

    background_color.red = 127;
    background_color.green = 0;
    background_color.blue = 127;
    background_color.alpha = 255;
}

void test_update(double dt)
{
    if (global_keys_pressed[SDL_SCANCODE_UP])
    {
        global_ball.y -= global_ball.speed * dt;
    }
    if (global_keys_pressed[SDL_SCANCODE_DOWN])
    {
        global_ball.y += global_ball.speed * dt;
    }
    if (global_keys_pressed[SDL_SCANCODE_LEFT])
    {
        global_ball.x -= global_ball.speed * dt;
    }
    if (global_keys_pressed[SDL_SCANCODE_RIGHT])
    {
        global_ball.x += global_ball.speed * dt;
    }

    double multiplier = 0.1;
    static bool red_decreasing = false;
    if (red_decreasing)
    {
        background_color.red -= multiplier * dt;
        if (background_color.red < 5) red_decreasing = false;
    }
    else
    {
        background_color.red += multiplier * dt;
        if (background_color.red > 250) red_decreasing = true;
    }

    static bool blue_decreasing = false;
    if (blue_decreasing)
    {
        background_color.blue -= multiplier * dt;
        if (background_color.blue < 5) blue_decreasing = false;
    }
    else
    {
        background_color.blue += multiplier * dt;
        if (background_color.blue > 250) blue_decreasing = true;
    }
}

void test_render(SDL_Renderer *renderer, double dt)
{
    SDL_SetRenderDrawColor(renderer,
                           (u8) round(background_color.red),
                           (u8) round(background_color.green),
                           (u8) round(background_color.blue),
                           (u8) round(background_color.alpha));
    SDL_RenderClear(renderer);

    SDL_Rect ball_rect;
    ball_rect.x = round(global_ball.x);
    ball_rect.y = round(global_ball.y);
    ball_rect.w = round(BALL_SCALE * global_ball.w);
    ball_rect.h = round(BALL_SCALE * global_ball.h);
    SDL_RenderCopy(renderer, global_ball.image_path, 0, &ball_rect);

    debug_draw_fps(dt);
}

#endif
<commit_msg>Add an example of mouse input handling<commit_after>#ifndef _TEST_H_
#define _TEST_H_

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <cmath>

#include "utils.hpp"
#include "debug.hpp"
#include "constants.hpp"

static const u8 *global_keys_pressed = 0;

typedef struct
{
    float red;
    float green;
    float blue;
    float alpha;
} color_t;

static color_t background_color;

typedef struct
{
    double x;
    double y;
    double speed;
    SDL_Texture *image_path;
    int w;
    int h;
} ball_t;

static ball_t global_keybd_ball;
static ball_t global_mouse_ball;

void test_initialize(SDL_Renderer *renderer, const u8 *keys_pressed)
{
    // keyboard controlled ball
    global_keybd_ball.image_path = IMG_LoadTexture(renderer, BALL_IMG_PATH);
    SDL_QueryTexture(global_keybd_ball.image_path, 0, 0, &global_keybd_ball.w, &global_keybd_ball.h);
    global_keybd_ball.x = 40;
    global_keybd_ball.y = 40;
    global_keybd_ball.speed = BALL_SPEED;

    // keyboard controlled ball
    global_mouse_ball.image_path = IMG_LoadTexture(renderer, BALL_IMG_PATH);
    SDL_QueryTexture(global_mouse_ball.image_path, 0, 0, &global_mouse_ball.w, &global_mouse_ball.h);
    global_mouse_ball.speed = BALL_SPEED; // FIXME: verify if needed

    debug_initialize_text(renderer);

    global_keys_pressed = keys_pressed;

    background_color.red = 127;
    background_color.green = 0;
    background_color.blue = 127;
    background_color.alpha = 255;
}

void test_update(double dt)
{
    if (global_keys_pressed[SDL_SCANCODE_UP])
    {
        global_keybd_ball.y -= global_keybd_ball.speed * dt;
    }
    if (global_keys_pressed[SDL_SCANCODE_DOWN])
    {
        global_keybd_ball.y += global_keybd_ball.speed * dt;
    }
    if (global_keys_pressed[SDL_SCANCODE_LEFT])
    {
        global_keybd_ball.x -= global_keybd_ball.speed * dt;
    }
    if (global_keys_pressed[SDL_SCANCODE_RIGHT])
    {
        global_keybd_ball.x += global_keybd_ball.speed * dt;
    }

    int mouse_x, mouse_y;
    SDL_GetMouseState(&mouse_x, &mouse_y);
    global_mouse_ball.x = mouse_x;
    global_mouse_ball.y = mouse_y;

    double multiplier = 0.1;
    static bool red_decreasing = false;
    if (red_decreasing)
    {
        background_color.red -= multiplier * dt;
        if (background_color.red < 5) red_decreasing = false;
    }
    else
    {
        background_color.red += multiplier * dt;
        if (background_color.red > 250) red_decreasing = true;
    }

    static bool blue_decreasing = false;
    if (blue_decreasing)
    {
        background_color.blue -= multiplier * dt;
        if (background_color.blue < 5) blue_decreasing = false;
    }
    else
    {
        background_color.blue += multiplier * dt;
        if (background_color.blue > 250) blue_decreasing = true;
    }
}

void test_render(SDL_Renderer *renderer, double dt)
{
    // repaint background
    SDL_SetRenderDrawColor(renderer,
                           (u8) round(background_color.red),
                           (u8) round(background_color.green),
                           (u8) round(background_color.blue),
                           (u8) round(background_color.alpha));
    SDL_RenderClear(renderer);

    // draw balls
    {
        SDL_Rect ball_rect;

        // keyboard ball
        ball_rect.x = round(global_keybd_ball.x - (BALL_SCALE * global_keybd_ball.w) / 2);
        ball_rect.y = round(global_keybd_ball.y - (BALL_SCALE * global_keybd_ball.h) / 2);
        ball_rect.w = round(BALL_SCALE * global_keybd_ball.w);
        ball_rect.h = round(BALL_SCALE * global_keybd_ball.h);
        SDL_RenderCopy(renderer, global_keybd_ball.image_path, 0, &ball_rect);

        // mouse ball
        ball_rect.x = round(global_mouse_ball.x - (BALL_SCALE * global_mouse_ball.w) / 2);
        ball_rect.y = round(global_mouse_ball.y - (BALL_SCALE * global_mouse_ball.h) / 2);
        ball_rect.w = round(BALL_SCALE * global_mouse_ball.w);
        ball_rect.h = round(BALL_SCALE * global_mouse_ball.h);
        SDL_RenderCopy(renderer, global_mouse_ball.image_path, 0, &ball_rect);
    }

    debug_draw_fps(dt);
}

#endif
<|endoftext|>


<commit_before>/****************************************************************************
**
** This file is part of the $PACKAGE_NAME$.
**
** Copyright (C) $THISYEAR$ $COMPANY_NAME$.
**
** $QT_EXTENDED_DUAL_LICENSE$
**
****************************************************************************/

#include "qcoordinate.h"

#include <QDateTime>
#include <QHash>
#include <QDebug>
#include <qnumeric.h>

#include <math.h>

static const double QCoordinate_EARTH_MEAN_RADIUS = 6371.0072;

inline static double QCoordinate_degToRad(double deg) { return deg * M_PI / 180; }
inline static double QCoordinate_radToDeg(double rad) { return rad * 180 / M_PI; }

class QCoordinatePrivate
{
public:
    double lat;
    double lng;
    double alt;
};

/*!
    \class QCoordinate
    \brief The QCoordinate class defines a geographical position on the surface of the Earth.

    A QCoordinate is defined by latitude, longitude, and optionally, altitude.

    Use type() to determine whether a coordinate is a 2D coordinate (has
    latitude and longitude only) or 3D coordinate (has latitude, longitude
    and altitude). Use distanceTo() and azimuthTo() to calculate the distance
    and bearing between coordinates.

    The coordinate values should be specified using the WGS84 datum.
*/

/*!
    \enum QCoordinate::CoordinateType
    Defines the types of a coordinate.

    \value InvalidCoordinate An invalid coordinate. A coordinate is invalid if its latitude or longitude values are invalid.
    \value Coordinate2D A coordinate with valid latitude and longitude values.
    \value Coordinate3D A coordinate with valid latitude and longitude values, and also an altitude value.
*/

/*!
    \enum QCoordinate::CoordinateFormat
    Defines the possible formatting options for toString().

    \value DecimalDegrees Returns a string representation of the coordinates in decimal degrees format.
    \value DecimalDegreesWithHemisphere Returns a string representation of the coordinates in decimal degrees format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
    \value DegreesMinutes Returns a string representation of the coordinates in degrees-minutes format.
    \value DegreesMinutesWithHemisphere Returns a string representation of the coordinates in degrees-minutes format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
    \value DegreesMinutesSeconds Returns a string representation of the coordinates in degrees-minutes-seconds format.
    \value DegreesMinutesSecondsWithHemisphere Returns a string representation of the coordinates in degrees-minutes-seconds format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.

    \sa toString()
*/

/*!
    Constructs a coordinate. The coordinate will be invalid until
    setLatitude() and setLongitude() have been called.
*/
QCoordinate::QCoordinate()
    : d(new QCoordinatePrivate)
{
    d->lat = qQNaN();
    d->lng = qQNaN();
    d->alt = qQNaN();
}

/*!
    Constructs a coordinate with the given \a latitude and \a longitude.
*/
QCoordinate::QCoordinate(double latitude, double longitude)
    : d(new QCoordinatePrivate)
{
    d->lat = latitude;
    d->lng = longitude;
    d->alt = qQNaN();
}

/*!
    Constructs a coordinate with the given \a latitude, \a longitude
    and \a altitude.
*/
QCoordinate::QCoordinate(double latitude, double longitude, double altitude)
    : d(new QCoordinatePrivate)
{
    d->lat = latitude;
    d->lng = longitude;
    d->alt = altitude;
}

/*!
    Constructs a coordinate from the contents of \a other.
*/
QCoordinate::QCoordinate(const QCoordinate &other)
    : d(new QCoordinatePrivate)
{
    operator=(other);
}

/*!
    Destroys the coordinate object.
*/
QCoordinate::~QCoordinate()
{
    delete d;
}

/*!
    Assigns \a other to this coordinate and returns a reference to this
    coordinate.
*/
QCoordinate &QCoordinate::operator=(const QCoordinate &other)
{
    if (this == &other)
        return *this;

    d->lat = other.d->lat;
    d->lng = other.d->lng;
    d->alt = other.d->alt;

    return *this;
}

/*!
    Returns true if the type() is Coordinate2D or Coordinate3D.
*/
bool QCoordinate::isValid() const
{
    CoordinateType t = type();
    return t == Coordinate2D || t == Coordinate3D;
}

/*!
    Returns the type of this coordinate.
*/
QCoordinate::CoordinateType QCoordinate::type() const
{
    if ( (d->lat >= -90 && d->lat <= 90)
          && (d->lng >= -180 && d->lng <= 180) ) {
        if (qIsNaN(d->alt))
            return Coordinate2D;
        return Coordinate3D;
    }
    return InvalidCoordinate;
}

/*!
    Returns the latitude, in decimal degrees. The return value is undefined
    if the latitude has not been set.

    A positive latitude indicates the Northern Hemisphere, and a negative
    latitude indicates the Southern Hemisphere.

    \sa setLatitude(), type()
*/
double QCoordinate::latitude() const
{
    return d->lat;
}

/*!
    Sets the latitude (in decimal degrees) to \a latitude. The value should
    be in the WGS84 datum.

    To be valid, the latitude must be between -90 to 90 inclusive.

    \sa latitude()
*/
void QCoordinate::setLatitude(double latitude)
{
    d->lat = latitude;
}

/*!
    Returns the longitude, in decimal degrees. The return value is undefined
    if the longitude has not been set.

    A positive longitude indicates the Eastern Hemisphere, and a negative
    longitude indicates the Western Hemisphere.

    \sa setLongitude(), type()
*/
double QCoordinate::longitude() const
{
    return d->lng;
}

/*!
    Sets the longitude (in decimal degrees) to \a longitude. The value should
    be in the WGS84 datum.

    To be valid, the longitude must be between -180 to 180 inclusive.

    \sa longitude()
*/
void QCoordinate::setLongitude(double longitude)
{
    d->lng = longitude;
}

/*!
    Returns the altitude (meters above sea level).

    The return value is undefined if the altitude has not been set.

    \sa setAltitude(), type()
*/
double QCoordinate::altitude() const
{
    return d->alt;
}

/*!
    Sets the altitude (meters above sea level) to \a altitude.

    \sa altitude()
*/
void QCoordinate::setAltitude(double altitude)
{
    d->alt = altitude;
}

/*!
    Returns the distance (in meters) from this coordinate to the coordinate
    specified by \a other. Altitude is not used in the calculation.

    This calculation returns the great-circle distance between the two
    coordinates, with an assumption that the Earth is spherical for the
    purpose of this calculation.

    Returns 0 if the type of this coordinate or the type of \a other is
    QCoordinate::InvalidCoordinate.
*/
qreal QCoordinate::distanceTo(const QCoordinate &other) const
{
    if (type() == QCoordinate::InvalidCoordinate
            || other.type() == QCoordinate::InvalidCoordinate) {
        return 0;
    }

    // Haversine formula
    double dlat = QCoordinate_degToRad(other.d->lat - d->lat);
    double dlon = QCoordinate_degToRad(other.d->lng - d->lng);
    double y = sin(dlat/2) * sin(dlat/2)
            + cos(QCoordinate_degToRad(d->lat))
            * cos(QCoordinate_degToRad(other.d->lat))
            * sin(dlon/2) * sin(dlon/2);
    double x = 2 * atan2(sqrt(y), sqrt(1-y));
    return qreal(x * QCoordinate_EARTH_MEAN_RADIUS * 1000);
}

/*!
    Returns the azimuth (or bearing) in degrees from this coordinate to the
    coordinate specified by \a other. Altitude is not used in the calculation.

    There is an assumption that the Earth is spherical for the purpose of
    this calculation.

    Returns 0 if the type of this coordinate or the type of \a other is
    QCoordinate::InvalidCoordinate.
*/
qreal QCoordinate::azimuthTo(const QCoordinate &other) const
{
    if (type() == QCoordinate::InvalidCoordinate
            || other.type() == QCoordinate::InvalidCoordinate) {
        return 0;
    }

    double dlon = QCoordinate_degToRad(other.d->lng - d->lng);
    double lat1Rad = QCoordinate_degToRad(d->lat);
    double lat2Rad = QCoordinate_degToRad(other.d->lat);

    double y = sin(dlon) * cos(lat2Rad);
    double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);

    double whole;
    double fraction = modf(QCoordinate_radToDeg(atan2(y, x)), &whole);
    return qreal((int(whole + 360) % 360) + fraction);
}

/*!
    Returns this coordinate as a string in the specified \a format.

    For example, if this coordinate latitude-longitude coordinates of
    (-27.46758\unicode{0xB0}, 153.027892\unicode{0xB0}), these are the
    strings that are returned depending on \a format:

    \table
    \header
        \o \a format value
        \o Returned string
    \row
        \o \l DecimalDegrees
        \o -27.46758\unicode{0xB0}, 153.02789\unicode{0xB0}
    \row
        \o \l DecimalDegreesWithHemisphere
        \o 27.46758\unicode{0xB0} S, 153.02789\unicode{0xB0} E
    \row
        \o \l DegreesMinutes
        \o -27\unicode{0xB0} 28.054', 153\unicode{0xB0} 1.673'
    \row
        \o \l DegreesMinutesWithHemisphere
        \o 27\unicode{0xB0} 28.054 S', 153\unicode{0xB0} 1.673' E
    \row
        \o \l DegreesMinutesSeconds
        \o -27\unicode{0xB0} 28' 3.2", 153\unicode{0xB0} 1' 40.4"
    \row
        \o \l DegreesMinutesSecondsWithHemisphere
        \o 27\unicode{0xB0} 28' 3.2" S, 153\unicode{0xB0} 1' 40.4" E
    \endtable
*/
QString QCoordinate::toString(CoordinateFormat format) const
{
    if (type() == QCoordinate::InvalidCoordinate)
        return QObject::tr("<Invalid coordinate>");

    QString latStr;
    QString longStr;

    double absLat = qAbs(d->lat);
    double absLng = qAbs(d->lng);
    QChar symbol(0x00B0);   // degrees symbol

    switch (format) {
        case DecimalDegrees:
        case DecimalDegreesWithHemisphere:
        {
            latStr = QString::number(absLat, 'f', 5) + symbol;
            longStr = QString::number(absLng, 'f', 5) + symbol;
            break;
        }
        case DegreesMinutes:
        case DegreesMinutesWithHemisphere:
        {
            double latMin = (absLat - int(absLat)) * 60;
            double lngMin = (absLng - int(absLng)) * 60;
            latStr = QString("%1%2 %3'")
                    .arg(QString::number(int(absLat)))
                    .arg(symbol)
                    .arg(QString::number(latMin, 'f', 3));
            longStr = QString("%1%2 %3'")
                    .arg(QString::number(int(absLng)))
                    .arg(symbol)
                    .arg(QString::number(lngMin, 'f', 3));
            break;
        }
        case DegreesMinutesSeconds:
        case DegreesMinutesSecondsWithHemisphere:
        {
            double latMin = (absLat - int(absLat)) * 60;
            double lngMin = (absLng - int(absLng)) * 60;
            double latSec = (latMin - int(latMin)) * 60;
            double lngSec = (lngMin - int(lngMin)) * 60;

            latStr = QString("%1%2 %3' %4\"")
                    .arg(QString::number(int(absLat)))
                    .arg(symbol)
                    .arg(QString::number(int(latMin)))
                    .arg(QString::number(latSec, 'f', 1));
            longStr = QString("%1%2 %3' %4\"")
                    .arg(QString::number(int(absLng)))
                    .arg(symbol)
                    .arg(QString::number(int(lngMin)))
                    .arg(QString::number(lngSec, 'f', 1));
            break;
        }
    }

    // now add the "-" to the start, or append the hemisphere char
    switch (format) {
        case DecimalDegrees:
        case DegreesMinutes:
        case DegreesMinutesSeconds:
        {
            if (d->lat < 0)
                latStr.insert(0, "-");
            if (d->lng < 0)
                longStr.insert(0, "-");
            break;
        }
        case DecimalDegreesWithHemisphere:
        case DegreesMinutesWithHemisphere:
        case DegreesMinutesSecondsWithHemisphere:
        {
            if (d->lat < 0)
                latStr.append(" S");
            else if (d->lat > 0)
                latStr.append(" N");
            if (d->lng < 0)
                longStr.append(" W");
            else if (d->lng > 0)
                longStr.append(" E");
            break;
        }
    }

    return QString("%1, %2").arg(latStr, longStr);
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QCoordinate &coord)
{
    dbg.nospace() << "QCoordinate(" << coord.latitude();
    dbg.nospace() << ", ";
    dbg.nospace() << coord.longitude();
    dbg.nospace() << ", ";
    if (coord.type() == QCoordinate::Coordinate3D)
        dbg.nospace() << coord.altitude();
    else
        dbg.nospace() << '?';
    dbg.nospace() << ')';
    return dbg;
}
#endif
<commit_msg>Define M_PI myself for when it's not found in math.h (causing build failure on Windows).<commit_after>/****************************************************************************
**
** This file is part of the $PACKAGE_NAME$.
**
** Copyright (C) $THISYEAR$ $COMPANY_NAME$.
**
** $QT_EXTENDED_DUAL_LICENSE$
**
****************************************************************************/

#include "qcoordinate.h"

#include <QDateTime>
#include <QHash>
#include <QDebug>
#include <qnumeric.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

static const double QCoordinate_EARTH_MEAN_RADIUS = 6371.0072;

inline static double QCoordinate_degToRad(double deg) { return deg * M_PI / 180; }
inline static double QCoordinate_radToDeg(double rad) { return rad * 180 / M_PI; }

class QCoordinatePrivate
{
public:
    double lat;
    double lng;
    double alt;
};

/*!
    \class QCoordinate
    \brief The QCoordinate class defines a geographical position on the surface of the Earth.

    A QCoordinate is defined by latitude, longitude, and optionally, altitude.

    Use type() to determine whether a coordinate is a 2D coordinate (has
    latitude and longitude only) or 3D coordinate (has latitude, longitude
    and altitude). Use distanceTo() and azimuthTo() to calculate the distance
    and bearing between coordinates.

    The coordinate values should be specified using the WGS84 datum.
*/

/*!
    \enum QCoordinate::CoordinateType
    Defines the types of a coordinate.

    \value InvalidCoordinate An invalid coordinate. A coordinate is invalid if its latitude or longitude values are invalid.
    \value Coordinate2D A coordinate with valid latitude and longitude values.
    \value Coordinate3D A coordinate with valid latitude and longitude values, and also an altitude value.
*/

/*!
    \enum QCoordinate::CoordinateFormat
    Defines the possible formatting options for toString().

    \value DecimalDegrees Returns a string representation of the coordinates in decimal degrees format.
    \value DecimalDegreesWithHemisphere Returns a string representation of the coordinates in decimal degrees format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
    \value DegreesMinutes Returns a string representation of the coordinates in degrees-minutes format.
    \value DegreesMinutesWithHemisphere Returns a string representation of the coordinates in degrees-minutes format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
    \value DegreesMinutesSeconds Returns a string representation of the coordinates in degrees-minutes-seconds format.
    \value DegreesMinutesSecondsWithHemisphere Returns a string representation of the coordinates in degrees-minutes-seconds format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.

    \sa toString()
*/

/*!
    Constructs a coordinate. The coordinate will be invalid until
    setLatitude() and setLongitude() have been called.
*/
QCoordinate::QCoordinate()
    : d(new QCoordinatePrivate)
{
    d->lat = qQNaN();
    d->lng = qQNaN();
    d->alt = qQNaN();
}

/*!
    Constructs a coordinate with the given \a latitude and \a longitude.
*/
QCoordinate::QCoordinate(double latitude, double longitude)
    : d(new QCoordinatePrivate)
{
    d->lat = latitude;
    d->lng = longitude;
    d->alt = qQNaN();
}

/*!
    Constructs a coordinate with the given \a latitude, \a longitude
    and \a altitude.
*/
QCoordinate::QCoordinate(double latitude, double longitude, double altitude)
    : d(new QCoordinatePrivate)
{
    d->lat = latitude;
    d->lng = longitude;
    d->alt = altitude;
}

/*!
    Constructs a coordinate from the contents of \a other.
*/
QCoordinate::QCoordinate(const QCoordinate &other)
    : d(new QCoordinatePrivate)
{
    operator=(other);
}

/*!
    Destroys the coordinate object.
*/
QCoordinate::~QCoordinate()
{
    delete d;
}

/*!
    Assigns \a other to this coordinate and returns a reference to this
    coordinate.
*/
QCoordinate &QCoordinate::operator=(const QCoordinate &other)
{
    if (this == &other)
        return *this;

    d->lat = other.d->lat;
    d->lng = other.d->lng;
    d->alt = other.d->alt;

    return *this;
}

/*!
    Returns true if the type() is Coordinate2D or Coordinate3D.
*/
bool QCoordinate::isValid() const
{
    CoordinateType t = type();
    return t == Coordinate2D || t == Coordinate3D;
}

/*!
    Returns the type of this coordinate.
*/
QCoordinate::CoordinateType QCoordinate::type() const
{
    if ( (d->lat >= -90 && d->lat <= 90)
          && (d->lng >= -180 && d->lng <= 180) ) {
        if (qIsNaN(d->alt))
            return Coordinate2D;
        return Coordinate3D;
    }
    return InvalidCoordinate;
}

/*!
    Returns the latitude, in decimal degrees. The return value is undefined
    if the latitude has not been set.

    A positive latitude indicates the Northern Hemisphere, and a negative
    latitude indicates the Southern Hemisphere.

    \sa setLatitude(), type()
*/
double QCoordinate::latitude() const
{
    return d->lat;
}

/*!
    Sets the latitude (in decimal degrees) to \a latitude. The value should
    be in the WGS84 datum.

    To be valid, the latitude must be between -90 to 90 inclusive.

    \sa latitude()
*/
void QCoordinate::setLatitude(double latitude)
{
    d->lat = latitude;
}

/*!
    Returns the longitude, in decimal degrees. The return value is undefined
    if the longitude has not been set.

    A positive longitude indicates the Eastern Hemisphere, and a negative
    longitude indicates the Western Hemisphere.

    \sa setLongitude(), type()
*/
double QCoordinate::longitude() const
{
    return d->lng;
}

/*!
    Sets the longitude (in decimal degrees) to \a longitude. The value should
    be in the WGS84 datum.

    To be valid, the longitude must be between -180 to 180 inclusive.

    \sa longitude()
*/
void QCoordinate::setLongitude(double longitude)
{
    d->lng = longitude;
}

/*!
    Returns the altitude (meters above sea level).

    The return value is undefined if the altitude has not been set.

    \sa setAltitude(), type()
*/
double QCoordinate::altitude() const
{
    return d->alt;
}

/*!
    Sets the altitude (meters above sea level) to \a altitude.

    \sa altitude()
*/
void QCoordinate::setAltitude(double altitude)
{
    d->alt = altitude;
}

/*!
    Returns the distance (in meters) from this coordinate to the coordinate
    specified by \a other. Altitude is not used in the calculation.

    This calculation returns the great-circle distance between the two
    coordinates, with an assumption that the Earth is spherical for the
    purpose of this calculation.

    Returns 0 if the type of this coordinate or the type of \a other is
    QCoordinate::InvalidCoordinate.
*/
qreal QCoordinate::distanceTo(const QCoordinate &other) const
{
    if (type() == QCoordinate::InvalidCoordinate
            || other.type() == QCoordinate::InvalidCoordinate) {
        return 0;
    }

    // Haversine formula
    double dlat = QCoordinate_degToRad(other.d->lat - d->lat);
    double dlon = QCoordinate_degToRad(other.d->lng - d->lng);
    double y = sin(dlat/2) * sin(dlat/2)
            + cos(QCoordinate_degToRad(d->lat))
            * cos(QCoordinate_degToRad(other.d->lat))
            * sin(dlon/2) * sin(dlon/2);
    double x = 2 * atan2(sqrt(y), sqrt(1-y));
    return qreal(x * QCoordinate_EARTH_MEAN_RADIUS * 1000);
}

/*!
    Returns the azimuth (or bearing) in degrees from this coordinate to the
    coordinate specified by \a other. Altitude is not used in the calculation.

    There is an assumption that the Earth is spherical for the purpose of
    this calculation.

    Returns 0 if the type of this coordinate or the type of \a other is
    QCoordinate::InvalidCoordinate.
*/
qreal QCoordinate::azimuthTo(const QCoordinate &other) const
{
    if (type() == QCoordinate::InvalidCoordinate
            || other.type() == QCoordinate::InvalidCoordinate) {
        return 0;
    }

    double dlon = QCoordinate_degToRad(other.d->lng - d->lng);
    double lat1Rad = QCoordinate_degToRad(d->lat);
    double lat2Rad = QCoordinate_degToRad(other.d->lat);

    double y = sin(dlon) * cos(lat2Rad);
    double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);

    double whole;
    double fraction = modf(QCoordinate_radToDeg(atan2(y, x)), &whole);
    return qreal((int(whole + 360) % 360) + fraction);
}

/*!
    Returns this coordinate as a string in the specified \a format.

    For example, if this coordinate latitude-longitude coordinates of
    (-27.46758\unicode{0xB0}, 153.027892\unicode{0xB0}), these are the
    strings that are returned depending on \a format:

    \table
    \header
        \o \a format value
        \o Returned string
    \row
        \o \l DecimalDegrees
        \o -27.46758\unicode{0xB0}, 153.02789\unicode{0xB0}
    \row
        \o \l DecimalDegreesWithHemisphere
        \o 27.46758\unicode{0xB0} S, 153.02789\unicode{0xB0} E
    \row
        \o \l DegreesMinutes
        \o -27\unicode{0xB0} 28.054', 153\unicode{0xB0} 1.673'
    \row
        \o \l DegreesMinutesWithHemisphere
        \o 27\unicode{0xB0} 28.054 S', 153\unicode{0xB0} 1.673' E
    \row
        \o \l DegreesMinutesSeconds
        \o -27\unicode{0xB0} 28' 3.2", 153\unicode{0xB0} 1' 40.4"
    \row
        \o \l DegreesMinutesSecondsWithHemisphere
        \o 27\unicode{0xB0} 28' 3.2" S, 153\unicode{0xB0} 1' 40.4" E
    \endtable
*/
QString QCoordinate::toString(CoordinateFormat format) const
{
    if (type() == QCoordinate::InvalidCoordinate)
        return QObject::tr("<Invalid coordinate>");

    QString latStr;
    QString longStr;

    double absLat = qAbs(d->lat);
    double absLng = qAbs(d->lng);
    QChar symbol(0x00B0);   // degrees symbol

    switch (format) {
        case DecimalDegrees:
        case DecimalDegreesWithHemisphere:
        {
            latStr = QString::number(absLat, 'f', 5) + symbol;
            longStr = QString::number(absLng, 'f', 5) + symbol;
            break;
        }
        case DegreesMinutes:
        case DegreesMinutesWithHemisphere:
        {
            double latMin = (absLat - int(absLat)) * 60;
            double lngMin = (absLng - int(absLng)) * 60;
            latStr = QString("%1%2 %3'")
                    .arg(QString::number(int(absLat)))
                    .arg(symbol)
                    .arg(QString::number(latMin, 'f', 3));
            longStr = QString("%1%2 %3'")
                    .arg(QString::number(int(absLng)))
                    .arg(symbol)
                    .arg(QString::number(lngMin, 'f', 3));
            break;
        }
        case DegreesMinutesSeconds:
        case DegreesMinutesSecondsWithHemisphere:
        {
            double latMin = (absLat - int(absLat)) * 60;
            double lngMin = (absLng - int(absLng)) * 60;
            double latSec = (latMin - int(latMin)) * 60;
            double lngSec = (lngMin - int(lngMin)) * 60;

            latStr = QString("%1%2 %3' %4\"")
                    .arg(QString::number(int(absLat)))
                    .arg(symbol)
                    .arg(QString::number(int(latMin)))
                    .arg(QString::number(latSec, 'f', 1));
            longStr = QString("%1%2 %3' %4\"")
                    .arg(QString::number(int(absLng)))
                    .arg(symbol)
                    .arg(QString::number(int(lngMin)))
                    .arg(QString::number(lngSec, 'f', 1));
            break;
        }
    }

    // now add the "-" to the start, or append the hemisphere char
    switch (format) {
        case DecimalDegrees:
        case DegreesMinutes:
        case DegreesMinutesSeconds:
        {
            if (d->lat < 0)
                latStr.insert(0, "-");
            if (d->lng < 0)
                longStr.insert(0, "-");
            break;
        }
        case DecimalDegreesWithHemisphere:
        case DegreesMinutesWithHemisphere:
        case DegreesMinutesSecondsWithHemisphere:
        {
            if (d->lat < 0)
                latStr.append(" S");
            else if (d->lat > 0)
                latStr.append(" N");
            if (d->lng < 0)
                longStr.append(" W");
            else if (d->lng > 0)
                longStr.append(" E");
            break;
        }
    }

    return QString("%1, %2").arg(latStr, longStr);
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QCoordinate &coord)
{
    dbg.nospace() << "QCoordinate(" << coord.latitude();
    dbg.nospace() << ", ";
    dbg.nospace() << coord.longitude();
    dbg.nospace() << ", ";
    if (coord.type() == QCoordinate::Coordinate3D)
        dbg.nospace() << coord.altitude();
    else
        dbg.nospace() << '?';
    dbg.nospace() << ')';
    return dbg;
}
#endif
<|endoftext|>


<commit_before>#include "queueloader.h"

#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <libgen.h>
#include <unistd.h>

#include "config.h"
#include "configcontainer.h"
#include "logger.h"
#include "stflpp.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

QueueLoader::QueueLoader(const std::string& file, ConfigContainer& cfg_,
	std::function<void()> cb_require_view_update_)
	: queuefile(file)
	, cfg(cfg_)
	, cb_require_view_update(cb_require_view_update_)
{
}

void QueueLoader::reload(std::vector<Download>& downloads, bool remove_unplayed)
{
	std::vector<Download> dltemp;
	std::vector<Download> deletion_list;
	std::fstream f;

	for (const auto& dl : downloads) {
		// we are not allowed to reload if a download is in progress!
		if (dl.status() == DlStatus::DOWNLOADING) {
			LOG(Level::INFO,
				"QueueLoader::reload: aborting reload due to "
				"DlStatus::DOWNLOADING status");
			return;
		}
		bool keep_entry = false;
		switch (dl.status()) {
		case DlStatus::QUEUED:
		case DlStatus::CANCELLED:
		case DlStatus::FAILED:
		case DlStatus::ALREADY_DOWNLOADED:
		case DlStatus::READY:
			LOG(Level::DEBUG,
				"QueueLoader::reload: storing %s to new vector",
				dl.url());
			keep_entry = true;
			break;
		case DlStatus::PLAYED:
		case DlStatus::FINISHED:
			if (!remove_unplayed) {
				LOG(Level::DEBUG,
					"QueueLoader::reload: storing %s to "
					"new "
					"vector",
					dl.url());
				keep_entry = true;
			}
			break;
		default:
			break;
		}

		if (keep_entry) {
			dltemp.push_back(dl);
		} else {
			deletion_list.push_back(dl);
		}
	}

	f.open(queuefile.c_str(), std::fstream::in);
	bool comments_ignored = false;
	if (f.is_open()) {
		std::string line;
		do {
			std::getline(f, line);
			if (!f.eof() && line.length() > 0) {
				LOG(Level::DEBUG,
					"QueueLoader::reload: loaded `%s' from "
					"queue file",
					line);
				std::vector<std::string> fields =
					utils::tokenize_quoted(line);
				bool url_found = false;

				if (fields.empty()) {
					if (!comments_ignored) {
						std::cout << strprintf::fmt(
								_("WARNING: Comment found "
									"in %s. The queue file is regenerated "
									"when podboat exits and comments will "
									"be deleted. Press Enter to continue or "
									"Ctrl+C to abort"),
								queuefile)
							<< std::endl;
						std::cin.ignore();
						comments_ignored = true;
					}
					continue;
				}

				for (const auto& dl : dltemp) {
					if (fields[0] == dl.url()) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' in old "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				for (const auto& dl : deletion_list) {
					if (fields[0] == dl.url()) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' in scheduled for deletion "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				if (!url_found) {
					LOG(Level::INFO,
						"QueueLoader::reload: found "
						"`%s' "
						"nowhere -> storing to new "
						"vector",
						line);
					Download d(cb_require_view_update);
					std::string fn;
					if (fields.size() == 1) {
						fn = get_filename(fields[0]);
					} else {
						fn = fields[1];
					}
					d.set_filename(fn);
					if (access(fn.c_str(), F_OK) == 0) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as already "
							"downloaded",
							fn);
						if (fields.size() >= 3) {
							if (fields[2] ==
								"downloaded")
								d.set_status(
									DlStatus::
									READY);
							if (fields[2] ==
								"played")
								d.set_status(
									DlStatus::
									PLAYED);
							if (fields[2] ==
								"finished")
								d.set_status(
									DlStatus::
									FINISHED);
						} else
							d.set_status(DlStatus::
								ALREADY_DOWNLOADED); // TODO: scrap DlStatus::ALREADY_DOWNLOADED state
					} else if (
						access((fn +
								ConfigContainer::
								PARTIAL_FILE_SUFFIX)
							.c_str(),
							F_OK) == 0) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as partially "
							"downloaded",
							fn +
							ConfigContainer::
							PARTIAL_FILE_SUFFIX);
						d.set_status(DlStatus::
							ALREADY_DOWNLOADED);
					}

					d.set_url(fields[0]);
					dltemp.push_back(d);
				}
			}
		} while (!f.eof());
		f.close();
	}

	f.open(queuefile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (const auto& dl : dltemp) {
			f << dl.url() << " " << utils::quote(dl.filename());
			if (dl.status() == DlStatus::READY) {
				f << " downloaded";
			}
			if (dl.status() == DlStatus::PLAYED) {
				f << " played";
			}
			if (dl.status() == DlStatus::FINISHED) {
				f << " finished";
			}
			f << std::endl;
		}
		f.close();
	}

	if (cfg.get_configvalue_as_bool("delete-played-files")) {
		for (const auto& dl : deletion_list) {
			const std::string filename = dl.filename();
			LOG(Level::INFO,
				"Deleting file %s",
				filename);
			if (std::remove(filename.c_str()) != 0) {
				if (errno != ENOENT) {
					LOG(Level::ERROR,
						"Failed to delete file %s, error code: %d (%s)",
						filename, errno, strerror(errno));
				}
			}
		}
	}

	downloads = dltemp;
}

std::string QueueLoader::get_filename(const std::string& str)
{
	std::string fn = cfg.get_configvalue("download-path");

	if (fn[fn.length() - 1] != NEWSBEUTER_PATH_SEP) {
		fn.push_back(NEWSBEUTER_PATH_SEP);
	}
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char* base = basename(buf);
	if (!base || strlen(base) == 0) {
		time_t t = time(nullptr);
		fn.append(utils::mt_strf_localtime("%Y-%b-%d-%H%M%S.unknown", t));
	} else {
		fn.append(utils::replace_all(base, "'", "%27"));
	}
	return fn;
}

} // namespace podboat
<commit_msg>pb-purge: Don't delete played files when they are not marked as finished<commit_after>#include "queueloader.h"

#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <libgen.h>
#include <unistd.h>

#include "config.h"
#include "configcontainer.h"
#include "logger.h"
#include "stflpp.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

QueueLoader::QueueLoader(const std::string& file, ConfigContainer& cfg_,
	std::function<void()> cb_require_view_update_)
	: queuefile(file)
	, cfg(cfg_)
	, cb_require_view_update(cb_require_view_update_)
{
}

void QueueLoader::reload(std::vector<Download>& downloads, bool remove_unplayed)
{
	std::vector<Download> dltemp;
	std::vector<Download> deletion_list;
	std::fstream f;

	for (const auto& dl : downloads) {
		// we are not allowed to reload if a download is in progress!
		if (dl.status() == DlStatus::DOWNLOADING) {
			LOG(Level::INFO,
				"QueueLoader::reload: aborting reload due to "
				"DlStatus::DOWNLOADING status");
			return;
		}
		bool keep_entry = false;
		switch (dl.status()) {
		case DlStatus::QUEUED:
		case DlStatus::CANCELLED:
		case DlStatus::FAILED:
		case DlStatus::ALREADY_DOWNLOADED:
		case DlStatus::READY:
		case DlStatus::PLAYED:
			LOG(Level::DEBUG,
				"QueueLoader::reload: storing %s to new vector",
				dl.url());
			keep_entry = true;
			break;
		case DlStatus::FINISHED:
			if (!remove_unplayed) {
				LOG(Level::DEBUG,
					"QueueLoader::reload: storing %s to "
					"new "
					"vector",
					dl.url());
				keep_entry = true;
			}
			break;
		default:
			break;
		}

		if (keep_entry) {
			dltemp.push_back(dl);
		} else {
			deletion_list.push_back(dl);
		}
	}

	f.open(queuefile.c_str(), std::fstream::in);
	bool comments_ignored = false;
	if (f.is_open()) {
		std::string line;
		do {
			std::getline(f, line);
			if (!f.eof() && line.length() > 0) {
				LOG(Level::DEBUG,
					"QueueLoader::reload: loaded `%s' from "
					"queue file",
					line);
				std::vector<std::string> fields =
					utils::tokenize_quoted(line);
				bool url_found = false;

				if (fields.empty()) {
					if (!comments_ignored) {
						std::cout << strprintf::fmt(
								_("WARNING: Comment found "
									"in %s. The queue file is regenerated "
									"when podboat exits and comments will "
									"be deleted. Press Enter to continue or "
									"Ctrl+C to abort"),
								queuefile)
							<< std::endl;
						std::cin.ignore();
						comments_ignored = true;
					}
					continue;
				}

				for (const auto& dl : dltemp) {
					if (fields[0] == dl.url()) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' in old "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				for (const auto& dl : deletion_list) {
					if (fields[0] == dl.url()) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' in scheduled for deletion "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				if (!url_found) {
					LOG(Level::INFO,
						"QueueLoader::reload: found "
						"`%s' "
						"nowhere -> storing to new "
						"vector",
						line);
					Download d(cb_require_view_update);
					std::string fn;
					if (fields.size() == 1) {
						fn = get_filename(fields[0]);
					} else {
						fn = fields[1];
					}
					d.set_filename(fn);
					if (access(fn.c_str(), F_OK) == 0) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as already "
							"downloaded",
							fn);
						if (fields.size() >= 3) {
							if (fields[2] ==
								"downloaded")
								d.set_status(
									DlStatus::
									READY);
							if (fields[2] ==
								"played")
								d.set_status(
									DlStatus::
									PLAYED);
							if (fields[2] ==
								"finished")
								d.set_status(
									DlStatus::
									FINISHED);
						} else
							d.set_status(DlStatus::
								ALREADY_DOWNLOADED); // TODO: scrap DlStatus::ALREADY_DOWNLOADED state
					} else if (
						access((fn +
								ConfigContainer::
								PARTIAL_FILE_SUFFIX)
							.c_str(),
							F_OK) == 0) {
						LOG(Level::INFO,
							"QueueLoader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as partially "
							"downloaded",
							fn +
							ConfigContainer::
							PARTIAL_FILE_SUFFIX);
						d.set_status(DlStatus::
							ALREADY_DOWNLOADED);
					}

					d.set_url(fields[0]);
					dltemp.push_back(d);
				}
			}
		} while (!f.eof());
		f.close();
	}

	f.open(queuefile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (const auto& dl : dltemp) {
			f << dl.url() << " " << utils::quote(dl.filename());
			if (dl.status() == DlStatus::READY) {
				f << " downloaded";
			}
			if (dl.status() == DlStatus::PLAYED) {
				f << " played";
			}
			if (dl.status() == DlStatus::FINISHED) {
				f << " finished";
			}
			f << std::endl;
		}
		f.close();
	}

	if (cfg.get_configvalue_as_bool("delete-played-files")) {
		for (const auto& dl : deletion_list) {
			const std::string filename = dl.filename();
			LOG(Level::INFO,
				"Deleting file %s",
				filename);
			if (std::remove(filename.c_str()) != 0) {
				if (errno != ENOENT) {
					LOG(Level::ERROR,
						"Failed to delete file %s, error code: %d (%s)",
						filename, errno, strerror(errno));
				}
			}
		}
	}

	downloads = dltemp;
}

std::string QueueLoader::get_filename(const std::string& str)
{
	std::string fn = cfg.get_configvalue("download-path");

	if (fn[fn.length() - 1] != NEWSBEUTER_PATH_SEP) {
		fn.push_back(NEWSBEUTER_PATH_SEP);
	}
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char* base = basename(buf);
	if (!base || strlen(base) == 0) {
		time_t t = time(nullptr);
		fn.append(utils::mt_strf_localtime("%Y-%b-%d-%H%M%S.unknown", t));
	} else {
		fn.append(utils::replace_all(base, "'", "%27"));
	}
	return fn;
}

} // namespace podboat
<|endoftext|>


<commit_before>/*
  +----------------------------------------------------------------------+
  | Swoole                                                               |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.0 of the Apache license,    |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
  | If you did not receive a copy of the Apache2.0 license and are unable|
  | to obtain it through the world-wide-web, please send a note to       |
  | license@swoole.com so we can mail you a copy immediately.            |
  +----------------------------------------------------------------------+
  | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
  +----------------------------------------------------------------------+
*/

#include "swoole_cxx.h"
#include "async.h"

#include "coroutine_c_api.h"
#include "coroutine_socket.h"
#include "coroutine_system.h"

using swoole::CallbackManager;
using swoole::coroutine::Socket;
using swoole::coroutine::System;

#ifdef SW_USE_MALLOC_TRIM
#ifdef __APPLE__
#include <sys/malloc.h>
#else
#include <malloc.h>
#endif
#endif

static void reactor_timeout(swReactor *reactor);
static void reactor_finish(swReactor *reactor);
static void reactor_begin(swReactor *reactor);
static void defer_task_do(swReactor *reactor);
static void defer_task_add(swReactor *reactor, swCallback callback, void *data);

int swReactor_create(swReactor *reactor, int max_event)
{
    int ret;
    bzero(reactor, sizeof(swReactor));

#ifdef HAVE_EPOLL
    ret = swReactorEpoll_create(reactor, max_event);
#elif defined(HAVE_KQUEUE)
    ret = swReactorKqueue_create(reactor, max_event);
#elif defined(HAVE_POLL)
    ret = swReactorPoll_create(reactor, max_event);
#else
    ret = swReactorSelect_create(reactor);
#endif

    reactor->running = 1;

    reactor->onFinish = reactor_finish;
    reactor->onTimeout = reactor_timeout;
    reactor->is_empty = swReactor_empty;
    reactor->can_exit = SwooleG.reactor_can_exit;

    reactor->write = swReactor_write;
    reactor->close = swReactor_close;

    reactor->defer = defer_task_add;
    reactor->defer_tasks = nullptr;

    reactor->default_write_handler = swReactor_onWrite;

    Socket::init_reactor(reactor);
    System::init_reactor(reactor);
    swClient_init_reactor(reactor);

    if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE])
    {
        swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE, reactor);
    }

    return ret;
}

int swReactor_set_handler(swReactor *reactor, int _fdtype, swReactor_handler handle)
{
    int fdtype = swReactor_fdtype(_fdtype);

    if (fdtype >= SW_MAX_FDTYPE)
    {
        swWarn("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE);
        return SW_ERR;
    }

    if (swReactor_event_read(_fdtype))
    {
        reactor->read_handler[fdtype] = handle;
    }
    else if (swReactor_event_write(_fdtype))
    {
        reactor->write_handler[fdtype] = handle;
    }
    else if (swReactor_event_error(_fdtype))
    {
        reactor->error_handler[fdtype] = handle;
    }
    else
    {
        swWarn("unknow fdtype");
        return SW_ERR;
    }

    return SW_OK;
}

int swReactor_empty(swReactor *reactor)
{
    if (reactor->timer && reactor->timer->num > 0)
    {
        return SW_FALSE;
    }
    if (reactor->defer_tasks)
    {
        return SW_FALSE;
    }
    if (swoole_coroutine_wait_count() > 0)
    {
        return SW_FALSE;
    }
    if (SwooleTG.reactor->co_signal_listener_num > 0)
    {
        return SW_FALSE;
    }

    int event_num = reactor->event_num;
    int empty = SW_FALSE;
    //aio thread pool
    if (SwooleTG.aio_init && SwooleTG.aio_task_num == 0)
    {
        event_num--;
    }
    //signalfd
    if (swReactor_isset_handler(reactor, SW_FD_SIGNAL))
    {
        event_num--;
    }
    //no event
    if (event_num == 0)
    {
        empty = SW_TRUE;
    }
    //custom
    if (reactor->can_exit && !reactor->can_exit(reactor))
    {
        empty = SW_FALSE;
    }
    return empty;
}

/**
 * execute when reactor timeout and reactor finish
 */
static void reactor_finish(swReactor *reactor)
{
    //check timer
    if (reactor->check_timer)
    {
        swTimer_select(reactor->timer);
    }
    //defer tasks
    if (reactor->defer_tasks)
    {
        defer_task_do(reactor);
    }
    //callback at the end
    if (reactor->idle_task.callback)
    {
        reactor->idle_task.callback(reactor->idle_task.data);
    }
    //check signal
    if (sw_unlikely(reactor->singal_no))
    {
        swSignal_callback(reactor->singal_no);
        reactor->singal_no = 0;
    }
    //the event loop is empty
    if (reactor->wait_exit && reactor->is_empty(reactor))
    {
        reactor->running = 0;
    }
#ifdef SW_USE_MALLOC_TRIM
    time_t now = time(NULL);
    if (reactor->last_malloc_trim_time < now - SW_MALLOC_TRIM_INTERVAL)
    {
        malloc_trim(SW_MALLOC_TRIM_PAD);
        reactor->last_malloc_trim_time = now;
    }
#endif
}

static void reactor_timeout(swReactor *reactor)
{
    reactor_finish(reactor);
}

void swReactor_activate_future_task(swReactor *reactor)
{
    reactor->onBegin = reactor_begin;
}

static void reactor_begin(swReactor *reactor)
{
    if (reactor->future_task.callback)
    {
        reactor->future_task.callback(reactor->future_task.data);
    }
}

int swReactor_close(swReactor *reactor, swSocket *socket)
{
    if (socket->out_buffer)
    {
        swBuffer_free(socket->out_buffer);
    }
    if (socket->in_buffer)
    {
        swBuffer_free(socket->in_buffer);
    }

    swTraceLog(SW_TRACE_CLOSE, "fd=%d", socket->fd);

    swSocket_free(socket);

    return SW_OK;
}

int swReactor_write(swReactor *reactor, swSocket *socket, const void *buf, int n)
{
    int ret;
    swBuffer *buffer = socket->out_buffer;
    const char *ptr = (const char *) buf;
    int fd = socket->fd;

    if (socket->buffer_size == 0)
    {
        socket->buffer_size = SwooleG.socket_buffer_size;
    }

    if (socket->nonblock == 0)
    {
        swoole_fcntl_set_option(socket->fd, 1, -1);
        socket->nonblock = 1;
    }

    if ((uint32_t) n > socket->buffer_size)
    {
        swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "data is too large, cannot exceed buffer size");
        return SW_ERR;
    }

    if (swBuffer_empty(buffer))
    {
#ifdef SW_USE_OPENSSL
        if (socket->ssl_send)
        {
            goto _do_buffer;
        }
#endif
        _do_send:
        ret = swSocket_send(socket, ptr, n, 0);

        if (ret > 0)
        {
            if (n == ret)
            {
                return ret;
            }
            else
            {
                ptr += ret;
                n -= ret;
                goto _do_buffer;
            }
        }
        else if (swSocket_error(errno) == SW_WAIT)
        {
            _do_buffer:
            if (!socket->out_buffer)
            {
                buffer = swBuffer_new(socket->chunk_size);
                if (!buffer)
                {
                    swWarn("create worker buffer failed");
                    return SW_ERR;
                }
                socket->out_buffer = buffer;
            }

            swReactor_add_write_event(reactor, socket);
            goto _append_buffer;
        }
        else if (errno == EINTR)
        {
            goto _do_send;
        }
        else
        {
            SwooleG.error = errno;
            return SW_ERR;
        }
    }
    else
    {
        _append_buffer:
        if (buffer->length > socket->buffer_size)
        {
            if (socket->dontwait)
            {
                SwooleG.error = SW_ERROR_OUTPUT_BUFFER_OVERFLOW;
                return SW_ERR;
            }
            else
            {
                swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "socket#%d output buffer overflow", fd);
                swYield();
                swSocket_wait(socket->fd, SW_SOCKET_OVERFLOW_WAIT, SW_EVENT_WRITE);
            }
        }

        if (swBuffer_append(buffer, ptr, n) < 0)
        {
            return SW_ERR;
        }
    }
    return SW_OK;
}

int swReactor_onWrite(swReactor *reactor, swEvent *ev)
{
    int ret;

    swSocket *socket = ev->socket;
    swBuffer_chunk *chunk = NULL;
    swBuffer *buffer = socket->out_buffer;

    //send to socket
    while (!swBuffer_empty(buffer))
    {
        chunk = swBuffer_get_chunk(buffer);
        if (chunk->type == SW_CHUNK_CLOSE)
        {
            _close_fd:
            reactor->close(reactor, ev->socket);
            return SW_OK;
        }
        else if (chunk->type == SW_CHUNK_SENDFILE)
        {
            ret = swSocket_onSendfile(socket, chunk);
        }
        else
        {
            ret = swSocket_buffer_send(socket);
        }

        if (ret < 0)
        {
            if (socket->close_wait)
            {
                goto _close_fd;
            }
            else if (socket->send_wait)
            {
                return SW_OK;
            }
        }
    }

    //remove EPOLLOUT event
    if (swBuffer_empty(buffer))
    {
        swReactor_remove_write_event(reactor, ev->socket);
    }

    return SW_OK;
}

int swReactor_wait_write_buffer(swReactor *reactor, swSocket *socket)
{
    swEvent event;

    if (!swBuffer_empty(socket->out_buffer))
    {
        swSocket_set_block(socket);
        event.fd = socket->fd;
        return swReactor_onWrite(reactor, &event);
    }
    return SW_OK;
}

void swReactor_add_destroy_callback(swReactor *reactor, swCallback cb, void *data)
{
    CallbackManager *cm = (CallbackManager *) reactor->destroy_callbacks;
    if (cm == nullptr)
    {
        cm = new CallbackManager;
        reactor->destroy_callbacks = cm;
    }
    cm->append(cb, data);
}

static void defer_task_do(swReactor *reactor)
{
    CallbackManager *cm = (CallbackManager *) reactor->defer_tasks;
    reactor->defer_tasks = nullptr;
    cm->execute();
    delete cm;
}

static void defer_task_add(swReactor *reactor, swCallback callback, void *data)
{
    CallbackManager *cm = (CallbackManager *) reactor->defer_tasks;
    if (cm == nullptr)
    {
        cm = new CallbackManager;
        reactor->defer_tasks = cm;
    }
    cm->append(callback, data);
}

void swReactor_destroy(swReactor *reactor)
{
    if (reactor->destroy_callbacks)
    {
        CallbackManager *cm = (CallbackManager *) reactor->destroy_callbacks;
        cm->execute();
        reactor->destroy_callbacks = nullptr;
        delete cm;
    }
    reactor->free(reactor);
}
<commit_msg>improve: set buffer NULL (#3210)<commit_after>/*
  +----------------------------------------------------------------------+
  | Swoole                                                               |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.0 of the Apache license,    |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
  | If you did not receive a copy of the Apache2.0 license and are unable|
  | to obtain it through the world-wide-web, please send a note to       |
  | license@swoole.com so we can mail you a copy immediately.            |
  +----------------------------------------------------------------------+
  | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
  +----------------------------------------------------------------------+
*/

#include "swoole_cxx.h"
#include "async.h"

#include "coroutine_c_api.h"
#include "coroutine_socket.h"
#include "coroutine_system.h"

using swoole::CallbackManager;
using swoole::coroutine::Socket;
using swoole::coroutine::System;

#ifdef SW_USE_MALLOC_TRIM
#ifdef __APPLE__
#include <sys/malloc.h>
#else
#include <malloc.h>
#endif
#endif

static void reactor_timeout(swReactor *reactor);
static void reactor_finish(swReactor *reactor);
static void reactor_begin(swReactor *reactor);
static void defer_task_do(swReactor *reactor);
static void defer_task_add(swReactor *reactor, swCallback callback, void *data);

int swReactor_create(swReactor *reactor, int max_event)
{
    int ret;
    bzero(reactor, sizeof(swReactor));

#ifdef HAVE_EPOLL
    ret = swReactorEpoll_create(reactor, max_event);
#elif defined(HAVE_KQUEUE)
    ret = swReactorKqueue_create(reactor, max_event);
#elif defined(HAVE_POLL)
    ret = swReactorPoll_create(reactor, max_event);
#else
    ret = swReactorSelect_create(reactor);
#endif

    reactor->running = 1;

    reactor->onFinish = reactor_finish;
    reactor->onTimeout = reactor_timeout;
    reactor->is_empty = swReactor_empty;
    reactor->can_exit = SwooleG.reactor_can_exit;

    reactor->write = swReactor_write;
    reactor->close = swReactor_close;

    reactor->defer = defer_task_add;
    reactor->defer_tasks = nullptr;

    reactor->default_write_handler = swReactor_onWrite;

    Socket::init_reactor(reactor);
    System::init_reactor(reactor);
    swClient_init_reactor(reactor);

    if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE])
    {
        swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE, reactor);
    }

    return ret;
}

int swReactor_set_handler(swReactor *reactor, int _fdtype, swReactor_handler handle)
{
    int fdtype = swReactor_fdtype(_fdtype);

    if (fdtype >= SW_MAX_FDTYPE)
    {
        swWarn("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE);
        return SW_ERR;
    }

    if (swReactor_event_read(_fdtype))
    {
        reactor->read_handler[fdtype] = handle;
    }
    else if (swReactor_event_write(_fdtype))
    {
        reactor->write_handler[fdtype] = handle;
    }
    else if (swReactor_event_error(_fdtype))
    {
        reactor->error_handler[fdtype] = handle;
    }
    else
    {
        swWarn("unknow fdtype");
        return SW_ERR;
    }

    return SW_OK;
}

int swReactor_empty(swReactor *reactor)
{
    if (reactor->timer && reactor->timer->num > 0)
    {
        return SW_FALSE;
    }
    if (reactor->defer_tasks)
    {
        return SW_FALSE;
    }
    if (swoole_coroutine_wait_count() > 0)
    {
        return SW_FALSE;
    }
    if (SwooleTG.reactor->co_signal_listener_num > 0)
    {
        return SW_FALSE;
    }

    int event_num = reactor->event_num;
    int empty = SW_FALSE;
    //aio thread pool
    if (SwooleTG.aio_init && SwooleTG.aio_task_num == 0)
    {
        event_num--;
    }
    //signalfd
    if (swReactor_isset_handler(reactor, SW_FD_SIGNAL))
    {
        event_num--;
    }
    //no event
    if (event_num == 0)
    {
        empty = SW_TRUE;
    }
    //custom
    if (reactor->can_exit && !reactor->can_exit(reactor))
    {
        empty = SW_FALSE;
    }
    return empty;
}

/**
 * execute when reactor timeout and reactor finish
 */
static void reactor_finish(swReactor *reactor)
{
    //check timer
    if (reactor->check_timer)
    {
        swTimer_select(reactor->timer);
    }
    //defer tasks
    if (reactor->defer_tasks)
    {
        defer_task_do(reactor);
    }
    //callback at the end
    if (reactor->idle_task.callback)
    {
        reactor->idle_task.callback(reactor->idle_task.data);
    }
    //check signal
    if (sw_unlikely(reactor->singal_no))
    {
        swSignal_callback(reactor->singal_no);
        reactor->singal_no = 0;
    }
    //the event loop is empty
    if (reactor->wait_exit && reactor->is_empty(reactor))
    {
        reactor->running = 0;
    }
#ifdef SW_USE_MALLOC_TRIM
    time_t now = time(NULL);
    if (reactor->last_malloc_trim_time < now - SW_MALLOC_TRIM_INTERVAL)
    {
        malloc_trim(SW_MALLOC_TRIM_PAD);
        reactor->last_malloc_trim_time = now;
    }
#endif
}

static void reactor_timeout(swReactor *reactor)
{
    reactor_finish(reactor);
}

void swReactor_activate_future_task(swReactor *reactor)
{
    reactor->onBegin = reactor_begin;
}

static void reactor_begin(swReactor *reactor)
{
    if (reactor->future_task.callback)
    {
        reactor->future_task.callback(reactor->future_task.data);
    }
}

int swReactor_close(swReactor *reactor, swSocket *socket)
{
    if (socket->out_buffer)
    {
        swBuffer_free(socket->out_buffer);
        socket->out_buffer = NULL;
    }
    if (socket->in_buffer)
    {
        swBuffer_free(socket->in_buffer);
        socket->in_buffer = NULL;
    }

    swTraceLog(SW_TRACE_CLOSE, "fd=%d", socket->fd);

    swSocket_free(socket);

    return SW_OK;
}

int swReactor_write(swReactor *reactor, swSocket *socket, const void *buf, int n)
{
    int ret;
    swBuffer *buffer = socket->out_buffer;
    const char *ptr = (const char *) buf;
    int fd = socket->fd;

    if (socket->buffer_size == 0)
    {
        socket->buffer_size = SwooleG.socket_buffer_size;
    }

    if (socket->nonblock == 0)
    {
        swoole_fcntl_set_option(socket->fd, 1, -1);
        socket->nonblock = 1;
    }

    if ((uint32_t) n > socket->buffer_size)
    {
        swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "data is too large, cannot exceed buffer size");
        return SW_ERR;
    }

    if (swBuffer_empty(buffer))
    {
#ifdef SW_USE_OPENSSL
        if (socket->ssl_send)
        {
            goto _do_buffer;
        }
#endif
        _do_send:
        ret = swSocket_send(socket, ptr, n, 0);

        if (ret > 0)
        {
            if (n == ret)
            {
                return ret;
            }
            else
            {
                ptr += ret;
                n -= ret;
                goto _do_buffer;
            }
        }
        else if (swSocket_error(errno) == SW_WAIT)
        {
            _do_buffer:
            if (!socket->out_buffer)
            {
                buffer = swBuffer_new(socket->chunk_size);
                if (!buffer)
                {
                    swWarn("create worker buffer failed");
                    return SW_ERR;
                }
                socket->out_buffer = buffer;
            }

            swReactor_add_write_event(reactor, socket);
            goto _append_buffer;
        }
        else if (errno == EINTR)
        {
            goto _do_send;
        }
        else
        {
            SwooleG.error = errno;
            return SW_ERR;
        }
    }
    else
    {
        _append_buffer:
        if (buffer->length > socket->buffer_size)
        {
            if (socket->dontwait)
            {
                SwooleG.error = SW_ERROR_OUTPUT_BUFFER_OVERFLOW;
                return SW_ERR;
            }
            else
            {
                swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "socket#%d output buffer overflow", fd);
                swYield();
                swSocket_wait(socket->fd, SW_SOCKET_OVERFLOW_WAIT, SW_EVENT_WRITE);
            }
        }

        if (swBuffer_append(buffer, ptr, n) < 0)
        {
            return SW_ERR;
        }
    }
    return SW_OK;
}

int swReactor_onWrite(swReactor *reactor, swEvent *ev)
{
    int ret;

    swSocket *socket = ev->socket;
    swBuffer_chunk *chunk = NULL;
    swBuffer *buffer = socket->out_buffer;

    //send to socket
    while (!swBuffer_empty(buffer))
    {
        chunk = swBuffer_get_chunk(buffer);
        if (chunk->type == SW_CHUNK_CLOSE)
        {
            _close_fd:
            reactor->close(reactor, ev->socket);
            return SW_OK;
        }
        else if (chunk->type == SW_CHUNK_SENDFILE)
        {
            ret = swSocket_onSendfile(socket, chunk);
        }
        else
        {
            ret = swSocket_buffer_send(socket);
        }

        if (ret < 0)
        {
            if (socket->close_wait)
            {
                goto _close_fd;
            }
            else if (socket->send_wait)
            {
                return SW_OK;
            }
        }
    }

    //remove EPOLLOUT event
    if (swBuffer_empty(buffer))
    {
        swReactor_remove_write_event(reactor, ev->socket);
    }

    return SW_OK;
}

int swReactor_wait_write_buffer(swReactor *reactor, swSocket *socket)
{
    swEvent event;

    if (!swBuffer_empty(socket->out_buffer))
    {
        swSocket_set_block(socket);
        event.fd = socket->fd;
        return swReactor_onWrite(reactor, &event);
    }
    return SW_OK;
}

void swReactor_add_destroy_callback(swReactor *reactor, swCallback cb, void *data)
{
    CallbackManager *cm = (CallbackManager *) reactor->destroy_callbacks;
    if (cm == nullptr)
    {
        cm = new CallbackManager;
        reactor->destroy_callbacks = cm;
    }
    cm->append(cb, data);
}

static void defer_task_do(swReactor *reactor)
{
    CallbackManager *cm = (CallbackManager *) reactor->defer_tasks;
    reactor->defer_tasks = nullptr;
    cm->execute();
    delete cm;
}

static void defer_task_add(swReactor *reactor, swCallback callback, void *data)
{
    CallbackManager *cm = (CallbackManager *) reactor->defer_tasks;
    if (cm == nullptr)
    {
        cm = new CallbackManager;
        reactor->defer_tasks = cm;
    }
    cm->append(callback, data);
}

void swReactor_destroy(swReactor *reactor)
{
    if (reactor->destroy_callbacks)
    {
        CallbackManager *cm = (CallbackManager *) reactor->destroy_callbacks;
        cm->execute();
        reactor->destroy_callbacks = nullptr;
        delete cm;
    }
    reactor->free(reactor);
}
<|endoftext|>