<commit_before>//
// This code is based on the HTTP Server 3 boost example from:
//     http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/examples.html
//
// Description:
//     HTTP Server 3
//     An HTTP server using a single io_service and a thread pool calling io_service::run().
//
// Ideally we could use the example as-is, but it uses a file-based scheme that is not at all RESTful.
// To update this code when a new example becomes available,
// do a diff between the old and new example code and paste in the changes.
//
//
// Original copyright notice:
//
// request_handler.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef QUICK_HTTP_SERVER_HANDLER_HPP
#define QUICK_HTTP_SERVER_HANDLER_HPP

#include "http_helpers.hpp"
#include "quick_http_components/mime_types.hpp"
#include "quick_http_components/reply.hpp"
#include "quick_http_components/request.hpp"

using namespace QuickHttp;

// This functor creates the server reply for the given request.
// You must override it to provide custom server replies.
class base_server_handler
  : private boost::noncopyable
{
public:

	explicit base_server_handler(
	    const vector<string>& includes,
        const vector<API_call*>& vpAPI,
        int max_body_size = 100000
	) :
        // init vars
	    vpAPI_(vpAPI),
        max_body_size_(max_body_size)
	{
	    load_favicon();
        set_html_includes(includes);
	    for (auto& pAPI : vpAPI_)
	    {
	        if (pAPI->load_static_html())
	            inject_includes(*pAPI);
	    }
	}

	virtual ~base_server_handler()
	{
	    for (auto& pcall : vpAPI_)
	    {
	        delete pcall;
	    }
	}

	virtual void operator() (const request& req, reply& rep)
	{
	    // Let's see what type of request we got.
	    // Slice up the URL and switch on the pieces.
	    // Our tokenizer will validate that the url was in the correct format.

	    std::string protocol, host, action, type;
	    std::vector<std::string> path_tokens;
	    vector<pair<string,string>> pair_tokens;

	    // Mark the request bad until we validate.
	    rep.status = reply::bad_request;

	    // Tokenize, and ignore malformed requests.
	    // That means we will have at least one path token and one action (or just the hostname).
	    API_call ac;
	    ac.method_ = req.method;
	    if (tokenize_API_url(req.uri,protocol,host,ac))
	    {
	        if (process_API_call(ac,rep))
	        {
	            rep.status = reply::ok;
	            if (!ac.types_.empty())
	                type = ac.types_[0];
	        }
	    }

	    if (rep.status == reply::bad_request)
	    {
	        // There is one hardcoded request that we handle by hand, the annoying favicon.ico.
	        if (req.uri.substr(req.uri.size()-11,11) == "favicon.ico")
	        {
	            rep.content = favicon_;
	            type = "ico";
                rep.status = reply::ok;

	        } else
	        {
	            // We respond to all bad requests with the API html.
	            // Note that this is no place to prevent DDOS - DDOS'ing to legit API calls is trivial.
	            // Help the user out.
                log(LV_ERROR,string("Received unrecognized request: ") + req.method + " " + req.uri);
                rep.content = "
A better Trader API
";
                rep.content += get_API_html("<button>","</button>","<button>","</button>","<button>","</button>","
");
	        }
	    }

	    // Build the headers.
	    rep.headers.resize(2);
	    rep.headers[0].name = "Content-Length";
	    rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
	    rep.headers[1].name = "Content-Type";
	    rep.headers[1].value = mime_types::extension_to_type(type);

	    if (rep.status == reply::bad_request)
	    {
	    }
	}

	inline void load_favicon()
	{
	    try
	    {
	        favicon_ = read_file("htdocs/favicon.ico");
	    }
	    catch(...)
	    {
            log(LV_WARNING,"WARNING: No favicon.ico file was found in [htdocs/].");

            // TODO add an encoded default
	    }
	}

	// We provide a means to inject include files into html.
    // This should be done for all our preloaded html on startup.
    inline void set_html_includes(const vector<string>& includes)
    {
        // We receive a series of filenames.
        // We take the filename and preload it from the local file system.
        // We also save the search string that should be replaced by the preloaded data in load_static_html().
        // KISS: We support css and js, that's it for now.

        try
        {
            for (auto& filename : includes)
            {
                if (filename.substr(filename.size()-4,4) == ".css")
                {
                    includes_.push_back(
                        pair<string,string>(
                            string("",
                            read_file(string("htdocs/") + filename)
                        )
                    );

                } else if (filename.substr(filename.size()-3,3) == ".js")
                {
                    includes_.push_back(
                        pair<string,string>(
                            string("<link href=\"") + filename + "\" rel=\"stylesheet\">",
                            read_file(string("htdocs/") + filename)
                        )
                    );

                } else
                {
                    // A file was requested to be inline-included but the type is not understood yet.
                    // Add another handler as needed!
                    assert(false);
                }
            }
        }
        catch(...)
        {
            log(LV_ERROR,"ERROR: Some HTML includes were not found.");
        }
    }

    void inject_includes(API_call& ac)
    {
        // We may need to build the relative path back to the root, based on the depth of the ac path.
        string relative_path;
        int steps = ac.path_tokens_.size() - 1;
        for (int n=0; n < steps; ++n)
            relative_path += "../";

        for (auto& include : includes_)
        {
            // Update any path to include the proper relative path.
            string target = include.first;
            replace(target,"href=\"",string("href\"") + relative_path);
            replace(target,"src=\"",string("src\"") + relative_path);

            // NOTE that this uses "replace" from utilities.hpp.
            replace(ac.static_html_, target, include.second);
        }
    }

    bool b_API_call_matches(const API_call& acDefinition, const API_call& ac, bool bIncludeParamPairs = false)
    {
        if (!strings_are_equal(acDefinition.method_,ac.method_))                                    return false;
        if (acDefinition.path_tokens_.size() != ac.path_tokens_.size())                             return false;

        for (int n = 0; n < ac.path_tokens_.size(); ++n)
        {
            if (acDefinition.path_tokens_[n][0] != ':')
            {
                if (!strings_are_equal(acDefinition.path_tokens_[n],ac.path_tokens_[n]))
                    return false;
            }
        }

        if (acDefinition.b_param_pairs_are_mandatory_)
        {
            if (acDefinition.pair_tokens_.size() != ac.pair_tokens_.size())
                return false;

            for (int n = 0; n < ac.pair_tokens_.size(); ++n)
            {
                if (!strings_are_equal(acDefinition.pair_tokens_[n].first,ac.pair_tokens_[n].first))
                    return false;
            }
        }

        return true;
    }

    bool process_API_call(API_call& ac, QuickHttp::reply& rep)
    {
        for (auto& acloop : vpAPI_)
        {
            if (b_API_call_matches(*acloop,ac))
                return acloop->handle_call(rep);
        }
        return false;
    }

    string get_API_html(const string& method1, const string& method2, const string& path1, const string& path2, const string& param1, const string& param2, const string& endline)
    {
        string html;
        for (auto& ac : vpAPI_)
        {
            html += method1 + ac->method_ + method2 + " /";
            for (int n = 0; n < ac->path_tokens_.size(); ++n)
            {
                const string& path = ac->path_tokens_[n];
                if (!path.empty() && path[0] == ':')
                    html += path1 + path + path2;
                else
                    html += path;
                if (n < ac->path_tokens_.size() - 1)
                    html += "/";
            }
            // html += ac->action_ + "." + ac->type_;
            for (int n = 0; n < ac->pair_tokens_.size(); ++n)
            {
                const pair<string,string>& tokenpair = ac->pair_tokens_[n];
                if (n==0) html += " ? ";
                else      html += " & ";
                html += tokenpair.first + "=";
                html += param1 + tokenpair.second + param2;
            }
            html += endline;
        }

        return html;
    }

    bool tokenize_API_url(const std::string& url, std::string& protocol, std::string& host, API_call& ac)
    {
        // This is not the ultimate URL parser (there are libraries for that when you need it - google-url, StrTk, etc.).
        // It only handles the formats we expect for our RESTful API.
        //
        // These include the following type of urls:
        //
        //      http(s)://server.com/version/action.type?param1=value1&param2=value2
        //      server.com/version/action.type?param1=value1&param2=value2
        //      /version/action.type?param1=value1&param2=value2
        //
        // If we do not find such a format, it does not fit our RESTful API model and we return false.
        // Specifically, we REQUIRE version and action.

        // We must at least have /version/action.  No buffer overflow attempts please.
        if (url.length() < 4 || url.length() > 700)
            return false;

        protocol.clear();
        host.clear();
        ac.path_tokens_.clear();
        ac.pair_tokens_.clear();

        // ===================================
        // protocol
        size_t walk1 = url.find("://",0);
        size_t walk2;
        if (walk1 == std::string::npos)
        {
            walk1 = 0;

        } else
        {
            protocol = url.substr(0,walk1);
            walk1 = walk1 + 3;
        }

        // If all we got was the protocol, that's not enough.
        if (url.length() <= walk1)
            return false;
        // ===================================

        // ===================================
        // host
        if (url[walk1] != '/')
        {
            walk2 = url.find_first_of('/',walk1);
            host = url.substr(walk1,walk2-walk1);

            // If all we got was the host, that's not enough.
            // Actually, we will allow it now, so we can provide a self-documenting API.
            // But we still need to return here.
            if (walk2 == std::string::npos || walk2 == url.length())
                return true;

            walk1 = walk2;
        }
        // ===================================

        // ===================================
        // paths

        // We should always be at the first '/' here, skip over it.
        ++walk1;

        while (walk1 < url.length())
        {
            walk2 = url.find_first_of('/',walk1);
            if (walk2 == std::string::npos)
                break;

            ac.path_tokens_.push_back(url.substr(walk1,walk2-walk1));
            walk1 = walk2 + 1;
        }

        // If we have anything beyond the hostname,
        // we need at least one path token for the API version,
        // and we also need an action.
        if (ac.path_tokens_.size() < 1 || url.length() < walk1 + 1)
            return false;
        // ===================================

        // ===================================
        // action
        // ===================================

        // We need to look for both "." and "?".
        walk2 = url.find_first_of(".?",walk1);
        ac.path_tokens_.push_back(url.substr(walk1,walk2-walk1));

        // It's ok if we stopped at action.
        if (walk2 == std::string::npos)
            return true;
        // ===================================

        // ===================================
        // type

        // We may be at a "." or a "?".  We only have a type if we're at ".".
        walk1 = walk2;
        if (url[walk1] == '.')
        {
            walk2 = url.find_first_of('?',walk1);
            ac.types_.push_back(url.substr(walk1+1,walk2-walk1-1));

            // If we DID have a ".", we should have a type.
            if (ac.types_[0].empty())
                return false;

            // It's ok to finish with no name-value pairs.
            if (walk2 == std::string::npos)
                return true;

            walk1 = walk2;
        }
        // ===================================

        // ===================================
        // pairs
        // break apart "param1=value1&param2=value2" name-value pairs

        // We should always be at the "?" here, skip over it.
        ++walk1;

        while (walk1 < url.length())
        {
            walk2 = url.find_first_of('=',walk1);

            // No good if we didn't have a full pair.
            if (walk2 == std::string::npos)
                return false;

            size_t walk3 = url.find_first_of('&',walk2);
            ac.pair_tokens_.push_back(
                std::pair<string,string>(
                    url.substr(walk1,walk2-walk1),
                    url.substr(walk2+1,walk3-walk2-1)
                )
            );

            // All done if we ran past the end.
            if (walk3 == std::string::npos)
                return true;

            walk1 = walk3 + 1;
        }
        // ===================================

        // We actually shouldn't hit this.
        return false;
    }

    std::size_t max_body_size_;

protected:

    const vector<API_call*>& vpAPI_;
    vector<pair<string,string>> includes_;
    string favicon_;
};

#endif // QUICK_HTTP_SERVER_HANDLER_HPP
<commit_msg>Javascript and CSS injection coded and working.<commit_after>//
// This code is based on the HTTP Server 3 boost example from:
//     http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/examples.html
//
// Description:
//     HTTP Server 3
//     An HTTP server using a single io_service and a thread pool calling io_service::run().
//
// Ideally we could use the example as-is, but it uses a file-based scheme that is not at all RESTful.
// To update this code when a new example becomes available,
// do a diff between the old and new example code and paste in the changes.
//
//
// Original copyright notice:
//
// request_handler.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef QUICK_HTTP_SERVER_HANDLER_HPP
#define QUICK_HTTP_SERVER_HANDLER_HPP

#include "http_helpers.hpp"
#include "quick_http_components/mime_types.hpp"
#include "quick_http_components/reply.hpp"
#include "quick_http_components/request.hpp"

using namespace QuickHttp;

// This functor creates the server reply for the given request.
// You must override it to provide custom server replies.
class base_server_handler
  : private boost::noncopyable
{
public:

	explicit base_server_handler(
	    const vector<string>& includes,
        const vector<API_call*>& vpAPI,
        int max_body_size = 100000
	) :
        // init vars
	    vpAPI_(vpAPI),
        max_body_size_(max_body_size)
	{
	    load_favicon();
        set_html_includes(includes);
	    for (auto& pAPI : vpAPI_)
	    {
	        if (pAPI->load_static_html())
	            inject_includes(*pAPI);
	    }
	}

	virtual ~base_server_handler()
	{
	    for (auto& pcall : vpAPI_)
	    {
	        delete pcall;
	    }
	}

	virtual void operator() (const request& req, reply& rep)
	{
	    // Let's see what type of request we got.
	    // Slice up the URL and switch on the pieces.
	    // Our tokenizer will validate that the url was in the correct format.

	    std::string protocol, host, action, type;
	    std::vector<std::string> path_tokens;
	    vector<pair<string,string>> pair_tokens;

	    // Mark the request bad until we validate.
	    rep.status = reply::bad_request;

	    // Tokenize, and ignore malformed requests.
	    // That means we will have at least one path token and one action (or just the hostname).
	    API_call ac;
	    ac.method_ = req.method;
	    if (tokenize_API_url(req.uri,protocol,host,ac))
	    {
	        if (process_API_call(ac,rep))
	        {
	            rep.status = reply::ok;
	            if (!ac.types_.empty())
	                type = ac.types_[0];
	        }
	    }

	    if (rep.status == reply::bad_request)
	    {
	        // There is one hardcoded request that we handle by hand, the annoying favicon.ico.
	        if (req.uri.substr(req.uri.size()-11,11) == "favicon.ico")
	        {
	            rep.content = favicon_;
	            type = "ico";
                rep.status = reply::ok;

	        } else
	        {
	            // We respond to all bad requests with the API html.
	            // Note that this is no place to prevent DDOS - DDOS'ing to legit API calls is trivial.
	            // Help the user out.
                log(LV_ERROR,string("Received unrecognized request: ") + req.method + " " + req.uri);
                rep.content = "
A better Trader API
";
                rep.content += get_API_html("<button>","</button>","<button>","</button>","<button>","</button>","
");
	        }
	    }

	    // Build the headers.
	    rep.headers.resize(2);
	    rep.headers[0].name = "Content-Length";
	    rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
	    rep.headers[1].name = "Content-Type";
	    rep.headers[1].value = mime_types::extension_to_type(type);

	    if (rep.status == reply::bad_request)
	    {
	    }
	}

	inline void load_favicon()
	{
	    try
	    {
	        favicon_ = read_file("htdocs/favicon.ico");
	    }
	    catch(...)
	    {
            log(LV_WARNING,"WARNING: No favicon.ico file was found in [htdocs/].");

            // TODO add an encoded default
	    }
	}

	// We provide a means to inject include files into html.
    // This should be done for all our preloaded html on startup.
    inline void set_html_includes(const vector<string>& includes)
    {
        // We receive a series of filenames.
        // We take the filename and preload it from the local file system.
        // We also save the search string that should be replaced by the preloaded data in load_static_html().
        // KISS: We support css and js, that's it for now.

        try
        {
            for (auto& filename : includes)
            {
                if (filename.substr(filename.size()-4,4) == ".css")
                {
                    includes_.push_back(
                        pair<string,string>(
                            string("<link href=\"") + filename + "\" rel=\"stylesheet\">",
                            string(""
                        )
                    );

                } else if (filename.substr(filename.size()-3,3) == ".js")
                {
                    includes_.push_back(
                        pair<string,string>(
                            string("",
                            string(""
                        )
                    );

                } else
                {
                    // A file was requested to be inline-included but the type is not understood yet.
                    // Add another handler as needed!
                    assert(false);
                }
            }
        }
        catch(...)
        {
            log(LV_ERROR,"ERROR: Some HTML includes were not found.");
        }
    }

    void inject_includes(API_call& ac)
    {
        // We may need to build the relative path back to the root, based on the depth of the ac path.
        string relative_path;
        int steps = ac.path_tokens_.size() - 1;
        for (int n=0; n < steps; ++n)
            relative_path += "../";

        for (auto& include : includes_)
        {
            // Update any path to include the proper relative path.
            string target = include.first;
            replace(target,"href=\"",string("href=\"") + relative_path);
            replace(target,"src=\"",string("src=\"") + relative_path);

            // NOTE that this uses "replace" from utilities.hpp.
            replace(ac.static_html_, target, include.second);
        }
    }

    bool b_API_call_matches(const API_call& acDefinition, const API_call& ac, bool bIncludeParamPairs = false)
    {
        if (!strings_are_equal(acDefinition.method_,ac.method_))                                    return false;
        if (acDefinition.path_tokens_.size() != ac.path_tokens_.size())                             return false;

        for (int n = 0; n < ac.path_tokens_.size(); ++n)
        {
            if (acDefinition.path_tokens_[n][0] != ':')
            {
                if (!strings_are_equal(acDefinition.path_tokens_[n],ac.path_tokens_[n]))
                    return false;
            }
        }

        if (acDefinition.b_param_pairs_are_mandatory_)
        {
            if (acDefinition.pair_tokens_.size() != ac.pair_tokens_.size())
                return false;

            for (int n = 0; n < ac.pair_tokens_.size(); ++n)
            {
                if (!strings_are_equal(acDefinition.pair_tokens_[n].first,ac.pair_tokens_[n].first))
                    return false;
            }
        }

        return true;
    }

    bool process_API_call(API_call& ac, QuickHttp::reply& rep)
    {
        for (auto& acloop : vpAPI_)
        {
            if (b_API_call_matches(*acloop,ac))
                return acloop->handle_call(rep);
        }
        return false;
    }

    string get_API_html(const string& method1, const string& method2, const string& path1, const string& path2, const string& param1, const string& param2, const string& endline)
    {
        string html;
        for (auto& ac : vpAPI_)
        {
            html += method1 + ac->method_ + method2 + " /";
            for (int n = 0; n < ac->path_tokens_.size(); ++n)
            {
                const string& path = ac->path_tokens_[n];
                if (!path.empty() && path[0] == ':')
                    html += path1 + path + path2;
                else
                    html += path;
                if (n < ac->path_tokens_.size() - 1)
                    html += "/";
            }
            // html += ac->action_ + "." + ac->type_;
            for (int n = 0; n < ac->pair_tokens_.size(); ++n)
            {
                const pair<string,string>& tokenpair = ac->pair_tokens_[n];
                if (n==0) html += " ? ";
                else      html += " & ";
                html += tokenpair.first + "=";
                html += param1 + tokenpair.second + param2;
            }
            html += endline;
        }

        return html;
    }

    bool tokenize_API_url(const std::string& url, std::string& protocol, std::string& host, API_call& ac)
    {
        // This is not the ultimate URL parser (there are libraries for that when you need it - google-url, StrTk, etc.).
        // It only handles the formats we expect for our RESTful API.
        //
        // These include the following type of urls:
        //
        //      http(s)://server.com/version/action.type?param1=value1&param2=value2
        //      server.com/version/action.type?param1=value1&param2=value2
        //      /version/action.type?param1=value1&param2=value2
        //
        // If we do not find such a format, it does not fit our RESTful API model and we return false.
        // Specifically, we REQUIRE version and action.

        // We must at least have /version/action.  No buffer overflow attempts please.
        if (url.length() < 4 || url.length() > 700)
            return false;

        protocol.clear();
        host.clear();
        ac.path_tokens_.clear();
        ac.pair_tokens_.clear();

        // ===================================
        // protocol
        size_t walk1 = url.find("://",0);
        size_t walk2;
        if (walk1 == std::string::npos)
        {
            walk1 = 0;

        } else
        {
            protocol = url.substr(0,walk1);
            walk1 = walk1 + 3;
        }

        // If all we got was the protocol, that's not enough.
        if (url.length() <= walk1)
            return false;
        // ===================================

        // ===================================
        // host
        if (url[walk1] != '/')
        {
            walk2 = url.find_first_of('/',walk1);
            host = url.substr(walk1,walk2-walk1);

            // If all we got was the host, that's not enough.
            // Actually, we will allow it now, so we can provide a self-documenting API.
            // But we still need to return here.
            if (walk2 == std::string::npos || walk2 == url.length())
                return true;

            walk1 = walk2;
        }
        // ===================================

        // ===================================
        // paths

        // We should always be at the first '/' here, skip over it.
        ++walk1;

        while (walk1 < url.length())
        {
            walk2 = url.find_first_of('/',walk1);
            if (walk2 == std::string::npos)
                break;

            ac.path_tokens_.push_back(url.substr(walk1,walk2-walk1));
            walk1 = walk2 + 1;
        }

        // If we have anything beyond the hostname,
        // we need at least one path token for the API version,
        // and we also need an action.
        if (ac.path_tokens_.size() < 1 || url.length() < walk1 + 1)
            return false;
        // ===================================

        // ===================================
        // action
        // ===================================

        // We need to look for both "." and "?".
        walk2 = url.find_first_of(".?",walk1);
        ac.path_tokens_.push_back(url.substr(walk1,walk2-walk1));

        // It's ok if we stopped at action.
        if (walk2 == std::string::npos)
            return true;
        // ===================================

        // ===================================
        // type

        // We may be at a "." or a "?".  We only have a type if we're at ".".
        walk1 = walk2;
        if (url[walk1] == '.')
        {
            walk2 = url.find_first_of('?',walk1);
            ac.types_.push_back(url.substr(walk1+1,walk2-walk1-1));

            // If we DID have a ".", we should have a type.
            if (ac.types_[0].empty())
                return false;

            // It's ok to finish with no name-value pairs.
            if (walk2 == std::string::npos)
                return true;

            walk1 = walk2;
        }
        // ===================================

        // ===================================
        // pairs
        // break apart "param1=value1&param2=value2" name-value pairs

        // We should always be at the "?" here, skip over it.
        ++walk1;

        while (walk1 < url.length())
        {
            walk2 = url.find_first_of('=',walk1);

            // No good if we didn't have a full pair.
            if (walk2 == std::string::npos)
                return false;

            size_t walk3 = url.find_first_of('&',walk2);
            ac.pair_tokens_.push_back(
                std::pair<string,string>(
                    url.substr(walk1,walk2-walk1),
                    url.substr(walk2+1,walk3-walk2-1)
                )
            );

            // All done if we ran past the end.
            if (walk3 == std::string::npos)
                return true;

            walk1 = walk3 + 1;
        }
        // ===================================

        // We actually shouldn't hit this.
        return false;
    }

    std::size_t max_body_size_;

protected:

    const vector<API_call*>& vpAPI_;
    vector<pair<string,string>> includes_;
    string favicon_;
};

#endif // QUICK_HTTP_SERVER_HANDLER_HPP
<|endoftext|>


<commit_before>//===- llvm/unittest/Support/MemoryBufferTest.cpp - MemoryBuffer tests ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements unit tests for the MemoryBuffer support class.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/MemoryBuffer.h"
#include "llvm/ADT/OwningPtr.h"
#include "gtest/gtest.h"

using namespace llvm;

class MemoryBufferTest : public testing::Test {
protected:
  MemoryBufferTest()
  : data("this is some data")
  { }

  virtual void SetUp() { }

  typedef OwningPtr<MemoryBuffer> OwningBuffer;

  std::string data;
};

namespace {

TEST_F(MemoryBufferTest, get) {
  // Default name and null-terminator flag
  OwningBuffer MB1(MemoryBuffer::getMemBuffer(data));
  EXPECT_TRUE(0 != MB1.get());

  // RequiresNullTerminator = false
  OwningBuffer MB2(MemoryBuffer::getMemBuffer(data, "one", false));
  EXPECT_TRUE(0 != MB2.get());

  // RequiresNullTerminator = true
  OwningBuffer MB3(MemoryBuffer::getMemBuffer(data, "two", true));
  EXPECT_TRUE(0 != MB3.get());

  // verify all 3 buffers point to the same address
  EXPECT_EQ(MB1->getBufferStart(), MB2->getBufferStart());
  EXPECT_EQ(MB2->getBufferStart(), MB3->getBufferStart());

  // verify the original data is unmodified after deleting the buffers
  MB1.reset();
  MB2.reset();
  MB3.reset();
  EXPECT_EQ("this is some data", data);
}

TEST_F(MemoryBufferTest, copy) {
  // copy with no name
  OwningBuffer MBC1(MemoryBuffer::getMemBufferCopy(data));
  EXPECT_TRUE(0 != MBC1.get());

  // copy with a name
  OwningBuffer MBC2(MemoryBuffer::getMemBufferCopy(data, "copy"));
  EXPECT_TRUE(0 != MBC2.get());

  // verify the two copies do not point to the same place
  EXPECT_NE(MBC1->getBufferStart(), MBC2->getBufferStart());
}

TEST_F(MemoryBufferTest, make_new) {
  // 0-sized buffer
  OwningBuffer Zero(MemoryBuffer::getNewUninitMemBuffer(0));
  EXPECT_TRUE(0 != Zero.get());

  // uninitialized buffer with no name
  OwningBuffer One(MemoryBuffer::getNewUninitMemBuffer(321));
  EXPECT_TRUE(0 != One.get());

  // uninitialized buffer with name
  OwningBuffer Two(MemoryBuffer::getNewUninitMemBuffer(123, "bla"));
  EXPECT_TRUE(0 != Two.get());

  // 0-initialized buffer with no name
  OwningBuffer Three(MemoryBuffer::getNewMemBuffer(321, data));
  EXPECT_TRUE(0 != Three.get());
  for (size_t i = 0; i < 321; ++i)
    EXPECT_EQ(0, Three->getBufferStart()[0]);

  // 0-initialized buffer with name
  OwningBuffer Four(MemoryBuffer::getNewMemBuffer(123, "zeros"));
  EXPECT_TRUE(0 != Four.get());
  for (size_t i = 0; i < 123; ++i)
    EXPECT_EQ(0, Four->getBufferStart()[0]);
}

}
<commit_msg>Add a simple unit test for MemoryBuffer::getOpenFile<commit_after>//===- llvm/unittest/Support/MemoryBufferTest.cpp - MemoryBuffer tests ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements unit tests for the MemoryBuffer support class.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/OwningPtr.h"
#include "gtest/gtest.h"

using namespace llvm;

class MemoryBufferTest : public testing::Test {
protected:
  MemoryBufferTest()
  : data("this is some data")
  { }

  virtual void SetUp() { }

  typedef OwningPtr<MemoryBuffer> OwningBuffer;

  std::string data;
};

namespace {

TEST_F(MemoryBufferTest, get) {
  // Default name and null-terminator flag
  OwningBuffer MB1(MemoryBuffer::getMemBuffer(data));
  EXPECT_TRUE(0 != MB1.get());

  // RequiresNullTerminator = false
  OwningBuffer MB2(MemoryBuffer::getMemBuffer(data, "one", false));
  EXPECT_TRUE(0 != MB2.get());

  // RequiresNullTerminator = true
  OwningBuffer MB3(MemoryBuffer::getMemBuffer(data, "two", true));
  EXPECT_TRUE(0 != MB3.get());

  // verify all 3 buffers point to the same address
  EXPECT_EQ(MB1->getBufferStart(), MB2->getBufferStart());
  EXPECT_EQ(MB2->getBufferStart(), MB3->getBufferStart());

  // verify the original data is unmodified after deleting the buffers
  MB1.reset();
  MB2.reset();
  MB3.reset();
  EXPECT_EQ("this is some data", data);
}

TEST_F(MemoryBufferTest, copy) {
  // copy with no name
  OwningBuffer MBC1(MemoryBuffer::getMemBufferCopy(data));
  EXPECT_TRUE(0 != MBC1.get());

  // copy with a name
  OwningBuffer MBC2(MemoryBuffer::getMemBufferCopy(data, "copy"));
  EXPECT_TRUE(0 != MBC2.get());

  // verify the two copies do not point to the same place
  EXPECT_NE(MBC1->getBufferStart(), MBC2->getBufferStart());
}

TEST_F(MemoryBufferTest, make_new) {
  // 0-sized buffer
  OwningBuffer Zero(MemoryBuffer::getNewUninitMemBuffer(0));
  EXPECT_TRUE(0 != Zero.get());

  // uninitialized buffer with no name
  OwningBuffer One(MemoryBuffer::getNewUninitMemBuffer(321));
  EXPECT_TRUE(0 != One.get());

  // uninitialized buffer with name
  OwningBuffer Two(MemoryBuffer::getNewUninitMemBuffer(123, "bla"));
  EXPECT_TRUE(0 != Two.get());

  // 0-initialized buffer with no name
  OwningBuffer Three(MemoryBuffer::getNewMemBuffer(321, data));
  EXPECT_TRUE(0 != Three.get());
  for (size_t i = 0; i < 321; ++i)
    EXPECT_EQ(0, Three->getBufferStart()[0]);

  // 0-initialized buffer with name
  OwningBuffer Four(MemoryBuffer::getNewMemBuffer(123, "zeros"));
  EXPECT_TRUE(0 != Four.get());
  for (size_t i = 0; i < 123; ++i)
    EXPECT_EQ(0, Four->getBufferStart()[0]);
}

TEST_F(MemoryBufferTest, getOpenFileNoNullTerminator) {
  // Test that MemoryBuffer::getOpenFile works properly when no null
  // terminator is requested and the size is large enough to trigger
  // the usage of memory mapping.
  int TestFD;
  SmallString<64> TestPath;
  // Create a temporary file and write data into it.
  sys::fs::createTemporaryFile("prefix", "temp", TestFD, TestPath);
  // OF is responsible for closing the file, and is unbuffered so that
  // the results are immediately visible through the fd.
  raw_fd_ostream OF(TestFD, true, true);
  for (int i = 0; i < 60000; ++i) {
    OF << "0123456789";
  }

  OwningBuffer Buf;
  error_code EC = MemoryBuffer::getOpenFile(TestFD,
                                            TestPath.c_str(),
                                            Buf,
                                            40000,    // Size
                                            -1,
                                            8000,     // Offset
                                            false);
  EXPECT_FALSE(EC);

  StringRef BufData = Buf->getBuffer();
  EXPECT_EQ(BufData.size(), 40000U);
  EXPECT_EQ(BufData[0], '0');
  EXPECT_EQ(BufData[9], '9');
}

}
<|endoftext|>


<commit_before>/*************************************************************************
*                                                                       *
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
* All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
*                                                                       *
* This library is free software; you can redistribute it and/or         *
* modify it under the terms of EITHER:                                  *
*   (1) The GNU Lesser General Public License as published by the Free  *
*       Software Foundation; either version 2.1 of the License, or (at  *
*       your option) any later version. The text of the GNU Lesser      *
*       General Public License is included with this library in the     *
*       file LICENSE.TXT.                                               *
*   (2) The BSD-style license that is included with this library in     *
*       the file LICENSE-BSD.TXT.                                       *
*                                                                       *
* This library is distributed in the hope that it will be useful,       *
* but WITHOUT ANY WARRANTY; without even the implied warranty of        *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
* LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
*                                                                       *
*************************************************************************/

/*

ODE initialization/finalization code

*/

#include <ode/common.h>
#include <ode/odemath.h>
#include <ode/odeinit.h>
// <ode/objects.h> included for dWorldQuickStepCleanup()
#include <ode/objects.h>
#include "config.h"
#include "collision_kernel.h"
#include "collision_trimesh_internal.h"
#include "odetls.h"
#include "odeou.h"
#include "util.h"

//****************************************************************************
// Initialization tracking variables

static unsigned int g_uiODEInitCounter = 0;
static unsigned int g_uiODEInitModes = 0;

enum EODEINITMODE
{
	OIM__MIN,

	OIM_AUTOTLSCLEANUP = OIM__MIN,
	OIM_MANUALTLSCLEANUP,

	OIM__MAX,
};

#if dTLS_ENABLED
static const EODETLSKIND g_atkTLSKindsByInitMode[OIM__MAX] =
{
	OTK_AUTOCLEANUP, // OIM_AUTOTLSCLEANUP,
	OTK_MANUALCLEANUP, // OIM_MANUALTLSCLEANUP,
};
#endif // #if dTLS_ENABLED

static inline bool IsODEModeInitialized(EODEINITMODE imInitMode)
{
	return (g_uiODEInitModes & (1U << imInitMode)) != 0;
}

static inline void SetODEModeInitialized(EODEINITMODE imInitMode)
{
	g_uiODEInitModes |= (1U << imInitMode);
}

static inline void ResetODEModeInitialized(EODEINITMODE imInitMode)
{
	g_uiODEInitModes &= ~(1U << imInitMode);
}

static inline bool IsODEAnyModeInitialized()
{
	return g_uiODEInitModes != 0;
}

enum
{
	TLD_INTERNAL_COLLISIONDATA_ALLOCATED = 0x00000001,
};

static bool AllocateThreadBasicDataIfNecessary(EODEINITMODE imInitMode)
{
	bool bResult = false;

	do
	{
#if dTLS_ENABLED
		EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

		const unsigned uDataAllocationFlags = COdeTls::GetDataAllocationFlags(tkTlsKind);

		// If no flags are set it may mean that TLS slot is not allocated yet
		if (uDataAllocationFlags == 0)
		{
			// Assign zero flags to make sure that TLS slot has been allocated
			if (!COdeTls::AssignDataAllocationFlags(tkTlsKind, 0))
			{
				break;
			}
		}

#endif // #if dTLS_ENABLED

		bResult = true;
	}
	while (false);

	return bResult;
}

static void FreeThreadBasicDataOnFailureIfNecessary(EODEINITMODE imInitMode)
{
#if dTLS_ENABLED

	if (imInitMode == OIM_MANUALTLSCLEANUP)
	{
		EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

		const unsigned uDataAllocationFlags = COdeTls::GetDataAllocationFlags(tkTlsKind);

		if (uDataAllocationFlags == 0)
		{
			// So far, only free TLS slot, if no subsystems have data allocated
			COdeTls::CleanupForThread();
		}
	}

#endif // #if dTLS_ENABLED
}

#if dTLS_ENABLED
static bool AllocateThreadCollisionData(EODETLSKIND tkTlsKind)
{
	bool bResult = false;

	do
	{
		dIASSERT(!(COdeTls::GetDataAllocationFlags(tkTlsKind) & TLD_INTERNAL_COLLISIONDATA_ALLOCATED));

#if dTRIMESH_ENABLED

		TrimeshCollidersCache *pccColliderCache = new TrimeshCollidersCache();
		if (!COdeTls::AssignTrimeshCollidersCache(tkTlsKind, pccColliderCache))
		{
			delete pccColliderCache;
			break;
		}

#endif // dTRIMESH_ENABLED

		COdeTls::SignalDataAllocationFlags(tkTlsKind, TLD_INTERNAL_COLLISIONDATA_ALLOCATED);

		bResult = true;
	}
	while (false);

	return bResult;
}
#endif // dTLS_ENABLED

static bool AllocateThreadCollisionDataIfNecessary(EODEINITMODE imInitMode, bool &bOutDataAllocated)
{
	bool bResult = false;
	bOutDataAllocated = false;

	do
	{
#if dTLS_ENABLED
		EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

		const unsigned uDataAllocationFlags = COdeTls::GetDataAllocationFlags(tkTlsKind);

		if ((uDataAllocationFlags & TLD_INTERNAL_COLLISIONDATA_ALLOCATED) == 0)
		{
			if (!AllocateThreadCollisionData(tkTlsKind))
			{
				break;
			}

			bOutDataAllocated = true;
		}

#endif // #if dTLS_ENABLED

		bResult = true;
	}
	while (false);

	return bResult;
}

static void FreeThreadCollisionData(EODEINITMODE imInitMode)
{
#if dTLS_ENABLED

	EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

	COdeTls::DestroyTrimeshCollidersCache(tkTlsKind);

	COdeTls::DropDataAllocationFlags(tkTlsKind, TLD_INTERNAL_COLLISIONDATA_ALLOCATED);

#endif // dTLS_ENABLED
}

static bool InitODEForMode(EODEINITMODE imInitMode)
{
	bool bResult = false;

#if dOU_ENABLED
	bool bOUCustomizationsDone = false;
#endif
#if dATOMICS_ENABLED
	bool bAtomicsInitialized = false;
#endif
#if dTLS_ENABLED
	EODETLSKIND tkTLSKindToInit = g_atkTLSKindsByInitMode[imInitMode];
	bool bTlsInitialized = false;
#endif

	do
	{
		bool bAnyModeAlreadyInitialized = IsODEAnyModeInitialized();

		if (!bAnyModeAlreadyInitialized)
		{
#if dOU_ENABLED
			if (!COdeOu::DoOUCustomizations())
			{
				break;
			}

			bOUCustomizationsDone = true;
#endif

#if dATOMICS_ENABLED
			if (!COdeOu::InitializeAtomics())
			{
				break;
			}

			bAtomicsInitialized = true;
#endif
		}

#if dTLS_ENABLED
		if (!COdeTls::Initialize(tkTLSKindToInit))
		{
			break;
		}

		bTlsInitialized = true;
#endif

		if (!bAnyModeAlreadyInitialized)
		{
#if dTRIMESH_ENABLED && dTRIMESH_OPCODE
			if (!Opcode::InitOpcode())
			{
				break;
			}
#endif

#if dTRIMESH_ENABLED && dTRIMESH_GIMPACT
			gimpact_init();
#endif

			dInitColliders();
		}

		bResult = true;
	}
	while (false);

	if (!bResult)
	{
#if dTLS_ENABLED
		if (bTlsInitialized)
		{
			COdeTls::Finalize(tkTLSKindToInit);
		}
#endif

#if dATOMICS_ENABLED
		if (bAtomicsInitialized)
		{
			COdeOu::FinalizeAtomics();
		}
#endif

#if dOU_ENABLED
		if (bOUCustomizationsDone)
		{
			COdeOu::UndoOUCustomizations();
		}
#endif
	}

	return bResult;
}

static bool AllocateODEDataForThreadForMode(EODEINITMODE imInitMode, unsigned int uiAllocateFlags)
{
	bool bResult = false;

	bool bCollisionDataAllocated = false;

	do
	{
		if (!AllocateThreadBasicDataIfNecessary(imInitMode))
		{
			break;
		}

		if (uiAllocateFlags & dAllocateFlagCollisionData)
		{
			if (!AllocateThreadCollisionDataIfNecessary(imInitMode, bCollisionDataAllocated))
			{
				break;
			}
		}

		bResult = true;
	}
	while (false);

	if (!bResult)
	{
		if (bCollisionDataAllocated)
		{
			FreeThreadCollisionData(imInitMode);
		}

		FreeThreadBasicDataOnFailureIfNecessary(imInitMode);
	}

	return bResult;
}

static void CloseODEForMode(EODEINITMODE imInitMode)
{
	bool bAnyModeStillInitialized = IsODEAnyModeInitialized();

	if (!bAnyModeStillInitialized)
	{
		dClearPosrCache();
		dFinitUserClasses();
		dFinitColliders();

#if dTRIMESH_ENABLED && dTRIMESH_GIMPACT
		gimpact_terminate();
#endif

#if dTRIMESH_ENABLED && dTRIMESH_OPCODE
		extern void opcode_collider_cleanup();
		// Free up static allocations in opcode
		opcode_collider_cleanup();

		Opcode::CloseOpcode();
#endif
	}

#if dTLS_ENABLED
	EODETLSKIND tkTLSKindToFinalize = g_atkTLSKindsByInitMode[imInitMode];
	COdeTls::Finalize(tkTLSKindToFinalize);
#endif

	if (!bAnyModeStillInitialized)
	{
#if dATOMICS_ENABLED
		COdeOu::FinalizeAtomics();
#endif

#if dOU_ENABLED
		COdeOu::UndoOUCustomizations();
#endif
	}
}

//****************************************************************************
// internal initialization and close routine implementations

static bool InternalInitODE(unsigned int uiInitFlags)
{
	bool bResult = false;

	do
	{
		EODEINITMODE imInitMode = (uiInitFlags & dInitFlagManualThreadCleanup) ? OIM_MANUALTLSCLEANUP : OIM_AUTOTLSCLEANUP;

		if (!IsODEModeInitialized(imInitMode))
		{
			if (!InitODEForMode(imInitMode))
			{
				break;
			}

			SetODEModeInitialized(imInitMode);
		}

		++g_uiODEInitCounter;
		bResult = true;
	}
	while (false);

	return bResult;
}

static void InternalCloseODE()
{
	unsigned int uiCurrentMode = (--g_uiODEInitCounter == 0) ? OIM__MIN : OIM__MAX;
	for (; uiCurrentMode != OIM__MAX; ++uiCurrentMode)
	{
		if (IsODEModeInitialized((EODEINITMODE)uiCurrentMode))
		{
			// Must be called before CloseODEForMode()
			ResetODEModeInitialized((EODEINITMODE)uiCurrentMode);

			// Must be called after ResetODEModeInitialized()
			CloseODEForMode((EODEINITMODE)uiCurrentMode);
		}
	}
}

static bool InternalAllocateODEDataForThread(unsigned int uiAllocateFlags)
{
	bool bAnyFailure = false;

	for (unsigned uiCurrentMode = OIM__MIN; uiCurrentMode != OIM__MAX; ++uiCurrentMode)
	{
		if (IsODEModeInitialized((EODEINITMODE)uiCurrentMode))
		{
			if (!AllocateODEDataForThreadForMode((EODEINITMODE)uiCurrentMode, uiAllocateFlags))
			{
				bAnyFailure = true;
				break;
			}
		}
	}

	bool bResult = !bAnyFailure;
	return bResult;
}

static void InternalCleanupODEAllDataForThread()
{
#if dTLS_ENABLED
	COdeTls::CleanupForThread();
#endif
}

//****************************************************************************
// initialization and shutdown routines - allocate and initialize data,
// cleanup before exiting

void dInitODE()
{
	int bInitResult = InternalInitODE(0);
	dIVERIFY(bInitResult);

	int ibAllocResult = InternalAllocateODEDataForThread(dAllocateMaskAll);
	dIVERIFY(ibAllocResult);
}

int dInitODE2(unsigned int uiInitFlags/*=0*/)
	{
	bool bResult = false;

	bool bODEInitialized = false;

	do
		{
		if (!InternalInitODE(uiInitFlags))
		{
			break;
		}

		bODEInitialized = true;

		if (!InternalAllocateODEDataForThread(dAllocateFlagBasicData))
		{
			break;
		}

		bResult = true;
	}
	while (false);

	if (!bResult)
	{
		if (bODEInitialized)
		{
			InternalCloseODE();
}
	}

	return bResult;
}

int dAllocateODEDataForThread(unsigned int uiAllocateFlags)
{
	dUASSERT(g_uiODEInitCounter != 0, "Call dInitODE2 first");

	bool bResult = InternalAllocateODEDataForThread(uiAllocateFlags);
	return bResult;
}

void dCleanupODEAllDataForThread()
{
	dUASSERT(g_uiODEInitCounter != 0, "Call dInitODE2 first or delay dCloseODE until all threads exit");

	InternalCleanupODEAllDataForThread();
}

void dCloseODE()
{
	dUASSERT(g_uiODEInitCounter != 0, "dCloseODE must not be called without dInitODE2 or if dInitODE2 fails"); // dCloseODE must not be called without dInitODE2 or if dInitODE2 fails

	InternalCloseODE();
}

<commit_msg>Cosmetic: Whitespace corrected<commit_after>/*************************************************************************
*                                                                       *
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
* All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
*                                                                       *
* This library is free software; you can redistribute it and/or         *
* modify it under the terms of EITHER:                                  *
*   (1) The GNU Lesser General Public License as published by the Free  *
*       Software Foundation; either version 2.1 of the License, or (at  *
*       your option) any later version. The text of the GNU Lesser      *
*       General Public License is included with this library in the     *
*       file LICENSE.TXT.                                               *
*   (2) The BSD-style license that is included with this library in     *
*       the file LICENSE-BSD.TXT.                                       *
*                                                                       *
* This library is distributed in the hope that it will be useful,       *
* but WITHOUT ANY WARRANTY; without even the implied warranty of        *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
* LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
*                                                                       *
*************************************************************************/

/*

ODE initialization/finalization code

*/

#include <ode/common.h>
#include <ode/odemath.h>
#include <ode/odeinit.h>
// <ode/objects.h> included for dWorldQuickStepCleanup()
#include <ode/objects.h>
#include "config.h"
#include "collision_kernel.h"
#include "collision_trimesh_internal.h"
#include "odetls.h"
#include "odeou.h"
#include "util.h"

//****************************************************************************
// Initialization tracking variables

static unsigned int g_uiODEInitCounter = 0;
static unsigned int g_uiODEInitModes = 0;

enum EODEINITMODE
{
	OIM__MIN,

	OIM_AUTOTLSCLEANUP = OIM__MIN,
	OIM_MANUALTLSCLEANUP,

	OIM__MAX,
};

#if dTLS_ENABLED
static const EODETLSKIND g_atkTLSKindsByInitMode[OIM__MAX] =
{
	OTK_AUTOCLEANUP, // OIM_AUTOTLSCLEANUP,
	OTK_MANUALCLEANUP, // OIM_MANUALTLSCLEANUP,
};
#endif // #if dTLS_ENABLED

static inline bool IsODEModeInitialized(EODEINITMODE imInitMode)
{
	return (g_uiODEInitModes & (1U << imInitMode)) != 0;
}

static inline void SetODEModeInitialized(EODEINITMODE imInitMode)
{
	g_uiODEInitModes |= (1U << imInitMode);
}

static inline void ResetODEModeInitialized(EODEINITMODE imInitMode)
{
	g_uiODEInitModes &= ~(1U << imInitMode);
}

static inline bool IsODEAnyModeInitialized()
{
	return g_uiODEInitModes != 0;
}

enum
{
	TLD_INTERNAL_COLLISIONDATA_ALLOCATED = 0x00000001,
};

static bool AllocateThreadBasicDataIfNecessary(EODEINITMODE imInitMode)
{
	bool bResult = false;

	do
	{
#if dTLS_ENABLED
		EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

		const unsigned uDataAllocationFlags = COdeTls::GetDataAllocationFlags(tkTlsKind);

		// If no flags are set it may mean that TLS slot is not allocated yet
		if (uDataAllocationFlags == 0)
		{
			// Assign zero flags to make sure that TLS slot has been allocated
			if (!COdeTls::AssignDataAllocationFlags(tkTlsKind, 0))
			{
				break;
			}
		}

#endif // #if dTLS_ENABLED

		bResult = true;
	}
	while (false);

	return bResult;
}

static void FreeThreadBasicDataOnFailureIfNecessary(EODEINITMODE imInitMode)
{
#if dTLS_ENABLED

	if (imInitMode == OIM_MANUALTLSCLEANUP)
	{
		EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

		const unsigned uDataAllocationFlags = COdeTls::GetDataAllocationFlags(tkTlsKind);

		if (uDataAllocationFlags == 0)
		{
			// So far, only free TLS slot, if no subsystems have data allocated
			COdeTls::CleanupForThread();
		}
	}

#endif // #if dTLS_ENABLED
}

#if dTLS_ENABLED
static bool AllocateThreadCollisionData(EODETLSKIND tkTlsKind)
{
	bool bResult = false;

	do
	{
		dIASSERT(!(COdeTls::GetDataAllocationFlags(tkTlsKind) & TLD_INTERNAL_COLLISIONDATA_ALLOCATED));

#if dTRIMESH_ENABLED

		TrimeshCollidersCache *pccColliderCache = new TrimeshCollidersCache();
		if (!COdeTls::AssignTrimeshCollidersCache(tkTlsKind, pccColliderCache))
		{
			delete pccColliderCache;
			break;
		}

#endif // dTRIMESH_ENABLED

		COdeTls::SignalDataAllocationFlags(tkTlsKind, TLD_INTERNAL_COLLISIONDATA_ALLOCATED);

		bResult = true;
	}
	while (false);

	return bResult;
}
#endif // dTLS_ENABLED

static bool AllocateThreadCollisionDataIfNecessary(EODEINITMODE imInitMode, bool &bOutDataAllocated)
{
	bool bResult = false;
	bOutDataAllocated = false;

	do
	{
#if dTLS_ENABLED
		EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

		const unsigned uDataAllocationFlags = COdeTls::GetDataAllocationFlags(tkTlsKind);

		if ((uDataAllocationFlags & TLD_INTERNAL_COLLISIONDATA_ALLOCATED) == 0)
		{
			if (!AllocateThreadCollisionData(tkTlsKind))
			{
				break;
			}

			bOutDataAllocated = true;
		}

#endif // #if dTLS_ENABLED

		bResult = true;
	}
	while (false);

	return bResult;
}

static void FreeThreadCollisionData(EODEINITMODE imInitMode)
{
#if dTLS_ENABLED

	EODETLSKIND tkTlsKind = g_atkTLSKindsByInitMode[imInitMode];

	COdeTls::DestroyTrimeshCollidersCache(tkTlsKind);

	COdeTls::DropDataAllocationFlags(tkTlsKind, TLD_INTERNAL_COLLISIONDATA_ALLOCATED);

#endif // dTLS_ENABLED
}

static bool InitODEForMode(EODEINITMODE imInitMode)
{
	bool bResult = false;

#if dOU_ENABLED
	bool bOUCustomizationsDone = false;
#endif
#if dATOMICS_ENABLED
	bool bAtomicsInitialized = false;
#endif
#if dTLS_ENABLED
	EODETLSKIND tkTLSKindToInit = g_atkTLSKindsByInitMode[imInitMode];
	bool bTlsInitialized = false;
#endif

	do
	{
		bool bAnyModeAlreadyInitialized = IsODEAnyModeInitialized();

		if (!bAnyModeAlreadyInitialized)
		{
#if dOU_ENABLED
			if (!COdeOu::DoOUCustomizations())
			{
				break;
			}

			bOUCustomizationsDone = true;
#endif

#if dATOMICS_ENABLED
			if (!COdeOu::InitializeAtomics())
			{
				break;
			}

			bAtomicsInitialized = true;
#endif
		}

#if dTLS_ENABLED
		if (!COdeTls::Initialize(tkTLSKindToInit))
		{
			break;
		}

		bTlsInitialized = true;
#endif

		if (!bAnyModeAlreadyInitialized)
		{
#if dTRIMESH_ENABLED && dTRIMESH_OPCODE
			if (!Opcode::InitOpcode())
			{
				break;
			}
#endif

#if dTRIMESH_ENABLED && dTRIMESH_GIMPACT
			gimpact_init();
#endif

			dInitColliders();
		}

		bResult = true;
	}
	while (false);

	if (!bResult)
	{
#if dTLS_ENABLED
		if (bTlsInitialized)
		{
			COdeTls::Finalize(tkTLSKindToInit);
		}
#endif

#if dATOMICS_ENABLED
		if (bAtomicsInitialized)
		{
			COdeOu::FinalizeAtomics();
		}
#endif

#if dOU_ENABLED
		if (bOUCustomizationsDone)
		{
			COdeOu::UndoOUCustomizations();
		}
#endif
	}

	return bResult;
}

static bool AllocateODEDataForThreadForMode(EODEINITMODE imInitMode, unsigned int uiAllocateFlags)
{
	bool bResult = false;

	bool bCollisionDataAllocated = false;

	do
	{
		if (!AllocateThreadBasicDataIfNecessary(imInitMode))
		{
			break;
		}

		if (uiAllocateFlags & dAllocateFlagCollisionData)
		{
			if (!AllocateThreadCollisionDataIfNecessary(imInitMode, bCollisionDataAllocated))
			{
				break;
			}
		}

		bResult = true;
	}
	while (false);

	if (!bResult)
	{
		if (bCollisionDataAllocated)
		{
			FreeThreadCollisionData(imInitMode);
		}

		FreeThreadBasicDataOnFailureIfNecessary(imInitMode);
	}

	return bResult;
}

static void CloseODEForMode(EODEINITMODE imInitMode)
{
	bool bAnyModeStillInitialized = IsODEAnyModeInitialized();

	if (!bAnyModeStillInitialized)
	{
		dClearPosrCache();
		dFinitUserClasses();
		dFinitColliders();

#if dTRIMESH_ENABLED && dTRIMESH_GIMPACT
		gimpact_terminate();
#endif

#if dTRIMESH_ENABLED && dTRIMESH_OPCODE
		extern void opcode_collider_cleanup();
		// Free up static allocations in opcode
		opcode_collider_cleanup();

		Opcode::CloseOpcode();
#endif
	}

#if dTLS_ENABLED
	EODETLSKIND tkTLSKindToFinalize = g_atkTLSKindsByInitMode[imInitMode];
	COdeTls::Finalize(tkTLSKindToFinalize);
#endif

	if (!bAnyModeStillInitialized)
	{
#if dATOMICS_ENABLED
		COdeOu::FinalizeAtomics();
#endif

#if dOU_ENABLED
		COdeOu::UndoOUCustomizations();
#endif
	}
}

//****************************************************************************
// internal initialization and close routine implementations

static bool InternalInitODE(unsigned int uiInitFlags)
{
	bool bResult = false;

	do
	{
		EODEINITMODE imInitMode = (uiInitFlags & dInitFlagManualThreadCleanup) ? OIM_MANUALTLSCLEANUP : OIM_AUTOTLSCLEANUP;

		if (!IsODEModeInitialized(imInitMode))
		{
			if (!InitODEForMode(imInitMode))
			{
				break;
			}

			SetODEModeInitialized(imInitMode);
		}

		++g_uiODEInitCounter;
		bResult = true;
	}
	while (false);

	return bResult;
}

static void InternalCloseODE()
{
	unsigned int uiCurrentMode = (--g_uiODEInitCounter == 0) ? OIM__MIN : OIM__MAX;
	for (; uiCurrentMode != OIM__MAX; ++uiCurrentMode)
	{
		if (IsODEModeInitialized((EODEINITMODE)uiCurrentMode))
		{
			// Must be called before CloseODEForMode()
			ResetODEModeInitialized((EODEINITMODE)uiCurrentMode);

			// Must be called after ResetODEModeInitialized()
			CloseODEForMode((EODEINITMODE)uiCurrentMode);
		}
	}
}

static bool InternalAllocateODEDataForThread(unsigned int uiAllocateFlags)
{
	bool bAnyFailure = false;

	for (unsigned uiCurrentMode = OIM__MIN; uiCurrentMode != OIM__MAX; ++uiCurrentMode)
	{
		if (IsODEModeInitialized((EODEINITMODE)uiCurrentMode))
		{
			if (!AllocateODEDataForThreadForMode((EODEINITMODE)uiCurrentMode, uiAllocateFlags))
			{
				bAnyFailure = true;
				break;
			}
		}
	}

	bool bResult = !bAnyFailure;
	return bResult;
}

static void InternalCleanupODEAllDataForThread()
{
#if dTLS_ENABLED
	COdeTls::CleanupForThread();
#endif
}

//****************************************************************************
// initialization and shutdown routines - allocate and initialize data,
// cleanup before exiting

void dInitODE()
{
	int bInitResult = InternalInitODE(0);
	dIVERIFY(bInitResult);

	int ibAllocResult = InternalAllocateODEDataForThread(dAllocateMaskAll);
	dIVERIFY(ibAllocResult);
}

int dInitODE2(unsigned int uiInitFlags/*=0*/)
{
	bool bResult = false;

	bool bODEInitialized = false;

	do
	{
		if (!InternalInitODE(uiInitFlags))
		{
			break;
		}

		bODEInitialized = true;

		if (!InternalAllocateODEDataForThread(dAllocateFlagBasicData))
		{
			break;
		}

		bResult = true;
	}
	while (false);

	if (!bResult)
	{
		if (bODEInitialized)
		{
			InternalCloseODE();
		}
	}

	return bResult;
}

int dAllocateODEDataForThread(unsigned int uiAllocateFlags)
{
	dUASSERT(g_uiODEInitCounter != 0, "Call dInitODE2 first");

	bool bResult = InternalAllocateODEDataForThread(uiAllocateFlags);
	return bResult;
}

void dCleanupODEAllDataForThread()
{
	dUASSERT(g_uiODEInitCounter != 0, "Call dInitODE2 first or delay dCloseODE until all threads exit");

	InternalCleanupODEAllDataForThread();
}

void dCloseODE()
{
	dUASSERT(g_uiODEInitCounter != 0, "dCloseODE must not be called without dInitODE2 or if dInitODE2 fails"); // dCloseODE must not be called without dInitODE2 or if dInitODE2 fails

	InternalCloseODE();
}

<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkBinaryThresholdImageFilterTest2.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkStatisticsImageFilter.h"
#include "itkBinaryThresholdImageFilter.h"

int itkBinaryThresholdImageFilterTest2(int ac, char* av[] )
{
  if(ac < 4)
    {
    std::cerr << "Usage: " << av[0] <<" InputImage1 InputImage2 OutputImage\n";
    return -1;
    }

  // Threshold one image based on the statistics of another image
  //
  //

  // Define the dimension of the images
  const unsigned int ImageDimension = 2;

  // Declare the types of the images
  typedef itk::Image<unsigned char, ImageDimension>  ImageType;
  typedef itk::Image<double, ImageDimension>  FloatImageType;

  // File reader and writer
  typedef itk::ImageFileReader<FloatImageType> ReaderType;
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName( av[1] );

  ReaderType::Pointer reader2 = ReaderType::New();
  reader2->SetFileName( av[2] );

  typedef itk::ImageFileWriter<ImageType> WriterType;
  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName( av[3] );

  // Declare the filter types
  typedef itk::StatisticsImageFilter<FloatImageType>  StatisticsType;
  typedef itk::BinaryThresholdImageFilter<FloatImageType, ImageType>  ThresholdType;

  // Create the filters
  StatisticsType::Pointer statistics = StatisticsType::New();
  ThresholdType::Pointer threshold = ThresholdType::New();

  // connect the standard pipeline connections
  statistics->SetInput( reader2->GetOutput() );
  threshold->SetInput( reader->GetOutput() );

  // now connect the inputs and outputs that are decorated scalars
  threshold->SetUpperThresholdInput( statistics->GetMeanOutput() );
  threshold->SetLowerThresholdInput( statistics->GetMinimumOutput() );

  // connect the writer
  writer->SetInput( threshold->GetOutput() );

  // Execute the filter
  try
    {
    writer->Update();
    }
  catch(...)
    {
    std::cerr << "Caught an unexpected exception. " << std::endl;
    std::cerr << "Test failed. " << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

<commit_msg>ENH: Print before lower/upper is set.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkBinaryThresholdImageFilterTest2.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkStatisticsImageFilter.h"
#include "itkBinaryThresholdImageFilter.h"

int itkBinaryThresholdImageFilterTest2(int ac, char* av[] )
{
  if(ac < 4)
    {
    std::cerr << "Usage: " << av[0] <<" InputImage1 InputImage2 OutputImage\n";
    return -1;
    }

  // Threshold one image based on the statistics of another image
  //
  //

  // Define the dimension of the images
  const unsigned int ImageDimension = 2;

  // Declare the types of the images
  typedef itk::Image<unsigned char, ImageDimension>  ImageType;
  typedef itk::Image<double, ImageDimension>  FloatImageType;

  // File reader and writer
  typedef itk::ImageFileReader<FloatImageType> ReaderType;
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName( av[1] );

  ReaderType::Pointer reader2 = ReaderType::New();
  reader2->SetFileName( av[2] );

  typedef itk::ImageFileWriter<ImageType> WriterType;
  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName( av[3] );

  // Declare the filter types
  typedef itk::StatisticsImageFilter<FloatImageType>  StatisticsType;
  typedef itk::BinaryThresholdImageFilter<FloatImageType, ImageType>  ThresholdType;

  // Create the filters
  StatisticsType::Pointer statistics = StatisticsType::New();
  ThresholdType::Pointer threshold = ThresholdType::New();

  // connect the standard pipeline connections
  statistics->SetInput( reader2->GetOutput() );
  threshold->SetInput( reader->GetOutput() );

  // print before assigning thresholds
  threshold->Print(std::cout);

  // now connect the inputs and outputs that are decorated scalars
  threshold->SetUpperThresholdInput( statistics->GetMeanOutput() );
  threshold->SetLowerThresholdInput( statistics->GetMinimumOutput() );

  // connect the writer
  writer->SetInput( threshold->GetOutput() );

  // Execute the filter
  try
    {
    writer->Update();
    }
  catch(...)
    {
    std::cerr << "Caught an unexpected exception. " << std::endl;
    std::cerr << "Test failed. " << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/register.h"
#include "kernel/celltypes.h"
#include "passes/techmap/libparse.h"

#include "kernel/log.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct statdata_t
{
	#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
			X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes)

	#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area)

	#define X(_name) int _name;
	STAT_INT_MEMBERS
	#undef X
	double area;

	std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
	std::set<RTLIL::IdString> unknown_cell_area;

	statdata_t operator+(const statdata_t &other) const
	{
		statdata_t sum = other;
	#define X(_name) sum._name += _name;
		STAT_NUMERIC_MEMBERS
	#undef X
		for (auto &it : num_cells_by_type)
			sum.num_cells_by_type[it.first] += it.second;
		return sum;
	}

	statdata_t operator*(int other) const
	{
		statdata_t sum = *this;
	#define X(_name) sum._name *= other;
		STAT_NUMERIC_MEMBERS
	#undef X
		for (auto &it : sum.num_cells_by_type)
			it.second *= other;
		return sum;
	}

	statdata_t()
	{
	#define X(_name) _name = 0;
		STAT_NUMERIC_MEMBERS
	#undef X
	}

	statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
	{
	#define X(_name) _name = 0;
		STAT_NUMERIC_MEMBERS
	#undef X

		for (auto &it : mod->wires_)
		{
			if (!design->selected(mod, it.second))
				continue;

			if (it.first[0] == '\\') {
				num_pub_wires++;
				num_pub_wire_bits += it.second->width;
			}

			num_wires++;
			num_wire_bits += it.second->width;
		}

		for (auto &it : mod->memories) {
			if (!design->selected(mod, it.second))
				continue;
			num_memories++;
			num_memory_bits += it.second->width * it.second->size;
		}

		for (auto &it : mod->cells_)
		{
			if (!design->selected(mod, it.second))
				continue;

			RTLIL::IdString cell_type = it.second->type;

			if (width_mode)
			{
				if (cell_type.in("$not", "$pos", "$neg",
						"$logic_not", "$logic_and", "$logic_or",
						"$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
						"$lut", "$and", "$or", "$xor", "$xnor",
						"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
						"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
						"$add", "$sub", "$mul", "$div", "$mod", "$pow")) {
					int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0;
					int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0;
					int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0;
					cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y}));
				}
				else if (cell_type.in("$mux", "$pmux"))
					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y")));
				else if (cell_type.in("$sr", "$dff", "$dffsr", "$adff", "$dlatch", "$dlatchsr"))
					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q")));
			}

			if (!cell_area.empty()) {
				if (cell_area.count(cell_type))
					area += cell_area.at(cell_type);
				else
					unknown_cell_area.insert(cell_type);
			}

			num_cells++;
			num_cells_by_type[cell_type]++;
		}

		for (auto &it : mod->processes) {
			if (!design->selected(mod, it.second))
				continue;
			num_processes++;
		}
	}

	void log_data()
	{
		log("   Number of wires:             %6d\n", num_wires);
		log("   Number of wire bits:         %6d\n", num_wire_bits);
		log("   Number of public wires:      %6d\n", num_pub_wires);
		log("   Number of public wire bits:  %6d\n", num_pub_wire_bits);
		log("   Number of memories:          %6d\n", num_memories);
		log("   Number of memory bits:       %6d\n", num_memory_bits);
		log("   Number of processes:         %6d\n", num_processes);
		log("   Number of cells:             %6d\n", num_cells);
		for (auto &it : num_cells_by_type)
			log("     %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);

		if (!unknown_cell_area.empty()) {
			log("\n");
			for (auto cell_type : unknown_cell_area)
				log("   Area for cell type %s is unknown!\n", cell_type.c_str());
		}

		if (area != 0) {
			log("\n");
			log("   Chip area for this module: %f\n", area);
		}
	}
};

statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level)
{
	statdata_t mod_data = mod_stat.at(mod);
	std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
	num_cells_by_type.swap(mod_data.num_cells_by_type);

	for (auto &it : num_cells_by_type)
		if (mod_stat.count(it.first) > 0) {
			log("     %*s%-*s %6d\n", 2*level, "", 26-2*level, RTLIL::id2cstr(it.first), it.second);
			mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1) * it.second;
			mod_data.num_cells -= it.second;
		} else {
			mod_data.num_cells_by_type[it.first] += it.second;
		}

	return mod_data;
}

void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file)
{
	std::ifstream f;
	f.open(liberty_file.c_str());
	if (f.fail())
		log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
	LibertyParser libparser(f);
	f.close();

	for (auto cell : libparser.ast->children)
	{
		if (cell->id != "cell" || cell->args.size() != 1)
			continue;

		LibertyAst *ar = cell->find("area");
		if (ar != NULL && !ar->value.empty())
			cell_area["\\" + cell->args[0]] = atof(ar->value.c_str());
	}
}

struct StatPass : public Pass {
	StatPass() : Pass("stat", "print some statistics") { }
	virtual void help()
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    stat [options] [selection]\n");
		log("\n");
		log("Print some statistics (number of objects) on the selected portion of the\n");
		log("design.\n");
		log("\n");
		log("    -top <module>\n");
		log("        print design hierarchy with this module as top. if the design is fully\n");
		log("        selected and a module has the 'top' attribute set, this module is used\n");
		log("        default value for this option.\n");
		log("\n");
		log("    -liberty <liberty_file>\n");
		log("        use cell area information from the provided liberty file\n");
		log("\n");
		log("    -width\n");
		log("        annotate internal cell types with their word width.\n");
		log("        e.g. $add_8 for an 8 bit wide $add cell.\n");
		log("\n");
	}
	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Printing statistics.\n");

		bool width_mode = false;
		RTLIL::Module *top_mod = NULL;
		std::map<RTLIL::IdString, statdata_t> mod_stat;
		dict<IdString, double> cell_area;

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			if (args[argidx] == "-width") {
				width_mode = true;
				continue;
			}
			if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
				string liberty_file = args[++argidx];
				rewrite_filename(liberty_file);
				read_liberty_cellarea(cell_area, liberty_file);
				continue;
			}
			if (args[argidx] == "-top" && argidx+1 < args.size()) {
				if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
					log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
				top_mod = design->modules_.at(RTLIL::escape_id(args[++argidx]));
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		for (auto mod : design->selected_modules())
		{
			if (!top_mod && design->full_selection())
				if (mod->get_bool_attribute("\\top"))
					top_mod = mod;

			statdata_t data(design, mod, width_mode, cell_area);
			mod_stat[mod->name] = data;

			log("\n");
			log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)");
			log("\n");
			data.log_data();
		}

		if (top_mod != NULL && GetSize(mod_stat) > 1)
		{
			log("\n");
			log("=== design hierarchy ===\n");
			log("\n");

			log("   %-28s %6d\n", RTLIL::id2cstr(top_mod->name), 1);
			statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0);

			log("\n");
			data.log_data();
		}

		log("\n");
	}
} StatPass;

