<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparamsbase.h"
#include "clientversion.h"
#include "rpcclient.h"
#include "rpcprotocol.h"
#include "util.h"
#include "utilstrencodings.h"

#include <boost/filesystem/operations.hpp>
#include <stdio.h>

#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>

#include "univalue/univalue.h"

using namespace std;

static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;

std::string HelpMessageCli()
{
    string strUsage;
    strUsage += HelpMessageGroup(_("Options:"));
    strUsage += HelpMessageOpt("-?", _("This help message"));
    strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
    strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
    AppendParamsHelpMessages(strUsage);
    strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
    strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
    strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
    strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));

    return strUsage;
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//

//
// Exception thrown on connection error.  This error is used to determine
// when to wait if -rpcwait is given.
//
class CConnectionFailed : public std::runtime_error
{
public:

    explicit inline CConnectionFailed(const std::string& msg) :
        std::runtime_error(msg)
    {}

};

static bool AppInitRPC(int argc, char* argv[])
{
    //
    // Parameters
    //
    ParseParameters(argc, argv);
    if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
        std::string strUsage = _("Bitcoin XT RPC client version") + " " + FormatFullVersion() + "\n";
        if (!mapArgs.count("-version")) {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoin-cli [options] <command> [params]  " + _("Send command to Bitcoin XT") + "\n" +
                  "  bitcoin-cli [options] help                " + _("List commands") + "\n" +
                  "  bitcoin-cli [options] help <command>      " + _("Get help for a command") + "\n";

            strUsage += "\n" + HelpMessageCli();
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return false;
    }
    if (!boost::filesystem::is_directory(GetDataDir(false))) {
        fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
        return false;
    }
    try {
        ReadConfigFile(mapArgs, mapMultiArgs);
    } catch (const std::exception& e) {
        fprintf(stderr,"Error reading configuration file: %s\n", e.what());
        return false;
    }
    // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
    try {
        SelectBaseParams(ChainNameFromCommandLine());
    } catch(std::exception &e) {
        fprintf(stderr, "Error: %s\n", e.what());
        return false;
    }
    if (GetBoolArg("-rpcssl", false))
    {
        fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
        return false;
    }
    return true;
}

/** Reply structure for request_done to fill in */
struct HTTPReply
{
    HTTPReply(): status(0), error(-1) {}

    int status;
    int error;
    std::string body;
};

const char *http_errorstring(int code)
{
    switch(code) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
    case EVREQ_HTTP_TIMEOUT:
        return "timeout reached";
    case EVREQ_HTTP_EOF:
        return "EOF reached";
    case EVREQ_HTTP_INVALID_HEADER:
        return "error while reading header, or invalid header";
    case EVREQ_HTTP_BUFFER_ERROR:
        return "error encountered while reading or writing";
    case EVREQ_HTTP_REQUEST_CANCEL:
        return "request was canceled";
    case EVREQ_HTTP_DATA_TOO_LONG:
        return "response body is larger than allowed";
#endif
    default:
        return "unknown";
    }
}

static void http_request_done(struct evhttp_request *req, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);

    if (req == NULL) {
        /* If req is NULL, it means an error occurred while connecting: the
         * error code will have been passed to http_error_cb.
         */
        reply->status = 0;
        return;
    }

    reply->status = evhttp_request_get_response_code(req);

    struct evbuffer *buf = evhttp_request_get_input_buffer(req);
    if (buf)
    {
        size_t size = evbuffer_get_length(buf);
        const char *data = (const char*)evbuffer_pullup(buf, size);
        if (data)
            reply->body = std::string(data, size);
        evbuffer_drain(buf, size);
    }
}

#if LIBEVENT_VERSION_NUMBER >= 0x02010300
static void http_error_cb(enum evhttp_request_error err, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);
    reply->error = err;
}
#endif

UniValue CallRPC(const string& strMethod, const UniValue& params)
{
    std::string host = GetArg("-rpcconnect", "127.0.0.1");
    int port = GetArg("-rpcport", BaseParams().RPCPort());

    // Create event base
    struct event_base *base = event_base_new(); // TODO RAII
    if (!base)
        throw runtime_error("cannot create event_base");

    // Synchronously look up hostname
    struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
    if (evcon == NULL)
        throw runtime_error("create connection failed");
    evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));

    HTTPReply response;
    struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
    if (req == NULL)
        throw runtime_error("create http request failed");
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
    evhttp_request_set_error_cb(req, http_error_cb);
#endif

    // Get credentials
    std::string strRPCUserColonPass;
    if (mapArgs["-rpcpassword"] == "") {
        throw runtime_error(strprintf(
            _("rpcpassword is not set in the configuration file (%s)"),
                GetConfigFile().string().c_str()));
    } else {
        strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
    }

    struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
    assert(output_headers);
    evhttp_add_header(output_headers, "Host", host.c_str());
    evhttp_add_header(output_headers, "Connection", "close");
    evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());

    // Attach request data
    std::string strRequest = JSONRPCRequest(strMethod, params, 1);
    struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
    assert(output_buffer);
    evbuffer_add(output_buffer, strRequest.data(), strRequest.size());

    int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
    if (r != 0) {
        evhttp_connection_free(evcon);
        event_base_free(base);
        throw CConnectionFailed("send http request failed");
    }

    event_base_dispatch(base);
    evhttp_connection_free(evcon);
    event_base_free(base);

    if (response.status == 0)
        throw CConnectionFailed(strprintf("couldn't connect to server\n(make sure server is running and you are connecting to the correct RPC port: %d %s)", response.error, http_errorstring(response.error)));
    else if (response.status == HTTP_UNAUTHORIZED)
        throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
    else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
        throw runtime_error(strprintf("server returned HTTP error %d", response.status));
    else if (response.body.empty())
        throw runtime_error("no response from server");

    // Parse reply
    UniValue valReply(UniValue::VSTR);
    if (!valReply.read(response.body))
        throw runtime_error("couldn't parse reply from server");
    const UniValue& reply = valReply.get_obj();
    if (reply.empty())
        throw runtime_error("expected reply to have result, error and id properties");

    return reply;
}

int CommandLineRPC(int argc, char *argv[])
{
    string strPrint;
    int nRet = 0;
    try {
        // Skip switches
        while (argc > 1 && IsSwitchChar(argv[1][0])) {
            argc--;
            argv++;
        }

        // Method
        if (argc < 2)
            throw runtime_error("too few parameters");
        string strMethod = argv[1];

        // Parameters default to strings
        std::vector<std::string> strParams(&argv[2], &argv[argc]);
        UniValue params = RPCConvertValues(strMethod, strParams);

        // Execute and handle connection failures with -rpcwait
        const bool fWait = GetBoolArg("-rpcwait", false);
        do {
            try {
                const UniValue reply = CallRPC(strMethod, params);

                // Parse reply
                const UniValue& result = find_value(reply, "result");
                const UniValue& error  = find_value(reply, "error");

                if (!error.isNull()) {
                    // Error
                    int code = error["code"].get_int();
                    if (fWait && code == RPC_IN_WARMUP)
                        throw CConnectionFailed("server in warmup");
                    strPrint = "error: " + error.write();
                    nRet = abs(code);
                } else {
                    // Result
                    if (result.isNull())
                        strPrint = "";
                    else if (result.isStr())
                        strPrint = result.get_str();
                    else
                        strPrint = result.write(2);
                }
                // Connection succeeded, no need to retry.
                break;
            }
            catch (const CConnectionFailed&) {
                if (fWait)
                    MilliSleep(1000);
                else
                    throw;
            }
        } while (fWait);
    }
    catch (const boost::thread_interrupted&) {
        throw;
    }
    catch (const std::exception& e) {
        strPrint = string("error: ") + e.what();
        nRet = EXIT_FAILURE;
    }
    catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
        throw;
    }

    if (strPrint != "") {
        fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
    }
    return nRet;
}

