<commit_before>#include "curltools.h"

#include "json_spirit.h"
#include "util.h"
#include <boost/thread.hpp>
#include <iostream>
#include <openssl/ssl.h>

boost::once_flag init_openssl_once_flag     = BOOST_ONCE_INIT;
boost::once_flag init_curl_global_once_flag = BOOST_ONCE_INIT;
boost::mutex     curl_global_init_lock;

class CurlCleaner
{
    CURL* curl_obj_to_clean;

public:
    CurlCleaner(CURL* Curl_obj_to_clean) : curl_obj_to_clean(Curl_obj_to_clean) {}

    ~CurlCleaner() { curl_easy_cleanup(curl_obj_to_clean); }
};

size_t cURLTools::CurlWrite_CallbackFunc_StdString(void* contents, size_t size, size_t nmemb,
                                                   std::deque<char>* s)
{
    size_t newLength = size * nmemb;
    size_t oldLength = s->size();
    try {
        s->resize(oldLength + newLength);
    } catch (std::bad_alloc& e) {
        std::stringstream msg;
        msg << "Error allocating memory: " << e.what() << std::endl;
        printf("%s", msg.str().c_str());
        return 0;
    }

    std::copy((char*)contents, (char*)contents + newLength, s->begin() + oldLength);
    return size * nmemb;
}

size_t cURLTools::CurlRead_CallbackFunc_StdString(void* dest, size_t /*size*/, size_t /*nmemb*/,
                                                  void* userp)
{
    std::string* from = (std::string*)userp;
    std::string* to   = (std::string*)dest;

    std::copy(from->begin(), from->end(), std::back_inserter(*to));

    return 0; /* no more data left to deliver */
}

size_t cURLTools::CurlWrite_CallbackFunc_File(void* contents, size_t size, size_t nmemb,
                                              boost::filesystem::fstream* fs)
{
    size_t newLength = size * nmemb;
    fs->write(reinterpret_cast<char*>(contents), newLength);
    return size * nmemb;
}

int cURLTools::CurlProgress_CallbackFunc(void*, double TotalToDownload, double NowDownloaded,
                                         double /*TotalToUpload*/, double /*NowUploaded*/)
{
    std::clog << "Download progress: " << ToString(NowDownloaded) << " / " << ToString(TotalToDownload)
              << std::endl;
    return CURLE_OK;
}

int cURLTools::CurlAtomicProgress_CallbackFunc(void* number, double TotalToDownload,
                                               double NowDownloaded, double /*TotalToUpload*/,
                                               double /*NowUploaded*/)
{
    std::atomic<float>* progress = reinterpret_cast<std::atomic<float>*>(number);
    float               val      = 0;
    if (TotalToDownload > 0.) {
        val = static_cast<float>(100. * NowDownloaded / TotalToDownload);
        if (val < 0.0001) {
            val = 0;
        }
    }
    progress->store(val, std::memory_order_relaxed);
    return CURLE_OK;
}

void cURLTools::CurlGlobalInit_ThreadSafe()
{
    boost::lock_guard<boost::mutex> lg(curl_global_init_lock);
    curl_global_init(CURL_GLOBAL_DEFAULT);
}

void cURLTools::GetLargeFileFromHTTPS(const std::string& URL, long ConnectionTimeout,
                                      const boost::filesystem::path& targetPath,
                                      std::atomic<float>&            progress,
                                      const std::set<CURLcode>&      errorsToIgnore)
{

#if OPENSSL_VERSION_NUMBER < 0x10100000L
    boost::call_once(init_openssl_once_flag, SSL_library_init);
#else
    boost::call_once(init_openssl_once_flag, OPENSSL_init_ssl, 0,
                     static_cast<const ossl_init_settings_st*>(NULL));
#endif

    CURL*    curl;
    CURLcode res;

    boost::call_once(init_curl_global_once_flag, CurlGlobalInit_ThreadSafe);

    curl = curl_easy_init();
    boost::filesystem::fstream outputStream(targetPath, std::ios::out | std::ios::binary);

    std::string agent = GetUserAgent();
    if (curl) {

        CurlCleaner cleaner(curl);

        curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // verify ssl peer
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // verify ssl hostname
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_File);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Dark Secret Ninja/1.0");
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outputStream);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, agent.c_str());

        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, CurlAtomicProgress_CallbackFunc);
        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progress);
        //        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //verbose output
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ConnectionTimeout);

        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK && errorsToIgnore.find(res) == errorsToIgnore.cend()) {
            std::string errorMsg(curl_easy_strerror(res));
            printf("Curl error: %s\n", errorMsg.c_str());
            throw std::runtime_error(std::string(errorMsg).c_str());
        } else {
            long http_response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response_code);
            if (http_response_code != 200) {
                std::string errorMsg("Error retrieving data with https protocol from URL \"" + URL +
                                     "\", error code: " + ToString(http_response_code) +
                                     ". Probably the URL is invalid.");
                printf("Curl http code error: %s\n", errorMsg.c_str());
                throw std::runtime_error(errorMsg);
            }
        }

        /* always cleanup */
        // This is replaced by a smart cleaning object with the destructor (CurlCleaner)
        // curl_easy_cleanup(curl);
    }
}

std::string cURLTools::GetUserAgent()
{
    std::string agent;
#ifdef QT_GUI
    agent += "Neblio-Qt";
#else
    agent += "Neblio";
#endif
    std::string version =
        std::to_string(CLIENT_VERSION_MAJOR) + "." + std::to_string(CLIENT_VERSION_MINOR) + "." +
        std::to_string(CLIENT_VERSION_REVISION) + "." + std::to_string(CLIENT_VERSION_BUILD);
    agent += "/";
    agent += version;
    return agent;
}

std::string cURLTools::GetFileFromHTTPS(const std::string& URL, long ConnectionTimeout,
                                        bool IncludeProgressBar)
{

#if OPENSSL_VERSION_NUMBER < 0x10100000L
    boost::call_once(init_openssl_once_flag, SSL_library_init);
#else
    boost::call_once(init_openssl_once_flag, OPENSSL_init_ssl, 0,
                     static_cast<const ossl_init_settings_st*>(NULL));
#endif

    CURL*    curl;
    CURLcode res;

    boost::call_once(init_curl_global_once_flag, CurlGlobalInit_ThreadSafe);

    curl = curl_easy_init();
    std::deque<char> s;

    std::string agent = GetUserAgent();
    if (curl) {

        CurlCleaner cleaner(curl);

        curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // verify ssl peer
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // verify ssl hostname
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Dark Secret Ninja/1.0");
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, agent.c_str());
        if (IncludeProgressBar) {
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
            curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, CurlProgress_CallbackFunc);
        } else {
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true);
        }
        //        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //verbose output
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ConnectionTimeout);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK) {
            std::string errorMsg(curl_easy_strerror(res));
            throw std::runtime_error(std::string(errorMsg).c_str());
        } else {
            long http_response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response_code);
            if (http_response_code != 200) {
                throw std::runtime_error("Error retrieving data with https protocol from URL \"" + URL +
                                         "\", error code: " + ToString(http_response_code) +
                                         ". Probably the URL is invalid.");
            }
        }

        /* always cleanup */
        // This is replaced by a smart cleaning object with the destructor (CurlCleaner)
        // curl_easy_cleanup(curl);
    }
    std::string fileStr(s.begin(), s.end());
    return fileStr;
}

std::string cURLTools::PostJsonToHTTPS(const std::string& URL, long ConnectionTimeout,
                                       const std::string& readdata, bool chunked)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    boost::call_once(init_openssl_once_flag, SSL_library_init);
#else
    boost::call_once(init_openssl_once_flag, OPENSSL_init_ssl, 0,
                     static_cast<const ossl_init_settings_st*>(NULL));