PRIVATE_NAMESPACE_END
<commit_msg>Add $alu to list of supported cells for "stat -width"<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/register.h"
#include "kernel/celltypes.h"
#include "passes/techmap/libparse.h"

#include "kernel/log.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct statdata_t
{
	#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
			X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes)

	#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area)

	#define X(_name) int _name;
	STAT_INT_MEMBERS
	#undef X
	double area;

	std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
	std::set<RTLIL::IdString> unknown_cell_area;

	statdata_t operator+(const statdata_t &other) const
	{
		statdata_t sum = other;
	#define X(_name) sum._name += _name;
		STAT_NUMERIC_MEMBERS
	#undef X
		for (auto &it : num_cells_by_type)
			sum.num_cells_by_type[it.first] += it.second;
		return sum;
	}

	statdata_t operator*(int other) const
	{
		statdata_t sum = *this;
	#define X(_name) sum._name *= other;
		STAT_NUMERIC_MEMBERS
	#undef X
		for (auto &it : sum.num_cells_by_type)
			it.second *= other;
		return sum;
	}

	statdata_t()
	{
	#define X(_name) _name = 0;
		STAT_NUMERIC_MEMBERS
	#undef X
	}

	statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
	{
	#define X(_name) _name = 0;
		STAT_NUMERIC_MEMBERS
	#undef X

		for (auto &it : mod->wires_)
		{
			if (!design->selected(mod, it.second))
				continue;

			if (it.first[0] == '\\') {
				num_pub_wires++;
				num_pub_wire_bits += it.second->width;
			}

			num_wires++;
			num_wire_bits += it.second->width;
		}

		for (auto &it : mod->memories) {
			if (!design->selected(mod, it.second))
				continue;
			num_memories++;
			num_memory_bits += it.second->width * it.second->size;
		}

		for (auto &it : mod->cells_)
		{
			if (!design->selected(mod, it.second))
				continue;

			RTLIL::IdString cell_type = it.second->type;

			if (width_mode)
			{
				if (cell_type.in("$not", "$pos", "$neg",
						"$logic_not", "$logic_and", "$logic_or",
						"$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
						"$lut", "$and", "$or", "$xor", "$xnor",
						"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
						"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
						"$add", "$sub", "$mul", "$div", "$mod", "$pow", "$alu")) {
					int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0;
					int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0;
					int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0;
					cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y}));
				}
				else if (cell_type.in("$mux", "$pmux"))
					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y")));
				else if (cell_type.in("$sr", "$dff", "$dffsr", "$adff", "$dlatch", "$dlatchsr"))
					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q")));
			}

			if (!cell_area.empty()) {
				if (cell_area.count(cell_type))
					area += cell_area.at(cell_type);
				else
					unknown_cell_area.insert(cell_type);
			}

			num_cells++;
			num_cells_by_type[cell_type]++;
		}

		for (auto &it : mod->processes) {
			if (!design->selected(mod, it.second))
				continue;
			num_processes++;
		}
	}

	void log_data()
	{
		log("   Number of wires:             %6d\n", num_wires);
		log("   Number of wire bits:         %6d\n", num_wire_bits);
		log("   Number of public wires:      %6d\n", num_pub_wires);
		log("   Number of public wire bits:  %6d\n", num_pub_wire_bits);
		log("   Number of memories:          %6d\n", num_memories);
		log("   Number of memory bits:       %6d\n", num_memory_bits);
		log("   Number of processes:         %6d\n", num_processes);
		log("   Number of cells:             %6d\n", num_cells);
		for (auto &it : num_cells_by_type)
			log("     %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);

		if (!unknown_cell_area.empty()) {
			log("\n");
			for (auto cell_type : unknown_cell_area)
				log("   Area for cell type %s is unknown!\n", cell_type.c_str());
		}

		if (area != 0) {
			log("\n");
			log("   Chip area for this module: %f\n", area);
		}
	}
};

statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level)
{
	statdata_t mod_data = mod_stat.at(mod);
	std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
	num_cells_by_type.swap(mod_data.num_cells_by_type);

	for (auto &it : num_cells_by_type)
		if (mod_stat.count(it.first) > 0) {
			log("     %*s%-*s %6d\n", 2*level, "", 26-2*level, RTLIL::id2cstr(it.first), it.second);
			mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1) * it.second;
			mod_data.num_cells -= it.second;
		} else {
			mod_data.num_cells_by_type[it.first] += it.second;
		}

	return mod_data;
}

void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file)
{
	std::ifstream f;
	f.open(liberty_file.c_str());
	if (f.fail())
		log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
	LibertyParser libparser(f);
	f.close();

	for (auto cell : libparser.ast->children)
	{
		if (cell->id != "cell" || cell->args.size() != 1)
			continue;

		LibertyAst *ar = cell->find("area");
		if (ar != NULL && !ar->value.empty())
			cell_area["\\" + cell->args[0]] = atof(ar->value.c_str());
	}
}

struct StatPass : public Pass {
	StatPass() : Pass("stat", "print some statistics") { }
	virtual void help()
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    stat [options] [selection]\n");
		log("\n");
		log("Print some statistics (number of objects) on the selected portion of the\n");
		log("design.\n");
		log("\n");
		log("    -top <module>\n");
		log("        print design hierarchy with this module as top. if the design is fully\n");
		log("        selected and a module has the 'top' attribute set, this module is used\n");
		log("        default value for this option.\n");
		log("\n");
		log("    -liberty <liberty_file>\n");
		log("        use cell area information from the provided liberty file\n");
		log("\n");
		log("    -width\n");
		log("        annotate internal cell types with their word width.\n");
		log("        e.g. $add_8 for an 8 bit wide $add cell.\n");
		log("\n");
	}
	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Printing statistics.\n");

		bool width_mode = false;
		RTLIL::Module *top_mod = NULL;
		std::map<RTLIL::IdString, statdata_t> mod_stat;
		dict<IdString, double> cell_area;

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			if (args[argidx] == "-width") {
				width_mode = true;
				continue;
			}
			if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
				string liberty_file = args[++argidx];
				rewrite_filename(liberty_file);
				read_liberty_cellarea(cell_area, liberty_file);
				continue;
			}
			if (args[argidx] == "-top" && argidx+1 < args.size()) {
				if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
					log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
				top_mod = design->modules_.at(RTLIL::escape_id(args[++argidx]));
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		for (auto mod : design->selected_modules())
		{
			if (!top_mod && design->full_selection())
				if (mod->get_bool_attribute("\\top"))
					top_mod = mod;

			statdata_t data(design, mod, width_mode, cell_area);
			mod_stat[mod->name] = data;

			log("\n");
			log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)");
			log("\n");
			data.log_data();
		}

		if (top_mod != NULL && GetSize(mod_stat) > 1)
		{
			log("\n");
			log("=== design hierarchy ===\n");
			log("\n");

			log("   %-28s %6d\n", RTLIL::id2cstr(top_mod->name), 1);
			statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0);

			log("\n");
			data.log_data();
		}

		log("\n");
	}
} StatPass;

PRIVATE_NAMESPACE_END
<|endoftext|>