int main(int argc, char* argv[])
{
    SetupEnvironment();
    if (!SetupNetworking()) {
        fprintf(stderr, "Error: Initializing networking failed\n");
        exit(1);
    }

    try {
        if(!AppInitRPC(argc, argv))
            return EXIT_FAILURE;
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInitRPC()");
        return EXIT_FAILURE;
    } catch (...) {
        PrintExceptionContinue(NULL, "AppInitRPC()");
        return EXIT_FAILURE;
    }

    int ret = EXIT_FAILURE;
    try {
        ret = CommandLineRPC(argc, argv);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "CommandLineRPC()");
    } catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
    }
    return ret;
}
<commit_msg>bitcoin-cli: Make error message less confusing<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparamsbase.h"
#include "clientversion.h"
#include "rpcclient.h"
#include "rpcprotocol.h"
#include "util.h"
#include "utilstrencodings.h"

#include <boost/filesystem/operations.hpp>
#include <stdio.h>

#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>

#include "univalue/univalue.h"

using namespace std;

static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;

std::string HelpMessageCli()
{
    string strUsage;
    strUsage += HelpMessageGroup(_("Options:"));
    strUsage += HelpMessageOpt("-?", _("This help message"));
    strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
    strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
    AppendParamsHelpMessages(strUsage);
    strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
    strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
    strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
    strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
    strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));

    return strUsage;
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//

//
// Exception thrown on connection error.  This error is used to determine
// when to wait if -rpcwait is given.
//
class CConnectionFailed : public std::runtime_error
{
public:

    explicit inline CConnectionFailed(const std::string& msg) :
        std::runtime_error(msg)
    {}

};

static bool AppInitRPC(int argc, char* argv[])
{
    //
    // Parameters
    //
    ParseParameters(argc, argv);
    if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
        std::string strUsage = _("Bitcoin XT RPC client version") + " " + FormatFullVersion() + "\n";
        if (!mapArgs.count("-version")) {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoin-cli [options] <command> [params]  " + _("Send command to Bitcoin XT") + "\n" +
                  "  bitcoin-cli [options] help                " + _("List commands") + "\n" +
                  "  bitcoin-cli [options] help <command>      " + _("Get help for a command") + "\n";

            strUsage += "\n" + HelpMessageCli();
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return false;
    }
    if (!boost::filesystem::is_directory(GetDataDir(false))) {
        fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
        return false;
    }
    try {
        ReadConfigFile(mapArgs, mapMultiArgs);
    } catch (const std::exception& e) {
        fprintf(stderr,"Error reading configuration file: %s\n", e.what());
        return false;
    }
    // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
    try {
        SelectBaseParams(ChainNameFromCommandLine());
    } catch(std::exception &e) {
        fprintf(stderr, "Error: %s\n", e.what());
        return false;
    }
    if (GetBoolArg("-rpcssl", false))
    {
        fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
        return false;
    }
    return true;
}

/** Reply structure for request_done to fill in */
struct HTTPReply
{
    HTTPReply(): status(0), error(-1) {}

    int status;
    int error;
    std::string body;
};

const char *http_errorstring(int code)
{
    switch(code) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
    case EVREQ_HTTP_TIMEOUT:
        return "timeout reached";
    case EVREQ_HTTP_EOF:
        return "EOF reached";
    case EVREQ_HTTP_INVALID_HEADER:
        return "error while reading header, or invalid header";
    case EVREQ_HTTP_BUFFER_ERROR:
        return "error encountered while reading or writing";
    case EVREQ_HTTP_REQUEST_CANCEL:
        return "request was canceled";
    case EVREQ_HTTP_DATA_TOO_LONG:
        return "response body is larger than allowed";
#endif
    default:
        return "unknown";
    }
}

static void http_request_done(struct evhttp_request *req, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);

    if (req == NULL) {
        /* If req is NULL, it means an error occurred while connecting: the
         * error code will have been passed to http_error_cb.
         */
        reply->status = 0;
        return;
    }

    reply->status = evhttp_request_get_response_code(req);

    struct evbuffer *buf = evhttp_request_get_input_buffer(req);
    if (buf)
    {
        size_t size = evbuffer_get_length(buf);
        const char *data = (const char*)evbuffer_pullup(buf, size);
        if (data)
            reply->body = std::string(data, size);
        evbuffer_drain(buf, size);
    }
}

#if LIBEVENT_VERSION_NUMBER >= 0x02010300
static void http_error_cb(enum evhttp_request_error err, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);
    reply->error = err;
}
#endif

UniValue CallRPC(const string& strMethod, const UniValue& params)
{
    std::string host = GetArg("-rpcconnect", "127.0.0.1");
    int port = GetArg("-rpcport", BaseParams().RPCPort());

    // Create event base
    struct event_base *base = event_base_new(); // TODO RAII
    if (!base)
        throw runtime_error("cannot create event_base");

    // Synchronously look up hostname
    struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
    if (evcon == NULL)
        throw runtime_error("create connection failed");
    evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));

    HTTPReply response;
    struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
    if (req == NULL)
        throw runtime_error("create http request failed");
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
    evhttp_request_set_error_cb(req, http_error_cb);
#endif

    // Get credentials
    std::string strRPCUserColonPass;
    if (mapArgs["-rpcpassword"] == "") {
        throw runtime_error(strprintf(
            _("rpcpassword is not set in the configuration file (%s)"),
                GetConfigFile().string().c_str()));
    } else {
        strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
    }

    struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
    assert(output_headers);
    evhttp_add_header(output_headers, "Host", host.c_str());
    evhttp_add_header(output_headers, "Connection", "close");
    evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());

    // Attach request data
    std::string strRequest = JSONRPCRequest(strMethod, params, 1);
    struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
    assert(output_buffer);
    evbuffer_add(output_buffer, strRequest.data(), strRequest.size());

    int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
    if (r != 0) {
        evhttp_connection_free(evcon);
        event_base_free(base);
        throw CConnectionFailed("send http request failed");
    }

    event_base_dispatch(base);
    evhttp_connection_free(evcon);
    event_base_free(base);

    if (response.status == 0)
        throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
    else if (response.status == HTTP_UNAUTHORIZED)
        throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
    else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
        throw runtime_error(strprintf("server returned HTTP error %d", response.status));
    else if (response.body.empty())
        throw runtime_error("no response from server");

    // Parse reply
    UniValue valReply(UniValue::VSTR);
    if (!valReply.read(response.body))
        throw runtime_error("couldn't parse reply from server");
    const UniValue& reply = valReply.get_obj();
    if (reply.empty())
        throw runtime_error("expected reply to have result, error and id properties");

    return reply;
}

int CommandLineRPC(int argc, char *argv[])
{
    string strPrint;
    int nRet = 0;
    try {
        // Skip switches
        while (argc > 1 && IsSwitchChar(argv[1][0])) {
            argc--;
            argv++;
        }

        // Method
        if (argc < 2)
            throw runtime_error("too few parameters");
        string strMethod = argv[1];

        // Parameters default to strings
        std::vector<std::string> strParams(&argv[2], &argv[argc]);
        UniValue params = RPCConvertValues(strMethod, strParams);

        // Execute and handle connection failures with -rpcwait
        const bool fWait = GetBoolArg("-rpcwait", false);
        do {
            try {
                const UniValue reply = CallRPC(strMethod, params);

                // Parse reply
                const UniValue& result = find_value(reply, "result");
                const UniValue& error  = find_value(reply, "error");

                if (!error.isNull()) {
                    // Error
                    int code = error["code"].get_int();
                    if (fWait && code == RPC_IN_WARMUP)
                        throw CConnectionFailed("server in warmup");
                    strPrint = "error: " + error.write();
                    nRet = abs(code);
                } else {
                    // Result
                    if (result.isNull())
                        strPrint = "";
                    else if (result.isStr())
                        strPrint = result.get_str();
                    else
                        strPrint = result.write(2);
                }
                // Connection succeeded, no need to retry.
                break;
            }
            catch (const CConnectionFailed&) {
                if (fWait)
                    MilliSleep(1000);
                else
                    throw;
            }
        } while (fWait);
    }
    catch (const boost::thread_interrupted&) {
        throw;
    }
    catch (const std::exception& e) {
        strPrint = string("error: ") + e.what();
        nRet = EXIT_FAILURE;
    }
    catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
        throw;
    }

    if (strPrint != "") {
        fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
    }
    return nRet;
}

