<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#ifndef NIX_GROUP_H
#define NIX_GROUP_H

#include <string>

#include <nix/hdf5/hdf5include.hpp>
#include <nix/hdf5/DataSet.hpp>

#include <nix/Hydra.hpp>
#include <nix/hdf5/DataSpace.hpp>

#include <nix/Platform.hpp>

namespace nix {
namespace hdf5 {

/**
 * TODO documentation
 */
class NIXAPI Group {

private:

    H5::Group h5group;

public:

    Group();

    Group(H5::Group h5group);

    Group(const Group &group);

    Group& operator=(const Group &group);

    bool hasAttr(const std::string &name) const;
    void removeAttr(const std::string &name) const;

    template <typename T>
    void setAttr(const std::string &name, const T &value) const;

    template <typename T>
    bool getAttr(const std::string &name, T &value) const;

    bool hasObject(const std::string &path) const;
    size_t objectCount() const;
    std::string objectName(size_t index) const;

    bool hasData(const std::string &name) const;
    DataSet openData(const std::string &name) const;
    void removeData(const std::string &name);

    template<typename T>
    void setData(const std::string &name, const T &value);
    template<typename T>
    bool getData(const std::string &name, T &value) const;

    bool hasGroup(const std::string &name) const;
    Group openGroup(const std::string &name, bool create = true) const;
    void removeGroup(const std::string &name);
    void renameGroup(const std::string &old_name, const std::string &new_name);

    /**
     * @brief Create a new hard link with the given name inside this group,
     *        that points to the target group.
     *
     * @param target    The target of the link to create.
     * @param linkname  The name of the link to create.
     *
     * @return The linked group.
     */
    Group createLink(const Group &target, const std::string &link_name);

    /**
     * @brief Renames all links of the object defined by the old name.
     *
     * This method will only change the links where the last part of the path
     * is the old name.
     *
     * @param old_name  The old name of the object
     * @param new_name  The new name of the object
     *
     * @return True if all links where changed successfully.
     */
    bool renameAllLinks(const std::string &old_name, const std::string &new_name);

    /**
     * @brief Removes all links to the object defined by the given name.
     *
     * @param name      The name of the object to remove.
     *
     * @return  True if all links were deleted.
     */
    bool removeAllLinks(const std::string &name);

    bool operator==(const Group &group) const;
    bool operator!=(const Group &group) const;

    H5::Group h5Group() const;
    virtual ~Group();

private:
    static void readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, void *data);
    static void readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, std::string *data);

    static void writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const void *data);
    static void writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const std::string *data);
}; // group Group

//template functions

template<typename T> void Group::setAttr(const std::string &name, const T &value) const
{
    typedef Hydra<const T> hydra_t;

    const hydra_t hydra(value);
    DataType dtype = hydra.element_data_type();
    NDSize shape = hydra.shape();

    H5::Attribute attr;

    if (hasAttr(name)) {
        attr = h5group.openAttribute(name);
    } else {
        H5::DataType fileType = data_type_to_h5_filetype(dtype);
        H5::DataSpace fileSpace = DataSpace::create(shape, false);
        attr = h5group.createAttribute(name, fileType, fileSpace);
    }

    writeAttr(attr, data_type_to_h5_memtype(dtype), shape, hydra.data());
}

template<typename T> bool Group::getAttr(const std::string &name, T &value) const
{
    if (!hasAttr(name)) {
        return false;
    }

    Hydra<T> hydra(value);

    //determine attr's size and resize value accordingly
    H5::Attribute attr = h5group.openAttribute(name);
    H5::DataSpace space = attr.getSpace();
    int rank = space.getSimpleExtentNdims();
    NDSize dims(static_cast<size_t>(rank));
    space.getSimpleExtentDims (dims.data(), nullptr);
    hydra.resize(dims);

    DataType dtype = hydra.element_data_type();
    H5::DataType mem_type = data_type_to_h5_memtype(dtype);

    readAttr(attr, mem_type, dims, hydra.data());

    return true;
}

template<typename T>
void Group::setData(const std::string &name, const T &value)
{
    const Hydra<const T> hydra(value);
    DataType dtype = hydra.element_data_type();
    NDSize shape = hydra.shape();

    DataSet ds;
    if (!hasData(name)) {
        ds = DataSet::create(h5group, name, dtype, shape);
    } else {
        ds = openData(name);
        ds.setExtent(shape);
    }

    ds.write(dtype, shape, hydra.data());
}

template<typename T>
bool Group::getData(const std::string &name, T &value) const
{
    if (!hasData(name)) {
        return false;
    }

    Hydra<T> hydra(value);
    DataSet ds = openData(name);

    DataType dtype = hydra.element_data_type();
    NDSize shape = ds.size();

    hydra.resize(shape);
    ds.read(dtype, shape, hydra.data());

    return true;
}

} // namespace hdf5
} // namespace nix

#endif /* NIX_GROUP_H */
<commit_msg>Update Group.hpp<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#ifndef NIX_GROUP_H
#define NIX_GROUP_H

#include <string>

#include <nix/hdf5/hdf5include.hpp>
#include <nix/hdf5/DataSet.hpp>

#include <nix/Hydra.hpp>
#include <nix/hdf5/DataSpace.hpp>

#include <nix/Platform.hpp>

namespace nix {
namespace hdf5 {

/**
 * TODO documentation
 */
class NIXAPI Group {

private:

    H5::Group h5group;

public:

    Group();

    Group(H5::Group h5group);

    Group(const Group &group);

    Group& operator=(const Group &group);

    bool hasAttr(const std::string &name) const;
    void removeAttr(const std::string &name) const;

    template <typename T>
    void setAttr(const std::string &name, const T &value) const;

    template <typename T>
    bool getAttr(const std::string &name, T &value) const;

    bool hasObject(const std::string &path) const;
    size_t objectCount() const;
    std::string objectName(size_t index) const;

    bool hasData(const std::string &name) const;
    DataSet openData(const std::string &name) const;
    void removeData(const std::string &name);

    template<typename T>
    void setData(const std::string &name, const T &value);
    template<typename T>
    bool getData(const std::string &name, T &value) const;

    bool hasGroup(const std::string &name) const;
    Group openGroup(const std::string &name, bool create = true) const;
    void removeGroup(const std::string &name);
    void renameGroup(const std::string &old_name, const std::string &new_name);

    /**
     * @brief Create a new hard link with the given name inside this group,
     *        that points to the target group.
     *
     * @param target    The target of the link to create.
     * @param linkname  The name of the link to create.
     *
     * @return The linked group.
     */
    Group createLink(const Group &target, const std::string &link_name);

    /**
     * @brief Renames all links of the object defined by the old name.
     *
     * This method will only change the links where the last part of the path
     * is the old name.
     *
     * @param old_name  The old name of the object
     * @param new_name  The new name of the object
     *
     * @return True if all links where changed successfully.
     */
    bool renameAllLinks(const std::string &old_name, const std::string &new_name);

    /**
     * @brief Removes all links to the object defined by the given name.
     *
     * @param name      The name of the object to remove.
     *
     * @return  True if all links were deleted.
     */
    bool removeAllLinks(const std::string &name);

    bool operator==(const Group &group) const;
    bool operator!=(const Group &group) const;

    H5::Group h5Group() const;
    virtual ~Group();

private:
    static void readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, void *data);
    static void readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, std::string *data);

    static void writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const void *data);
    static void writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const std::string *data);
}; // group Group

//template functions

template<typename T> void Group::setAttr(const std::string &name, const T &value) const
{
    typedef Hydra<const T> hydra_t;

    const hydra_t hydra(value);
    DataType dtype = hydra.element_data_type();
    NDSize shape = hydra.shape();

    H5::Attribute attr;

    if (hasAttr(name)) {
        attr = h5group.openAttribute(name);
    } else {
        H5::DataType fileType = data_type_to_h5_filetype(dtype);
        H5::DataSpace fileSpace = DataSpace::create(shape, false);
        attr = h5group.createAttribute(name, fileType, fileSpace);
    }

    writeAttr(attr, data_type_to_h5_memtype(dtype), shape, hydra.data());
}

template<typename T> bool Group::getAttr(const std::string &name, T &value) const
{
    if (!hasAttr(name)) {
        return false;
    }

    Hydra<T> hydra(value);

    //determine attr's size and resize value accordingly
    H5::Attribute attr = h5group.openAttribute(name);
    H5::DataSpace space = attr.getSpace();
    int rank = space.getSimpleExtentNdims();
    NDSize dims(static_cast<size_t>(rank));
    space.getSimpleExtentDims (dims.data(), nullptr);
    hydra.resize(dims);

    DataType dtype = hydra.element_data_type();
    H5::DataType mem_type = data_type_to_h5_memtype(dtype);

    readAttr(attr, mem_type, dims, hydra.data());

    return true;
}

template<typename T>
void Group::setData(const std::string &name, const T &value)
{
    const Hydra<const T> hydra(value);
    DataType dtype = hydra.element_data_type();
    NDSize shape = hydra.shape();

    DataSet ds;
    if (!hasData(name)) {
        ds = DataSet::create(h5group, name, dtype, shape);
    } else {
        ds = openData(name);
        ds.setExtent(shape);
    }

    ds.write(dtype, shape, hydra.data());
}

template<typename T>
bool Group::getData(const std::string &name, T &value) const
{
    if (!hasData(name)) {
        return false;
    }

    Hydra<T> hydra(value);
    DataSet ds = openData(name);

    DataType dtype = hydra.element_data_type();
    NDSize shape = ds.size();

    hydra.resize(shape);
    ds.read(dtype, shape, hydra.data());

    return true;
}

} // namespace hdf5
} // namespace nix

#endif /* NIX_GROUP_H */
<|endoftext|>


<commit_before>#ifndef LIBTEN_HTTP_SERVER_HH
#define LIBTEN_HTTP_SERVER_HH

#include <fnmatch.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <chrono>
#include <functional>
#include <netinet/tcp.h>

#include "ten/buffer.hh"
#include "ten/logging.hh"
#include "ten/task.hh"
#include "ten/net.hh"
#include "ten/http/http_message.hh"
#include "ten/uri.hh"

namespace ten {

//! basic http server
class http_server : public netsock_server {
public:

    //! wrapper around an http request/response
    struct request {
        http_request &req;
        netsock &sock;
        http_response resp {404};
        bool resp_sent {false};
        std::chrono::high_resolution_clock::time_point start;

        request(http_request &req_, netsock &sock_)
            : req(req_), sock(sock_),
              start(std::chrono::steady_clock::now()) {}

        //! compose a uri from the request uri
        uri get_uri(std::string host="") {
            if (host.empty()) {
                host = req.get("Host");
                // TODO: transform to preserve passed in host
                if (boost::starts_with(req.uri, "http://")) {
                    return req.uri;
                }
            }

            if (host.empty()) {
                // just make up a host
                host = "localhost";
            }
            uri tmp;
            tmp.host = host;
            tmp.scheme = "http";
            tmp.path = req.uri;
            return tmp.compose();
        }