#endif

    CURL*    curl;
    CURLcode res;

    boost::call_once(init_curl_global_once_flag, CurlGlobalInit_ThreadSafe);

    /* get a curl handle */
    curl = curl_easy_init();
    std::deque<char> writedata;
    std::string      agent = GetUserAgent();
    if (curl) {

        CurlCleaner cleaner(curl);

        struct curl_slist* headers = NULL;

        /* First set the URL that is about to receive our POST. This URL can
         just as well be a https:// URL if that is what should receive the
         data. */
        curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
        //        curl_easy_setopt(curl, CURLOPT_READDATA, &readdata);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writedata);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // verify ssl peer
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // verify ssl hostname
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, CurlRead_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        /* Now specify the POST data */
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, readdata.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, agent.c_str());

        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, "charsets: utf-8");

        //        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // verbose output
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ConnectionTimeout);
        if (chunked) {

            headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
            /* use curl_slist_free_all() after the *perform() call to free this
         list again */
        } else {
            /* Set the expected POST size. If you want to POST large amounts of data,
       consider CURLOPT_POSTFIELDSIZE_LARGE */
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (curl_off_t)readdata.size());
        }

        /*
              Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue"
              header.  You can disable this header with CURLOPT_HTTPHEADER as usual.
              NOTE: if you want chunked transfer too, you need to combine these two
              since you can only set one list of headers with CURLOPT_HTTPHEADER. */

        /* A less good option would be to enforce HTTP 1.0, but that might also
               have other implications. */
        const bool disable_expect = false;
        if (disable_expect) {
            headers = curl_slist_append(headers, "Expect:");
            /* use curl_slist_free_all() after the *perform() call to free this
                 list again */
        }

        res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK) {
            std::string errorMsg(curl_easy_strerror(res));
            throw std::runtime_error(std::string(errorMsg).c_str());
        } else {
            long http_response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response_code);
            if (http_response_code != 200) {
                json_spirit::Value errorData;
                if (json_spirit::read(std::string(writedata.begin(), writedata.end()), errorData)) {
                    json_spirit::Value errorMsg;
                    errorMsg = json_spirit::find_value(errorData.get_obj(), "message");
                    throw std::runtime_error("Failed to create transaction with error: " +
                                             errorMsg.get_str());
                } else {
                    throw std::runtime_error("Error posting data with https protocol from URL \"" + URL +
                                             "\", error code: " + ToString(http_response_code) +
                                             ". Probably the URL is invalid. Could not retrieve more "
                                             "information on the error.");
                }
            }
        }

        /* always cleanup */
        // This is replaced by a smart cleaning object with the destructor (CurlCleaner)
        // curl_easy_cleanup(curl);
    }
    std::string fileStr(writedata.begin(), writedata.end());
    return fileStr;
}
<commit_msg>Fix curl options before merge<commit_after>#include "curltools.h"

#include "json_spirit.h"
#include "util.h"
#include <boost/thread.hpp>
#include <iostream>
#include <openssl/ssl.h>

boost::once_flag init_openssl_once_flag     = BOOST_ONCE_INIT;
boost::once_flag init_curl_global_once_flag = BOOST_ONCE_INIT;
boost::mutex     curl_global_init_lock;

class CurlCleaner
{
    CURL* curl_obj_to_clean;

public:
    CurlCleaner(CURL* Curl_obj_to_clean) : curl_obj_to_clean(Curl_obj_to_clean) {}

    ~CurlCleaner() { curl_easy_cleanup(curl_obj_to_clean); }
};

size_t cURLTools::CurlWrite_CallbackFunc_StdString(void* contents, size_t size, size_t nmemb,
                                                   std::deque<char>* s)
{
    size_t newLength = size * nmemb;
    size_t oldLength = s->size();
    try {
        s->resize(oldLength + newLength);
    } catch (std::bad_alloc& e) {
        std::stringstream msg;
        msg << "Error allocating memory: " << e.what() << std::endl;
        printf("%s", msg.str().c_str());
        return 0;
    }

    std::copy((char*)contents, (char*)contents + newLength, s->begin() + oldLength);
    return size * nmemb;
}

size_t cURLTools::CurlRead_CallbackFunc_StdString(void* dest, size_t /*size*/, size_t /*nmemb*/,
                                                  void* userp)
{
    std::string* from = (std::string*)userp;
    std::string* to   = (std::string*)dest;

    std::copy(from->begin(), from->end(), std::back_inserter(*to));

    return 0; /* no more data left to deliver */
}

size_t cURLTools::CurlWrite_CallbackFunc_File(void* contents, size_t size, size_t nmemb,
                                              boost::filesystem::fstream* fs)
{
    size_t newLength = size * nmemb;
    fs->write(reinterpret_cast<char*>(contents), newLength);
    return size * nmemb;
}

int cURLTools::CurlProgress_CallbackFunc(void*, double TotalToDownload, double NowDownloaded,
                                         double /*TotalToUpload*/, double /*NowUploaded*/)
{
    std::clog << "Download progress: " << ToString(NowDownloaded) << " / " << ToString(TotalToDownload)
              << std::endl;
    return CURLE_OK;
}

int cURLTools::CurlAtomicProgress_CallbackFunc(void* number, double TotalToDownload,
                                               double NowDownloaded, double /*TotalToUpload*/,
                                               double /*NowUploaded*/)
{
    std::atomic<float>* progress = reinterpret_cast<std::atomic<float>*>(number);
    float               val      = 0;
    if (TotalToDownload > 0.) {
        val = static_cast<float>(100. * NowDownloaded / TotalToDownload);
        if (val < 0.0001) {
            val = 0;
        }
    }
    progress->store(val, std::memory_order_relaxed);
    return CURLE_OK;
}

void cURLTools::CurlGlobalInit_ThreadSafe()
{
    boost::lock_guard<boost::mutex> lg(curl_global_init_lock);
    curl_global_init(CURL_GLOBAL_DEFAULT);
}

void cURLTools::GetLargeFileFromHTTPS(const std::string& URL, long ConnectionTimeout,
                                      const boost::filesystem::path& targetPath,
                                      std::atomic<float>&            progress,
                                      const std::set<CURLcode>&      errorsToIgnore)
{

#if OPENSSL_VERSION_NUMBER < 0x10100000L
    boost::call_once(init_openssl_once_flag, SSL_library_init);
#else
    boost::call_once(init_openssl_once_flag, OPENSSL_init_ssl, 0,
                     static_cast<const ossl_init_settings_st*>(NULL));
#endif

    CURL*    curl;
    CURLcode res;

    boost::call_once(init_curl_global_once_flag, CurlGlobalInit_ThreadSafe);

    curl = curl_easy_init();
    boost::filesystem::fstream outputStream(targetPath, std::ios::out | std::ios::binary);

    std::string agent = GetUserAgent();
    if (curl) {

        CurlCleaner cleaner(curl);

        curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_File);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Dark Secret Ninja/1.0");
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outputStream);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, agent.c_str());

        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, CurlAtomicProgress_CallbackFunc);
        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progress);
        //        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //verbose output
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ConnectionTimeout);

        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK && errorsToIgnore.find(res) == errorsToIgnore.cend()) {
            std::string errorMsg(curl_easy_strerror(res));
            printf("Curl error: %s\n", errorMsg.c_str());
            throw std::runtime_error(std::string(errorMsg).c_str());
        } else {
            long http_response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response_code);
            if (http_response_code != 200) {
                std::string errorMsg("Error retrieving data with https protocol from URL \"" + URL +
                                     "\", error code: " + ToString(http_response_code) +
                                     ". Probably the URL is invalid.");
                printf("Curl http code error: %s\n", errorMsg.c_str());
                throw std::runtime_error(errorMsg);
            }
        }

        /* always cleanup */
        // This is replaced by a smart cleaning object with the destructor (CurlCleaner)
        // curl_easy_cleanup(curl);
    }
}

std::string cURLTools::GetUserAgent()
{
    std::string agent;
#ifdef QT_GUI
    agent += "Neblio-Qt";
#else
    agent += "Neblio";
#endif
    std::string version =
        std::to_string(CLIENT_VERSION_MAJOR) + "." + std::to_string(CLIENT_VERSION_MINOR) + "." +
        std::to_string(CLIENT_VERSION_REVISION) + "." + std::to_string(CLIENT_VERSION_BUILD);
    agent += "/";
    agent += version;
    return agent;
}