int main(int argc, char* argv[])
{
    SetupEnvironment();
    if (!SetupNetworking()) {
        fprintf(stderr, "Error: Initializing networking failed\n");
        exit(1);
    }

    try {
        if(!AppInitRPC(argc, argv))
            return EXIT_FAILURE;
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInitRPC()");
        return EXIT_FAILURE;
    } catch (...) {
        PrintExceptionContinue(NULL, "AppInitRPC()");
        return EXIT_FAILURE;
    }

    int ret = EXIT_FAILURE;
    try {
        ret = CommandLineRPC(argc, argv);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "CommandLineRPC()");
    } catch (...) {
        PrintExceptionContinue(NULL, "CommandLineRPC()");
    }
    return ret;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include <bitset>         // for std::bitset
#include <utility>        // for std::make_pair

#include "hashes.hh"

template <size_t N=8192>
class BloomFilter {
	// [http://blog.michaelschmatz.com/2016/04/11/how-to-write-a-bloom-filter-cpp/]
	// [http://citeseer.ist.psu.edu/viewdoc/download;jsessionid=4060353E67A356EF9528D2C57C064F5A?doi=10.1.1.152.579&rep=rep1&type=pdf]
	// P = 0.000001
	// k = -ln(P) / ln(2)
	// k = 13.815510558 / 0.6931471806
	// k = 19.9315685693
	// k = 20
	// m = N * k / ln(2)
	// m = N * 19.9315685693 / 0.6931471806
	// m = N * 28.7551751321
	// m = N * 32
	// m = 8192 * 32
	// m = 262144 bits => 32768 bytes => 32K
	static constexpr size_t k = 20;
	static constexpr size_t m = N * 32;
	std::bitset<m> bits;

	auto hash(const char* data, size_t len) const {
		return std::make_pair(
			xxh64::hash(data, len),
			fnv1ah64::hash(data, len)
		);
	}

public:
	void add(const char* data, size_t len) {
		auto hashes = hash(data, len);
		for (auto n = k; n; --n) {
			bits[(hashes.first + n * hashes.second) % m] = true;
		}
	}

	bool contains(const char* data, size_t len) const {
		auto hashes = hash(data, len);
		for (auto n = k; n; --n) {
			if (!bits[(hashes.first + n * hashes.second) % m]) {
				return false;
			}
		}
		return true;
	}
};
<commit_msg>BloomFilter: Added salt for hashes<commit_after>/*
 * Copyright (C) 2018,2019 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include <bitset>         // for std::bitset
#include <utility>        // for std::make_pair

#include "cassert.h"      // for ASSERT
#include "hashes.hh"      // for xxh64::hash, fnv1ah64::hash

template <size_t N=8192>
class BloomFilter {
	// [http://blog.michaelschmatz.com/2016/04/11/how-to-write-a-bloom-filter-cpp/]
	// [http://citeseer.ist.psu.edu/viewdoc/download;jsessionid=4060353E67A356EF9528D2C57C064F5A?doi=10.1.1.152.579&rep=rep1&type=pdf]
	// P = 0.000001
	// k = -ln(P) / ln(2)
	// k = 13.815510558 / 0.6931471806
	// k = 19.9315685693
	// k = 20
	// m = N * k / ln(2)
	// m = N * 19.9315685693 / 0.6931471806
	// m = N * 28.7551751321
	// m = N * 32
	// m = 8192 * 32
	// m = 262144 bits => 32768 bytes => 32K
	static constexpr size_t k = 20;
	static constexpr size_t m = N * 32;
	std::bitset<m> bits;

	auto hash(const char* data, size_t len, uint64_t salt) const {
		ASSERT(salt);
		return std::make_pair(
			xxh64::hash(data, len),
			fnv1ah64::hash(data, len) * salt
		);
	}

public:
	void add(const char* data, size_t len, uint64_t salt = 1) {
		auto hashes = hash(data, len, salt);
		for (auto n = k; n; --n) {
			bits[(hashes.first + n * hashes.second) % m] = true;
		}
	}

	bool contains(const char* data, size_t len, uint64_t salt = 1) const {
		auto hashes = hash(data, len, salt);
		for (auto n = k; n; --n) {
			if (!bits[(hashes.first + n * hashes.second) % m]) {
				return false;
			}
		}
		return true;
	}
};
<|endoftext|>


<commit_before>/*
*  A subfilter of banjax that publishes all log information for each
*  request in a zmq socket so botbanger-python can grab them and
*  does svm computation with them
*
*  Vmon: Oct 2013: Initial version.
*/
#include <string>
#include <list>
#include <vector>
#include <ctime>

#include <stdio.h>
#include <zmq.hpp>

#include <re2/re2.h> //google re2

#include <ts/ts.h>

using namespace std;

#include "banjax_common.h"
#include "util.h"
#include "bot_sniffer.h"
#include "ip_database.h"

#define VALID_OR_EMPTY(validity, part) ((validity & part) ? transaction_parts.at(part) : "")
/**
  Reads botbanger's port from the config
 */
void
BotSniffer::load_config()
{

  try
   {
     botbanger_port = cfg["botbanger_port"].as<unsigned int>();

     string passphrase = cfg["key"].as<std::string>();

     SHA256((const unsigned char*)passphrase.c_str(), passphrase.length(), encryption_key);

   }
   catch(YAML::RepresentationException& e)
     {
       TSDebug(BANJAX_PLUGIN_NAME, "Error loading bot sniffer conf [%s].", e.what());
       throw;
     }

   TSDebug(BANJAX_PLUGIN_NAME, "Connecting to botbanger server...");
   zmqsock.bind(("tcp://"+botbanger_server +":"+to_string(botbanger_port)).c_str());

}