        //! send response to this request
        ssize_t send_response() {
            if (resp_sent) return 0;
            resp_sent = true;
            std::string data;
            // TODO: Content-Length might be another good one to add here
            // but only if Transfer-Encoding isn't chunked?
            // HTTP/1.1 requires Date, so lets add it
            if (resp.get("Date").empty()) {
                char buf[128];
                struct tm tm;
                time_t now = std::chrono::system_clock::to_time_t(procnow());
                strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&now, &tm));
                resp.set("Date", buf);
            }
            std::string conn = req.get("Connection");
            if (!conn.empty() && resp.get("Connection").empty()) {
                // obey clients wishes if we have none of our own
                resp.set("Connection", conn);
            }
            if (req.http_version == "HTTP/1.0" && req.get("Connection").empty()) {
                resp.set("Connection", "close");
            }

            data = resp.data();
            if (!resp.body.empty() && req.method != "HEAD") {
                data += resp.body;
            }
            ssize_t nw = sock.send(data.data(), data.size());
            return nw;
        }

        //! the ip of the host making the request
        //! might use the X-Forwarded-For header
        std::string agent_ip(bool use_xff=false) const {
            if (use_xff) {
                std::string xffs = req.get("X-Forwarded-For");
                const char *xff = xffs.c_str();
                if (xff) {
                    // pick the first addr
                    int i;
                    for (i=0; *xff && i<256 && !isdigit((unsigned char)*xff); i++, xff++) {}
                    if (*xff && i < 256) {
                        // now, find the end
                        const char *e = xff;
                        for (i = 0; *e && i<256 && (isdigit((unsigned char)*e) || *e == '.'); i++, e++) {}
                        if (i < 256 && e >= xff + 7 ) {
                            return std::string(xff, e - xff);
                        }
                    }
                }
            }
            address addr;
            if (sock.getpeername(addr)) {
                char buf[INET6_ADDRSTRLEN];
                if (addr.ntop(buf, sizeof(buf))) {
                    return buf;
                }
            }
            return "";
        }

        ~request() {
            if (!resp_sent) {
                // ensure a response is sent
                resp = http_response(404, http_headers{"Content-Length", 0});
                send_response();
            }

            if (boost::iequals(resp.get("Connection"), "close")) {
                sock.close();
            }
        }
    };

    typedef std::function<void (request &)> callback_type;
public:
    std::function<void ()> connect_watch;
    std::function<void ()> disconnect_watch;
protected:
    struct route {
        std::string pattern;
        callback_type callback;
        int fnmatch_flags;

        route(const std::string &pattern_,
                const callback_type &callback_,
                int fnmatch_flags_=0)
            : pattern(pattern_), callback(callback_), fnmatch_flags(fnmatch_flags_) {}
    };

    std::vector<route> _routes;
    callback_type _log_func;
public:
    http_server(size_t stacksize_=default_stacksize, unsigned timeout_ms_=0)
        : netsock_server("http", stacksize_, timeout_ms_)
    {
    }

    //! add a callback for a uri with an fnmatch pattern
    void add_route(const std::string &pattern,
            const callback_type &callback,
            int fnmatch_flags = 0)
    {
        _routes.emplace_back(pattern, callback, fnmatch_flags);
    }

    //! set logging function, called after every request
    void set_log_callback(const callback_type &f) {
        _log_func = f;
    }

    void on_connection(netsock &s) {
        // TODO: tuneable buffer sizes
        buffer buf(4*1024);
        http_parser parser;

        if (connect_watch) {
            connect_watch();
        }
        // TODO: might want to enable this later after
        // we know this will be a long-lived connection
        // otherwise the overhead of the syscall hurts concurrency
        //s.s.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1);

        http_request req;
        for (;;) {
            req.parser_init(&parser);
            bool got_headers = false;
            for (;;) {
                buf.reserve(4*1024);
                ssize_t nr = -1;
                if (buf.size() == 0) {
                    nr = s.recv(buf.back(), buf.available(), _timeout_ms);
                    if (nr < 0) goto done;
                    buf.commit(nr);
                }
                size_t nparse = buf.size();
                req.parse(&parser, buf.front(), nparse);
                buf.remove(nparse);
                if (req.complete) {
                    // handle request
                    handle_request(req, s);
                    break;
                }
                if (nr == 0) goto done;
                if (!got_headers && !req.method.empty()) {
                    got_headers = true;
                    if (req.get("Expect") == "100-continue") {
                        http_response cont_resp(100);
                        std::string data = cont_resp.data();
                        ssize_t nw = s.send(data.data(), data.size());
                        (void)nw;
                    }
                }
            }
        }
done:
        if (disconnect_watch) {
            disconnect_watch();
        }
    }

    void handle_request(http_request &req, netsock &s) {
        request r(req, s);
        const auto path = req.path();
        DVLOG(5) << "path: " << path;
        // not super efficient, but good enough
        for (const auto &i : _routes) {
            DVLOG(5) << "matching pattern: " << i.pattern;
            if (fnmatch(i.pattern.c_str(), path.c_str(), i.fnmatch_flags) == 0) {
                try {
                    i.callback(r);
                } catch (std::exception &e) {
                    DVLOG(2) << "unhandled exception in route [" << i.pattern << "]: " << e.what();
                    r.resp = http_response(500, http_headers{"Connection", "close"});
                    std::string msg = e.what();
                    if (!msg.empty() && *msg.rbegin() != '\n')
                        msg += '\n';
                    r.resp.set_body(msg);
                    r.send_response();
                }
                break;
            }
        }
        if (_log_func) {
            _log_func(r);
        }
    }
};

} // end namespace ten

#endif // LIBTEN_HTTP_SERVER_HH
<commit_msg>empty pattern string for fallback handler<commit_after>#ifndef LIBTEN_HTTP_SERVER_HH
#define LIBTEN_HTTP_SERVER_HH

#include <fnmatch.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <chrono>
#include <functional>
#include <netinet/tcp.h>

#include "ten/buffer.hh"
#include "ten/logging.hh"
#include "ten/task.hh"
#include "ten/net.hh"
#include "ten/http/http_message.hh"
#include "ten/uri.hh"

namespace ten {

//! basic http server
class http_server : public netsock_server {
public:

    //! wrapper around an http request/response
    struct request {
        http_request &req;
        netsock &sock;
        http_response resp {404};
        bool resp_sent {false};
        std::chrono::high_resolution_clock::time_point start;

        request(http_request &req_, netsock &sock_)
            : req(req_), sock(sock_),
              start(std::chrono::steady_clock::now()) {}

        //! compose a uri from the request uri
        uri get_uri(std::string host="") {
            if (host.empty()) {
                host = req.get("Host");
                // TODO: transform to preserve passed in host
                if (boost::starts_with(req.uri, "http://")) {
                    return req.uri;
                }
            }

            if (host.empty()) {
                // just make up a host
                host = "localhost";
            }
            uri tmp;
            tmp.host = host;
            tmp.scheme = "http";
            tmp.path = req.uri;
            return tmp.compose();
        }

        //! send response to this request
        ssize_t send_response() {
            if (resp_sent) return 0;
            resp_sent = true;
            std::string data;
            // TODO: Content-Length might be another good one to add here
            // but only if Transfer-Encoding isn't chunked?
            // HTTP/1.1 requires Date, so lets add it
            if (resp.get("Date").empty()) {
                char buf[128];
                struct tm tm;
                time_t now = std::chrono::system_clock::to_time_t(procnow());
                strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&now, &tm));
                resp.set("Date", buf);
            }
            std::string conn = req.get("Connection");
            if (!conn.empty() && resp.get("Connection").empty()) {
                // obey clients wishes if we have none of our own
                resp.set("Connection", conn);
            }
            if (req.http_version == "HTTP/1.0" && req.get("Connection").empty()) {
                resp.set("Connection", "close");
            }

            data = resp.data();
            if (!resp.body.empty() && req.method != "HEAD") {
                data += resp.body;
            }
            ssize_t nw = sock.send(data.data(), data.size());
            return nw;
        }

        //! the ip of the host making the request
        //! might use the X-Forwarded-For header
        std::string agent_ip(bool use_xff=false) const {
            if (use_xff) {
                std::string xffs = req.get("X-Forwarded-For");
                const char *xff = xffs.c_str();
                if (xff) {
                    // pick the first addr
                    int i;
                    for (i=0; *xff && i<256 && !isdigit((unsigned char)*xff); i++, xff++) {}
                    if (*xff && i < 256) {
                        // now, find the end
                        const char *e = xff;
                        for (i = 0; *e && i<256 && (isdigit((unsigned char)*e) || *e == '.'); i++, e++) {}
                        if (i < 256 && e >= xff + 7 ) {
                            return std::string(xff, e - xff);
                        }
                    }
                }
            }
            address addr;
            if (sock.getpeername(addr)) {
                char buf[INET6_ADDRSTRLEN];
                if (addr.ntop(buf, sizeof(buf))) {
                    return buf;
                }
            }
            return "";
        }

        ~request() {
            if (!resp_sent) {
                // ensure a response is sent
                resp = http_response(404, http_headers{"Content-Length", 0});
                send_response();
            }

            if (boost::iequals(resp.get("Connection"), "close")) {
                sock.close();
            }
        }
    };

    typedef std::function<void (request &)> callback_type;
public:
    std::function<void ()> connect_watch;
    std::function<void ()> disconnect_watch;
protected:
    struct route {
        std::string pattern;
        callback_type callback;
        int fnmatch_flags;

        route(const std::string &pattern_,
                const callback_type &callback_,
                int fnmatch_flags_=0)
            : pattern(pattern_), callback(callback_), fnmatch_flags(fnmatch_flags_) {}
    };

    std::vector<route> _routes;
    callback_type _log_func;