std::string cURLTools::GetFileFromHTTPS(const std::string& URL, long ConnectionTimeout,
                                        bool IncludeProgressBar)
{

#if OPENSSL_VERSION_NUMBER < 0x10100000L
    boost::call_once(init_openssl_once_flag, SSL_library_init);
#else
    boost::call_once(init_openssl_once_flag, OPENSSL_init_ssl, 0,
                     static_cast<const ossl_init_settings_st*>(NULL));
#endif

    CURL*    curl;
    CURLcode res;

    boost::call_once(init_curl_global_once_flag, CurlGlobalInit_ThreadSafe);

    curl = curl_easy_init();
    std::deque<char> s;

    std::string agent = GetUserAgent();
    if (curl) {

        CurlCleaner cleaner(curl);

        curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Dark Secret Ninja/1.0");
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, agent.c_str());
        if (IncludeProgressBar) {
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
            curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, CurlProgress_CallbackFunc);
        } else {
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true);
        }
        //        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //verbose output
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ConnectionTimeout);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK) {
            std::string errorMsg(curl_easy_strerror(res));
            throw std::runtime_error(std::string(errorMsg).c_str());
        } else {
            long http_response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response_code);
            if (http_response_code != 200) {
                throw std::runtime_error("Error retrieving data with https protocol from URL \"" + URL +
                                         "\", error code: " + ToString(http_response_code) +
                                         ". Probably the URL is invalid.");
            }
        }

        /* always cleanup */
        // This is replaced by a smart cleaning object with the destructor (CurlCleaner)
        // curl_easy_cleanup(curl);
    }
    std::string fileStr(s.begin(), s.end());
    return fileStr;
}

std::string cURLTools::PostJsonToHTTPS(const std::string& URL, long ConnectionTimeout,
                                       const std::string& readdata, bool chunked)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    boost::call_once(init_openssl_once_flag, SSL_library_init);
#else
    boost::call_once(init_openssl_once_flag, OPENSSL_init_ssl, 0,
                     static_cast<const ossl_init_settings_st*>(NULL));
#endif

    CURL*    curl;
    CURLcode res;

    boost::call_once(init_curl_global_once_flag, CurlGlobalInit_ThreadSafe);

    /* get a curl handle */
    curl = curl_easy_init();
    std::deque<char> writedata;
    std::string      agent = GetUserAgent();
    if (curl) {

        CurlCleaner cleaner(curl);

        struct curl_slist* headers = NULL;

        /* First set the URL that is about to receive our POST. This URL can
         just as well be a https:// URL if that is what should receive the
         data. */
        curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
        //        curl_easy_setopt(curl, CURLOPT_READDATA, &readdata);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writedata);
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, CurlRead_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        /* Now specify the POST data */
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, readdata.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, agent.c_str());

        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, "charsets: utf-8");

        //        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // verbose output
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ConnectionTimeout);
        if (chunked) {

            headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
            /* use curl_slist_free_all() after the *perform() call to free this
         list again */
        } else {
            /* Set the expected POST size. If you want to POST large amounts of data,
       consider CURLOPT_POSTFIELDSIZE_LARGE */
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (curl_off_t)readdata.size());
        }

        /*
              Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue"
              header.  You can disable this header with CURLOPT_HTTPHEADER as usual.
              NOTE: if you want chunked transfer too, you need to combine these two
              since you can only set one list of headers with CURLOPT_HTTPHEADER. */

        /* A less good option would be to enforce HTTP 1.0, but that might also
               have other implications. */
        const bool disable_expect = false;
        if (disable_expect) {
            headers = curl_slist_append(headers, "Expect:");
            /* use curl_slist_free_all() after the *perform() call to free this
                 list again */
        }

        res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK) {
            std::string errorMsg(curl_easy_strerror(res));
            throw std::runtime_error(std::string(errorMsg).c_str());
        } else {
            long http_response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response_code);
            if (http_response_code != 200) {
                json_spirit::Value errorData;
                if (json_spirit::read(std::string(writedata.begin(), writedata.end()), errorData)) {
                    json_spirit::Value errorMsg;
                    errorMsg = json_spirit::find_value(errorData.get_obj(), "message");
                    throw std::runtime_error("Failed to create transaction with error: " +
                                             errorMsg.get_str());
                } else {
                    throw std::runtime_error("Error posting data with https protocol from URL \"" + URL +
                                             "\", error code: " + ToString(http_response_code) +
                                             ". Probably the URL is invalid. Could not retrieve more "
                                             "information on the error.");
                }
            }
        }

        /* always cleanup */
        // This is replaced by a smart cleaning object with the destructor (CurlCleaner)
        // curl_easy_cleanup(curl);
    }
    std::string fileStr(writedata.begin(), writedata.end());
    return fileStr;
}
<|endoftext|>


<commit_before>#ifndef SERVER_HTTP_HPP
#define	SERVER_HTTP_HPP

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/regex.hpp>

#include <unordered_map>
#include <thread>
#include <functional>
#include <iostream>
#include <sstream>

namespace SimpleWeb {
    template <class socket_type>
    class ServerBase {
    public:
        class Response : public std::ostream {
            friend class ServerBase<socket_type>;
        private:
            boost::asio::yield_context& yield;

            boost::asio::streambuf streambuf;

            socket_type &socket;

            Response(socket_type &socket, boost::asio::yield_context& yield):
                    std::ostream(&streambuf), yield(yield), socket(socket) {}

        public:
            size_t size() {
                return streambuf.size();
            }
            void flush() {
                boost::system::error_code ec;
                boost::asio::async_write(socket, streambuf, yield[ec]);

                if(ec)
                    throw std::runtime_error(ec.message());
            }
        };

        class Content : public std::istream {
            friend class ServerBase<socket_type>;
        public:
            size_t size() {
                return streambuf.size();
            }
            std::string string() {
                std::stringstream ss;
                ss << rdbuf();
                return ss.str();
            }
        private:
            boost::asio::streambuf &streambuf;
            Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
        };

        class Request {
            friend class ServerBase<socket_type>;
        public:
            std::string method, path, http_version;

            Content content;

            std::unordered_multimap<std::string, std::string> header;

            boost::smatch path_match;

            std::string remote_endpoint_address;
            unsigned short remote_endpoint_port;

        private:
            Request(boost::asio::io_service &io_service): content(streambuf), strand(io_service) {}

            boost::asio::streambuf streambuf;

            boost::asio::strand strand;

            void read_remote_endpoint_data(socket_type& socket) {
                try {
                    remote_endpoint_address=socket.lowest_layer().remote_endpoint().address().to_string();
                    remote_endpoint_port=socket.lowest_layer().remote_endpoint().port();
                }
                catch(const std::exception& e) {}
            }
        };

        class Config {
            friend class ServerBase<socket_type>;
        private:
            Config(unsigned short port, size_t num_threads): port(port), num_threads(num_threads) {}
            unsigned short port;
            size_t num_threads;
        public:
            ///IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
            ///If empty, the address will be any address.
            std::string address;
            ///Set to false to avoid binding the socket to an address that is already in use.
            bool reuse_address=true;
        };
        ///Set before calling start().
        Config config;

        std::unordered_map<std::string, std::unordered_map<std::string,
            std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > >  resource;

        std::unordered_map<std::string,
            std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > default_resource;

    private:
        std::vector<std::pair<std::string, std::vector<std::pair<boost::regex,
            std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > > > > opt_resource;

    public:
        void start() {
            //Copy the resources to opt_resource for more efficient request processing
            opt_resource.clear();
            for(auto& res: resource) {
                for(auto& res_method: res.second) {
                    auto it=opt_resource.end();
                    for(auto opt_it=opt_resource.begin();opt_it!=opt_resource.end();opt_it++) {
                        if(res_method.first==opt_it->first) {
                            it=opt_it;
                            break;
                        }
                    }
                    if(it==opt_resource.end()) {
                        opt_resource.emplace_back();
                        it=opt_resource.begin()+(opt_resource.size()-1);
                        it->first=res_method.first;
                    }
                    it->second.emplace_back(boost::regex(res.first), res_method.second);
                }
            }

            if(io_service.stopped())
                io_service.reset();

            boost::asio::ip::tcp::endpoint endpoint;
            if(config.address.size()>0)
                endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port);
            else
                endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
            acceptor.open(endpoint.protocol());
            acceptor.set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
            acceptor.bind(endpoint);
            acceptor.listen();

            accept();

            //If num_threads>1, start m_io_service.run() in (num_threads-1) threads for thread-pooling
            threads.clear();
            for(size_t c=1;c<config.num_threads;c++) {
                threads.emplace_back([this](){
                    io_service.run();
                });
            }