FilterResponse BotSniffer::execute(const TransactionParts& transaction_parts)
{

  /*TSDebug("banjax", "sending log to botbanger");
  TSDebug("banjax", "ip = %s", cd->client_ip);
  TSDebug("banjax", "url = %s", cd->url);
  TSDebug("banjax", "ua = %s", cd->ua);
  TSDebug("banjax", "size = %d", (int) cd->request_len);
  TSDebug("banjax", "status = %d", stat);
  TSDebug("banjax", "protocol = %s", cd->protocol);
  TSDebug("banjax", "hit = %d", cd->hit);*/

  std::time_t rawtime;
  std::time(&rawtime);
  std::tm* timeinfo = std::gmtime(&rawtime);

  char time_buffer[80];
  std::strftime(time_buffer,80,"%Y-%m-%dT%H:%M:%S",timeinfo);

  uint64_t* cur_validity = (uint64_t*)transaction_parts.at(TransactionMuncher::VALIDITY_STAT).data();

  //TODO: This is a temp solution, we can't afford losing logs due
  //to failing acquiring the lock
  //really?
  TSDebug(BANJAX_PLUGIN_NAME, "locking the botsniffer socket...");
  if (TSMutexLockTry(bot_sniffer_mutex) == TS_SUCCESS) {

    string plaintext_log;
    send_zmq_mess(zmqsock, BOTBANGER_LOG, true);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::IP), true);
    plaintext_log += VALID_OR_EMPTY(*cur_validity, TransactionMuncher::IP);

    //send_zmq_mess(zmqsock, time_buffer, true);
    plaintext_log += "," + string(time_buffer);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::URL_WITH_HOST), true);
    plaintext_log += "," +  VALID_OR_EMPTY(*cur_validity, TransactionMuncher::URL_WITH_HOST);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::PROTOCOL), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::PROTOCOL);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::STATUS), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::STATUS);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::CONTENT_LENGTH), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::CONTENT_LENGTH);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::UA), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::UA);

    //send_zmq_mess(zmqsock, transaction_parts.count(TransactionMuncher::MISS) ? "MISS" : "HIT");
    plaintext_log += "," + transaction_parts.count(TransactionMuncher::MISS) ? string("MISS") : string("HIT");

    send_zmq_encrypted_message(zmqsock, plaintext_log, encryption_key);

    TSMutexUnlock(bot_sniffer_mutex);
  }
  //botbanger_interface.add_log(transaction_parts[IP], cd->url, cd->protocol, stat, (long) cd->request_len, cd->ua, cd->hit);
  //botbanger_interface.add_log(cd->client_ip, time_str, cd->url, protocol, status, size, cd->ua, hit);
  return FilterResponse(FilterResponse::GO_AHEAD_NO_COMMENT);

}

<commit_msg>fixed the bug in with + and ? operator rank in HIT/MISS str in Bot Sniffer<commit_after>/*
*  A subfilter of banjax that publishes all log information for each
*  request in a zmq socket so botbanger-python can grab them and
*  does svm computation with them
*
*  Vmon: Oct 2013: Initial version.
*/
#include <string>
#include <list>
#include <vector>
#include <ctime>

#include <stdio.h>
#include <zmq.hpp>

#include <re2/re2.h> //google re2

#include <ts/ts.h>

using namespace std;

#include "banjax_common.h"
#include "util.h"
#include "bot_sniffer.h"
#include "ip_database.h"

#define VALID_OR_EMPTY(validity, part) ((validity & part) ? transaction_parts.at(part) : "")
/**
  Reads botbanger's port from the config
 */
void
BotSniffer::load_config()
{

  try
   {
     botbanger_port = cfg["botbanger_port"].as<unsigned int>();

     string passphrase = cfg["key"].as<std::string>();

     SHA256((const unsigned char*)passphrase.c_str(), passphrase.length(), encryption_key);

   }
   catch(YAML::RepresentationException& e)
     {
       TSDebug(BANJAX_PLUGIN_NAME, "Error loading bot sniffer conf [%s].", e.what());
       throw;
     }

   TSDebug(BANJAX_PLUGIN_NAME, "Connecting to botbanger server...");
   zmqsock.bind(("tcp://"+botbanger_server +":"+to_string(botbanger_port)).c_str());

}

FilterResponse BotSniffer::execute(const TransactionParts& transaction_parts)
{

  /*TSDebug("banjax", "sending log to botbanger");
  TSDebug("banjax", "ip = %s", cd->client_ip);
  TSDebug("banjax", "url = %s", cd->url);
  TSDebug("banjax", "ua = %s", cd->ua);
  TSDebug("banjax", "size = %d", (int) cd->request_len);
  TSDebug("banjax", "status = %d", stat);
  TSDebug("banjax", "protocol = %s", cd->protocol);
  TSDebug("banjax", "hit = %d", cd->hit);*/

  std::time_t rawtime;
  std::time(&rawtime);
  std::tm* timeinfo = std::gmtime(&rawtime);

  char time_buffer[80];
  std::strftime(time_buffer,80,"%Y-%m-%dT%H:%M:%S",timeinfo);

  uint64_t* cur_validity = (uint64_t*)transaction_parts.at(TransactionMuncher::VALIDITY_STAT).data();

  //TODO: This is a temp solution, we can't afford losing logs due
  //to failing acquiring the lock
  //really?
  TSDebug(BANJAX_PLUGIN_NAME, "locking the botsniffer socket...");
  if (TSMutexLockTry(bot_sniffer_mutex) == TS_SUCCESS) {

    string plaintext_log;
    send_zmq_mess(zmqsock, BOTBANGER_LOG, true);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::IP), true);
    plaintext_log += VALID_OR_EMPTY(*cur_validity, TransactionMuncher::IP);

    //send_zmq_mess(zmqsock, time_buffer, true);
    plaintext_log += "," + string(time_buffer);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::URL_WITH_HOST), true);
    plaintext_log += "," +  VALID_OR_EMPTY(*cur_validity, TransactionMuncher::URL_WITH_HOST);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::PROTOCOL), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::PROTOCOL);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::STATUS), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::STATUS);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::CONTENT_LENGTH), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::CONTENT_LENGTH);

    //send_zmq_mess(zmqsock, VALID_OR_EMPTY(*cur_validity, TransactionMuncher::UA), true);
    plaintext_log += "," + VALID_OR_EMPTY(*cur_validity, TransactionMuncher::UA);

    //send_zmq_mess(zmqsock, transaction_parts.count(TransactionMuncher::MISS) ? "MISS" : "HIT");
    std::string hit_mis_str = (transaction_parts.count(TransactionMuncher::MISS) ? string("MISS") : string("HIT"));
    plaintext_log += "," + hit_mis_str;

    send_zmq_encrypted_message(zmqsock, plaintext_log, encryption_key);

    TSMutexUnlock(bot_sniffer_mutex);
  }
  //botbanger_interface.add_log(transaction_parts[IP], cd->url, cd->protocol, stat, (long) cd->request_len, cd->ua, cd->hit);
  //botbanger_interface.add_log(cd->client_ip, time_str, cd->url, protocol, status, size, cd->ua, hit);
  return FilterResponse(FilterResponse::GO_AHEAD_NO_COMMENT);

}

<|endoftext|>


<commit_before>/***************************************************************************
                          boundedfifo.cpp  -  description
                             -------------------
    begin                : sam mai 17 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.      *
*                                                                         *
* This software is published under the terms of the Apache Software       *
* License version 1.1, a copy of which has been included with this        *
* distribution in the LICENSE.txt file.                                   *
***************************************************************************/

#include <log4cxx/helpers/boundedfifo.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/exception.h>

using namespace log4cxx::helpers;
using namespace log4cxx::spi;

IMPLEMENT_LOG4CXX_OBJECT(BoundedFIFO)

BoundedFIFO::BoundedFIFO(int maxSize) : numElements(0), first(0), next(0)
{
	if(maxSize < 1)
	{
		tostringstream oss;
		oss << _T("The maxSize argument (") << maxSize
			<< _T(") is not a positive integer.");
		throw new IllegalArgumentException(oss.str());
	}
	buf = new LoggingEvent *[maxSize];
}

LoggingEvent * BoundedFIFO::get()
{
	if(numElements == 0)
	{
		return 0;
	}

	LoggingEvent * r = buf[first];
	buf[first] = 0;

	if(++first == maxSize)
	{
		first = 0;
	}

	numElements--;
	return r;
}

void BoundedFIFO::put(log4cxx::spi::LoggingEvent * o)
{
	if(numElements != maxSize)
	{
		buf[next] = o;
		if(++next == maxSize)
		{
			next = 0;
		}
		numElements++;
	}
}