public:
    http_server(size_t stacksize_=default_stacksize, unsigned timeout_ms_=0)
        : netsock_server("http", stacksize_, timeout_ms_)
    {
    }

    //! add a callback for a uri with an fnmatch pattern
    void add_route(const std::string &pattern,
            const callback_type &callback,
            int fnmatch_flags = 0)
    {
        _routes.emplace_back(pattern, callback, fnmatch_flags);
    }

    //! set logging function, called after every request
    void set_log_callback(const callback_type &f) {
        _log_func = f;
    }

    void on_connection(netsock &s) {
        // TODO: tuneable buffer sizes
        buffer buf(4*1024);
        http_parser parser;

        if (connect_watch) {
            connect_watch();
        }
        // TODO: might want to enable this later after
        // we know this will be a long-lived connection
        // otherwise the overhead of the syscall hurts concurrency
        //s.s.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1);

        http_request req;
        for (;;) {
            req.parser_init(&parser);
            bool got_headers = false;
            for (;;) {
                buf.reserve(4*1024);
                ssize_t nr = -1;
                if (buf.size() == 0) {
                    nr = s.recv(buf.back(), buf.available(), _timeout_ms);
                    if (nr < 0) goto done;
                    buf.commit(nr);
                }
                size_t nparse = buf.size();
                req.parse(&parser, buf.front(), nparse);
                buf.remove(nparse);
                if (req.complete) {
                    // handle request
                    handle_request(req, s);
                    break;
                }
                if (nr == 0) goto done;
                if (!got_headers && !req.method.empty()) {
                    got_headers = true;
                    if (req.get("Expect") == "100-continue") {
                        http_response cont_resp(100);
                        std::string data = cont_resp.data();
                        ssize_t nw = s.send(data.data(), data.size());
                        (void)nw;
                    }
                }
            }
        }
done:
        if (disconnect_watch) {
            disconnect_watch();
        }
    }

    void handle_request(http_request &req, netsock &s) {
        request r(req, s);
        const auto path = req.path();
        DVLOG(5) << "path: " << path;
        // not super efficient, but good enough
        // note: empty string pattern matches everything
        for (const auto &i : _routes) {
            DVLOG(5) << "matching pattern: " << i.pattern;
            if (i.pattern.empty() || fnmatch(i.pattern.c_str(), path.c_str(), i.fnmatch_flags) == 0) {
                try {
                    i.callback(r);
                } catch (std::exception &e) {
                    DVLOG(2) << "unhandled exception in route [" << i.pattern << "]: " << e.what();
                    r.resp = http_response(500, http_headers{"Connection", "close"});
                    std::string msg = e.what();
                    if (!msg.empty() && *msg.rbegin() != '\n')
                        msg += '\n';
                    r.resp.set_body(msg);
                    r.send_response();
                }
                break;
            }
        }
        if (_log_func) {
            _log_func(r);
        }
    }
};

} // end namespace ten

#endif // LIBTEN_HTTP_SERVER_HH
<|endoftext|>


<commit_before>/*
	Copyright 2016 Jonathan Bayle, Thomas Medioni

	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.
*/

#ifndef V_ALLEGRO_HPP
#define V_ALLEGRO_HPP
#pragma once

// Deleter template using Alleg's destroy functions (for unique_ptr, shared_ptr, ...);
template <class al_type, void (*del_func)(al_type*)> class al_deleter {
public:
	void operator()(al_type* ptr)
	{
		del_func(ptr);
	}
};

#include <allegro5/allegro.h>

class al_config_deleter: public al_deleter<ALLEGRO_CONFIG, al_destroy_config> {};
class al_display_deleter: public al_deleter<ALLEGRO_DISPLAY, al_destroy_display> {};
class al_event_queue_deleter: public al_deleter<ALLEGRO_EVENT_QUEUE, al_destroy_event_queue> {};
//class al_file_deleter: public al_deleter<ALLEGRO_FILE, al_fclose> {}; // returns bool, TODO: create function template to suppress returned value of a function
class al_fs_entry_deleter: public al_deleter<ALLEGRO_FS_ENTRY, al_destroy_fs_entry> {};
class al_bitmap_deleter: public al_deleter<ALLEGRO_BITMAP, al_destroy_bitmap> {};
class al_path_deleter: public al_deleter<ALLEGRO_PATH, al_destroy_path> {};
class al_shader_deleter: public al_deleter<ALLEGRO_SHADER, al_destroy_shader> {};
class al_thread_deleter: public al_deleter<ALLEGRO_THREAD, al_destroy_thread> {};
class al_cond_deleter: public al_deleter<ALLEGRO_COND, al_destroy_cond> {};
class al_mutex_deleter: public al_deleter<ALLEGRO_MUTEX, al_destroy_mutex> {};
class al_timer_deleter: public al_deleter<ALLEGRO_TIMER, al_destroy_timer> {};
class al_ustr_deleter: public al_deleter<ALLEGRO_USTR, al_ustr_free> {};

#include <allegro5/allegro_audio.h>

class al_voice_deleter: public al_deleter<ALLEGRO_VOICE, al_destroy_voice> {};
class al_sample_deleter: public al_deleter<ALLEGRO_SAMPLE, al_destroy_sample> {};
class al_sample_instance_deleter: public al_deleter<ALLEGRO_SAMPLE_INSTANCE, al_destroy_sample_instance> {};
class al_mixer_deleter: public al_deleter<ALLEGRO_MIXER, al_destroy_mixer> {};
class al_audio_stream_deleter: public al_deleter<ALLEGRO_AUDIO_STREAM, al_destroy_audio_stream> {};
//class al_audio_recorder_deleter: public al_deleter<ALLEGRO_AUDIO_RECORDER, al_destroy_audio_recorder> {}; // not found??

#include <allegro5/allegro_color.h>

#include <allegro5/allegro_font.h>

class al_font_deleter: public al_deleter<ALLEGRO_FONT, al_destroy_font> {};

#include <allegro5/allegro_memfile.h>

#include <allegro5/allegro_primitives.h>

class al_vertex_decl_deleter: public al_deleter<ALLEGRO_VERTEX_DECL, al_destroy_vertex_decl> {};
class al_vertex_buffer_deleter: public al_deleter<ALLEGRO_VERTEX_BUFFER, al_destroy_vertex_buffer> {};
class al_index_buffer_deleter: public al_deleter<ALLEGRO_INDEX_BUFFER, al_destroy_index_buffer> {};

#ifdef WANT_IMAGE
#include <allegro5/allegro_image.h>
#endif

#ifdef WANT_ACODEC
#include <allegro5/allegro_acodec.h>
#endif

#ifdef WANT_TTF
#include <allegro5/allegro_ttf.h>
#endif

#ifdef WANT_PHYSFS
#include <allegro5/allegro_physfs.h>
#endif

#ifdef WANT_VIDEO
#include <allegro5/allegro_video.h>
class al_video_deleter: public al_deleter<ALLEGRO_VIDEO, al_close_video> {};
#endif

#ifdef WANT_DIALOG
#include <allegro5/allegro_native_dialog.h>
class al_filechooser_deleter: public al_deleter<ALLEGRO_FILECHOOSER, al_destroy_native_file_dialog> {};
class al_textlog_deleter: public al_deleter<ALLEGRO_TEXTLOG, al_close_native_text_log> {};
class al_menu_deleter: public al_deleter<ALLEGRO_MENU, al_destroy_menu> {};
#endif

#endif // V_ALLEGRO_HPP
<commit_msg>fix allegro deleter so it can call non void functions<commit_after>/*
	Copyright 2016 Jonathan Bayle, Thomas Medioni

	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.
*/

#ifndef V_ALLEGRO_HPP
#define V_ALLEGRO_HPP
#pragma once

// Deleter template using Alleg's destroy functions (for unique_ptr, shared_ptr, ...);
template <class al_type, class ret_type, ret_type (*del_func)(al_type*)> class al_deleter {
public:
	void operator()(al_type* ptr)
	{
		del_func(ptr);
	}
};

#include <allegro5/allegro.h>

class al_config_deleter: public al_deleter<ALLEGRO_CONFIG, void, al_destroy_config> {};
class al_display_deleter: public al_deleter<ALLEGRO_DISPLAY, void, al_destroy_display> {};
class al_event_queue_deleter: public al_deleter<ALLEGRO_EVENT_QUEUE, void, al_destroy_event_queue> {};
class al_file_deleter: public al_deleter<ALLEGRO_FILE, bool, al_fclose> {}; // returns bool, TODO: create function template to suppress returned value of a function
class al_fs_entry_deleter: public al_deleter<ALLEGRO_FS_ENTRY, void, al_destroy_fs_entry> {};
class al_bitmap_deleter: public al_deleter<ALLEGRO_BITMAP, void, al_destroy_bitmap> {};
class al_path_deleter: public al_deleter<ALLEGRO_PATH, void, al_destroy_path> {};
class al_shader_deleter: public al_deleter<ALLEGRO_SHADER, void, al_destroy_shader> {};
class al_thread_deleter: public al_deleter<ALLEGRO_THREAD, void, al_destroy_thread> {};
class al_cond_deleter: public al_deleter<ALLEGRO_COND, void, al_destroy_cond> {};
class al_mutex_deleter: public al_deleter<ALLEGRO_MUTEX, void, al_destroy_mutex> {};
class al_timer_deleter: public al_deleter<ALLEGRO_TIMER, void, al_destroy_timer> {};
class al_ustr_deleter: public al_deleter<ALLEGRO_USTR, void, al_ustr_free> {};

#include <allegro5/allegro_audio.h>

class al_voice_deleter: public al_deleter<ALLEGRO_VOICE, void, al_destroy_voice> {};
class al_sample_deleter: public al_deleter<ALLEGRO_SAMPLE, void, al_destroy_sample> {};
class al_sample_instance_deleter: public al_deleter<ALLEGRO_SAMPLE_INSTANCE, void, al_destroy_sample_instance> {};
class al_mixer_deleter: public al_deleter<ALLEGRO_MIXER, void, al_destroy_mixer> {};
class al_audio_stream_deleter: public al_deleter<ALLEGRO_AUDIO_STREAM, void, al_destroy_audio_stream> {};
//class al_audio_recorder_deleter: public al_deleter<ALLEGRO_AUDIO_RECORDER, al_destroy_audio_recorder> {}; // not found??

#include <allegro5/allegro_color.h>

#include <allegro5/allegro_font.h>

class al_font_deleter: public al_deleter<ALLEGRO_FONT, void, al_destroy_font> {};

#include <allegro5/allegro_memfile.h>

#include <allegro5/allegro_primitives.h>

class al_vertex_decl_deleter: public al_deleter<ALLEGRO_VERTEX_DECL, void, al_destroy_vertex_decl> {};
class al_vertex_buffer_deleter: public al_deleter<ALLEGRO_VERTEX_BUFFER, void, al_destroy_vertex_buffer> {};
class al_index_buffer_deleter: public al_deleter<ALLEGRO_INDEX_BUFFER, void, al_destroy_index_buffer> {};

#ifdef WANT_IMAGE
#include <allegro5/allegro_image.h>
#endif

#ifdef WANT_ACODEC
#include <allegro5/allegro_acodec.h>
#endif

#ifdef WANT_TTF
#include <allegro5/allegro_ttf.h>
#endif

#ifdef WANT_PHYSFS
#include <allegro5/allegro_physfs.h>
#endif

#ifdef WANT_VIDEO
#include <allegro5/allegro_video.h>
class al_video_deleter: public al_deleter<ALLEGRO_VIDEO, void, al_close_video> {};
#endif

#ifdef WANT_DIALOG
#include <allegro5/allegro_native_dialog.h>
class al_filechooser_deleter: public al_deleter<ALLEGRO_FILECHOOSER, void, al_destroy_native_file_dialog> {};
class al_textlog_deleter: public al_deleter<ALLEGRO_TEXTLOG, void, al_close_native_text_log> {};
class al_menu_deleter: public al_deleter<ALLEGRO_MENU, void, al_destroy_menu> {};
#endif

#endif // V_ALLEGRO_HPP
<|endoftext|>