            //Main thread
            io_service.run();

            //Wait for the rest of the threads, if any, to finish as well
            for(auto& t: threads) {
                t.join();
            }
        }

        void stop() {
            acceptor.close();
            io_service.stop();
        }

    protected:
        boost::asio::io_service io_service;
        boost::asio::ip::tcp::acceptor acceptor;
        std::vector<std::thread> threads;

        size_t timeout_request;
        size_t timeout_content;

        ServerBase(unsigned short port, size_t num_threads, size_t timeout_request, size_t timeout_send_or_receive) :
                config(port, num_threads), acceptor(io_service),
                timeout_request(timeout_request), timeout_content(timeout_send_or_receive) {}

        virtual void accept()=0;

        std::shared_ptr<boost::asio::deadline_timer> set_timeout_on_socket(std::shared_ptr<socket_type> socket, size_t seconds) {
            std::shared_ptr<boost::asio::deadline_timer> timer(new boost::asio::deadline_timer(io_service));
            timer->expires_from_now(boost::posix_time::seconds(seconds));
            timer->async_wait([socket](const boost::system::error_code& ec){
                if(!ec) {
                    boost::system::error_code ec;
                    socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
                    socket->lowest_layer().close();
                }
            });
            return timer;
        }

        std::shared_ptr<boost::asio::deadline_timer> set_timeout_on_socket(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request, size_t seconds) {
            std::shared_ptr<boost::asio::deadline_timer> timer(new boost::asio::deadline_timer(io_service));
            timer->expires_from_now(boost::posix_time::seconds(seconds));
            timer->async_wait(request->strand.wrap([socket](const boost::system::error_code& ec){
                if(!ec) {
                    boost::system::error_code ec;
                    socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
                    socket->lowest_layer().close();
                }
            }));
            return timer;
        }

        void read_request_and_content(std::shared_ptr<socket_type> socket) {
            //Create new streambuf (Request::streambuf) for async_read_until()
            //shared_ptr is used to pass temporary objects to the asynchronous functions
            std::shared_ptr<Request> request(new Request(io_service));
            request->read_remote_endpoint_data(*socket);

            //Set timeout on the following boost::asio::async-read or write function
            std::shared_ptr<boost::asio::deadline_timer> timer;
            if(timeout_request>0)
                timer=set_timeout_on_socket(socket, timeout_request);

            boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n",
                    [this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
                if(timeout_request>0)
                    timer->cancel();
                if(!ec) {
                    //request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
                    //"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
                    //The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
                    //streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
                    size_t num_additional_bytes=request->streambuf.size()-bytes_transferred;

                    parse_request(request, request->content);

                    //If content, read that as well
                    const auto it=request->header.find("Content-Length");
                    if(it!=request->header.end()) {
                        //Set timeout on the following boost::asio::async-read or write function
                        std::shared_ptr<boost::asio::deadline_timer> timer;
                        if(timeout_content>0)
                            timer=set_timeout_on_socket(socket, timeout_content);
                        unsigned long long content_length;
                        try {
                            content_length=stoull(it->second);
                        }
                        catch(const std::exception &e) {
                            return;
                        }
                        boost::asio::async_read(*socket, request->streambuf,
                                boost::asio::transfer_exactly(content_length-num_additional_bytes),
                                [this, socket, request, timer]
                                (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                            if(timeout_content>0)
                                timer->cancel();
                            if(!ec)
                                find_resource(socket, request);
                        });
                    }
                    else {
                        find_resource(socket, request);
                    }
                }
            });
        }

        void parse_request(std::shared_ptr<Request> request, std::istream& stream) const {
            std::string line;
            getline(stream, line);
            size_t method_end;
            if((method_end=line.find(' '))!=std::string::npos) {
                size_t path_end;
                if((path_end=line.find(' ', method_end+1))!=std::string::npos) {
                    request->method=line.substr(0, method_end);
                    request->path=line.substr(method_end+1, path_end-method_end-1);
                    if((path_end+6)<line.size())
                        request->http_version=line.substr(path_end+6, line.size()-(path_end+6)-1);
                    else
                        request->http_version="1.0";

                    getline(stream, line);
                    size_t param_end;
                    while((param_end=line.find(':'))!=std::string::npos) {
                        size_t value_start=param_end+1;
                        if((value_start)<line.size()) {
                            if(line[value_start]==' ')
                                value_start++;
                            if(value_start<line.size())
                                request->header.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1)));
                        }

                        getline(stream, line);
                    }
                }
            }
        }

        void find_resource(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request) {
            //Find path- and method-match, and call write_response
            for(auto& res: opt_resource) {
                if(request->method==res.first) {
                    for(auto& res_path: res.second) {
                        boost::smatch sm_res;
                        if(boost::regex_match(request->path, sm_res, res_path.first)) {
                            request->path_match=std::move(sm_res);
                            write_response(socket, request, res_path.second);
                            return;
                        }
                    }
                }
            }
            auto it_method=default_resource.find(request->method);
            if(it_method!=default_resource.end()) {
                write_response(socket, request, it_method->second);
            }
        }

        void write_response(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request,
                std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)>& resource_function) {
            //Set timeout on the following boost::asio::async-read or write function
            std::shared_ptr<boost::asio::deadline_timer> timer;
            if(timeout_content>0)
                timer=set_timeout_on_socket(socket, request, timeout_content);

            boost::asio::spawn(request->strand, [this, &resource_function, socket, request, timer](boost::asio::yield_context yield) {
                Response response(*socket, yield);

                try {
                    resource_function(response, request);
                }
                catch(const std::exception& e) {
                    return;
                }

                if(response.size()>0) {
                    try {
                        response.flush();
                    }
                    catch(const std::exception &e) {
                        return;
                    }
                }
                if(timeout_content>0)
                    timer->cancel();
                float http_version;
                try {
                    http_version=stof(request->http_version);
                }
                catch(const std::exception &e) {
                    return;
                }
                if(http_version>1.05)
                    read_request_and_content(socket);
            });
        }
    };

    template<class socket_type>
    class Server : public ServerBase<socket_type> {};

    typedef boost::asio::ip::tcp::socket HTTP;

    template<>
    class Server<HTTP> : public ServerBase<HTTP> {
    public:
        Server(unsigned short port, size_t num_threads=1, size_t timeout_request=5, size_t timeout_content=300) :
                ServerBase<HTTP>::ServerBase(port, num_threads, timeout_request, timeout_content) {}

    private:
        void accept() {
            //Create new socket for this connection
            //Shared_ptr is used to pass temporary objects to the asynchronous functions
            std::shared_ptr<HTTP> socket(new HTTP(io_service));

            acceptor.async_accept(*socket, [this, socket](const boost::system::error_code& ec){
                //Immediately start accepting a new connection
                accept();

                if(!ec) {
                    boost::asio::ip::tcp::no_delay option(true);
                    socket->set_option(option);

                    read_request_and_content(socket);
                }
            });
        }
    };
}
#endif	/* SERVER_HTTP_HPP */
<commit_msg>fixed vs2012 compile<commit_after>#ifndef SERVER_HTTP_HPP
#define	SERVER_HTTP_HPP

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/regex.hpp>

#include <unordered_map>
#include <thread>
#include <functional>
#include <iostream>
#include <sstream>

namespace SimpleWeb {
    template <class socket_type>
    class ServerBase {
    public:
        class Response : public std::ostream {
            friend class ServerBase<socket_type>;
        private:
            boost::asio::yield_context& yield;

            boost::asio::streambuf streambuf;

            socket_type &socket;

            Response(socket_type &socket, boost::asio::yield_context& yield):
                    std::ostream(&streambuf), yield(yield), socket(socket) {}

        public:
            size_t size() {
                return streambuf.size();
            }
            void flush() {
                boost::system::error_code ec;
                boost::asio::async_write(socket, streambuf, yield[ec]);

                if(ec)
                    throw std::runtime_error(ec.message());
            }
        };

        class Content : public std::istream {
            friend class ServerBase<socket_type>;
        public:
            size_t size() {
                return streambuf.size();
            }
            std::string string() {
                std::stringstream ss;
                ss << rdbuf();
                return ss.str();
            }
        private:
            boost::asio::streambuf &streambuf;
            Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
        };