void BoundedFIFO::resize(int newSize)
{
	synchronized sync(this);

	if(newSize == maxSize)
	{
		return;
	}

	LoggingEvent * * tmp = new LoggingEvent *[newSize];

	// we should not copy beyond the buf array
	int len1 = maxSize - first;

	// we should not copy beyond the tmp array
	len1 = min(len1, newSize);

	// er.. how much do we actually need to copy?
	// We should not copy more than the actual number of elements.
	len1 = min(len1, numElements);

	// Copy from buf starting a first, to tmp, starting at position 0, len1
	memcpy(tmp, buf + first, len1 * sizeof(LoggingEvent *));

	// Are there any uncopied elements and
	// is there still space in the new array?
	int len2 = 0;
	if((len1 < numElements) && (len1 < newSize))
	{
		len2 = numElements - len1;
		len2 = min(len2, newSize - len1);
		memcpy(tmp, buf + len1, len2 * sizeof(LoggingEvent *));
	}

	this->buf = tmp;
	this->maxSize = newSize;
	this->first=0;
	this->numElements = len1+len2;
	this->next = this->numElements;

	// this should never happen, but again, it just might.
	if(this->next == this->maxSize)
	{
		this->next = 0;
	}
}

<commit_msg>fixed AsynAppender problem<commit_after>/***************************************************************************
                          boundedfifo.cpp  -  description
                             -------------------
    begin                : sam mai 17 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.      *
*                                                                         *
* This software is published under the terms of the Apache Software       *
* License version 1.1, a copy of which has been included with this        *
* distribution in the LICENSE.txt file.                                   *
***************************************************************************/

#include <log4cxx/helpers/boundedfifo.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/exception.h>

using namespace log4cxx::helpers;
using namespace log4cxx::spi;

IMPLEMENT_LOG4CXX_OBJECT(BoundedFIFO)

BoundedFIFO::BoundedFIFO(int maxSize)
 : numElements(0), first(0), next(0), maxSize(maxSize)
{
	if(maxSize < 1)
	{
		tostringstream oss;
		oss << _T("The maxSize argument (") << maxSize
			<< _T(") is not a positive integer.");
		throw new IllegalArgumentException(oss.str());
	}
	buf = new LoggingEvent *[maxSize];
}

LoggingEvent * BoundedFIFO::get()
{
	if(numElements == 0)
	{
		return 0;
	}

	LoggingEvent * r = buf[first];
	buf[first] = 0;

	if(++first == maxSize)
	{
		first = 0;
	}

	numElements--;
	return r;
}

void BoundedFIFO::put(log4cxx::spi::LoggingEvent * o)
{
	if(numElements != maxSize)
	{
		buf[next] = o;
		if(++next == maxSize)
		{
			next = 0;
		}
		numElements++;
	}
}

void BoundedFIFO::resize(int newSize)
{
	synchronized sync(this);

	if(newSize == maxSize)
	{
		return;
	}

	LoggingEvent * * tmp = new LoggingEvent *[newSize];

	// we should not copy beyond the buf array
	int len1 = maxSize - first;

	// we should not copy beyond the tmp array
	len1 = min(len1, newSize);

	// er.. how much do we actually need to copy?
	// We should not copy more than the actual number of elements.
	len1 = min(len1, numElements);

	// Copy from buf starting a first, to tmp, starting at position 0, len1
	memcpy(tmp, buf + first, len1 * sizeof(LoggingEvent *));

	// Are there any uncopied elements and
	// is there still space in the new array?
	int len2 = 0;
	if((len1 < numElements) && (len1 < newSize))
	{
		len2 = numElements - len1;
		len2 = min(len2, newSize - len1);
		memcpy(tmp, buf + len1, len2 * sizeof(LoggingEvent *));
	}

	this->buf = tmp;
	this->maxSize = newSize;
	this->first=0;
	this->numElements = len1+len2;
	this->next = this->numElements;

	// this should never happen, but again, it just might.
	if(this->next == this->maxSize)
	{
		this->next = 0;
	}
}

<|endoftext|>


<commit_before>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _SRC_BOUNDOBJECT_HPP_
#define _SRC_BOUNDOBJECT_HPP_

#include <string>
#include <set>
#include <boost/thread/recursive_mutex.hpp>
#include <qimessaging/api.hpp>
#include <qimessaging/session.hpp>
#include <qimessaging/transportserver.hpp>
#include <qi/atomic.hpp>

namespace qi {

  class GenericObject;
  class ServiceDirectoryClient;

  // (service, linkId)
  struct RemoteLink
  {
    RemoteLink()
      : localLinkId(0)
      , event(0)
    {}
    RemoteLink(unsigned int localLinkId, unsigned int event)
    : localLinkId(localLinkId)
    , event(event) {}
    unsigned int localLinkId;
    unsigned int event;
  };

  class BoundObject {
  public:
    //Server Interface
    virtual void onMessage(const qi::Message &msg, TransportSocketPtr socket) = 0;
    virtual void onSocketDisconnected(qi::TransportSocketPtr socket, int error) = 0;
  };

  //Bound Object, represent an object bound on a server
  // this is not an object..
  class ServiceBoundObject : public BoundObject {
  public:
    ServiceBoundObject(unsigned int serviceId, qi::ObjectPtr obj, qi::MetaCallType mct = qi::MetaCallType_Queued);
    virtual ~ServiceBoundObject();

  public:
    //PUBLIC BOUND METHODS
    unsigned int   registerEvent(unsigned int serviceId, unsigned int eventId, unsigned int linkId);
    void           unregisterEvent(unsigned int serviceId, unsigned int eventId, unsigned int linkId);
    qi::MetaObject metaObject(unsigned int serviceId);

  public:
    inline qi::TransportSocketPtr currentSocket() const {
#ifndef NDEBUG
      if (_callType != MetaCallType_Direct)
        qiLogWarning("qi.boundobject") << " currentSocket() used but callType is not direct";
#endif
      return _currentSocket;
    }

  public:
    //BoundObject Interface
    virtual void onMessage(const qi::Message &msg, TransportSocketPtr socket);
    virtual void onSocketDisconnected(qi::TransportSocketPtr socket, int error);

  private:
    qi::ObjectPtr createServiceBoundObjectType(ServiceBoundObject *self);

  private:
    // remote link id -> local link id
    typedef std::map<unsigned int, RemoteLink>             ServiceLinks;
    typedef std::map<qi::TransportSocketPtr, ServiceLinks> BySocketServiceLinks;

    //Event handling (no lock needed)
    BySocketServiceLinks  _links;

  private:
    qi::TransportSocketPtr _currentSocket;
    unsigned int           _serviceId;
    qi::ObjectPtr          _object;
    qi::ObjectPtr          _self;
    qi::MetaCallType       _callType;
    boost::mutex           _mutex; // prevent parallel onMessage on self execution
  };

  typedef boost::shared_ptr<BoundObject> BoundObjectPtr;

  qi::BoundObjectPtr makeServiceBoundObjectPtr(unsigned int serviceId, qi::ObjectPtr object, qi::MetaCallType mct = qi::MetaCallType_Auto);

}

QI_TYPE_NOT_CONSTRUCTIBLE(qi::ServiceBoundObject);

#endif  // _SRC_SESSIONSERVER_HPP_
<commit_msg>BoundObject: Fix incorrect include.<commit_after>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _SRC_BOUNDOBJECT_HPP_
#define _SRC_BOUNDOBJECT_HPP_

#include <string>
#include <set>
#include <boost/thread/mutex.hpp>
#include <qimessaging/api.hpp>
#include <qimessaging/session.hpp>
#include <qimessaging/transportserver.hpp>
#include <qi/atomic.hpp>

namespace qi {

  class GenericObject;
  class ServiceDirectoryClient;

  // (service, linkId)
  struct RemoteLink
  {
    RemoteLink()
      : localLinkId(0)
      , event(0)
    {}
    RemoteLink(unsigned int localLinkId, unsigned int event)
    : localLinkId(localLinkId)
    , event(event) {}
    unsigned int localLinkId;
    unsigned int event;
  };