<commit_before>// SBW
// compilation: g++ -std=c++11 -o fuzzy_matcher fuzzy_matcher.cpp

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <vector>
#include <string>
#include <memory>
#include <deque>
#include <functional>
#include <cstring>

class BKTree
{
public:
	static const size_t MAX_WORD_SIZE = 1024;
	static int size;
	typedef std::deque<std::pair<int, std::string>> QueryResult;

	int v0[MAX_WORD_SIZE];
	int v1[MAX_WORD_SIZE];

public:
	int distParent;
	std::string word;
	std::vector<std::unique_ptr<BKTree> > subnodes;

	BKTree() :
		distParent(0)
	{
		++size;
	}

	BKTree(const std::string& in, int inDist) :
		word(in),
		distParent(inDist)
	{
		++size;
	}

	BKTree(std::string&& in, int inDist) :
		word(std::move(in)),
		distParent(inDist)
	{
		std::cout << "DSADSAD" << std::endl;
		++size;
	}

	~BKTree()
	{
	}

	void insert(const std::string& in)
	{
		if (word.empty())
		{
			word = in;
			distParent = 0;
			return;
		}
		else
		{
			int inDst = levensthein_distance(word.c_str(), in.c_str());

			if (subnodes.size() > 0)
			{
				for (const std::unique_ptr<BKTree>& sub : subnodes)
				{
					if (sub->distParent == inDst)
					{
						sub->insert(in);
						return;
					}
				}
			}

			/*subnodes.push_back(new BKTree(in, inDst));*/
			subnodes.push_back(
				std::unique_ptr<BKTree>(new BKTree(in, inDst))
				);
			return;
		}
	}

	void query(const std::string& in, int tolerance, QueryResult& out)
	{
		int inDst = levensthein_distance(word.c_str(), in.c_str());

		if (inDst <= tolerance)
		{
			out.push_back(std::make_pair(inDst, word));
		}

		for (const auto& sub : subnodes)
		{
			if ((sub->distParent <= (inDst + tolerance)) &&
				(sub->distParent >= (inDst - tolerance)))
			{
				sub->query(in, tolerance, out);
			}
		}

		return;
	}

	void print()
	{
		std::cout << word << " " << distParent << " " << subnodes.size() << std::endl;

		for (const std::unique_ptr<BKTree>& sub : subnodes)
		{
			sub->print();
		}
	}
private:
	int levensthein_distance(const char* s, const char* t)
	{
		if (s == t) return 0;

		const size_t sLen = strlen(s);
		const size_t tLen = strlen(t);

		if (sLen == 0) return tLen;
		if (tLen == 0) return sLen;

		const size_t vecLen = tLen + 1;

		for (size_t i = 0; i < vecLen; ++i)
		{
			v0[i] = i;
		}

		for (size_t i = 0; i < sLen; ++i)
		{
			v1[0] = i + 1;

			for (size_t j = 0; j < tLen; ++j)
			{
				int cost = (s[i] == t[j]) ? 0 : 1;
				v1[j + 1] = std::min(v1[j] + 1, v0[j + 1] + 1);
				v1[j + 1] = std::min(v1[j + 1], v0[j] + cost);
			}

			for (size_t j = 0; j < vecLen; ++j)
			{
				v0[j] = v1[j];
			}
		}

		return v1[tLen];
	}
};

int BKTree::size = 0;

int main()
{
	std::ios::ios_base::sync_with_stdio(false);

	std::unique_ptr<BKTree> bk_tree(new BKTree()); // Tree containing known words.
	std::vector<std::string> input_words; // Collection of input words.
	std::vector<std::string> matched_words; // Collection of matched words.

	std::ifstream fwords("dict.txt");
	std::string line;
	int numWords = 0;

	// Load known words from dictionary
	while (!fwords.eof())
	{
		std::getline(fwords, line);
		if (!line.empty())
		{
			bk_tree->insert(line);
			++numWords;
		}
	}
	fwords.close();

	// Load input words from file.
	std::ifstream input("in.txt");
	std::string in_word;
	while (!input.eof())
	{
		std::getline(input, line);
		if (!line.empty()) input_words.push_back(line);
	}
	input.close();

	matched_words.reserve(input_words.size());

	for (auto& in_word : input_words)
	{
		BKTree::QueryResult result;
		const char *query_input = in_word.c_str();

		// Query tree for input word
		bk_tree->query(query_input, 4, result);

		if (!result.empty())
		{
			std::sort(std::begin(result), std::end(result));
			matched_words.push_back(result.front().second);
			std::cout << in_word << ":" << result.front().second << ":" << result.front().first << "\n";
		}
	}

	std::ofstream output("out.txt");
	for (auto& matched_word : matched_words)
	{
		output << matched_word << std::endl;
	}
	output.close();

	getchar();

	return 0;
}
<commit_msg>Adjust search for E<commit_after>// SBW
// compilation: g++ -std=c++11 -o fuzzy_matcher fuzzy_matcher.cpp

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <vector>
#include <string>
#include <memory>
#include <deque>
#include <functional>
#include <cctype>
#include <cstring>

class BKTree
{
public:
	static const size_t MAX_WORD_SIZE = 1024;
	static int size;
	typedef std::deque<std::pair<int, std::string>> QueryResult;

	int v0[MAX_WORD_SIZE];
	int v1[MAX_WORD_SIZE];

public:
	int distParent;
	std::string word;
	std::vector<std::unique_ptr<BKTree> > subnodes;

	BKTree() :
		distParent(0)
	{
		++size;
	}

	BKTree(const std::string& in, int inDist) :
		word(in),
		distParent(inDist)
	{
		++size;
	}

	BKTree(std::string&& in, int inDist) :
		word(std::move(in)),
		distParent(inDist)
	{
		std::cout << "DSADSAD" << std::endl;
		++size;
	}

	~BKTree()
	{
	}

	void insert(const std::string& in)
	{
		if (word.empty())
		{
			word = in;
			distParent = 0;
			return;
		}
		else
		{
			int inDst = levensthein_distance(word.c_str(), in.c_str());

			if (subnodes.size() > 0)
			{
				for (const std::unique_ptr<BKTree>& sub : subnodes)
				{
					if (sub->distParent == inDst)
					{
						sub->insert(in);
						return;
					}
				}
			}

			/*subnodes.push_back(new BKTree(in, inDst));*/
			subnodes.push_back(
				std::unique_ptr<BKTree>(new BKTree(in, inDst))
				);
			return;
		}
	}

	void query(const std::string& in, int tolerance, QueryResult& out)
	{
		int inDst = levensthein_distance(word.c_str(), in.c_str());

		if (inDst <= tolerance)
		{
			out.push_back(std::make_pair(inDst, word));
		}

		for (const auto& sub : subnodes)
		{
			if ((sub->distParent <= (inDst + tolerance)) &&
				(sub->distParent >= (inDst - tolerance)))
			{
				sub->query(in, tolerance, out);
			}
		}

		return;
	}

	void print()
	{
		std::cout << word << " " << distParent << " " << subnodes.size() << std::endl;

		for (const std::unique_ptr<BKTree>& sub : subnodes)
		{
			sub->print();
		}
	}
private:
	int levensthein_distance(const char* s, const char* t)
	{
		if (s == t) return 0;

		const size_t sLen = strlen(s);
		const size_t tLen = strlen(t);

		if (sLen == 0) return tLen;
		if (tLen == 0) return sLen;

		const size_t vecLen = tLen + 1;

		for (size_t i = 0; i < vecLen; ++i)
		{
			v0[i] = i;
		}

		for (size_t i = 0; i < sLen; ++i)
		{
			v1[0] = i + 1;

			for (size_t j = 0; j < tLen; ++j)
			{
				int cost = (s[i] == t[j]) ? 0 : 1;
				v1[j + 1] = std::min(v1[j] + 1, v0[j + 1] + 1);
				v1[j + 1] = std::min(v1[j + 1], v0[j] + cost);
			}

			for (size_t j = 0; j < vecLen; ++j)
			{
				v0[j] = v1[j];
			}
		}

		return v1[tLen];
	}
};

int BKTree::size = 0;

int main()
{
	std::ios::ios_base::sync_with_stdio(false);

	std::unique_ptr<BKTree> bk_tree(new BKTree()); // Tree containing known words.
	std::vector<std::string> input_words; // Collection of input words.
	std::vector<std::string> matched_words; // Collection of matched words.

	std::ifstream fwords("dict.txt");
	std::string line;
	int numWords = 0;

	// Load known words from dictionary
	while (!fwords.eof())
	{
		std::getline(fwords, line);
		if (!line.empty())
		{
			bk_tree->insert(line);
			++numWords;
		}
	}
	fwords.close();

	// Load input words from file.
	std::ifstream input("in.txt");
	std::string in_word;
	while (!input.eof())
	{
		std::getline(input, line);
		if (!line.empty()) input_words.push_back(line);
	}
	input.close();

	matched_words.reserve(input_words.size());

	for (auto& in_word : input_words)
	{
		BKTree::QueryResult result;

		int max_distance = 4;

		char save = in_word[0];

		if (in_word.size() < 5 &&
			in_word.size() > 1 &&
			isdigit(in_word[1]))
		{
			in_word[0] = 'E';
			max_distance = 0;
		}

		// Query tree for input word
		bk_tree->query(in_word, max_distance, result);

		in_word[0] = save;

		if (!result.empty())
		{
			std::sort(std::begin(result), std::end(result));
			matched_words.push_back(result.front().second);
			std::cout << in_word << ":" << result.front().second << ":" << result.front().first << "\n";
		}
	}

	std::ofstream output("out.txt");
	for (auto& matched_word : matched_words)
	{
		output << matched_word << std::endl;
	}
	output.close();

	getchar();

	return 0;
}
<|endoftext|>


<commit_before>#include "sysdef.h"
#include "csengine/cdobj.h"
#include "csphyzik/phyziks.h"
#include "cstso.h"

static ctCollidingContact contact_heap[1024];  // no more than that
static int contact_heap_index = 0;

csRigidSpaceTimeObj *csRigidSpaceTimeObj::space_time_continuum[ MAX_SPACE_TIME_NUM ];
long csRigidSpaceTimeObj::continuum_end = 0;

// hideously inefficient collision detection/response algorithm
// just wanted to see some stuff bouncing around for now.

csSpaceTimeObj::csSpaceTimeObj()
{
//  space_time_continuum[continuum_end++] = this;
  //what_type = ST_SPACETIME;
}

csRigidSpaceTimeObj::csRigidSpaceTimeObj( csSprite3D *psprt, ctRigidBody *prb )
{
  space_time_continuum[continuum_end++] = this;
 // what_type = ST_SPACETIME;
 // col = pcollide;
  sprt = psprt;
  rb = prb;
  CHK (col = new csCollider (sprt));
  csColliderPointerObject::SetCollider (*sprt, col, true);
  what_type = ST_RIGID;

}

void set_no_rewind( ctEntity *ppe )
{
  ppe->set_rewind(false);
}