        class Request {
            friend class ServerBase<socket_type>;
        public:
            std::string method, path, http_version;

            Content content;

            std::unordered_multimap<std::string, std::string> header;

            boost::smatch path_match;

            std::string remote_endpoint_address;
            unsigned short remote_endpoint_port;

        private:
            Request(boost::asio::io_service &io_service): content(streambuf), strand(io_service) {}

            boost::asio::streambuf streambuf;

            boost::asio::strand strand;

            void read_remote_endpoint_data(socket_type& socket) {
                try {
                    remote_endpoint_address=socket.lowest_layer().remote_endpoint().address().to_string();
                    remote_endpoint_port=socket.lowest_layer().remote_endpoint().port();
                }
                catch(const std::exception& e) {}
            }
        };

        class Config {
            friend class ServerBase<socket_type>;
        private:
            Config(unsigned short port, size_t num_threads): port(port), num_threads(num_threads), reuse_address(true) {}
            unsigned short port;
            size_t num_threads;
        public:
            ///IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
            ///If empty, the address will be any address.
            std::string address;
            ///Set to false to avoid binding the socket to an address that is already in use.
            bool reuse_address;
        };
        ///Set before calling start().
        Config config;

        std::unordered_map<std::string, std::unordered_map<std::string,
            std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > >  resource;

        std::unordered_map<std::string,
            std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > default_resource;

    private:
        std::vector<std::pair<std::string, std::vector<std::pair<boost::regex,
            std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > > > > opt_resource;

    public:
        void start() {
            //Copy the resources to opt_resource for more efficient request processing
            opt_resource.clear();
            for(auto& res: resource) {
                for(auto& res_method: res.second) {
                    auto it=opt_resource.end();
                    for(auto opt_it=opt_resource.begin();opt_it!=opt_resource.end();opt_it++) {
                        if(res_method.first==opt_it->first) {
                            it=opt_it;
                            break;
                        }
                    }
                    if(it==opt_resource.end()) {
                        opt_resource.emplace_back();
                        it=opt_resource.begin()+(opt_resource.size()-1);
                        it->first=res_method.first;
                    }
                    it->second.emplace_back(boost::regex(res.first), res_method.second);
                }
            }

            if(io_service.stopped())
                io_service.reset();

            boost::asio::ip::tcp::endpoint endpoint;
            if(config.address.size()>0)
                endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port);
            else
                endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
            acceptor.open(endpoint.protocol());
            acceptor.set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
            acceptor.bind(endpoint);
            acceptor.listen();

            accept();

            //If num_threads>1, start m_io_service.run() in (num_threads-1) threads for thread-pooling
            threads.clear();
            for(size_t c=1;c<config.num_threads;c++) {
                threads.emplace_back([this](){
                    io_service.run();
                });
            }

            //Main thread
            io_service.run();

            //Wait for the rest of the threads, if any, to finish as well
            for(auto& t: threads) {
                t.join();
            }
        }

        void stop() {
            acceptor.close();
            io_service.stop();
        }

    protected:
        boost::asio::io_service io_service;
        boost::asio::ip::tcp::acceptor acceptor;
        std::vector<std::thread> threads;

        size_t timeout_request;
        size_t timeout_content;

        ServerBase(unsigned short port, size_t num_threads, size_t timeout_request, size_t timeout_send_or_receive) :
                config(port, num_threads), acceptor(io_service),
                timeout_request(timeout_request), timeout_content(timeout_send_or_receive) {}

        virtual void accept()=0;

        std::shared_ptr<boost::asio::deadline_timer> set_timeout_on_socket(std::shared_ptr<socket_type> socket, size_t seconds) {
            std::shared_ptr<boost::asio::deadline_timer> timer(new boost::asio::deadline_timer(io_service));
            timer->expires_from_now(boost::posix_time::seconds(seconds));
            timer->async_wait([socket](const boost::system::error_code& ec){
                if(!ec) {
                    boost::system::error_code ec;
                    socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
                    socket->lowest_layer().close();
                }
            });
            return timer;
        }

        std::shared_ptr<boost::asio::deadline_timer> set_timeout_on_socket(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request, size_t seconds) {
            std::shared_ptr<boost::asio::deadline_timer> timer(new boost::asio::deadline_timer(io_service));
            timer->expires_from_now(boost::posix_time::seconds(seconds));
            timer->async_wait(request->strand.wrap([socket](const boost::system::error_code& ec){
                if(!ec) {
                    boost::system::error_code ec;
                    socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
                    socket->lowest_layer().close();
                }
            }));
            return timer;
        }

        void read_request_and_content(std::shared_ptr<socket_type> socket) {
            //Create new streambuf (Request::streambuf) for async_read_until()
            //shared_ptr is used to pass temporary objects to the asynchronous functions
            std::shared_ptr<Request> request(new Request(io_service));
            request->read_remote_endpoint_data(*socket);

            //Set timeout on the following boost::asio::async-read or write function
            std::shared_ptr<boost::asio::deadline_timer> timer;
            if(timeout_request>0)
                timer=set_timeout_on_socket(socket, timeout_request);

            boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n",
                    [this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
                if(timeout_request>0)
                    timer->cancel();
                if(!ec) {
                    //request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
                    //"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
                    //The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
                    //streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
                    size_t num_additional_bytes=request->streambuf.size()-bytes_transferred;

                    parse_request(request, request->content);

                    //If content, read that as well
                    const auto it=request->header.find("Content-Length");
                    if(it!=request->header.end()) {
                        //Set timeout on the following boost::asio::async-read or write function
                        std::shared_ptr<boost::asio::deadline_timer> timer;
                        if(timeout_content>0)
                            timer=set_timeout_on_socket(socket, timeout_content);
                        unsigned long long content_length;
                        try {
                            content_length=stoull(it->second);
                        }
                        catch(const std::exception &e) {
                            return;
                        }
                        boost::asio::async_read(*socket, request->streambuf,
                                boost::asio::transfer_exactly(content_length-num_additional_bytes),
                                [this, socket, request, timer]
                                (const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
                            if(timeout_content>0)
                                timer->cancel();
                            if(!ec)
                                find_resource(socket, request);
                        });
                    }
                    else {
                        find_resource(socket, request);
                    }
                }
            });
        }

        void parse_request(std::shared_ptr<Request> request, std::istream& stream) const {
            std::string line;
            getline(stream, line);
            size_t method_end;
            if((method_end=line.find(' '))!=std::string::npos) {
                size_t path_end;
                if((path_end=line.find(' ', method_end+1))!=std::string::npos) {
                    request->method=line.substr(0, method_end);
                    request->path=line.substr(method_end+1, path_end-method_end-1);
                    if((path_end+6)<line.size())
                        request->http_version=line.substr(path_end+6, line.size()-(path_end+6)-1);
                    else
                        request->http_version="1.0";

                    getline(stream, line);
                    size_t param_end;
                    while((param_end=line.find(':'))!=std::string::npos) {
                        size_t value_start=param_end+1;
                        if((value_start)<line.size()) {
                            if(line[value_start]==' ')
                                value_start++;
                            if(value_start<line.size())
                                request->header.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1)));
                        }

                        getline(stream, line);
                    }
                }
            }
        }

        void find_resource(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request) {
            //Find path- and method-match, and call write_response
            for(auto& res: opt_resource) {
                if(request->method==res.first) {
                    for(auto& res_path: res.second) {
                        boost::smatch sm_res;
                        if(boost::regex_match(request->path, sm_res, res_path.first)) {
                            request->path_match=std::move(sm_res);
                            write_response(socket, request, res_path.second);
                            return;
                        }
                    }
                }
            }
            auto it_method=default_resource.find(request->method);
            if(it_method!=default_resource.end()) {
                write_response(socket, request, it_method->second);
            }
        }

        void write_response(std::shared_ptr<socket_type> socket, std::shared_ptr<Request> request,
                std::function<void(typename ServerBase<socket_type>::Response&, std::shared_ptr<typename ServerBase<socket_type>::Request>)>& resource_function) {
            //Set timeout on the following boost::asio::async-read or write function
            std::shared_ptr<boost::asio::deadline_timer> timer;
            if(timeout_content>0)
                timer=set_timeout_on_socket(socket, request, timeout_content);

            boost::asio::spawn(request->strand, [this, &resource_function, socket, request, timer](boost::asio::yield_context yield) {
                Response response(*socket, yield);

                try {
                    resource_function(response, request);
                }
                catch(const std::exception& e) {
                    return;
                }

                if(response.size()>0) {
                    try {
                        response.flush();
                    }
                    catch(const std::exception &e) {
                        return;
                    }
                }
                if(timeout_content>0)
                    timer->cancel();
                float http_version;
                try {
                    http_version=stof(request->http_version);
                }
                catch(const std::exception &e) {
                    return;
                }
                if(http_version>1.05)
                    read_request_and_content(socket);
            });
        }
    };

    template<class socket_type>
    class Server : public ServerBase<socket_type> {};

    typedef boost::asio::ip::tcp::socket HTTP;

    template<>
    class Server<HTTP> : public ServerBase<HTTP> {
    public:
        Server(unsigned short port, size_t num_threads=1, size_t timeout_request=5, size_t timeout_content=300) :
                ServerBase<HTTP>::ServerBase(port, num_threads, timeout_request, timeout_content) {}

    private:
        void accept() {
            //Create new socket for this connection
            //Shared_ptr is used to pass temporary objects to the asynchronous functions
            std::shared_ptr<HTTP> socket(new HTTP(io_service));

            acceptor.async_accept(*socket, [this, socket](const boost::system::error_code& ec){
                //Immediately start accepting a new connection
                accept();

                if(!ec) {
                    boost::asio::ip::tcp::no_delay option(true);
                    socket->set_option(option);

                    read_request_and_content(socket);
                }
            });
        }
    };
}
#endif	/* SERVER_HTTP_HPP */
<|endoftext|>