  class BoundObject {
  public:
    //Server Interface
    virtual void onMessage(const qi::Message &msg, TransportSocketPtr socket) = 0;
    virtual void onSocketDisconnected(qi::TransportSocketPtr socket, int error) = 0;
  };

  //Bound Object, represent an object bound on a server
  // this is not an object..
  class ServiceBoundObject : public BoundObject {
  public:
    ServiceBoundObject(unsigned int serviceId, qi::ObjectPtr obj, qi::MetaCallType mct = qi::MetaCallType_Queued);
    virtual ~ServiceBoundObject();

  public:
    //PUBLIC BOUND METHODS
    unsigned int   registerEvent(unsigned int serviceId, unsigned int eventId, unsigned int linkId);
    void           unregisterEvent(unsigned int serviceId, unsigned int eventId, unsigned int linkId);
    qi::MetaObject metaObject(unsigned int serviceId);

  public:
    inline qi::TransportSocketPtr currentSocket() const {
#ifndef NDEBUG
      if (_callType != MetaCallType_Direct)
        qiLogWarning("qi.boundobject") << " currentSocket() used but callType is not direct";
#endif
      return _currentSocket;
    }

  public:
    //BoundObject Interface
    virtual void onMessage(const qi::Message &msg, TransportSocketPtr socket);
    virtual void onSocketDisconnected(qi::TransportSocketPtr socket, int error);

  private:
    qi::ObjectPtr createServiceBoundObjectType(ServiceBoundObject *self);

  private:
    // remote link id -> local link id
    typedef std::map<unsigned int, RemoteLink>             ServiceLinks;
    typedef std::map<qi::TransportSocketPtr, ServiceLinks> BySocketServiceLinks;

    //Event handling (no lock needed)
    BySocketServiceLinks  _links;

  private:
    qi::TransportSocketPtr _currentSocket;
    unsigned int           _serviceId;
    qi::ObjectPtr          _object;
    qi::ObjectPtr          _self;
    qi::MetaCallType       _callType;
    boost::mutex           _mutex; // prevent parallel onMessage on self execution
  };

  typedef boost::shared_ptr<BoundObject> BoundObjectPtr;

  qi::BoundObjectPtr makeServiceBoundObjectPtr(unsigned int serviceId, qi::ObjectPtr object, qi::MetaCallType mct = qi::MetaCallType_Auto);

}

QI_TYPE_NOT_CONSTRUCTIBLE(qi::ServiceBoundObject);

#endif  // _SRC_SESSIONSERVER_HPP_
<|endoftext|>


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

#include <boost/python.hpp>
#include <boost/optional.hpp>

#include <nix.hpp>

#include <accessors.hpp>
#include <transmorgify.hpp>

//we want only the newest and freshest API
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <numpy/arrayobject.h>
#include <numpy/ndarrayobject.h>

#include <PyEntity.hpp>

#include <stdexcept>

using namespace nix;
using namespace boost::python;

namespace nixpy {

// Label

void setLabel(DataArray& da, const boost::optional<std::string>& label) {
    if (label)
        da.label(*label);
    else
        da.label(boost::none);
}

// Unit

void setUnit(DataArray& da, const boost::optional<std::string> &unit) {
    if (unit)
        da.unit(*unit);
    else
        da.unit(boost::none);
}

// Expansion origin

void setExpansionOrigin(DataArray& da, const boost::optional<double>& eo) {
    if (eo)
        da.expansionOrigin(*eo);
    else
        da.expansionOrigin(boost::none);
}

// Polynom coefficients

void setPolynomCoefficients(DataArray& da, const std::vector<double>& pc) {
    if (!pc.empty())
        da.polynomCoefficients(pc);
    else
        da.polynomCoefficients(boost::none);
}

// Data

static nix::DataType pyDtypeToNixDtype(const PyArray_Descr *dtype)
{
    if (dtype == nullptr) {
        return nix::DataType::Nothing;
    }

    if (dtype->byteorder != '=' && dtype->byteorder != '|') {
        //TODO: Handle case where specified byteorder *is*
        //the native byteorder (via BOOST_BIG_ENDIAN macros)
        return nix::DataType::Nothing;
    }

    switch (dtype->kind) {

    case 'u':
        switch (dtype->elsize) {
        case 1: return nix::DataType::UInt8;
        case 2: return nix::DataType::UInt16;
        case 4: return nix::DataType::UInt32;
        case 8: return nix::DataType::UInt64;
        }
        break;

    case 'i':
        switch (dtype->elsize) {
        case 1: return nix::DataType::Int8;
        case 2: return nix::DataType::Int16;
        case 4: return nix::DataType::Int32;
        case 8: return nix::DataType::Int64;
        }
        break;

    case 'f':
        switch (dtype->elsize) {
        case 4: return nix::DataType::Float;
        case 8: return nix::DataType::Double;
        }
        break;

    case 'V':
        return nix::DataType::Opaque;
        //we are ignoring the size information here
        //since we only support NATIVE_OPAQUE in H5
        //maybe we should throw an exception instead
        break;

    default:
        break;
    }
    return nix::DataType::Nothing;
}

static std::string nixDtypeToPyDtypeStr(nix::DataType nix_dtype)
{
    switch (nix_dtype) {
    case nix::DataType::UInt8:  return "<u1";
    case nix::DataType::UInt16: return "<u2";
    case nix::DataType::UInt32: return "<u4";
    case nix::DataType::UInt64: return "<u8";
    case nix::DataType::Int8:   return "<i1";
    case nix::DataType::Int16:  return "<i2";
    case nix::DataType::Int32:  return "<i4";
    case nix::DataType::Int64:  return "<i8";
    case nix::DataType::Float:  return "<f4";
    case nix::DataType::Double: return "<f8";
    case nix::DataType::Opaque: return "|V1";
    default:                    return "";
    }
}

static nix::DataType arrayDescAsDtype(PyArrayObject *array) {
    nix::DataType nix_dtype = pyDtypeToNixDtype(PyArray_DESCR(array));

    if (nix_dtype == nix::DataType::Nothing) {
        throw std::invalid_argument("Unsupported dtype for data");
    }

    return nix_dtype;
}

static PyArrayObject *makeArray(PyObject *data, int requirements) {
    if (! PyArray_Check(data)) {
        throw std::invalid_argument("Data not a NumPy array");
    }

    PyArrayObject *array = reinterpret_cast<PyArrayObject *>(data);

     if (! PyArray_CHKFLAGS(array, requirements)) {
        throw std::invalid_argument("data must be c-contiguous and aligned");
    }

     return array;
}

static NDSize arrayShapeAsNDSize(PyArrayObject *array) {
    int array_rank = PyArray_NDIM(array);
    npy_intp *array_shape = PyArray_SHAPE(array);

    nix::NDSize data_shape(array_rank);
    for (int i = 0; i < array_rank; i++) {
        data_shape[i] = array_shape[i];
    }

    return data_shape;
}

static void arrayEnsureShapeAndCount(PyArrayObject *array,
                                         nix::NDSize    count,
                                         nix::NDSize    offset) {

    if (! count) {
        count = arrayShapeAsNDSize(array);
    }

    if (! offset) {
        offset = nix::NDSize(count.size(), 0);
    }
}

static void readData(DataArray& da, PyObject *data, nix::NDSize count, nix::NDSize offset) {

    PyArrayObject *array = makeArray(data, NPY_ARRAY_CARRAY);

    nix::DataType nix_dtype = arrayDescAsDtype(array);

    arrayEnsureShapeAndCount(array, count, offset);
    da.getData(nix_dtype, PyArray_DATA(array), count, offset);
}

static void writeData(DataArray& da, PyObject *data, nix::NDSize count, nix::NDSize offset) {

    PyArrayObject *array = makeArray(data, NPY_ARRAY_CARRAY_RO);

    nix::DataType nix_dtype = arrayDescAsDtype(array);

    arrayEnsureShapeAndCount(array, count, offset);
    da.setData(nix_dtype, PyArray_DATA(array), count, offset);
}

static std::string getDataType(const DataArray& da)
{
    nix::DataType nix_dtype = da.dataType();
    return nixDtypeToPyDtypeStr(nix_dtype);
}

// Dimensions

PyObject* getDimension(const DataArray& da, size_t index) {
    Dimension dim = da.getDimension(index);
    SetDimension set;
    RangeDimension range;
    SampledDimension sample;
    DimensionType type = dim.dimensionType();

    switch(type) {
        case DimensionType::Set:
            set = dim;
            return incref(object(set).ptr());
        case DimensionType::Range:
            range = dim;
            return incref(object(range).ptr());
        case DimensionType::Sample:
            sample = dim;
            return incref(object(sample).ptr());
        default:
            Py_RETURN_NONE;
    }
}

//

struct dtype_transmogrify {
    typedef boost::python::converter::rvalue_from_python_stage1_data py_s1_data;
    typedef boost::python::converter::rvalue_from_python_storage<nix::DataType> py_storage;