// using sloooooooow but accurate collision detection.
// this can be sped up in many ways.
// - use eulers method or mid-point for ODE just during CD
// - use prune and sweep algorithm with axis aligned, sorted bounding boxes
// - only evolve objects related to colliding objects when searching for c-time
// - don't forget to handle 'tunneling' problem as well.
void csRigidSpaceTimeObj::evolve_system( real t1, real t2, ctWorld *time_world, csWorld *space_world )
{
  static real collision_epsilon = 0.02;
  static real time_epsilon = 0.001;
  real ta, tb;

  long loops = 300;  //make sure we don't go into an infinite loop

  ta = t1;
  tb = t2;
  real min_col_dist;

  time_world->apply_function_to_body_list( set_no_rewind );

  while( ta < t2 && loops-- > 0){

    time_world->evolve( ta, tb );

    min_col_dist = collision_check( space_world );
    // if there are objects intersecting
    if( min_col_dist > 0 ){
      // if interpenetrations distance is very small => time of impact found
      // or if the time step is really small
      if( min_col_dist <= collision_epsilon || ( tb - ta ) <= time_epsilon ){
        // respond to collision
        collision_response( space_world );
        ta = tb;
        tb = t2;
      }else{
        // bisect time backwards to search for time of impact
        time_world->rewind(ta, tb);   // rewind state back to ta
        tb -= (tb - ta)*0.5;
  //      ctVector3 px = space_time_continuum[0]->rb->get_pos();
    //    px = space_time_continuum[0]->rb->get_pos();
      }
    // if we have not arrived at the end of our time interval
    }else if( tb < t2 ){
      // search forward in time for time of impact for collision(s)
      ta = tb;
      // if we are at our min time step, just finish this time step, collision
      // will get resolved next time.
      if( (t2 - ta) <= time_epsilon ){
        tb = t2;
        time_world->evolve( ta, tb );

        ta = tb;
      }else{
        tb += (t2 - tb)*0.5;
      }
    }else{
      ta = tb;
      tb = t2;
    }
  }

  update_space();

}

void csRigidSpaceTimeObj::update_space()
{
ctVector3 px;
csMatrix3 m;
ctMatrix3 M;
csRigidSpaceTimeObj *sto;

  for( int i = 0; i < continuum_end; i++ ){
    sto = space_time_continuum[i];
    px = sto->rb->get_pos();

    csVector3 new_p( px[0], px[1], px[2] );
    sto->sprt->SetMove ( new_p );

    M = sto->rb->get_R();   // get orientation for this link
    // ctMatrix3 and csMatrix3 not directly compatable yet
    m.Set( M[0][0], M[0][1], M[0][2],
         M[1][0], M[1][1], M[1][2],
         M[2][0], M[2][1], M[2][2]);    // set orientation of sprite
    sto->sprt->SetTransform(m);
  }
}

real csRigidSpaceTimeObj::collision_check( csWorld *space )
{
csCollider *coli;
//csSprite3D *sprt;
csSector* first_sector;
ctMatrix3 M;
csMatrix3 m;
csVector3 n;
csVector3 x;
ctVector3 X;
csVector3 trime;
CDTriangle *wall;
CDTriangle *htri;
real max_depth;
real current_depth;
csOrthoTransform tfm;
ctCollidingContact *this_contact;
//ctCollidingContact *prev_contact;
collision_pair *CD_contact = NULL;
//bool hit_found;

  max_depth = 0;

  for( int i = 0; i < continuum_end; i++ ){

    first_sector = (csSector*)(space_time_continuum[i]->sprt->sectors[0]);

    // Start collision detection.
    csCollider::CollideReset ();
    csCollider::firstHit = false;
    coli = space_time_continuum[i]->col;
   // for ( ; num_sectors-- ; )
    M = space_time_continuum[i]->rb->get_world_to_this();
    m.Set( M[0][0], M[0][1], M[0][2],
               M[1][0], M[1][1], M[1][2],
               M[2][0], M[2][1], M[2][2]);    // orientation of sprite
//    hits += CollisionDetect (col, first_sector, &m);
    X = space_time_continuum[i]->rb->get_pos();
    x.x = X[0]; x.y = X[1]; x.z = X[2];
    // this IS a transformaition from other to this space.
    tfm.SetO2T( m );
    // NOT a traslation from other space to this space.  Actually opposite.
    tfm.SetO2TTranslation( x );

    // Check collision with this sector.
    csCollider::numHits = 0;
    if( first_sector ){
      csCollider::CollidePair (coli, csColliderPointerObject::GetCollider(*first_sector), &tfm);
      CD_contact = csCollider::GetCollisions ();
    }

    space_time_continuum[i]->num_collisions = csCollider::numHits;
    space_time_continuum[i]->contact = NULL;
    contact_heap_index = 0;
    // determine type of collision and penetration depth
    if( space_time_continuum[i]->num_collisions == 0 ){
      // no collision
    }else{
      //IMPORTANT: turn NOREWIND off if there is a collision!
      space_time_continuum[i]->rb->flags &= (~CTF_NOREWIND);
      this_contact = &(contact_heap[contact_heap_index]);
      this_contact->next = NULL;

      for( int acol = 0; acol < csCollider::numHits; acol++ ){
        space_time_continuum[i]->cd_contact[acol] = CD_contact[acol];

        // here is where the body hit should be recorded as well
        // NULL means we hit an immovable object, like a wall
        this_contact->body_b = NULL;

        this_contact->restitution = 0.75;

        wall = CD_contact[acol].tr2;
        n = ((wall->p2-wall->p1)%(wall->p3-wall->p2)).Unit();
      //  CsPrintf( MSG_DEBUG_1, "n %f, %f, %f\n", n.x, n.y, n.z );
        this_contact->n =
          ctVector3(n.x, n.y, n.z );

      // just one collision at a time here.
//      this_contact->next = NULL;

 /*   // ignore ojbects that aren't really moving towards collision.
    // they may "seep" though floor.
    if( fabs(space_time_continuum[i]->rb->get_v() * this_contact->n)  < MIN_REAL*1000.0)
      return 0;
*/

        htri = CD_contact[acol].tr1;

        // check each point of this triangle to see which penetrated the most

        for( int j = 0; j < 3 ; j++ ){
          if( j == 0 )
            trime = tfm.This2Other( htri->p1 );
          else if ( j == 1 )
            trime = tfm.This2Other( htri->p2 );
          else
            trime = tfm.This2Other( htri->p3 );

          current_depth = -(trime - wall->p1)*n;
          // this is the collision point
          if( current_depth > max_depth ){
            max_depth = current_depth;
          }

          if( current_depth > 0.0 ){
            this_contact->contact_p =
              ctVector3( trime.x, trime.y, trime.z );

            ctCollidingContact *chk = space_time_continuum[i]->contact;
            bool duplicate = false;
            while( chk != NULL && !duplicate ){
              if( chk->contact_p[0] == this_contact->contact_p[0] &&
                chk->contact_p[1] == this_contact->contact_p[1] &&
                chk->contact_p[2] == this_contact->contact_p[2] ){
                duplicate = true;
              }
              chk = chk->next;
            }
            if( !duplicate ){
              this_contact->next = space_time_continuum[i]->contact;
              space_time_continuum[i]->contact = this_contact;
              this_contact = &(contact_heap[++contact_heap_index]);
              this_contact->body_b = NULL;
              this_contact->restitution = space_time_continuum[i]->contact->restitution;
              this_contact->n = space_time_continuum[i]->contact->n;

            }
            // add collision point to other object as well, here
          }
        }
      }

    }

  }
  return sqrt(max_depth);

}

void csRigidSpaceTimeObj::collision_response( csWorld *space )
{
csRigidSpaceTimeObj *sto;

  for( int i = 0; i < continuum_end; i++ ){
    sto = space_time_continuum[i];
    if( sto->num_collisions > 0 && sto->contact != NULL ){
      sto->rb->resolve_collision( sto->contact );
    }
  }
}
<commit_msg>Adjustments for MSVC<commit_after>#include "sysdef.h"
#include "csengine/cdobj.h"
#include "csphyzik/phyziks.h"
#include "cstso.h"

static ctCollidingContact contact_heap[1024];  // no more than that
static int contact_heap_index = 0;

csRigidSpaceTimeObj *csRigidSpaceTimeObj::space_time_continuum[ MAX_SPACE_TIME_NUM ];
long csRigidSpaceTimeObj::continuum_end = 0;

// hideously inefficient collision detection/response algorithm
// just wanted to see some stuff bouncing around for now.

csSpaceTimeObj::csSpaceTimeObj()
{
//  space_time_continuum[continuum_end++] = this;
  //what_type = ST_SPACETIME;
}

csRigidSpaceTimeObj::csRigidSpaceTimeObj( csSprite3D *psprt, ctRigidBody *prb )
{
  space_time_continuum[continuum_end++] = this;
 // what_type = ST_SPACETIME;
 // col = pcollide;
  sprt = psprt;
  rb = prb;
  CHK (col = new csCollider (sprt));
  csColliderPointerObject::SetCollider (*sprt, col, true);
  what_type = ST_RIGID;

}

void set_no_rewind( ctEntity *ppe )
{
  ppe->set_rewind(false);
}

// using sloooooooow but accurate collision detection.
// this can be sped up in many ways.
// - use eulers method or mid-point for ODE just during CD
// - use prune and sweep algorithm with axis aligned, sorted bounding boxes
// - only evolve objects related to colliding objects when searching for c-time
// - don't forget to handle 'tunneling' problem as well.
void csRigidSpaceTimeObj::evolve_system( real t1, real t2, ctWorld *time_world, csWorld *space_world )
{
  static real collision_epsilon = 0.02;
  static real time_epsilon = 0.001;
  real ta, tb;

  long loops = 300;  //make sure we don't go into an infinite loop

  ta = t1;
  tb = t2;
  real min_col_dist;

  time_world->apply_function_to_body_list( set_no_rewind );

  while( ta < t2 && loops-- > 0){

    time_world->evolve( ta, tb );

    min_col_dist = collision_check( space_world );
    // if there are objects intersecting
    if( min_col_dist > 0 ){
      // if interpenetrations distance is very small => time of impact found
      // or if the time step is really small
      if( min_col_dist <= collision_epsilon || ( tb - ta ) <= time_epsilon ){
        // respond to collision
        collision_response( space_world );
        ta = tb;
        tb = t2;
      }else{
        // bisect time backwards to search for time of impact
        time_world->rewind(ta, tb);   // rewind state back to ta
        tb -= (tb - ta)*0.5;
  //      ctVector3 px = space_time_continuum[0]->rb->get_pos();
    //    px = space_time_continuum[0]->rb->get_pos();
      }
    // if we have not arrived at the end of our time interval
    }else if( tb < t2 ){
      // search forward in time for time of impact for collision(s)
      ta = tb;
      // if we are at our min time step, just finish this time step, collision
      // will get resolved next time.
      if( (t2 - ta) <= time_epsilon ){
        tb = t2;
        time_world->evolve( ta, tb );

        ta = tb;
      }else{
        tb += (t2 - tb)*0.5;
      }
    }else{
      ta = tb;
      tb = t2;
    }
  }

  update_space();

}