<commit_before>#ifndef TCM_MATRIX_HPP
#define TCM_MATRIX_HPP

#include <cassert>
#include <complex>
#include <memory>
#include <type_traits>
#include <algorithm>

#include <boost/align/aligned_allocator_adaptor.hpp>

#include <utils.hpp>
#include <iterator.hpp>

///////////////////////////////////////////////////////////////////////////////
/// \file matrix.hpp
/// \brief This file implements a Matrix class and some operations associated
///        with it.
///////////////////////////////////////////////////////////////////////////////

namespace tcm {

///////////////////////////////////////////////////////////////////////////////
/// \brief A simple wrapper around the (data, ldim, width) representation
///        of matrices used in LAPACK. Uses column-major ordering.

/// This is a _container_ class in the sense that it manages its own memory.
/// Matrix class is meant to be used with LAPACK/BLAS and thus focuses on
/// fundamental numeric types. There are two important differences
/// between this class and, for example, an %std::vector.
/// 1) As we plan to use matrices with LAPACK/BLAS routines, it helps
///    (from the performance point of view) to correctly align our matrices.
///    From Intel MKL's manual:
///    > To improve performance of your application that calls Intel MKL,
///    > align your arrays on 64-byte boundaries and ensure that the leading
///    > dimensions of the arrays are divisible by 64/element_size, where
///    > element_size is the number of bytes for the matrix elements
///    > (4 for single-precision real, 8 for double-precision real and
///    > single-precision complex, and 16 for double-precision complex).
/// 2) We don't initialise storage.
///
/// \tparam _Tp Element type of the matrix.
/// \tparam _Align Alignment. Must be a power of 2. This argument is used
///                for two things:
///                1) Allocation of storage that is aligned at least to _Alloc.
///                2) Computing the leading dimension of the matrix to ensure
///                   that `this->data(0, 1)` is again aligned to _Alloc.
///
///                We use %boost::alignment::aligned_allocator_adaptor
///                for this.
///
///////////////////////////////////////////////////////////////////////////////
template< class _Tp
        , std::size_t _Align = 64 // std::alignment_of<_Tp>::value
        , class _Alloc = std::allocator<_Tp>
        >
class Matrix {

private:
	// using _Aligned_Alloc = _Alloc;
	using _Aligned_Alloc = boost::alignment::
		aligned_allocator_adaptor<_Alloc, _Align>;
	using _Storage_type = utils::_Storage<_Tp, _Aligned_Alloc>;

	static_assert( (_Align != 0) and ((_Align & (_Align - 1)) == 0)
	             , "_Align must be a power of 2" );
	static_assert( _Align % std::alignment_of<_Tp>::value == 0
	             , "Invalid alignment." );

public:
	using value_type        = _Tp;
	using reference         = typename _Storage_type::reference;
	using const_reference   = typename _Storage_type::const_reference;
	using pointer           = typename _Storage_type::pointer;
	using const_pointer     = typename _Storage_type::const_pointer;
	using size_type         = typename _Storage_type::size_type;
	using difference_type   = typename _Storage_type::difference_type;
	using allocator_type    = typename _Storage_type::allocator_type;

private:
	size_type       _height;
	size_type       _width;
	size_type       _ldim;
	_Storage_type   _storage;

	static constexpr auto round_up(size_type const n) -> size_type
	{
		constexpr auto multiple = _Align / std::alignment_of<_Tp>::value;
		return ((n + multiple - 1) / multiple) * multiple;
	}

public:

	///////////////////////////////////////////////////////////////////////////
	/// \brief Default constructor.
	///////////////////////////////////////////////////////////////////////////
	Matrix()
		noexcept(std::is_nothrow_default_constructible<_Storage_type>::value)
		: _height{ 0 }
		, _width{ 0 }
		, _ldim{ 0 }
	    , _storage{}
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Move constructor.
	///////////////////////////////////////////////////////////////////////////
	Matrix(Matrix && x)
		noexcept(std::is_nothrow_move_constructible<_Storage_type>::value)
		: _height{ 0 }
		, _width{ 0 }
		, _ldim{ 0 }
	    , _storage{ std::move(x._storage) }
	{
		using std::swap;
		swap(_height, x._height);
		swap(_width, x._width);
		swap(_ldim, x._ldim);
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Copy constructor.
	///////////////////////////////////////////////////////////////////////////
	Matrix(Matrix const& x)
		noexcept(std::is_nothrow_copy_constructible<_Storage_type>::value)
		: _height{ x._height }
		, _width{ x._width }
		, _ldim{ x._ldim }
	    , _storage{ x._storage }
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Constructs a Matrix of given dimensions.

	/// \param height Height of the matrix, i.e. number of rows.
	/// \param width  Width of the matrix, i.e. number of columns.
	///
	/// \sa Matrix(size_type const, size_type const, difference_type const).
	///////////////////////////////////////////////////////////////////////////
	Matrix(size_type const height, size_type const width)
		: _height{ height }
		, _width{ width }
		, _ldim{ height }
		// , _ldim{ round_up(height) }
		, _storage{ height * width }
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Destructor.
	///////////////////////////////////////////////////////////////////////////
	~Matrix()
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// Move assignment operator.

	/// No copy assignment operator is provided
	/// to make it less convenient to perform copies all the time.
	///////////////////////////////////////////////////////////////////////////
	auto operator=(Matrix && other) noexcept -> Matrix &
	{ swap(*this, other);
	  return *this;
	}

	///////////////////////////////////////////////////////////////////////////
	/// Returns the height of the matrix.

	/// \sa width(), ldim().
	///////////////////////////////////////////////////////////////////////////
	constexpr auto height() const noexcept -> size_type
	{ return _height; }

	///////////////////////////////////////////////////////////////////////////
	/// Returns the width of the matrix.

	/// \sa height(), ldim().
	///////////////////////////////////////////////////////////////////////////
	constexpr auto width() const noexcept -> size_type
	{ return _width; }

	///////////////////////////////////////////////////////////////////////////
	/// Returns the leading dimension of the matrix.

	/// \sa width(), height().
	///////////////////////////////////////////////////////////////////////////
	constexpr auto ldim() const noexcept -> size_type
	{ return _ldim; }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a const pointer to the underlying one-dimensional array
	/// of elements.

	/// \sa data()
	///////////////////////////////////////////////////////////////////////////
	constexpr auto data() const noexcept -> const_pointer
	{ return _storage.data(); }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a non-const pointer to the underlying one-dimensional
	/// array of elements.

	/// \sa data() const
	///////////////////////////////////////////////////////////////////////////
	constexpr auto data() noexcept -> pointer
	{ return _storage.data(); }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a const pointer to the element at row \p i and
	/// column \p j.

	/// \se data(std::size_t const, std::size_t const)
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto data( size_type const i
	         , size_type const j ) const noexcept -> const_pointer
	{ return data() + (i + _ldim * j); }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a non-const pointer to the element at row \p i and
	/// column \p j.

	/// \se data(std::size_t const, std::size_t const) const
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto data( size_type const i
	         , size_type const j ) noexcept -> pointer
	{ return data() + (i + _ldim * j); }

	///////////////////////////////////////////////////////////////////////////
	/// Returns the element at row \p i and column \p j.

	/// \warning Mind you: not a const-reference, by the element by value!
	/// This is due to the fact that this class is meant to be used with
	/// elementary number types such as `float` or `int`.
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto operator() ( size_type const i
	                , size_type const j ) const noexcept -> value_type
	{ assert(i < height() and j < width() and "Index out of bounds.");
	  return *data(i, j);
	}

	///////////////////////////////////////////////////////////////////////////
	/// Returns a non-const reference to the element at row \p i and
	/// column \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto operator() ( size_type const i
	                , size_type const j ) noexcept -> reference
	{ assert(i < height() and j < width() and "Index out of bounds.");
	  return *data(i, j);
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant row iterator to the beginning of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cbegin_row(size_type const i) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return const_blas_iterator<value_type>{ data(i, 0),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating row iterator to the beginning of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto begin_row(size_type const i) noexcept
		-> blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return blas_iterator<value_type>{ data(i, 0),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant row iterator to the end of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cend_row(size_type const i) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return const_blas_iterator<value_type>{ data(i, width()),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating row iterator to the end of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto end_row(size_type const i) noexcept
		-> blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return blas_iterator<value_type>{ data(i, width()),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant column iterator to the beginning of column
	/// \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cbegin_column(size_type const j) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return {data(0, j), 1};
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating column iterator to the beginning of column
	/// \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto begin_column(size_type const j) noexcept
		-> blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return blas_iterator<value_type>{data(0, j), 1};
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant column iterator to the end of column \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cend_column(size_type const j) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return const_blas_iterator<value_type>{data(height(), j), 1};
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating column iterator to the end of column \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto end_column(size_type const j) noexcept
		-> blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return blas_iterator<value_type>{data(height(), j), 1};
	}

private:
	friend
	auto swap(Matrix<value_type>& lhs, Matrix<value_type>& rhs) noexcept
	{ using std::swap;
	  swap(lhs._storage, rhs._storage);
	  swap(lhs._height, rhs._height);
	  swap(lhs._width, rhs._width);
	  swap(lhs._ldim, rhs._ldim);
	}
};

///////////////////////////////////////////////////////////////////////////////
/// Returns true if the given matrix is just a row vector.
///////////////////////////////////////////////////////////////////////////////
template <class _M>
constexpr
auto is_row(_M const& A) noexcept -> bool { return A.height() == 1; }

///////////////////////////////////////////////////////////////////////////////
/// Returns true if the given matrix is just a column vector.
///////////////////////////////////////////////////////////////////////////////
template <class _M>
constexpr
auto is_column(_M const& A) noexcept -> bool { return A.width() == 1; }

///////////////////////////////////////////////////////////////////////////////
/// Returns true if the given matrix is a square matrix.
///////////////////////////////////////////////////////////////////////////////
template <class _M>
constexpr
auto is_square(_M const& A) noexcept -> bool {return A.width() == A.height();}

template <class _T, std::size_t _Align, class _Alloc>
auto operator<< (std::ostream& out, Matrix<_T, _Align, _Alloc> const& A)
	-> std::ostream&
{
	for (std::size_t i = 0; i < A.height(); ++i) {
		for (std::size_t j = 0; j < A.width(); ++j)
			out << A(i, j) << '\t';
		out << '\n';
	}
	return out;
}

template<class _T, std::size_t _Align, class _Alloc>
auto operator>> (std::istream& in, Matrix<_T, _Align, _Alloc>& A)
	-> std::istream&
{
	for (std::size_t i = 0; i < A.height(); ++i) {
		for (std::size_t j = 0; j < A.width(); ++j) {
			in >> A(i, j);
		}
	}
	return in;
}

template<class Func>
auto build_matrix( std::size_t const height, std::size_t const width
                 , Func f )
{
	using T = decltype( f( std::declval<std::size_t>()
	                     , std::declval<std::size_t>()
	                     ) );
	Matrix<T> result{height, width};
	for (std::size_t j = 0; j < width; ++j)
		for (std::size_t i = 0; i < height; ++i)
			result(i, j) = f(i, j);
	return result;
}

} // namespace tcm

#endif // TCM_MATRIX_HPP
<commit_msg>Dummy<commit_after>#ifndef TCM_MATRIX_HPP
#define TCM_MATRIX_HPP

#include <cassert>
#include <complex>
#include <memory>
#include <type_traits>
#include <algorithm>

#include <boost/align/aligned_allocator_adaptor.hpp>

#include <utils.hpp>
#include <iterator.hpp>

///////////////////////////////////////////////////////////////////////////////
/// \file matrix.hpp
/// \brief This file implements a Matrix class and some operations associated
///        with it.
///////////////////////////////////////////////////////////////////////////////

namespace tcm {

///////////////////////////////////////////////////////////////////////////////
/// \brief A simple wrapper around the (data, ldim, width) representation
///        of matrices used in LAPACK. Uses column-major ordering.

/// This is a _container_ class in the sense that it manages its own memory.
/// Matrix class is meant to be used with LAPACK/BLAS and thus focuses on
/// fundamental numeric types. There are two important differences
/// between this class and, for example, an %std::vector.
/// 1) As we plan to use matrices with LAPACK/BLAS routines, it helps
///    (from the performance point of view) to correctly align our matrices.
///    From Intel MKL's manual:
///    > To improve performance of your application that calls Intel MKL,
///    > align your arrays on 64-byte boundaries and ensure that the leading
///    > dimensions of the arrays are divisible by 64/element_size, where
///    > element_size is the number of bytes for the matrix elements
///    > (4 for single-precision real, 8 for double-precision real and
///    > single-precision complex, and 16 for double-precision complex).
/// 2) We don't initialise storage.
///
/// \tparam _Tp Element type of the matrix.
/// \tparam _Align Alignment. Must be a power of 2. This argument is used
///                for two things:
///                1) Allocation of storage that is aligned at least to _Alloc.
///                2) Computing the leading dimension of the matrix to ensure
///                   that `this->data(0, 1)` is again aligned to _Alloc.
///
///                We use %boost::alignment::aligned_allocator_adaptor
///                for this.
///
///////////////////////////////////////////////////////////////////////////////
template< class _Tp
        , std::size_t _Align = 64 // std::alignment_of<_Tp>::value
        , class _Alloc = std::allocator<_Tp>
        >
class Matrix {

private:
	using _Aligned_Alloc = boost::alignment::
		aligned_allocator_adaptor<_Alloc, _Align>;
	using _Storage_type = utils::_Storage<_Tp, _Aligned_Alloc>;

	static_assert( (_Align != 0) and ((_Align & (_Align - 1)) == 0)
	             , "_Align must be a power of 2" );
	static_assert( _Align % std::alignment_of<_Tp>::value == 0
	             , "Invalid alignment." );

public:
	using value_type        = _Tp;
	using reference         = typename _Storage_type::reference;
	using const_reference   = typename _Storage_type::const_reference;
	using pointer           = typename _Storage_type::pointer;
	using const_pointer     = typename _Storage_type::const_pointer;
	using size_type         = typename _Storage_type::size_type;
	using difference_type   = typename _Storage_type::difference_type;
	using allocator_type    = typename _Storage_type::allocator_type;

private:
	size_type       _height;
	size_type       _width;
	size_type       _ldim;
	_Storage_type   _storage;

	static constexpr auto round_up(size_type const n) -> size_type
	{
		constexpr auto multiple = _Align / std::alignment_of<_Tp>::value;
		return ((n + multiple - 1) / multiple) * multiple;
	}

public:

	///////////////////////////////////////////////////////////////////////////
	/// \brief Default constructor.
	///////////////////////////////////////////////////////////////////////////
	Matrix()
		noexcept(std::is_nothrow_default_constructible<_Storage_type>::value)
		: _height{ 0 }
		, _width{ 0 }
		, _ldim{ 0 }
	    , _storage{}
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Move constructor.
	///////////////////////////////////////////////////////////////////////////
	Matrix(Matrix && x)
		noexcept(std::is_nothrow_move_constructible<_Storage_type>::value)
		: _height{ 0 }
		, _width{ 0 }
		, _ldim{ 0 }
	    , _storage{ std::move(x._storage) }
	{
		using std::swap;
		swap(_height, x._height);
		swap(_width, x._width);
		swap(_ldim, x._ldim);
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Copy constructor.
	///////////////////////////////////////////////////////////////////////////
	Matrix(Matrix const& x)
		noexcept(std::is_nothrow_copy_constructible<_Storage_type>::value)
		: _height{ x._height }
		, _width{ x._width }
		, _ldim{ x._ldim }
	    , _storage{ x._storage }
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Constructs a Matrix of given dimensions.

	/// \param height Height of the matrix, i.e. number of rows.
	/// \param width  Width of the matrix, i.e. number of columns.
	///
	/// \sa Matrix(size_type const, size_type const, difference_type const).
	///////////////////////////////////////////////////////////////////////////
	Matrix(size_type const height, size_type const width)
		: _height{ height }
		, _width{ width }
		// , _ldim{ height }
		, _ldim{ round_up(height) }
		, _storage{ height * width }
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Destructor.
	///////////////////////////////////////////////////////////////////////////
	~Matrix()
	{
	}

	///////////////////////////////////////////////////////////////////////////
	/// Move assignment operator.

	/// No copy assignment operator is provided
	/// to make it less convenient to perform copies all the time.
	///////////////////////////////////////////////////////////////////////////
	auto operator=(Matrix && other) noexcept -> Matrix &
	{ swap(*this, other);
	  return *this;
	}

	///////////////////////////////////////////////////////////////////////////
	/// Returns the height of the matrix.

	/// \sa width(), ldim().
	///////////////////////////////////////////////////////////////////////////
	constexpr auto height() const noexcept -> size_type
	{ return _height; }

	///////////////////////////////////////////////////////////////////////////
	/// Returns the width of the matrix.

	/// \sa height(), ldim().
	///////////////////////////////////////////////////////////////////////////
	constexpr auto width() const noexcept -> size_type
	{ return _width; }

	///////////////////////////////////////////////////////////////////////////
	/// Returns the leading dimension of the matrix.

	/// \sa width(), height().
	///////////////////////////////////////////////////////////////////////////
	constexpr auto ldim() const noexcept -> size_type
	{ return _ldim; }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a const pointer to the underlying one-dimensional array
	/// of elements.

	/// \sa data()
	///////////////////////////////////////////////////////////////////////////
	constexpr auto data() const noexcept -> const_pointer
	{ return _storage.data(); }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a non-const pointer to the underlying one-dimensional
	/// array of elements.

	/// \sa data() const
	///////////////////////////////////////////////////////////////////////////
	constexpr auto data() noexcept -> pointer
	{ return _storage.data(); }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a const pointer to the element at row \p i and
	/// column \p j.

	/// \se data(std::size_t const, std::size_t const)
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto data( size_type const i
	         , size_type const j ) const noexcept -> const_pointer
	{ return data() + (i + _ldim * j); }

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a non-const pointer to the element at row \p i and
	/// column \p j.

	/// \se data(std::size_t const, std::size_t const) const
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto data( size_type const i
	         , size_type const j ) noexcept -> pointer
	{ return data() + (i + _ldim * j); }

	///////////////////////////////////////////////////////////////////////////
	/// Returns the element at row \p i and column \p j.

	/// \warning Mind you: not a const-reference, by the element by value!
	/// This is due to the fact that this class is meant to be used with
	/// elementary number types such as `float` or `int`.
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto operator() ( size_type const i
	                , size_type const j ) const noexcept -> value_type
	{ assert(i < height() and j < width() and "Index out of bounds.");
	  return *data(i, j);
	}

	///////////////////////////////////////////////////////////////////////////
	/// Returns a non-const reference to the element at row \p i and
	/// column \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr
	auto operator() ( size_type const i
	                , size_type const j ) noexcept -> reference
	{ assert(i < height() and j < width() and "Index out of bounds.");
	  return *data(i, j);
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant row iterator to the beginning of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cbegin_row(size_type const i) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return const_blas_iterator<value_type>{ data(i, 0),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating row iterator to the beginning of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto begin_row(size_type const i) noexcept
		-> blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return blas_iterator<value_type>{ data(i, 0),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant row iterator to the end of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cend_row(size_type const i) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return const_blas_iterator<value_type>{ data(i, width()),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating row iterator to the end of row \p i.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto end_row(size_type const i) noexcept
		-> blas_iterator<value_type>
	{ assert(i < height() and "Index out of bounds.");
	  return blas_iterator<value_type>{ data(i, width()),
	      static_cast<difference_type>(ldim()) };
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant column iterator to the beginning of column
	/// \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cbegin_column(size_type const j) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return {data(0, j), 1};
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating column iterator to the beginning of column
	/// \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto begin_column(size_type const j) noexcept
		-> blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return blas_iterator<value_type>{data(0, j), 1};
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a constant column iterator to the end of column \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto cend_column(size_type const j) const noexcept
		-> const_blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return const_blas_iterator<value_type>{data(height(), j), 1};
	}

	///////////////////////////////////////////////////////////////////////////
	/// \brief Returns a mutating column iterator to the end of column \p j.
	///////////////////////////////////////////////////////////////////////////
	constexpr auto end_column(size_type const j) noexcept
		-> blas_iterator<value_type>
	{ assert(j < width() and "Index out of bounds.");
	  return blas_iterator<value_type>{data(height(), j), 1};
	}

private:
	friend
	auto swap(Matrix<value_type>& lhs, Matrix<value_type>& rhs) noexcept
	{ using std::swap;
	  swap(lhs._storage, rhs._storage);
	  swap(lhs._height, rhs._height);
	  swap(lhs._width, rhs._width);
	  swap(lhs._ldim, rhs._ldim);
	}
};

///////////////////////////////////////////////////////////////////////////////
/// Returns true if the given matrix is just a row vector.
///////////////////////////////////////////////////////////////////////////////
template <class _M>
constexpr
auto is_row(_M const& A) noexcept -> bool { return A.height() == 1; }

///////////////////////////////////////////////////////////////////////////////
/// Returns true if the given matrix is just a column vector.
///////////////////////////////////////////////////////////////////////////////
template <class _M>
constexpr
auto is_column(_M const& A) noexcept -> bool { return A.width() == 1; }

///////////////////////////////////////////////////////////////////////////////
/// Returns true if the given matrix is a square matrix.
///////////////////////////////////////////////////////////////////////////////
template <class _M>
constexpr
auto is_square(_M const& A) noexcept -> bool {return A.width() == A.height();}

template <class _T, std::size_t _Align, class _Alloc>
auto operator<< (std::ostream& out, Matrix<_T, _Align, _Alloc> const& A)
	-> std::ostream&
{
	for (std::size_t i = 0; i < A.height(); ++i) {
		for (std::size_t j = 0; j < A.width(); ++j)
			out << A(i, j) << '\t';
		out << '\n';
	}
	return out;
}

template<class _T, std::size_t _Align, class _Alloc>
auto operator>> (std::istream& in, Matrix<_T, _Align, _Alloc>& A)
	-> std::istream&
{
	for (std::size_t i = 0; i < A.height(); ++i) {
		for (std::size_t j = 0; j < A.width(); ++j) {
			in >> A(i, j);
		}
	}
	return in;
}

template<class Func>
auto build_matrix( std::size_t const height, std::size_t const width
                 , Func f )
{
	using T = decltype( f( std::declval<std::size_t>()
	                     , std::declval<std::size_t>()
	                     ) );
	Matrix<T> result{height, width};
	for (std::size_t j = 0; j < width; ++j)
		for (std::size_t i = 0; i < height; ++i)
			result(i, j) = f(i, j);
	return result;
}

} // namespace tcm

#endif // TCM_MATRIX_HPP
<|endoftext|>