       // PyObject* -> nix::DataType
    static void register_from_python() {
        boost::python::converter::registry::push_back(is_convertible,
                                                      construct,
                                                      boost::python::type_id<nix::DataType>());
    }

    static void* is_convertible(PyObject *obj) {
        namespace bp = boost::python;

        PyArray_Descr* py_dtype = nullptr;

        if (! PyArray_DescrConverter(obj, &py_dtype)) {
            return nullptr;
        }

        Py_DECREF(py_dtype);
        return obj;
    }

    static void construct(PyObject *obj, py_s1_data *data) {
        namespace bp = boost::python;

        void *raw = static_cast<void *>(reinterpret_cast<py_storage *>(data)->storage.bytes);

        PyArray_Descr* py_dtype = nullptr;
        //should work, because we checked with is_convertible
        PyArray_DescrConverter(obj, &py_dtype);

        nix::DataType * const dtype_ptr = static_cast<nix::DataType *>(raw);
        *dtype_ptr = pyDtypeToNixDtype(py_dtype);
        data->convertible = raw;
    }

};

void PyDataArray::do_export() {

    // For numpy to work
    import_array();

    PyEntityWithSources<base::IDataArray>::do_export("DataArray");
    class_<DataArray, bases<base::EntityWithSources<base::IDataArray>>>("DataArray")
        .add_property("label",
                      OPT_GETTER(std::string, DataArray, label),
                      setLabel,
                      doc::data_array_label)
        .add_property("unit",
                      OPT_GETTER(std::string, DataArray, unit),
                      setUnit,
                      doc::data_array_unit)
        .add_property("expansion_origin",
                      OPT_GETTER(double, DataArray, expansionOrigin),
                      setExpansionOrigin,
                      doc::data_array_expansion_origin)
        .add_property("polynom_coefficients",
                      GETTER(std::vector<double>, DataArray, polynomCoefficients),
                      setPolynomCoefficients,
                      doc::data_array_polynom_coefficients)
        .add_property("data_extent",
                      GETTER(NDSize, DataArray, dataExtent),
                      SETTER(NDSize&, DataArray, dataExtent),
                      doc::data_array_data_extent)
        // Data
        .add_property("data_type", &DataArray::dataType,
                      doc::data_array_data_type)
        .def("_write_data", writeData)
        .def("_read_data", readData)
        .def("_get_dtype", getDataType)

        // Dimensions
        .def("create_set_dimension", &DataArray::createSetDimension,
             doc::data_array_create_set_dimension)
        .def("create_sampled_dimension", &DataArray::createSampledDimension,
             doc::data_array_create_sampled_dimension)
        .def("create_range_dimension", &DataArray::createRangeDimension,
             doc::data_array_create_range_dimension)
        .def("append_set_dimension", &DataArray::appendSetDimension,
             doc::data_array_append_set_dimension)
        .def("append_sampled_dimension", &DataArray::appendSampledDimension,
             doc::data_array_append_sampled_dimension)
        .def("append_range_dimension", &DataArray::appendRangeDimension,
             doc::data_array_append_range_dimension)
        .def("_dimension_count", &DataArray::dimensionCount)
        .def("_delete_dimension_by_pos", &DataArray::deleteDimension)
        .def("_get_dimension_by_pos", getDimension)
        // Other
        .def("__str__", &toStr<DataArray>)
        .def("__repr__", &toStr<DataArray>)
        ;

    to_python_converter<std::vector<DataArray>, vector_transmogrify<DataArray>>();
    vector_transmogrify<DataArray>::register_from_python();

    to_python_converter<boost::optional<DataArray>, option_transmogrify<DataArray>>();
    option_transmogrify<DataArray>::register_from_python();

    dtype_transmogrify::register_from_python();
}

}
<commit_msg>PyDataArray: own check alignment for void datatype<commit_after>// Copyright (c) 2014, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE source in the root of the Project.

#include <boost/python.hpp>
#include <boost/optional.hpp>

#include <nix.hpp>

#include <accessors.hpp>
#include <transmorgify.hpp>

//we want only the newest and freshest API
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <numpy/arrayobject.h>
#include <numpy/ndarrayobject.h>

#include <PyEntity.hpp>

#include <stdexcept>

using namespace nix;
using namespace boost::python;