void csRigidSpaceTimeObj::update_space()
{
ctVector3 px;
csMatrix3 m;
ctMatrix3 M;
csRigidSpaceTimeObj *sto;

  for( int i = 0; i < continuum_end; i++ ){
    sto = space_time_continuum[i];
    px = sto->rb->get_pos();

    csVector3 new_p( px[0], px[1], px[2] );
    sto->sprt->SetMove ( new_p );

    M = sto->rb->get_R();   // get orientation for this link
    // ctMatrix3 and csMatrix3 not directly compatable yet
    m.Set( M[0][0], M[0][1], M[0][2],
         M[1][0], M[1][1], M[1][2],
         M[2][0], M[2][1], M[2][2]);    // set orientation of sprite
    sto->sprt->SetTransform(m);
  }
}

real csRigidSpaceTimeObj::collision_check( csWorld* /*space*/ )
{
csCollider *coli;
//csSprite3D *sprt;
csSector* first_sector;
ctMatrix3 M;
csMatrix3 m;
csVector3 n;
csVector3 x;
ctVector3 X;
csVector3 trime;
CDTriangle *wall;
CDTriangle *htri;
real max_depth;
real current_depth;
csOrthoTransform tfm;
ctCollidingContact *this_contact;
//ctCollidingContact *prev_contact;
collision_pair *CD_contact = NULL;
//bool hit_found;

  max_depth = 0;

  for( int i = 0; i < continuum_end; i++ ){

    first_sector = (csSector*)(space_time_continuum[i]->sprt->sectors[0]);

    // Start collision detection.
    csCollider::CollideReset ();
    csCollider::firstHit = false;
    coli = space_time_continuum[i]->col;
   // for ( ; num_sectors-- ; )
    M = space_time_continuum[i]->rb->get_world_to_this();
    m.Set( M[0][0], M[0][1], M[0][2],
               M[1][0], M[1][1], M[1][2],
               M[2][0], M[2][1], M[2][2]);    // orientation of sprite
//    hits += CollisionDetect (col, first_sector, &m);
    X = space_time_continuum[i]->rb->get_pos();
    x.x = X[0]; x.y = X[1]; x.z = X[2];
    // this IS a transformaition from other to this space.
    tfm.SetO2T( m );
    // NOT a traslation from other space to this space.  Actually opposite.
    tfm.SetO2TTranslation( x );

    // Check collision with this sector.
    csCollider::numHits = 0;
    if( first_sector ){
      csCollider::CollidePair (coli, csColliderPointerObject::GetCollider(*first_sector), &tfm);
      CD_contact = csCollider::GetCollisions ();
    }

    space_time_continuum[i]->num_collisions = csCollider::numHits;
    space_time_continuum[i]->contact = NULL;
    contact_heap_index = 0;
    // determine type of collision and penetration depth
    if( space_time_continuum[i]->num_collisions == 0 ){
      // no collision
    }else{
      //IMPORTANT: turn NOREWIND off if there is a collision!
      space_time_continuum[i]->rb->flags &= (~CTF_NOREWIND);
      this_contact = &(contact_heap[contact_heap_index]);
      this_contact->next = NULL;

      for( int acol = 0; acol < csCollider::numHits; acol++ ){
        space_time_continuum[i]->cd_contact[acol] = CD_contact[acol];

        // here is where the body hit should be recorded as well
        // NULL means we hit an immovable object, like a wall
        this_contact->body_b = NULL;

        this_contact->restitution = 0.75;

        wall = CD_contact[acol].tr2;
        n = ((wall->p2-wall->p1)%(wall->p3-wall->p2)).Unit();
      //  CsPrintf( MSG_DEBUG_1, "n %f, %f, %f\n", n.x, n.y, n.z );
        this_contact->n =
          ctVector3(n.x, n.y, n.z );

      // just one collision at a time here.
//      this_contact->next = NULL;

 /*   // ignore ojbects that aren't really moving towards collision.
    // they may "seep" though floor.
    if( fabs(space_time_continuum[i]->rb->get_v() * this_contact->n)  < MIN_REAL*1000.0)
      return 0;
*/

        htri = CD_contact[acol].tr1;

        // check each point of this triangle to see which penetrated the most

        for( int j = 0; j < 3 ; j++ ){
          if( j == 0 )
            trime = tfm.This2Other( htri->p1 );
          else if ( j == 1 )
            trime = tfm.This2Other( htri->p2 );
          else
            trime = tfm.This2Other( htri->p3 );

          current_depth = -(trime - wall->p1)*n;
          // this is the collision point
          if( current_depth > max_depth ){
            max_depth = current_depth;
          }

          if( current_depth > 0.0 ){
            this_contact->contact_p =
              ctVector3( trime.x, trime.y, trime.z );

            ctCollidingContact *chk = space_time_continuum[i]->contact;
            bool duplicate = false;
            while( chk != NULL && !duplicate ){
              if( chk->contact_p[0] == this_contact->contact_p[0] &&
                chk->contact_p[1] == this_contact->contact_p[1] &&
                chk->contact_p[2] == this_contact->contact_p[2] ){
                duplicate = true;
              }
              chk = chk->next;
            }
            if( !duplicate ){
              this_contact->next = space_time_continuum[i]->contact;
              space_time_continuum[i]->contact = this_contact;
              this_contact = &(contact_heap[++contact_heap_index]);
              this_contact->body_b = NULL;
              this_contact->restitution = space_time_continuum[i]->contact->restitution;
              this_contact->n = space_time_continuum[i]->contact->n;

            }
            // add collision point to other object as well, here
          }
        }
      }

    }

  }
  return sqrt(max_depth);

}

void csRigidSpaceTimeObj::collision_response( csWorld* /*space*/ )
{
csRigidSpaceTimeObj *sto;

  for( int i = 0; i < continuum_end; i++ ){
    sto = space_time_continuum[i];
    if( sto->num_collisions > 0 && sto->contact != NULL ){
      sto->rb->resolve_collision( sto->contact );
    }
  }
}
<|endoftext|>


<commit_before>/*
 * gdk-pixbuf-texture-tool - Play with texture files
 *
 * Copyright © 2011 Intel Corporation
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses>
 *
 * Authors: Damien Lespiau <damien.lespiau@intel.com>
 *
 */

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

#define GDK_PIXBUF_ENABLE_BACKEND

#include <gdk-pixbuf/gdk-pixbuf.h>

#include "gdk-pixbuf-pvr.h"
#include "PVRTexLib.h"
using namespace pvrtexlib;

typedef struct
{
  CPVRTexture *decompressed;
} PvrContext;

static const gchar *
standard_pixel_type_to_string (PixelType pixel_type)
{
  switch (pixel_type)
    {
    case eInt8StandardPixelType:
      return "R8G8B8A8";
    case eInt16StandardPixelType:
      return "A16B16G16R16";
    case eInt32StandardPixelType:
      return "R32G32B32A32";
    case eFloatStandardPixelType:
      return "R32G32B32A32 (float)";

    /* should not happen as we should only be given standard pixel types */
    default:
        return "Other";
    }
}

static void
on_pixbuf_destroyed (guchar *pixels,
                     gpointer data)
{
  PvrContext *context = (PvrContext *) data;

  g_message ("destroyed");
  delete context->decompressed;
  g_free (context);
}

static GdkPixbuf *
pvrtexlib_gdk_pixbuf_new_from_memory (const guchar  *data,
                                      GError       **error)
{
  CPVRTexture *decompressed;
  PvrContext *context;
  GdkPixbuf *pixbuf;

  PVRTRY
    {
      PVRTextureUtilities utils;
      PixelType pixel_type;

      CPVRTexture compressed (data);

      decompressed = new CPVRTexture();
      utils.DecompressPVR (compressed, *decompressed);

      pixel_type = decompressed->getPixelType ();
      if (pixel_type != eInt8StandardPixelType)
        {
          const gchar *type;

          type = standard_pixel_type_to_string (pixel_type);
          g_set_error (error,
                       GDK_PIXBUF_ERROR,
                       GDK_PIXBUF_ERROR_FAILED,
                       "Image type currently not supported (%s)", type);

          return NULL;

        }

      CPVRTextureData& data = decompressed->getData();

      /* FIXME: The data resulting of the decompression will always have alpha.
       * might worth repacking the pixbuf to RGB if the original texture did
       * not have any alpha before handing the pixbuf back to the user */

      context = g_new0 (PvrContext, 1);
      context->decompressed = decompressed;

      pixbuf = gdk_pixbuf_new_from_data (data.getData(),
                                         GDK_COLORSPACE_RGB,
                                         TRUE,
                                         8,
                                         decompressed->getWidth (),
                                         decompressed->getHeight (),
                                         decompressed->getWidth () * 4,
                                         on_pixbuf_destroyed,
                                         context);

      if (compressed.isFlipped ())
        {
          GdkPixbuf *flipped;

          flipped = gdk_pixbuf_flip (pixbuf, FALSE);
          g_object_unref (pixbuf);
          pixbuf = flipped;
        }
    }
  PVRCATCH(aaaahhh)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           aaaahhh.what());

      return NULL;
    }

  return pixbuf;
}

static GdkPixbuf *
gdk_pixbuf__pvr_image_load (FILE    *f,
                            GError **error)
{
  GdkPixbuf *pixbuf;
  guchar *content;
  GError *decompress_error = NULL;
  struct stat st;
  int fd;

  fd = fileno (f);
  if (fd == -1)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Invalid FILE object");
      return NULL;
    }

  if (fstat (fd, &st) == -1)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Failed to get attributes");
      return NULL;

    }

  if (st.st_size == 0 || st.st_size > G_MAXSIZE)
    {
      content = NULL;
    }
  else
    {
      content = (unsigned char *) mmap (NULL, st.st_size, PROT_READ,
                                        MAP_PRIVATE, fd, 0);
    }

  if (content == NULL || content == MAP_FAILED)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Failed to map file");
      return NULL;
    }

  pixbuf = pvrtexlib_gdk_pixbuf_new_from_memory (content,
                                                 &decompress_error);
  if (decompress_error)
    {
      g_propagate_error (error, decompress_error);
      return NULL;
    }

  return pixbuf;
}

static bool
is_p2 (unsigned int x)
{
    return ((x != 0) && !(x & (x - 1)));
}

static gboolean
parse_format (const gchar *format,
              PixelType   *type)
{
  if (g_strcmp0 (format, "PVRTC2") == 0)
    {
      *type = OGL_PVRTC2;
      return TRUE;
    }
  if (g_strcmp0 (format, "PVRTC4") == 0)
    {
      *type = OGL_PVRTC4;
      return TRUE;
    }
  if (g_strcmp0 (format, "ETC1") == 0)
    {
      *type = ETC_RGB_4BPP;
      return TRUE;
    }

  *type = ETC_RGB_4BPP;
  return FALSE;
}

static gboolean
validate_pixbuf_pvrtc (GdkPixbuf  *pixbuf,
                       GError    **error)
{
  int width, height;

  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);

  if (!is_p2 (width))
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Width needs to be a power of 2");
      return FALSE;
    }

  if (!is_p2 (height))
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Height needs to be a power of 2");
      return FALSE;
    }

  return TRUE;
}

static gboolean
gdk_pixbuf__pvr_image_save (FILE       *f,
                            GdkPixbuf  *pixbuf,
                            gchar     **param_keys,
                            gchar     **param_values,
                            GError    **error_out)
{
  GdkPixbuf *with_alpha = NULL;
  PixelType opt_format = ETC_RGB_4BPP;
  gboolean valid;
  GError *error = NULL;

  /* parse the parameters */
  if (param_keys)
    {
      gchar **key_p = param_keys, **value_p = param_values;

      while (*key_p)
        {
          if (g_strcmp0 (*key_p, "format") == 0)
            {
              if (!parse_format (*value_p, &opt_format))
                {
                  g_set_error (error_out,
                               GDK_PIXBUF_ERROR,
                               GDK_PIXBUF_ERROR_FAILED,
                               "Invalid format %s", *value_p);
                  return FALSE;
                }
            }
          else
            {
              g_warning ("Unknown option %s", *key_p);
            }

          key_p++;
          value_p++;
        }
    }

  /* validate the pixbuf if needed */
  switch (opt_format)
    {
    case OGL_PVRTC2:
    case OGL_PVRTC4:
      valid = validate_pixbuf_pvrtc (pixbuf, &error);
      break;
    default:
      valid = TRUE;
    }

  if (!valid)
    {
      g_propagate_error (error_out, error);
      return FALSE;
    }

  PVRTRY
    {
      PVRTextureUtilities utils;
      int width, height;
      guchar *pixels;

      width = gdk_pixbuf_get_width (pixbuf);
      height = gdk_pixbuf_get_height (pixbuf);

      /* The standard format that PVRTexLib takes is RGBA 8888 so we need
       * to add an alpha channel if the original image does not have one */
      if (!gdk_pixbuf_get_has_alpha (pixbuf))
        {
          with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
          pixbuf = with_alpha;
        }

      if (gdk_pixbuf_get_rowstride (pixbuf) != 4 * width)
        {
          g_set_error_literal (error_out,
                               GDK_PIXBUF_ERROR,
                               GDK_PIXBUF_ERROR_FAILED,
                               "A row stride larger than the width is not "
                               "allowed");
          if (with_alpha)
            g_object_unref (with_alpha);

          return FALSE;
        }

      /* make a CPVRTexture instance from the GdkPixbuf */
      pixels = gdk_pixbuf_get_pixels (pixbuf);
      CPVRTexture uncompressed (width,
                               height,
                               0,                       /* u32MipMapCount */
                               1,                       /* u32NumSurfaces */
                               false,                   /* bBorder */
                               false,                   /* bTwiddled */
                               false,                   /* bCubeMap */
                               false,                   /* bVolume */
                               false,                   /* bFalseMips */
                               true,                    /* bHasAlpha */
                               false,                   /* bFlipped */
                               eInt8StandardPixelType,  /* ePixelType */
                               0.0f,                    /* fNormalMap */
                               pixels);                 /* pPixelData */

      /* create texture to encode to */
      CPVRTexture compressed (uncompressed.getHeader());

      /* FIXME: Remove the alpha channel from the compressed texture is the
       * original GdkPixbuf does not have alpha (But we still need to create
       * the uncompressed texture with hasAlpha to TRUE as the pixbuf is 4
       * bytes per pixel anyway */

      /* TODO: Add support for generating the mipmaps */

      /* TODO: Add support for selection with compression we want */

      /* set required encoded pixel type */
      compressed.setPixelType (opt_format);

      /* encode texture */
      utils.CompressPVR (uncompressed, compressed);

      /* write to file */
      compressed.getHeader().writeToFile (f);
      compressed.getData().writeToFile (f);
    }
  PVRCATCH(aaaahhh)
    {
      g_set_error_literal (error_out,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           aaaahhh.what());

      if (with_alpha)
        g_object_unref (with_alpha);

      return FALSE;
    }

  if (with_alpha)
    g_object_unref (with_alpha);

  return TRUE;
}

/*
 * Note: The library provided by Imagination does not seem to provide anything
 * that would allow us to support "incremental loading". What we can do though
 * is to fake incremental loading by accumulating the data inside an array and
 * decode it at the end. This allows us to work with GdkPixbufLoader (which a
 * lot of apps use) at the very least we can signal the size of the level 0
 * mipmap after having read the header.
 */

typedef struct
{
  GdkPixbufModuleSizeFunc size_func;
  GdkPixbufModulePreparedFunc prepared_func;
  GdkPixbufModuleUpdatedFunc updated_func;
  gpointer user_data;

  GArray *buffer;

  guint got_header : 1;
} PvrIncContext;

static gpointer
gdk_pixbuf__pvr_begin_load (GdkPixbufModuleSizeFunc       size_func,
                            GdkPixbufModulePreparedFunc   prepared_func,
                            GdkPixbufModuleUpdatedFunc    updated_func,
                            gpointer                      user_data,
                            GError                      **error)
{
  PvrIncContext *context;

  context = g_new0 (PvrIncContext, 1);
  context->size_func = size_func;
  context->prepared_func = prepared_func;
  context->updated_func  = updated_func;
  context->user_data = user_data;

  /* initialized so can contain a 512x512 image with 4 bits per pixel without
   * the need to reallocate the buffer */
  context->buffer = g_array_sized_new (FALSE, FALSE, 1, 512 * 512 / 2);

  return context;
}

static gboolean
gdk_pixbuf__pvr_stop_load (gpointer   contextp,
                           GError   **error)
{
  PvrIncContext *context = (PvrIncContext *) contextp;
  GdkPixbuf *pixbuf;
  GError *decompress_error = NULL;

  pixbuf =
    pvrtexlib_gdk_pixbuf_new_from_memory ((guchar *)context->buffer->data,
                                          &decompress_error);
  if (decompress_error)
    {
      g_propagate_error (error, decompress_error);
      return FALSE;
    }

  if (context->prepared_func)
    context->prepared_func (pixbuf, NULL, context->user_data);

  g_free (context);

  return TRUE;
}

static gboolean
gdk_pixbuf__pvr_load_increment (gpointer       contextp,
                                const guchar  *buf,
                                guint          size,
                                GError       **error)
{
  PvrIncContext *context = (PvrIncContext *) contextp;

  g_array_append_vals (context->buffer, buf, size);

  if (!context->got_header && context->buffer->len >= sizeof (PVRHeader))
    {
      context->got_header = TRUE;

      if (context->size_func)
        {
          gint width, height;
          PVRHeader *header;

          header = (PVRHeader *) context->buffer->data;
          width = header->width;
          height = header->height;

          (*context->size_func) (&width, &height, context->user_data);

          if (width == 0 || height == 0)
            {
              /* used to signal we are going to stop loading the image, return
               * an error for good measure and not fall in the case we return
               * FALSE without an error set */
              g_set_error_literal (error,
                                   GDK_PIXBUF_ERROR,
                                   GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                   "Zero width or height requested");
              return FALSE;
            }
        }
    }

  return TRUE;
}

extern "C" {

G_MODULE_EXPORT void
fill_vtable (GdkPixbufModule *module)
{
  module->load = gdk_pixbuf__pvr_image_load;
  module->save = gdk_pixbuf__pvr_image_save;

  module->begin_load = gdk_pixbuf__pvr_begin_load;
  module->stop_load = gdk_pixbuf__pvr_stop_load;
  module->load_increment = gdk_pixbuf__pvr_load_increment;
}

G_MODULE_EXPORT void
fill_info (GdkPixbufFormat *info)
{
  static GdkPixbufModulePattern signature_new[] = {
        { NULL, NULL, 0 }
  };

  static gchar *mime_types[] =
    {
      "image/x-pvr",
      NULL
    };
  static gchar *extensions[] =
    {
      "pvr",
      NULL
    };

  info->name        = "pvr";
  info->description = "PVR image";
  info->signature = signature_new;
  info->mime_types  = mime_types;
  info->extensions  = extensions;
  info->flags       = GDK_PIXBUF_FORMAT_WRITABLE;
  info->license     = "LGPL";
}

} /* extern "C" */
<commit_msg>loader: Remove spurious g_message()<commit_after>/*
 * gdk-pixbuf-texture-tool - Play with texture files
 *
 * Copyright © 2011 Intel Corporation
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses>
 *
 * Authors: Damien Lespiau <damien.lespiau@intel.com>
 *
 */

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

#define GDK_PIXBUF_ENABLE_BACKEND

#include <gdk-pixbuf/gdk-pixbuf.h>

#include "gdk-pixbuf-pvr.h"
#include "PVRTexLib.h"
using namespace pvrtexlib;

typedef struct
{
  CPVRTexture *decompressed;
} PvrContext;

static const gchar *
standard_pixel_type_to_string (PixelType pixel_type)
{
  switch (pixel_type)
    {
    case eInt8StandardPixelType:
      return "R8G8B8A8";
    case eInt16StandardPixelType:
      return "A16B16G16R16";
    case eInt32StandardPixelType:
      return "R32G32B32A32";
    case eFloatStandardPixelType:
      return "R32G32B32A32 (float)";

    /* should not happen as we should only be given standard pixel types */
    default:
        return "Other";
    }
}

static void
on_pixbuf_destroyed (guchar *pixels,
                     gpointer data)
{
  PvrContext *context = (PvrContext *) data;

  delete context->decompressed;
  g_free (context);
}

static GdkPixbuf *
pvrtexlib_gdk_pixbuf_new_from_memory (const guchar  *data,
                                      GError       **error)
{
  CPVRTexture *decompressed;
  PvrContext *context;
  GdkPixbuf *pixbuf;

  PVRTRY
    {
      PVRTextureUtilities utils;
      PixelType pixel_type;

      CPVRTexture compressed (data);

      decompressed = new CPVRTexture();
      utils.DecompressPVR (compressed, *decompressed);

      pixel_type = decompressed->getPixelType ();
      if (pixel_type != eInt8StandardPixelType)
        {
          const gchar *type;

          type = standard_pixel_type_to_string (pixel_type);
          g_set_error (error,
                       GDK_PIXBUF_ERROR,
                       GDK_PIXBUF_ERROR_FAILED,
                       "Image type currently not supported (%s)", type);

          return NULL;

        }

      CPVRTextureData& data = decompressed->getData();

      /* FIXME: The data resulting of the decompression will always have alpha.
       * might worth repacking the pixbuf to RGB if the original texture did
       * not have any alpha before handing the pixbuf back to the user */

      context = g_new0 (PvrContext, 1);
      context->decompressed = decompressed;

      pixbuf = gdk_pixbuf_new_from_data (data.getData(),
                                         GDK_COLORSPACE_RGB,
                                         TRUE,
                                         8,
                                         decompressed->getWidth (),
                                         decompressed->getHeight (),
                                         decompressed->getWidth () * 4,
                                         on_pixbuf_destroyed,
                                         context);

      if (compressed.isFlipped ())
        {
          GdkPixbuf *flipped;

          flipped = gdk_pixbuf_flip (pixbuf, FALSE);
          g_object_unref (pixbuf);
          pixbuf = flipped;
        }
    }
  PVRCATCH(aaaahhh)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           aaaahhh.what());

      return NULL;
    }

  return pixbuf;
}