namespace nixpy {

// Label

void setLabel(DataArray& da, const boost::optional<std::string>& label) {
    if (label)
        da.label(*label);
    else
        da.label(boost::none);
}

// Unit

void setUnit(DataArray& da, const boost::optional<std::string> &unit) {
    if (unit)
        da.unit(*unit);
    else
        da.unit(boost::none);
}

// Expansion origin

void setExpansionOrigin(DataArray& da, const boost::optional<double>& eo) {
    if (eo)
        da.expansionOrigin(*eo);
    else
        da.expansionOrigin(boost::none);
}

// Polynom coefficients

void setPolynomCoefficients(DataArray& da, const std::vector<double>& pc) {
    if (!pc.empty())
        da.polynomCoefficients(pc);
    else
        da.polynomCoefficients(boost::none);
}

// Data

static nix::DataType pyDtypeToNixDtype(const PyArray_Descr *dtype)
{
    if (dtype == nullptr) {
        return nix::DataType::Nothing;
    }

    if (dtype->byteorder != '=' && dtype->byteorder != '|') {
        //TODO: Handle case where specified byteorder *is*
        //the native byteorder (via BOOST_BIG_ENDIAN macros)
        return nix::DataType::Nothing;
    }

    switch (dtype->kind) {

    case 'u':
        switch (dtype->elsize) {
        case 1: return nix::DataType::UInt8;
        case 2: return nix::DataType::UInt16;
        case 4: return nix::DataType::UInt32;
        case 8: return nix::DataType::UInt64;
        }
        break;

    case 'i':
        switch (dtype->elsize) {
        case 1: return nix::DataType::Int8;
        case 2: return nix::DataType::Int16;
        case 4: return nix::DataType::Int32;
        case 8: return nix::DataType::Int64;
        }
        break;

    case 'f':
        switch (dtype->elsize) {
        case 4: return nix::DataType::Float;
        case 8: return nix::DataType::Double;
        }
        break;

    case 'V':
        return nix::DataType::Opaque;
        //we are ignoring the size information here
        //since we only support NATIVE_OPAQUE in H5
        //maybe we should throw an exception instead
        break;

    default:
        break;
    }
    return nix::DataType::Nothing;
}

static std::string nixDtypeToPyDtypeStr(nix::DataType nix_dtype)
{
    switch (nix_dtype) {
    case nix::DataType::UInt8:  return "<u1";
    case nix::DataType::UInt16: return "<u2";
    case nix::DataType::UInt32: return "<u4";
    case nix::DataType::UInt64: return "<u8";
    case nix::DataType::Int8:   return "<i1";
    case nix::DataType::Int16:  return "<i2";
    case nix::DataType::Int32:  return "<i4";
    case nix::DataType::Int64:  return "<i8";
    case nix::DataType::Float:  return "<f4";
    case nix::DataType::Double: return "<f8";
    case nix::DataType::Opaque: return "|V1";
    default:                    return "";
    }
}

static nix::DataType arrayDescAsDtype(PyArrayObject *array) {
    nix::DataType nix_dtype = pyDtypeToNixDtype(PyArray_DESCR(array));

    if (nix_dtype == nix::DataType::Nothing) {
        throw std::invalid_argument("Unsupported dtype for data");
    }

    return nix_dtype;
}

static PyArrayObject *makeArray(PyObject *data, int requirements) {
    if (! PyArray_Check(data)) {
        throw std::invalid_argument("Data not a NumPy array");
    }

    PyArrayObject *array = reinterpret_cast<PyArrayObject *>(data);
    PyArray_Descr *descr = PyArray_DESCR(array);

    if (requirements & NPY_ARRAY_ALIGNED && descr->kind == 'V') {
       //workaround a strange behaviour of numpy 1.9 to return
       //is_aligned == FALSE for void datatypes with an alignment
       //requirement == 1
       if (descr->alignment == 1) {
          requirements &= ~NPY_ARRAY_ALIGNED;
       }
    }

     if (! PyArray_CHKFLAGS(array, requirements)) {
        throw std::invalid_argument("array does not meet requirements");
    }

     return array;
}

static NDSize arrayShapeAsNDSize(PyArrayObject *array) {
    int array_rank = PyArray_NDIM(array);
    npy_intp *array_shape = PyArray_SHAPE(array);

    nix::NDSize data_shape(array_rank);
    for (int i = 0; i < array_rank; i++) {
        data_shape[i] = array_shape[i];
    }

    return data_shape;
}

static void arrayEnsureShapeAndCount(PyArrayObject *array,
                                         nix::NDSize    count,
                                         nix::NDSize    offset) {

    if (! count) {
        count = arrayShapeAsNDSize(array);
    }

    if (! offset) {
        offset = nix::NDSize(count.size(), 0);
    }
}

static void readData(DataArray& da, PyObject *data, nix::NDSize count, nix::NDSize offset) {

    PyArrayObject *array = makeArray(data, NPY_ARRAY_CARRAY);

    nix::DataType nix_dtype = arrayDescAsDtype(array);

    arrayEnsureShapeAndCount(array, count, offset);
    da.getData(nix_dtype, PyArray_DATA(array), count, offset);
}

static void writeData(DataArray& da, PyObject *data, nix::NDSize count, nix::NDSize offset) {

    PyArrayObject *array = makeArray(data, NPY_ARRAY_CARRAY_RO);

    nix::DataType nix_dtype = arrayDescAsDtype(array);

    arrayEnsureShapeAndCount(array, count, offset);
    da.setData(nix_dtype, PyArray_DATA(array), count, offset);
}

static std::string getDataType(const DataArray& da)
{
    nix::DataType nix_dtype = da.dataType();
    return nixDtypeToPyDtypeStr(nix_dtype);
}

// Dimensions

PyObject* getDimension(const DataArray& da, size_t index) {
    Dimension dim = da.getDimension(index);
    SetDimension set;
    RangeDimension range;
    SampledDimension sample;
    DimensionType type = dim.dimensionType();

    switch(type) {
        case DimensionType::Set:
            set = dim;
            return incref(object(set).ptr());
        case DimensionType::Range:
            range = dim;
            return incref(object(range).ptr());
        case DimensionType::Sample:
            sample = dim;
            return incref(object(sample).ptr());
        default:
            Py_RETURN_NONE;
    }
}

//

struct dtype_transmogrify {
    typedef boost::python::converter::rvalue_from_python_stage1_data py_s1_data;
    typedef boost::python::converter::rvalue_from_python_storage<nix::DataType> py_storage;

       // PyObject* -> nix::DataType
    static void register_from_python() {
        boost::python::converter::registry::push_back(is_convertible,
                                                      construct,
                                                      boost::python::type_id<nix::DataType>());
    }

    static void* is_convertible(PyObject *obj) {
        namespace bp = boost::python;

        PyArray_Descr* py_dtype = nullptr;

        if (! PyArray_DescrConverter(obj, &py_dtype)) {
            return nullptr;
        }

        Py_DECREF(py_dtype);
        return obj;
    }

    static void construct(PyObject *obj, py_s1_data *data) {
        namespace bp = boost::python;

        void *raw = static_cast<void *>(reinterpret_cast<py_storage *>(data)->storage.bytes);

        PyArray_Descr* py_dtype = nullptr;
        //should work, because we checked with is_convertible
        PyArray_DescrConverter(obj, &py_dtype);

        nix::DataType * const dtype_ptr = static_cast<nix::DataType *>(raw);
        *dtype_ptr = pyDtypeToNixDtype(py_dtype);
        data->convertible = raw;
    }

};

void PyDataArray::do_export() {

    // For numpy to work
    import_array();

    PyEntityWithSources<base::IDataArray>::do_export("DataArray");
    class_<DataArray, bases<base::EntityWithSources<base::IDataArray>>>("DataArray")
        .add_property("label",
                      OPT_GETTER(std::string, DataArray, label),
                      setLabel,
                      doc::data_array_label)
        .add_property("unit",
                      OPT_GETTER(std::string, DataArray, unit),
                      setUnit,
                      doc::data_array_unit)
        .add_property("expansion_origin",
                      OPT_GETTER(double, DataArray, expansionOrigin),
                      setExpansionOrigin,
                      doc::data_array_expansion_origin)
        .add_property("polynom_coefficients",
                      GETTER(std::vector<double>, DataArray, polynomCoefficients),
                      setPolynomCoefficients,
                      doc::data_array_polynom_coefficients)
        .add_property("data_extent",
                      GETTER(NDSize, DataArray, dataExtent),
                      SETTER(NDSize&, DataArray, dataExtent),
                      doc::data_array_data_extent)
        // Data
        .add_property("data_type", &DataArray::dataType,
                      doc::data_array_data_type)
        .def("_write_data", writeData)
        .def("_read_data", readData)
        .def("_get_dtype", getDataType)

        // Dimensions
        .def("create_set_dimension", &DataArray::createSetDimension,
             doc::data_array_create_set_dimension)
        .def("create_sampled_dimension", &DataArray::createSampledDimension,
             doc::data_array_create_sampled_dimension)
        .def("create_range_dimension", &DataArray::createRangeDimension,
             doc::data_array_create_range_dimension)
        .def("append_set_dimension", &DataArray::appendSetDimension,
             doc::data_array_append_set_dimension)
        .def("append_sampled_dimension", &DataArray::appendSampledDimension,
             doc::data_array_append_sampled_dimension)
        .def("append_range_dimension", &DataArray::appendRangeDimension,
             doc::data_array_append_range_dimension)
        .def("_dimension_count", &DataArray::dimensionCount)
        .def("_delete_dimension_by_pos", &DataArray::deleteDimension)
        .def("_get_dimension_by_pos", getDimension)
        // Other
        .def("__str__", &toStr<DataArray>)
        .def("__repr__", &toStr<DataArray>)
        ;

    to_python_converter<std::vector<DataArray>, vector_transmogrify<DataArray>>();
    vector_transmogrify<DataArray>::register_from_python();

    to_python_converter<boost::optional<DataArray>, option_transmogrify<DataArray>>();
    option_transmogrify<DataArray>::register_from_python();

    dtype_transmogrify::register_from_python();
}

}
<|endoftext|>