static GdkPixbuf *
gdk_pixbuf__pvr_image_load (FILE    *f,
                            GError **error)
{
  GdkPixbuf *pixbuf;
  guchar *content;
  GError *decompress_error = NULL;
  struct stat st;
  int fd;

  fd = fileno (f);
  if (fd == -1)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Invalid FILE object");
      return NULL;
    }

  if (fstat (fd, &st) == -1)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Failed to get attributes");
      return NULL;

    }

  if (st.st_size == 0 || st.st_size > G_MAXSIZE)
    {
      content = NULL;
    }
  else
    {
      content = (unsigned char *) mmap (NULL, st.st_size, PROT_READ,
                                        MAP_PRIVATE, fd, 0);
    }

  if (content == NULL || content == MAP_FAILED)
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Failed to map file");
      return NULL;
    }

  pixbuf = pvrtexlib_gdk_pixbuf_new_from_memory (content,
                                                 &decompress_error);
  if (decompress_error)
    {
      g_propagate_error (error, decompress_error);
      return NULL;
    }

  return pixbuf;
}

static bool
is_p2 (unsigned int x)
{
    return ((x != 0) && !(x & (x - 1)));
}

static gboolean
parse_format (const gchar *format,
              PixelType   *type)
{
  if (g_strcmp0 (format, "PVRTC2") == 0)
    {
      *type = OGL_PVRTC2;
      return TRUE;
    }
  if (g_strcmp0 (format, "PVRTC4") == 0)
    {
      *type = OGL_PVRTC4;
      return TRUE;
    }
  if (g_strcmp0 (format, "ETC1") == 0)
    {
      *type = ETC_RGB_4BPP;
      return TRUE;
    }

  *type = ETC_RGB_4BPP;
  return FALSE;
}

static gboolean
validate_pixbuf_pvrtc (GdkPixbuf  *pixbuf,
                       GError    **error)
{
  int width, height;

  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);

  if (!is_p2 (width))
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Width needs to be a power of 2");
      return FALSE;
    }

  if (!is_p2 (height))
    {
      g_set_error_literal (error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           "Height needs to be a power of 2");
      return FALSE;
    }

  return TRUE;
}

static gboolean
gdk_pixbuf__pvr_image_save (FILE       *f,
                            GdkPixbuf  *pixbuf,
                            gchar     **param_keys,
                            gchar     **param_values,
                            GError    **error_out)
{
  GdkPixbuf *with_alpha = NULL;
  PixelType opt_format = ETC_RGB_4BPP;
  gboolean valid;
  GError *error = NULL;

  /* parse the parameters */
  if (param_keys)
    {
      gchar **key_p = param_keys, **value_p = param_values;

      while (*key_p)
        {
          if (g_strcmp0 (*key_p, "format") == 0)
            {
              if (!parse_format (*value_p, &opt_format))
                {
                  g_set_error (error_out,
                               GDK_PIXBUF_ERROR,
                               GDK_PIXBUF_ERROR_FAILED,
                               "Invalid format %s", *value_p);
                  return FALSE;
                }
            }
          else
            {
              g_warning ("Unknown option %s", *key_p);
            }

          key_p++;
          value_p++;
        }
    }

  /* validate the pixbuf if needed */
  switch (opt_format)
    {
    case OGL_PVRTC2:
    case OGL_PVRTC4:
      valid = validate_pixbuf_pvrtc (pixbuf, &error);
      break;
    default:
      valid = TRUE;
    }

  if (!valid)
    {
      g_propagate_error (error_out, error);
      return FALSE;
    }

  PVRTRY
    {
      PVRTextureUtilities utils;
      int width, height;
      guchar *pixels;

      width = gdk_pixbuf_get_width (pixbuf);
      height = gdk_pixbuf_get_height (pixbuf);

      /* The standard format that PVRTexLib takes is RGBA 8888 so we need
       * to add an alpha channel if the original image does not have one */
      if (!gdk_pixbuf_get_has_alpha (pixbuf))
        {
          with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
          pixbuf = with_alpha;
        }

      if (gdk_pixbuf_get_rowstride (pixbuf) != 4 * width)
        {
          g_set_error_literal (error_out,
                               GDK_PIXBUF_ERROR,
                               GDK_PIXBUF_ERROR_FAILED,
                               "A row stride larger than the width is not "
                               "allowed");
          if (with_alpha)
            g_object_unref (with_alpha);

          return FALSE;
        }

      /* make a CPVRTexture instance from the GdkPixbuf */
      pixels = gdk_pixbuf_get_pixels (pixbuf);
      CPVRTexture uncompressed (width,
                               height,
                               0,                       /* u32MipMapCount */
                               1,                       /* u32NumSurfaces */
                               false,                   /* bBorder */
                               false,                   /* bTwiddled */
                               false,                   /* bCubeMap */
                               false,                   /* bVolume */
                               false,                   /* bFalseMips */
                               true,                    /* bHasAlpha */
                               false,                   /* bFlipped */
                               eInt8StandardPixelType,  /* ePixelType */
                               0.0f,                    /* fNormalMap */
                               pixels);                 /* pPixelData */

      /* create texture to encode to */
      CPVRTexture compressed (uncompressed.getHeader());

      /* FIXME: Remove the alpha channel from the compressed texture is the
       * original GdkPixbuf does not have alpha (But we still need to create
       * the uncompressed texture with hasAlpha to TRUE as the pixbuf is 4
       * bytes per pixel anyway */

      /* TODO: Add support for generating the mipmaps */

      /* TODO: Add support for selection with compression we want */

      /* set required encoded pixel type */
      compressed.setPixelType (opt_format);

      /* encode texture */
      utils.CompressPVR (uncompressed, compressed);

      /* write to file */
      compressed.getHeader().writeToFile (f);
      compressed.getData().writeToFile (f);
    }
  PVRCATCH(aaaahhh)
    {
      g_set_error_literal (error_out,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_FAILED,
                           aaaahhh.what());

      if (with_alpha)
        g_object_unref (with_alpha);

      return FALSE;
    }

  if (with_alpha)
    g_object_unref (with_alpha);

  return TRUE;
}

/*
 * Note: The library provided by Imagination does not seem to provide anything
 * that would allow us to support "incremental loading". What we can do though
 * is to fake incremental loading by accumulating the data inside an array and
 * decode it at the end. This allows us to work with GdkPixbufLoader (which a
 * lot of apps use) at the very least we can signal the size of the level 0
 * mipmap after having read the header.
 */

typedef struct
{
  GdkPixbufModuleSizeFunc size_func;
  GdkPixbufModulePreparedFunc prepared_func;
  GdkPixbufModuleUpdatedFunc updated_func;
  gpointer user_data;

  GArray *buffer;

  guint got_header : 1;
} PvrIncContext;

static gpointer
gdk_pixbuf__pvr_begin_load (GdkPixbufModuleSizeFunc       size_func,
                            GdkPixbufModulePreparedFunc   prepared_func,
                            GdkPixbufModuleUpdatedFunc    updated_func,
                            gpointer                      user_data,
                            GError                      **error)
{
  PvrIncContext *context;

  context = g_new0 (PvrIncContext, 1);
  context->size_func = size_func;
  context->prepared_func = prepared_func;
  context->updated_func  = updated_func;
  context->user_data = user_data;

  /* initialized so can contain a 512x512 image with 4 bits per pixel without
   * the need to reallocate the buffer */
  context->buffer = g_array_sized_new (FALSE, FALSE, 1, 512 * 512 / 2);

  return context;
}

static gboolean
gdk_pixbuf__pvr_stop_load (gpointer   contextp,
                           GError   **error)
{
  PvrIncContext *context = (PvrIncContext *) contextp;
  GdkPixbuf *pixbuf;
  GError *decompress_error = NULL;

  pixbuf =
    pvrtexlib_gdk_pixbuf_new_from_memory ((guchar *)context->buffer->data,
                                          &decompress_error);
  if (decompress_error)
    {
      g_propagate_error (error, decompress_error);
      return FALSE;
    }

  if (context->prepared_func)
    context->prepared_func (pixbuf, NULL, context->user_data);

  g_free (context);

  return TRUE;
}

static gboolean
gdk_pixbuf__pvr_load_increment (gpointer       contextp,
                                const guchar  *buf,
                                guint          size,
                                GError       **error)
{
  PvrIncContext *context = (PvrIncContext *) contextp;

  g_array_append_vals (context->buffer, buf, size);

  if (!context->got_header && context->buffer->len >= sizeof (PVRHeader))
    {
      context->got_header = TRUE;

      if (context->size_func)
        {
          gint width, height;
          PVRHeader *header;

          header = (PVRHeader *) context->buffer->data;
          width = header->width;
          height = header->height;

          (*context->size_func) (&width, &height, context->user_data);

          if (width == 0 || height == 0)
            {
              /* used to signal we are going to stop loading the image, return
               * an error for good measure and not fall in the case we return
               * FALSE without an error set */
              g_set_error_literal (error,
                                   GDK_PIXBUF_ERROR,
                                   GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                   "Zero width or height requested");
              return FALSE;
            }
        }
    }

  return TRUE;
}

extern "C" {

G_MODULE_EXPORT void
fill_vtable (GdkPixbufModule *module)
{
  module->load = gdk_pixbuf__pvr_image_load;
  module->save = gdk_pixbuf__pvr_image_save;

  module->begin_load = gdk_pixbuf__pvr_begin_load;
  module->stop_load = gdk_pixbuf__pvr_stop_load;
  module->load_increment = gdk_pixbuf__pvr_load_increment;
}

G_MODULE_EXPORT void
fill_info (GdkPixbufFormat *info)
{
  static GdkPixbufModulePattern signature_new[] = {
        { NULL, NULL, 0 }
  };

  static gchar *mime_types[] =
    {
      "image/x-pvr",
      NULL
    };
  static gchar *extensions[] =
    {
      "pvr",
      NULL
    };

  info->name        = "pvr";
  info->description = "PVR image";
  info->signature = signature_new;
  info->mime_types  = mime_types;
  info->extensions  = extensions;
  info->flags       = GDK_PIXBUF_FORMAT_WRITABLE;
  info->license     = "LGPL";
}

} /* extern "C" */
<|endoftext|>