<commit_before>/*****************************************************************************************
 *                                                                                *
 * GHOUL                                                                                *
 * General Helpful Open Utility Library                                                  *
 *                                                                                *
 * Copyright (c) 2012 Alexander Bock                                                     *
 *                                                                                *
 * 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.                                         *
 ****************************************************************************************/

#include "gtest\gtest.h"
#include <ghoul/misc/configurationmanager.h>
#include <glm/glm.hpp>
#include <random>

namespace {
    // A non-existing configuration file
    const std::string _configuration0 = "${TEST_DIR}/configurationmanager/test0.cfg";

    // The configuration1 test configuration has one key "t" = 1
    const std::string _configuration1 = "${TEST_DIR}/configurationmanager/test1.cfg";

    // The configuration1 test configuration has two keys "t" and "s"
    const std::string _configuration2 = "${TEST_DIR}/configurationmanager/test2.cfg";

    // More complicated configuration file with nested tables
    const std::string _configuration3 = "${TEST_DIR}/configurationmanager/test3.cfg";
}

/*
Test checklist:
+++ loadConfiguration: existing file
+++ loadConfiguration: non-existing file
+++ getValue: key does not exist
+++ getValue: subtable does not exist
+++ getValue: overriding previous configuration
+++ getValue: function does not change passed value on error
+++ getValue: nested keys
--- getValue: deep nesting of keys
--- getValue: correct values returned for each type
--- getValue: all basic types implemented
--- getValue: glm::vec2, glm::vec3, glm::vec4, glm::mat3, glm::mat4 implemented
--- setValue: all types implemented
--- setValue: create subtables on the way
--- setValue: value gets set correctly for each type
--- setValue: value overwrites setting in configuration file
--- setValue: deep nesting of keys
--- setValue: nested keys
--- setValue: glm::vec2, glm::vec3, glm::vec4, glm::mat3, glm::mat4 implemented
--- hasKeys: deep nesting of keys
--- hasKeys: subtables on the way do not exist
--- hasKeys: correct values for all types
+++ hasKeys: nestedKeys
*/

class ConfigurationManagerTest : public testing::Test {
protected:
    ConfigurationManagerTest() {
        _m = new ghoul::ConfigurationManager;
        _m->initialize();
    }

    ~ConfigurationManagerTest() {
        delete _m;
        _m = nullptr;
    }

    ghoul::ConfigurationManager* _m;
};

TEST_F(ConfigurationManagerTest, DeinitDeath) {
    // Accessing the ConfigurationManager after it has been deinitialized gets an assert
    _m->deinitialize();
    EXPECT_DEATH(_m->keys();, "");
}

TEST_F(ConfigurationManagerTest, LoadConfigurationTest) {
    const bool success0 = _m->loadConfiguration(_configuration0);
    ASSERT_EQ(success0, false) << "Loading a non-existing file should fail gracefully";
    const bool success1 = _m->loadConfiguration(_configuration1);
    ASSERT_EQ(success1, true) << "Loading of configuration file 'test1.cfg'";
    const bool success2 = _m->loadConfiguration(_configuration2);
    ASSERT_EQ(success2, true) << "Loading of configuration file 'test2.cfg'";
    const bool success3 = _m->loadConfiguration(_configuration3);
    ASSERT_EQ(success3, true) << "Loading of configuration file 'test3.cfg'";
}

TEST_F(ConfigurationManagerTest, KeysFunction) {
    // The empty configuration should not have any keys
    size_t nKeys = _m->keys().size();
    EXPECT_EQ(nKeys, 0) << "The empty configuration should not have any keys";

    _m->loadConfiguration(_configuration1);
    nKeys = _m->keys().size();
    EXPECT_EQ(nKeys, 1) << "test1";

    _m->loadConfiguration(_configuration3);
    nKeys = _m->keys().size();
    EXPECT_EQ(nKeys, 2) << "base: test1 + test3";

    nKeys = _m->keys("s").size();
    EXPECT_EQ(nKeys, 3) << "s: test1 + test3";

    nKeys = _m->keys("s.3").size();
    EXPECT_EQ(nKeys, 2) << "s.3: test1 + test3";
}

TEST_F(ConfigurationManagerTest, GetValueFunction) {
    bool test;
    bool success = _m->getValue("key", test);
    EXPECT_EQ(success, false) << "Empty configuration";

    success = _m->getValue("key.key", test);
    EXPECT_EQ(success, false) << "Empty configuration recursive";

    _m->loadConfiguration(_configuration1);
    _m->loadConfiguration(_configuration3);
    int testInt;
    success = _m->getValue("t", testInt);
    EXPECT_EQ(success, true) << "test1+test3 (t)";
    EXPECT_EQ(testInt, 1) << "test1+test3 (t)";

    success = _m->getValue("s.a", test);
    EXPECT_EQ(success, false) << "test1+test3 (s.a)";

    success = _m->getValue("s.1", test);
    EXPECT_EQ(success, true) << "test1+test3 (s.1)";

    success = _m->getValue("s.1.a", test);
    EXPECT_EQ(success, false) << "test1+test3 (s.1.a)";

    success = _m->getValue("s.3.a", test);
    EXPECT_EQ(success, true) << "test1+test3 (s.3.a)";

    success = _m->getValue("s.1", testInt);
    EXPECT_EQ(success, true) << "test1+test3 (s.1)";
    EXPECT_EQ(testInt, 1) << "test1+test3 (s.1)";

    success = _m->getValue("s.2", testInt);
    EXPECT_EQ(success, true) << "test1+test3 (s.2)";
    EXPECT_EQ(testInt, 2) << "test1+test3 (s.2)";

    std::vector<int> testVec;
    success = _m->getValue("key", testVec);
    EXPECT_EQ(success, false) << "test1+test3: Vector access";

}

TEST_F(ConfigurationManagerTest, InvalidKeyAccessInvariant) {
    // Accessing an invalid key should not change the tested argument
    std::mt19937 rd;
    {
        std::uniform_int_distribution<int> dist;
        for (int i = 0; i < 10; ++i) {
            const int testValue = dist(rd);
            int test = testValue;
            _m->getValue("key", test);
            ASSERT_EQ(test, testValue) << "invariant int";
        }
    }

    {
        std::uniform_real_distribution<float> dist;
        for (int i = 0; i < 10; ++i) {
            const float testValue = dist(rd);
            float test = testValue;
            _m->getValue("key", test);
            ASSERT_EQ(test, testValue) << "invariant float";
        }
    }
}

TEST_F(ConfigurationManagerTest, HasKeyFunction) {
    bool success = _m->hasKey("key");
    EXPECT_EQ(success, false) << "empty configuration";

    _m->loadConfiguration(_configuration1);
    success = _m->hasKey("t");
    EXPECT_EQ(success, true) << "test1 (t)";

    success = _m->hasKey("s");
    EXPECT_EQ(success, false) << "test1 (s)";

    _m->loadConfiguration(_configuration2);
    success = _m->hasKey("s.a");
    EXPECT_EQ(success, true) << "test1+test2 (s.a)";

    success = _m->hasKey("s.c");
    EXPECT_EQ(success, false) << "test1+test2 (s.c)";
}

TEST_F(ConfigurationManagerTest, MultipleKeyLoadOverwrite) {
    _m->loadConfiguration(_configuration1);
    int value;
    _m->getValue("t", value);
    EXPECT_EQ(value, 1);

    _m->loadConfiguration(_configuration2);

    // configuration2 should overwrite the value t in configuration1
    _m->getValue("t", value);
    EXPECT_EQ(value, 2);
}
<commit_msg>add more tests for ConfigurationManager<commit_after>/*****************************************************************************************
 *                                                                                *
 * GHOUL                                                                                *
 * General Helpful Open Utility Library                                                  *
 *                                                                                *
 * Copyright (c) 2012 Alexander Bock                                                     *
 *                                                                                *
 * 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.                                         *
 ****************************************************************************************/

#include "gtest\gtest.h"
#include <ghoul/misc/configurationmanager.h>
#include <glm/glm.hpp>
#include <random>

namespace {
    // A non-existing configuration file
    const std::string _configuration0 = "${TEST_DIR}/configurationmanager/test0.cfg";

    // The configuration1 test configuration has one key "t" = 1
    const std::string _configuration1 = "${TEST_DIR}/configurationmanager/test1.cfg";

    // The configuration1 test configuration has two keys "t" and "s"
    const std::string _configuration2 = "${TEST_DIR}/configurationmanager/test2.cfg";

    // More complicated configuration file with nested tables
    const std::string _configuration3 = "${TEST_DIR}/configurationmanager/test3.cfg";

    // Deeply nested configuration file with 12 level
    const std::string _configuration4 = "${TEST_DIR}/configurationmanager/test4.cfg";
}

/*
Test checklist:
+++ loadConfiguration: existing file
+++ loadConfiguration: non-existing file
+++ getValue: key does not exist
+++ getValue: subtable does not exist
+++ getValue: overriding previous configuration
+++ getValue: function does not change passed value on error
+++ getValue: nested keys
+++ getValue: deep nesting of keys
+++ getValue: correct values returned for each type
+++ getValue: are all basic types implemented
--- getValue: glm::vec2, glm::vec3, glm::vec4, glm::mat3, glm::mat4 implemented
+++ getValue: valid conversions
--- setValue: all types implemented
--- setValue: create subtables on the way
--- setValue: value gets set correctly for each type
--- setValue: value overwrites setting in configuration file
--- setValue: deep nesting of keys
--- setValue: nested keys
--- setValue: glm::vec2, glm::vec3, glm::vec4, glm::mat3, glm::mat4 implemented
--- hasKeys: deep nesting of keys
--- hasKeys: subtables on the way do not exist
--- hasKeys: correct values for all types
+++ hasKeys: nestedKeys
*/

class ConfigurationManagerTest : public testing::Test {
protected:
    ConfigurationManagerTest() {
        _m = new ghoul::ConfigurationManager;
        _m->initialize();
    }

    ~ConfigurationManagerTest() {
        delete _m;
        _m = nullptr;
    }

    ghoul::ConfigurationManager* _m;
};

TEST_F(ConfigurationManagerTest, DeinitDeath) {
    // Accessing the ConfigurationManager after it has been deinitialized gets an assert
    _m->deinitialize();
    EXPECT_DEATH(_m->keys();, "");
}

TEST_F(ConfigurationManagerTest, LoadConfigurationTest) {
    const bool success0 = _m->loadConfiguration(_configuration0);
    ASSERT_EQ(success0, false) << "Loading a non-existing file should fail gracefully";
    const bool success1 = _m->loadConfiguration(_configuration1);
    ASSERT_EQ(success1, true) << "Loading of configuration file 'test1.cfg'";
    const bool success2 = _m->loadConfiguration(_configuration2);
    ASSERT_EQ(success2, true) << "Loading of configuration file 'test2.cfg'";
    const bool success3 = _m->loadConfiguration(_configuration3);
    ASSERT_EQ(success3, true) << "Loading of configuration file 'test3.cfg'";
    const bool success4 = _m->loadConfiguration(_configuration4);
    ASSERT_EQ(success4, true) << "Loading of configuration file 'test4.cfg'";
}

TEST_F(ConfigurationManagerTest, KeysFunction) {
    // The empty configuration should not have any keys
    size_t nKeys = _m->keys().size();
    EXPECT_EQ(nKeys, 0) << "The empty configuration should not have any keys";

    _m->loadConfiguration(_configuration1);
    nKeys = _m->keys().size();
    EXPECT_EQ(nKeys, 1) << "test1";

    _m->loadConfiguration(_configuration3);
    nKeys = _m->keys().size();
    EXPECT_EQ(nKeys, 2) << "base: test1 + test3";

    nKeys = _m->keys("s").size();
    EXPECT_EQ(nKeys, 3) << "s: test1 + test3";

    nKeys = _m->keys("s.3").size();
    EXPECT_EQ(nKeys, 2) << "s.3: test1 + test3";

    _m->loadConfiguration(_configuration4);

    const char* keys[] = {
        "a", "a.a", "a.a.a", "a.a.a.a", "a.a.a.a.a", "a.a.a.a.a.a", "a.a.a.a.a.a.a",
        "a.a.a.a.a.a.a.a", "a.a.a.a.a.a.a.a.a", "a.a.a.a.a.a.a.a.a.a",
        "a.a.a.a.a.a.a.a.a.a.a", "a.a.a.a.a.a.a.a.a.a.a.a"
    };

    for (int i = 0; i < 12; ++i) {
        nKeys = _m->keys(keys[i]).size();
        EXPECT_EQ(nKeys, 2) << keys[i] <<": test1 + test3";
    }

    //nKeys = _
}

TEST_F(ConfigurationManagerTest, GetValueFunction) {
    bool test;
    bool success = _m->getValue("key", test);
    EXPECT_EQ(success, false) << "Empty configuration";

    success = _m->getValue("key.key", test);
    EXPECT_EQ(success, false) << "Empty configuration recursive";

    _m->loadConfiguration(_configuration1);
    _m->loadConfiguration(_configuration3);
    int testInt;
    success = _m->getValue("t", testInt);
    EXPECT_EQ(success, true) << "test1+test3 (t)";
    EXPECT_EQ(testInt, 1) << "test1+test3 (t)";

    success = _m->getValue("s.a", test);
    EXPECT_EQ(success, false) << "test1+test3 (s.a)";

    success = _m->getValue("s.1", test);
    EXPECT_EQ(success, true) << "test1+test3 (s.1)";

    success = _m->getValue("s.1.a", test);
    EXPECT_EQ(success, false) << "test1+test3 (s.1.a)";

    success = _m->getValue("s.3.a", test);
    EXPECT_EQ(success, true) << "test1+test3 (s.3.a)";

    success = _m->getValue("s.1", testInt);
    EXPECT_EQ(success, true) << "test1+test3 (s.1)";
    EXPECT_EQ(testInt, 1) << "test1+test3 (s.1)";

    success = _m->getValue("s.2", testInt);
    EXPECT_EQ(success, true) << "test1+test3 (s.2)";
    EXPECT_EQ(testInt, 2) << "test1+test3 (s.2)";

    std::vector<int> testVec;
    success = _m->getValue("key", testVec);
    EXPECT_EQ(success, false) << "test1+test3: Vector access";
}

template <class T>
void correctnessHelperGetValue(ghoul::ConfigurationManager* m, const std::string& key) {
    T value = T(0);
    const bool success = m->getValue(key, value);
    EXPECT_EQ(success, true) << "Type: " << typeid(T).name();
    EXPECT_EQ(value, T(1)) << "Type: " << typeid(T).name();
}

TEST_F(ConfigurationManagerTest, GetValueCorrectness) {
    _m->loadConfiguration(_configuration1);

    correctnessHelperGetValue<bool>(_m, "t");
    correctnessHelperGetValue<char>(_m, "t");
    correctnessHelperGetValue<signed char>(_m, "t");
    correctnessHelperGetValue<unsigned char>(_m, "t");
    correctnessHelperGetValue<wchar_t>(_m, "t");
    correctnessHelperGetValue<short>(_m, "t");
    correctnessHelperGetValue<unsigned short>(_m, "t");
    correctnessHelperGetValue<int>(_m, "t");
    correctnessHelperGetValue<unsigned int>(_m, "t");
    correctnessHelperGetValue<long>(_m, "t");
    correctnessHelperGetValue<unsigned long>(_m, "t");
    correctnessHelperGetValue<long long>(_m, "t");
    correctnessHelperGetValue<unsigned long long>(_m, "t");
    correctnessHelperGetValue<float>(_m, "t");
    correctnessHelperGetValue<double>(_m, "t");
    correctnessHelperGetValue<long double>(_m, "t");

    std::string value;
    const bool success = _m->getValue("t", value);
    EXPECT_EQ(success, true) << "Type: " << typeid(std::string).name();
    EXPECT_STREQ(value.c_str(), "1") << "Type: " << typeid(std::string).name();
}

TEST_F(ConfigurationManagerTest, GetValueConversions) {
    // converting from 1 -> all types is done in GetValueCorrectness
    _m->loadConfiguration(_configuration2);

    correctnessHelperGetValue<bool>(_m, "s.a1");
    correctnessHelperGetValue<char>(_m, "s.a1");
    correctnessHelperGetValue<signed char>(_m, "s.a1");
    correctnessHelperGetValue<unsigned char>(_m, "s.a1");
    correctnessHelperGetValue<wchar_t>(_m, "s.a1");
    correctnessHelperGetValue<short>(_m, "s.a1");
    correctnessHelperGetValue<unsigned short>(_m, "s.a1");
    correctnessHelperGetValue<int>(_m, "s.a1");
    correctnessHelperGetValue<unsigned int>(_m, "s.a1");
    correctnessHelperGetValue<long>(_m, "s.a1");
    correctnessHelperGetValue<unsigned long>(_m, "s.a1");
    correctnessHelperGetValue<long long>(_m, "s.a1");
    correctnessHelperGetValue<unsigned long long>(_m, "s.a1");
    correctnessHelperGetValue<float>(_m, "s.a1");
    correctnessHelperGetValue<double>(_m, "s.a1");
    correctnessHelperGetValue<long double>(_m, "s.a1");

    std::string value;
    const bool success = _m->getValue("s.a1", value);
    EXPECT_EQ(success, true) << "Type: " << typeid(std::string).name();
    EXPECT_STREQ(value.c_str(), "1") << "Type: " << typeid(std::string).name();

}

TEST_F(ConfigurationManagerTest, InvalidKeyAccessInvariant) {
    // Accessing an invalid key should not change the tested argument
    std::mt19937 rd;
    {
        std::uniform_int_distribution<int> dist;
        for (int i = 0; i < 10; ++i) {
            const int testValue = dist(rd);
            int test = testValue;
            _m->getValue("key", test);
            ASSERT_EQ(test, testValue) << "invariant int";
        }
    }

    {
        std::uniform_real_distribution<float> dist;
        for (int i = 0; i < 10; ++i) {
            const float testValue = dist(rd);
            float test = testValue;
            _m->getValue("key", test);
            ASSERT_EQ(test, testValue) << "invariant float";
        }
    }
}

TEST_F(ConfigurationManagerTest, HasKeyFunction) {
    bool success = _m->hasKey("key");
    EXPECT_EQ(success, false) << "empty configuration";

    _m->loadConfiguration(_configuration1);
    success = _m->hasKey("t");
    EXPECT_EQ(success, true) << "test1 (t)";

    success = _m->hasKey("s");
    EXPECT_EQ(success, false) << "test1 (s)";

    _m->loadConfiguration(_configuration2);
    success = _m->hasKey("s.a");
    EXPECT_EQ(success, true) << "test1+test2 (s.a)";

    success = _m->hasKey("s.c");
    EXPECT_EQ(success, false) << "test1+test2 (s.c)";
}

TEST_F(ConfigurationManagerTest, MultipleKeyLoadOverwrite) {
    _m->loadConfiguration(_configuration1);
    int value;
    _m->getValue("t", value);
    EXPECT_EQ(value, 1);

    _m->loadConfiguration(_configuration2);

    // configuration2 should overwrite the value t in configuration1
    _m->getValue("t", value);
    EXPECT_EQ(value, 2);
}
<|endoftext|>


<commit_before>
#include "VM.h"

#include <libevm/VMFace.h>
#include <libevm/VM.h>

#include "ExecutionEngine.h"
#include "Compiler.h"
#include "Type.h"

namespace dev
{
namespace eth
{
namespace jit
{

bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
{
	Compiler::Options defaultOptions;
	auto module = Compiler(defaultOptions).compile(_ext.code);

	ExecutionEngine engine;
	auto exitCode = engine.run(std::move(module), m_gas, false, _ext);

	switch (static_cast<ReturnCode>(exitCode))
	{
	case ReturnCode::BadJumpDestination:
		BOOST_THROW_EXCEPTION(BadJumpDestination());
	case ReturnCode::OutOfGas:
		BOOST_THROW_EXCEPTION(OutOfGas());
	case ReturnCode::StackTooSmall:
		BOOST_THROW_EXCEPTION(StackTooSmall());
	case ReturnCode::BadInstruction:
		BOOST_THROW_EXCEPTION(BadInstruction());
	default:
		break;
	}

	m_output = std::move(engine.returnData);
	return {m_output.data(), m_output.size()};  // TODO: This all bytesConstRef stuff sucks
}

}
}
}
<commit_msg>JIT VM updated<commit_after>
#include "VM.h"

#include <libevm/VMFace.h>
#include <libevm/VM.h>

#include "../libevmjit/ExecutionEngine.h"
#include "../libevmjit/Compiler.h"

namespace dev
{
namespace eth
{
namespace jit
{

bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
{
	Compiler::Options defaultOptions;
	auto module = Compiler(defaultOptions).compile(_ext.code);

	RuntimeData data = {};

#define set(INDEX, VALUE) data.elems[INDEX] = eth2llvm(VALUE)
	set(RuntimeData::Gas, m_gas);
	set(RuntimeData::Address, fromAddress(_ext.myAddress));
	set(RuntimeData::Caller, fromAddress(_ext.caller));
	set(RuntimeData::Origin, fromAddress(_ext.origin));
	set(RuntimeData::CallValue, _ext.value);
	set(RuntimeData::CallDataSize, _ext.data.size());
	set(RuntimeData::GasPrice, _ext.gasPrice);
	set(RuntimeData::PrevHash, _ext.previousBlock.hash);
	set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress));
	set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp);
	set(RuntimeData::Number, _ext.currentBlock.number);
	set(RuntimeData::Difficulty, _ext.currentBlock.difficulty);
	set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit);
	set(RuntimeData::CodeSize, _ext.code.size());   // TODO: Use constant
	data.callData = _ext.data.data();
	data.code = _ext.code.data();
#undef set

	ExecutionEngine engine;
	auto env = reinterpret_cast<Env*>(&_ext);
	auto exitCode = engine.run(std::move(module), &data, env);

	switch (static_cast<ReturnCode>(exitCode))
	{
	case ReturnCode::BadJumpDestination:
		BOOST_THROW_EXCEPTION(BadJumpDestination());
	case ReturnCode::OutOfGas:
		BOOST_THROW_EXCEPTION(OutOfGas());
	case ReturnCode::StackTooSmall:
		BOOST_THROW_EXCEPTION(StackTooSmall());
	case ReturnCode::BadInstruction:
		BOOST_THROW_EXCEPTION(BadInstruction());
	default:
		break;
	}

	m_output = std::move(engine.returnData);
	return {m_output.data(), m_output.size()};  // TODO: This all bytesConstRef stuff sucks
}

}
}
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: bridge.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 22:33:42 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "bridges/cpp_uno/shared/bridge.hxx"

#include "component.hxx"

#include "bridges/cpp_uno/shared/cppinterfaceproxy.hxx"
#include "bridges/cpp_uno/shared/unointerfaceproxy.hxx"

#include "com/sun/star/uno/XInterface.hpp"
#include "osl/diagnose.h"
#include "osl/interlck.h"
#include "rtl/ustring.h"
#include "sal/types.h"
#include "typelib/typedescription.h"
#include "uno/dispatcher.h"
#include "uno/environment.h"
#include "uno/mapping.h"

using bridges::cpp_uno::shared::Bridge;

uno_Mapping * Bridge::createMapping(
    uno_ExtEnvironment * pCppEnv, uno_ExtEnvironment * pUnoEnv,
    bool bExportCpp2Uno) SAL_THROW(())
{
    Bridge * bridge = new Bridge(pCppEnv, pUnoEnv, bExportCpp2Uno);
    return bExportCpp2Uno ? &bridge->aCpp2Uno : &bridge->aUno2Cpp;
}

void Bridge::freeMapping(uno_Mapping * pMapping) SAL_THROW(())
{
    delete static_cast< Mapping * >( pMapping )->pBridge;
}

void Bridge::acquire() SAL_THROW(())
{
    if (1 == osl_incrementInterlockedCount( &nRef ))
    {
        if (bExportCpp2Uno)
        {
            uno_Mapping * pMapping = &aCpp2Uno;
            ::uno_registerMapping(
                &pMapping, freeMapping,
                (uno_Environment *)pCppEnv, (uno_Environment *)pUnoEnv, 0 );
        }
        else
        {
            uno_Mapping * pMapping = &aUno2Cpp;
            ::uno_registerMapping(
                &pMapping, freeMapping,
                (uno_Environment *)pUnoEnv, (uno_Environment *)pCppEnv, 0 );
        }
    }
}

void Bridge::release() SAL_THROW(())
{
    if (! osl_decrementInterlockedCount( &nRef ))
    {
        ::uno_revokeMapping( bExportCpp2Uno ? &aCpp2Uno : &aUno2Cpp );
    }
}

Bridge::Bridge(
    uno_ExtEnvironment * pCppEnv_, uno_ExtEnvironment * pUnoEnv_,
    bool bExportCpp2Uno_) SAL_THROW(())
    : nRef( 1 )
    , pCppEnv( pCppEnv_ )
    , pUnoEnv( pUnoEnv_ )
    , bExportCpp2Uno( bExportCpp2Uno_ )
{
    bridges::cpp_uno::shared::g_moduleCount.modCnt.acquire(
        &bridges::cpp_uno::shared::g_moduleCount.modCnt );

    aCpp2Uno.pBridge = this;
    aCpp2Uno.acquire = acquireMapping;
    aCpp2Uno.release = releaseMapping;
    aCpp2Uno.mapInterface = cpp2unoMapping;

    aUno2Cpp.pBridge = this;
    aUno2Cpp.acquire = acquireMapping;
    aUno2Cpp.release = releaseMapping;
    aUno2Cpp.mapInterface = uno2cppMapping;

    (*((uno_Environment *)pCppEnv)->acquire)( (uno_Environment *)pCppEnv );
    (*((uno_Environment *)pUnoEnv)->acquire)( (uno_Environment *)pUnoEnv );
}

Bridge::~Bridge() SAL_THROW(())
{
    (*((uno_Environment *)pUnoEnv)->release)( (uno_Environment *)pUnoEnv );
    (*((uno_Environment *)pCppEnv)->release)( (uno_Environment *)pCppEnv );
    bridges::cpp_uno::shared::g_moduleCount.modCnt.release(
        &bridges::cpp_uno::shared::g_moduleCount.modCnt );
}

void Bridge::acquireMapping(uno_Mapping * pMapping) SAL_THROW(())
{
    static_cast< Mapping * >( pMapping )->pBridge->acquire();
}

void Bridge::releaseMapping(uno_Mapping * pMapping) SAL_THROW(())
{
    static_cast< Mapping * >( pMapping )->pBridge->release();
}

void Bridge::cpp2unoMapping(
    uno_Mapping * pMapping, void ** ppUnoI, void * pCppI,
    typelib_InterfaceTypeDescription * pTypeDescr) SAL_THROW(())
{
    OSL_ENSURE( ppUnoI && pTypeDescr, "### null ptr!" );
    if (*ppUnoI)
    {
        (*reinterpret_cast< uno_Interface * >( *ppUnoI )->release)(
            reinterpret_cast< uno_Interface * >( *ppUnoI ) );
        *ppUnoI = 0;
    }
    if (pCppI)
    {
        Bridge * pBridge = static_cast< Mapping * >( pMapping )->pBridge;

        // get object id of interface to be wrapped
        rtl_uString * pOId = 0;
        (*pBridge->pCppEnv->getObjectIdentifier)(
            pBridge->pCppEnv, &pOId, pCppI );
        OSL_ASSERT( pOId );

        // try to get any known interface from target environment
        (*pBridge->pUnoEnv->getRegisteredInterface)(
            pBridge->pUnoEnv, ppUnoI, pOId, pTypeDescr );

        if (! *ppUnoI) // no existing interface, register new proxy interface
        {
            // try to publish a new proxy (refcount initially 1)
            uno_Interface * pSurrogate
                = bridges::cpp_uno::shared::UnoInterfaceProxy::create(
                    pBridge,
                    static_cast< ::com::sun::star::uno::XInterface * >( pCppI ),
                    pTypeDescr, pOId );

            // proxy may be exchanged during registration
            (*pBridge->pUnoEnv->registerProxyInterface)(
                pBridge->pUnoEnv, reinterpret_cast< void ** >( &pSurrogate ),
                bridges::cpp_uno::shared::UnoInterfaceProxy::free, pOId,
                pTypeDescr );

            *ppUnoI = pSurrogate;
        }
        ::rtl_uString_release( pOId );
    }
}

void Bridge::uno2cppMapping(
    uno_Mapping * pMapping, void ** ppCppI, void * pUnoI,
    typelib_InterfaceTypeDescription * pTypeDescr) SAL_THROW(())
{
    OSL_ASSERT( ppCppI && pTypeDescr );
    if (*ppCppI)
    {
        static_cast< ::com::sun::star::uno::XInterface * >( *ppCppI )->
            release();
        *ppCppI = 0;
    }
    if (pUnoI)
    {
        Bridge * pBridge = static_cast< Mapping * >( pMapping )->pBridge;

        // get object id of uno interface to be wrapped
        rtl_uString * pOId = 0;
        (*pBridge->pUnoEnv->getObjectIdentifier)(
            pBridge->pUnoEnv, &pOId, pUnoI );
        OSL_ASSERT( pOId );

        // try to get any known interface from target environment
        (*pBridge->pCppEnv->getRegisteredInterface)(
            pBridge->pCppEnv, ppCppI, pOId, pTypeDescr );

        if (! *ppCppI) // no existing interface, register new proxy interface
        {
            // try to publish a new proxy (ref count initially 1)
            com::sun::star::uno::XInterface * pProxy
                = bridges::cpp_uno::shared::CppInterfaceProxy::create(
                    pBridge, static_cast< uno_Interface * >( pUnoI ),
                    pTypeDescr, pOId );

            // proxy may be exchanged during registration
            (*pBridge->pCppEnv->registerProxyInterface)(
                pBridge->pCppEnv, reinterpret_cast< void ** >( &pProxy ),
                bridges::cpp_uno::shared::CppInterfaceProxy::free, pOId,
                pTypeDescr );

            *ppCppI = pProxy;
        }
        ::rtl_uString_release( pOId );
    }
}
<commit_msg>INTEGRATION: CWS warnings01 (1.2.100); FILE MERGED 2005/09/22 18:26:43 sb 1.2.100.2: RESYNC: (1.2-1.3); FILE MERGED 2005/09/09 12:37:43 sb 1.2.100.1: #i53898# Made code warning-free.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: bridge.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 23:45:29 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "bridges/cpp_uno/shared/bridge.hxx"

#include "component.hxx"

#include "bridges/cpp_uno/shared/cppinterfaceproxy.hxx"
#include "bridges/cpp_uno/shared/unointerfaceproxy.hxx"

#include "com/sun/star/uno/XInterface.hpp"
#include "osl/diagnose.h"
#include "osl/interlck.h"
#include "rtl/ustring.h"
#include "sal/types.h"
#include "typelib/typedescription.h"
#include "uno/dispatcher.h"
#include "uno/environment.h"
#include "uno/mapping.h"

namespace bridges { namespace cpp_uno { namespace shared {

void freeMapping(uno_Mapping * pMapping)
{
    delete static_cast< Bridge::Mapping * >( pMapping )->pBridge;
}

void acquireMapping(uno_Mapping * pMapping)
{
    static_cast< Bridge::Mapping * >( pMapping )->pBridge->acquire();
}

void releaseMapping(uno_Mapping * pMapping)
{
    static_cast< Bridge::Mapping * >( pMapping )->pBridge->release();
}

void cpp2unoMapping(
    uno_Mapping * pMapping, void ** ppUnoI, void * pCppI,
    typelib_InterfaceTypeDescription * pTypeDescr)
{
    OSL_ENSURE( ppUnoI && pTypeDescr, "### null ptr!" );
    if (*ppUnoI)
    {
        (*reinterpret_cast< uno_Interface * >( *ppUnoI )->release)(
            reinterpret_cast< uno_Interface * >( *ppUnoI ) );
        *ppUnoI = 0;
    }
    if (pCppI)
    {
        Bridge * pBridge = static_cast< Bridge::Mapping * >( pMapping )->pBridge;

        // get object id of interface to be wrapped
        rtl_uString * pOId = 0;
        (*pBridge->pCppEnv->getObjectIdentifier)(
            pBridge->pCppEnv, &pOId, pCppI );
        OSL_ASSERT( pOId );

        // try to get any known interface from target environment
        (*pBridge->pUnoEnv->getRegisteredInterface)(
            pBridge->pUnoEnv, ppUnoI, pOId, pTypeDescr );

        if (! *ppUnoI) // no existing interface, register new proxy interface
        {
            // try to publish a new proxy (refcount initially 1)
            uno_Interface * pSurrogate
                = bridges::cpp_uno::shared::UnoInterfaceProxy::create(
                    pBridge,
                    static_cast< ::com::sun::star::uno::XInterface * >( pCppI ),
                    pTypeDescr, pOId );

            // proxy may be exchanged during registration
            (*pBridge->pUnoEnv->registerProxyInterface)(
                pBridge->pUnoEnv, reinterpret_cast< void ** >( &pSurrogate ),
                freeUnoInterfaceProxy, pOId,
                pTypeDescr );

            *ppUnoI = pSurrogate;
        }
        ::rtl_uString_release( pOId );
    }
}

void uno2cppMapping(
    uno_Mapping * pMapping, void ** ppCppI, void * pUnoI,
    typelib_InterfaceTypeDescription * pTypeDescr)
{
    OSL_ASSERT( ppCppI && pTypeDescr );
    if (*ppCppI)
    {
        static_cast< ::com::sun::star::uno::XInterface * >( *ppCppI )->
            release();
        *ppCppI = 0;
    }
    if (pUnoI)
    {
        Bridge * pBridge = static_cast< Bridge::Mapping * >( pMapping )->pBridge;

        // get object id of uno interface to be wrapped
        rtl_uString * pOId = 0;
        (*pBridge->pUnoEnv->getObjectIdentifier)(
            pBridge->pUnoEnv, &pOId, pUnoI );
        OSL_ASSERT( pOId );

        // try to get any known interface from target environment
        (*pBridge->pCppEnv->getRegisteredInterface)(
            pBridge->pCppEnv, ppCppI, pOId, pTypeDescr );

        if (! *ppCppI) // no existing interface, register new proxy interface
        {
            // try to publish a new proxy (ref count initially 1)
            com::sun::star::uno::XInterface * pProxy
                = bridges::cpp_uno::shared::CppInterfaceProxy::create(
                    pBridge, static_cast< uno_Interface * >( pUnoI ),
                    pTypeDescr, pOId );

            // proxy may be exchanged during registration
            (*pBridge->pCppEnv->registerProxyInterface)(
                pBridge->pCppEnv, reinterpret_cast< void ** >( &pProxy ),
                freeCppInterfaceProxy, pOId,
                pTypeDescr );

            *ppCppI = pProxy;
        }
        ::rtl_uString_release( pOId );
    }
}

uno_Mapping * Bridge::createMapping(
    uno_ExtEnvironment * pCppEnv, uno_ExtEnvironment * pUnoEnv,
    bool bExportCpp2Uno) SAL_THROW(())
{
    Bridge * bridge = new Bridge(pCppEnv, pUnoEnv, bExportCpp2Uno);
    return bExportCpp2Uno ? &bridge->aCpp2Uno : &bridge->aUno2Cpp;
}

void Bridge::acquire() SAL_THROW(())
{
    if (1 == osl_incrementInterlockedCount( &nRef ))
    {
        if (bExportCpp2Uno)
        {
            uno_Mapping * pMapping = &aCpp2Uno;
            ::uno_registerMapping(
                &pMapping, freeMapping, (uno_Environment *)pCppEnv,
                (uno_Environment *)pUnoEnv, 0 );
        }
        else
        {
            uno_Mapping * pMapping = &aUno2Cpp;
            ::uno_registerMapping(
                &pMapping, freeMapping, (uno_Environment *)pUnoEnv,
                (uno_Environment *)pCppEnv, 0 );
        }
    }
}

void Bridge::release() SAL_THROW(())
{
    if (! osl_decrementInterlockedCount( &nRef ))
    {
        ::uno_revokeMapping( bExportCpp2Uno ? &aCpp2Uno : &aUno2Cpp );
    }
}

Bridge::Bridge(
    uno_ExtEnvironment * pCppEnv_, uno_ExtEnvironment * pUnoEnv_,
    bool bExportCpp2Uno_) SAL_THROW(())
    : nRef( 1 )
    , pCppEnv( pCppEnv_ )
    , pUnoEnv( pUnoEnv_ )
    , bExportCpp2Uno( bExportCpp2Uno_ )
{
    bridges::cpp_uno::shared::g_moduleCount.modCnt.acquire(
        &bridges::cpp_uno::shared::g_moduleCount.modCnt );

    aCpp2Uno.pBridge = this;
    aCpp2Uno.acquire = acquireMapping;
    aCpp2Uno.release = releaseMapping;
    aCpp2Uno.mapInterface = cpp2unoMapping;

    aUno2Cpp.pBridge = this;
    aUno2Cpp.acquire = acquireMapping;
    aUno2Cpp.release = releaseMapping;
    aUno2Cpp.mapInterface = uno2cppMapping;

    (*((uno_Environment *)pCppEnv)->acquire)( (uno_Environment *)pCppEnv );
    (*((uno_Environment *)pUnoEnv)->acquire)( (uno_Environment *)pUnoEnv );
}

Bridge::~Bridge() SAL_THROW(())
{
    (*((uno_Environment *)pUnoEnv)->release)( (uno_Environment *)pUnoEnv );
    (*((uno_Environment *)pCppEnv)->release)( (uno_Environment *)pCppEnv );
    bridges::cpp_uno::shared::g_moduleCount.modCnt.release(
        &bridges::cpp_uno::shared::g_moduleCount.modCnt );
}

} } }
<|endoftext|>


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

#define _WITH_DPRINTF

#include <cstdlib>
#include <cstdio>
#include <cassert>

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <iostream>

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

#include "debug.h"
#include "httpclient.h"
#include "httprepo.h"
#include "util.h"

#define D_READ 0
#define D_WRITE 1

using namespace std;

/*
 * HttpClient
 */
HttpClient::HttpClient(const std::string &remotePath)
{
    string tmp;
    size_t portPos, pathPos;

    if (remotePath.substr(0, 7) == "http://") {
        tmp = remotePath.substr(7);
    } else {
        assert(false);
    }

    portPos = tmp.find(':');
    pathPos = tmp.find('/');
    if (portPos != -1) {
        assert(portPos < pathPos);
        remotePort = tmp.substr(portPos + 1, pathPos - portPos - 1);
    } else {
        portPos = pathPos;
        remotePort = "80";
    }
    assert(pathPos != -1);

    remoteHost = tmp.substr(0, portPos);
    remoteRepo = tmp.substr(pathPos + 1);
}

HttpClient::~HttpClient()
{
    // Close
}

int
HttpClient::connect()
{
    string id;
    string version;
    string headRev;
    uint16_t port;

    base = event_base_new();
    dnsBase = evdns_base_new(base, /* Add DNS servers */ 0);

    port = strtoul(remotePort.c_str(), NULL, 10);

    con = evhttp_connection_base_new(base, dnsBase, remoteHost.c_str(), port);

    return 0;
}

void
HttpClient::disconnect()
{
    if (con)
        evhttp_connection_free(con);
    if (dnsBase)
        evdns_base_free(dnsBase, 0);
    if (base)
        event_base_free(base);
    con = NULL;
    dnsBase = NULL;
    base = NULL;
}

bool
HttpClient::connected()
{
    return false;
}

void
HttpClient_requestDoneCB(struct evhttp_request *req, void *c)
{
    int status;
    HttpClient *client = (HttpClient *)c;
    struct evkeyvalq *headers;
    struct evbuffer *bufIn;

    if (!req) {
        cout << "req is NULL!" << endl;
        return;
    }

    status = evhttp_request_get_response_code(req);
    if (status != HTTP_OK) {
        cout << "Failed!" << endl;
        return;
    }

    headers = evhttp_request_get_input_headers(req);
    bufIn = evhttp_request_get_input_buffer(req);

    /*
     * XXX: This is a hack to prevent the corruption we were seeing. Not sure
     * why this works, but the data needs to be read out here into a string and
     * we should signal the client to wakeup or register a callback.
     */
    int len = evbuffer_get_length(bufIn);
    evbuffer_pullup(bufIn, len);

    event_base_loopexit(client->base, NULL);
}

int
HttpClient::getRequest(const string &command, string &response)
{
    int status;
    struct evhttp_request *req;

    req = evhttp_request_new(HttpClient_requestDoneCB, (void *)this);

    evhttp_add_header(evhttp_request_get_output_headers(req),
                      "Connection", "keep-alive");

    status = evhttp_make_request(con, req, EVHTTP_REQ_GET, command.c_str());
    if (status == -1) {
        cout << "Request failure!" << endl;
        return -1;
    }

    // XXX: Create dedicated event loop
    event_base_dispatch(base);

    struct evbuffer *bufIn = evhttp_request_get_input_buffer(req);

    int len = evbuffer_get_length(bufIn);
    char *data = (char *)evbuffer_pullup(bufIn, len);

    response.assign(data, len);

    return 0;
}

int
HttpClient::putRequest(const string &command,
                       const string &payload,
                       string &response)
{
    NOT_IMPLEMENTED(false);
    return -1;
}

int
cmd_httpclient(int argc, const char *argv[])
{
    if (argc < 2) {
        printf("Usage: ori httpclient http://hostname:port/path\n");
        exit(1);
    }

    HttpClient client = HttpClient(std::string(argv[1]));
    if (client.connect() < 0) {
        printf("Error connecting to %s\n", argv[1]);
        exit(1);
    }

    dprintf(STDOUT_FILENO, "Connected\n");

    string repoId;
    string ver;
    string headId;
    client.getRequest("/id", repoId);
    client.getRequest("/version", ver);
    client.getRequest("/HEAD", headId);

    cout << "Repository ID: " << repoId << endl;
    cout << "Version: " << ver << endl;
    cout << "HEAD: " << headId << endl << endl;

    HttpRepo repo(&client);
    set<ObjectInfo> objs = repo.listObjects();
    for (set<ObjectInfo>::iterator it = objs.begin();
         it != objs.end();
         it++) {
        printf("%s\n", (*it).hash.c_str());
    }

    printf("Terminating HTTP connection...\n");
    return 0;
}
<commit_msg>Fixing bug in HTTPClient.<commit_after>/*
 * Copyright (c) 2012 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define _WITH_DPRINTF

#include <cstdlib>
#include <cstdio>
#include <cassert>

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <iostream>

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

#include "debug.h"
#include "httpclient.h"
#include "httprepo.h"
#include "util.h"

#define D_READ 0
#define D_WRITE 1

using namespace std;

/*
 * HttpClient
 */
HttpClient::HttpClient(const std::string &remotePath)
{
    string tmp;
    size_t portPos, pathPos;

    if (remotePath.substr(0, 7) == "http://") {
        tmp = remotePath.substr(7);
    } else {
        assert(false);
    }

    portPos = tmp.find(':');
    pathPos = tmp.find('/');
    if (portPos != -1) {
        assert(portPos < pathPos);
        remotePort = tmp.substr(portPos + 1, pathPos - portPos - 1);
    } else {
        portPos = pathPos;
        remotePort = "80";
    }
    assert(pathPos != -1);

    remoteHost = tmp.substr(0, portPos);
    remoteRepo = tmp.substr(pathPos + 1);
}

HttpClient::~HttpClient()
{
    // Close
}

int
HttpClient::connect()
{
    string id;
    string version;
    string headRev;
    uint16_t port;

    base = event_base_new();
    dnsBase = evdns_base_new(base, /* Add DNS servers */ 0);

    port = strtoul(remotePort.c_str(), NULL, 10);

    con = evhttp_connection_base_new(base, dnsBase, remoteHost.c_str(), port);

    return 0;
}

void
HttpClient::disconnect()
{
    if (con)
        evhttp_connection_free(con);
    if (dnsBase)
        evdns_base_free(dnsBase, 0);
    if (base)
        event_base_free(base);
    con = NULL;
    dnsBase = NULL;
    base = NULL;
}

bool
HttpClient::connected()
{
    return false;
}

struct RequestCB
{
    HttpClient *client;
    string *response;
};

void
HttpClient_requestDoneCB(struct evhttp_request *req, void *r)
{
    int status;
    RequestCB *cb = (RequestCB *)r;
    HttpClient *client = cb->client;
    struct evkeyvalq *headers;
    struct evbuffer *bufIn;

    if (!req) {
        cout << "req is NULL!" << endl;
        return;
    }

    status = evhttp_request_get_response_code(req);
    if (status != HTTP_OK) {
        cout << "Failed!" << endl;
        return;
    }

    headers = evhttp_request_get_input_headers(req);
    bufIn = evhttp_request_get_input_buffer(req);

    /*
     * XXX: This is a hack to prevent the corruption we were seeing. Not sure
     * why this works, but the data needs to be read out here into a string and
     * we should signal the client to wakeup or register a callback.
     */
    int len = evbuffer_get_length(bufIn);
    char *data = (char *)evbuffer_pullup(bufIn, len);

    cb->response->assign(data, len);

    event_base_loopexit(client->base, NULL);
}

int
HttpClient::getRequest(const string &command, string &response)
{
    int status;
    RequestCB cb;
    struct evhttp_request *req;

    cb.client = this;
    cb.response = &response;
    req = evhttp_request_new(HttpClient_requestDoneCB, (void *)&cb);

    evhttp_add_header(evhttp_request_get_output_headers(req),
                      "Connection", "keep-alive");

    status = evhttp_make_request(con, req, EVHTTP_REQ_GET, command.c_str());
    if (status == -1) {
        cout << "Request failure!" << endl;
        return -1;
    }

    // XXX: Create dedicated event loop
    event_base_dispatch(base);

    return 0;
}

int
HttpClient::putRequest(const string &command,
                       const string &payload,
                       string &response)
{
    NOT_IMPLEMENTED(false);
    return -1;
}

int
cmd_httpclient(int argc, const char *argv[])
{
    if (argc < 2) {
        printf("Usage: ori httpclient http://hostname:port/path\n");
        exit(1);
    }

    HttpClient client = HttpClient(std::string(argv[1]));
    if (client.connect() < 0) {
        printf("Error connecting to %s\n", argv[1]);
        exit(1);
    }

    dprintf(STDOUT_FILENO, "Connected\n");

    string repoId;
    string ver;
    string headId;
    client.getRequest("/id", repoId);
    client.getRequest("/version", ver);
    client.getRequest("/HEAD", headId);

    cout << "Repository ID: " << repoId << endl;
    cout << "Version: " << ver << endl;
    cout << "HEAD: " << headId << endl << endl;

    HttpRepo repo(&client);
    set<ObjectInfo> objs = repo.listObjects();
    for (set<ObjectInfo>::iterator it = objs.begin();
         it != objs.end();
         it++) {
        printf("%s\n", (*it).hash.c_str());
    }

    printf("Terminating HTTP connection...\n");
    return 0;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *  Copyright (c) 2014 Jamis Hoo
 *  Distributed under the MIT license
 *  (See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT)
 *
 *  Project:
 *  Filename: test.cc
 *  Version: 1.0
 *  Author: Jamis Hoo
 *  E-mail: hjm211324@gmail.com
 *  Date: Dec. 29, 2014
 *  Time: 11:10:09
 *  Description:
 *****************************************************************************/
#include <iostream>

int main() {
    std::cout << "Hello world!" << std::endl;

}

<commit_msg>--allow-empty_message<commit_after>/******************************************************************************
 *  Copyright (c) 2014 Jamis Hoo
 *  Distributed under the MIT license
 *  (See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT)
 *
 *  Project:
 *  Filename: test.cc
 *  Version: 1.0
 *  Author: Jamis Hoo
 *  E-mail: hjm211324@gmail.com
 *  Date: Dec. 29, 2014
 *  Time: 11:10:09
 *  Description:
 *****************************************************************************/
#include <iostream>

int main() {
    std::cout << "Hello world!" << std::endl;

}

<|endoftext|>


<commit_before>#include <cmath>
#include <QDateTime>
#include <QsLog.h>
#include <QTimer>
#include "ac_sensor.h"
#include "control_loop.h"
#include "multi.h"
#include "multi_phase_data.h"
#include "power_info.h"
#include "settings.h"

ControlLoop::ControlLoop(Multi *multi, Phase phase, AcSensor *AcSensor,
						 Settings *settings, Clock *clock, QObject *parent):
	QObject(parent),
	mMulti(multi),
	mAcSensor(AcSensor),
	mSettings(settings),
	mTimer(new QTimer(this)),
	mClock(clock),
	mPhase(phase),
	mMultiUpdate(false),
	mMeterUpdate(false)
{
	Q_ASSERT(multi != 0);
	Q_ASSERT(multi->getPhaseData(phase) != 0);
	Q_ASSERT(AcSensor != 0);
	Q_ASSERT(settings != 0);
	if (mClock == 0)
		mClock.reset(new DefaultClock());
	mTimer->setInterval(5000);
	mTimer->start();
	connect(mMulti, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mMulti->getPhaseData(mPhase), SIGNAL(acPowerOutChanged()),
			this, SLOT(onPowerFromMulti()));
	connect(mAcSensor, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mAcSensor->getPowerInfo(mPhase), SIGNAL(powerChanged()),
			this, SLOT(onPowerFromMeter()));
	connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
}

Phase ControlLoop::phase() const
{
	return mPhase;
}

void ControlLoop::onDestroyed()
{
	deleteLater();
}

void ControlLoop::onTimer()
{
	QLOG_DEBUG() << "Update timeout";
	mMultiUpdate = true;
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMulti()
{
	mMultiUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMeter()
{
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::checkStep()
{
	if (!mMeterUpdate || !mMultiUpdate)
		return;
	performStep();
	mMeterUpdate = false;
	mMultiUpdate = false;
	mTimer->start();
}

void ControlLoop::performStep()
{
	// pMultiNew > 0: battery is discharging
	// pMultiNew < 0: battery is charging
	double pMultiNew = 0;
	double maxChargePct = qMax(0.0, qMin(100.0, mSettings->maxChargePercentage()));
	double maxDischargePct = qMax(0.0, qMin(100.0, mSettings->maxDischargePercentage()));
	double minPower =
			-maxChargePct *
			mMulti->maxChargeCurrent() *
			mMulti->dcVoltage() / 100;

	if (mSettings->maintenanceInterval() == 0) {
		setHub4State(Hub4External);
	} else if (mSettings->state() == Hub4External) {
		setHub4State(Hub4SelfConsumption);
	}

	switch (mSettings->state()) {
	case Hub4SelfConsumption:
	{
		pMultiNew = computeSetpoint();
		QDateTime nextCharge = mSettings->maintenanceDate();
		if (!nextCharge.isValid()) {
			updateMaintenanceDate();
			nextCharge = mSettings->maintenanceDate();
		}
		nextCharge = nextCharge.addDays(mSettings->maintenanceInterval());
		QDateTime now = mClock->now();
		if (now >= nextCharge && now.time().hour() == 12) {
			setHub4State(Hub4ChargeFromGrid);
		}

		if (isMultiCharged()) {
			setHub4State(Hub4Charged);
		}
		break;
	}
	case Hub4Charged:
		pMultiNew = computeSetpoint();
		if (!isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4External:
		pMultiNew = computeSetpoint();
		break;
	case Hub4ChargeFromGrid:
		pMultiNew = minPower;
		if (isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4Storage:
		pMultiNew = minPower;
		if (isMultiCharged())
			mSettings->setMaintenanceDate(QDateTime());
		break;
	}

	bool feedbackDisabled = maxDischargePct < 50;
	// It seems that enabling the the ChargeDisabled flag disables both charge
	// and discharge. So we're only setting the flags when we are not
	// discharging.
	bool chargeDisabled = maxChargePct <= 0 &&
						  (feedbackDisabled || pMultiNew < -30);
	pMultiNew = qMax(minPower, pMultiNew);

	// Ugly workaround: the value of pMultiNew must always be sent over the
	// D-Bus, even when it does not change, because the multi will reset its
	// power setpoint if no value has been set during the last 10 seconds.
	// So we add a random value to ensure pMultiNew changes. Other solution
	// would involve changes in velib (VBusItem and friends).
	pMultiNew += qrand() / (100.0 * RAND_MAX);

	mMulti->setIsChargeDisabled(chargeDisabled);
	mMulti->setIsFeedbackDisabled(feedbackDisabled);
	// If feedback is disabled and the multi is 'on', it will start inverting
	// when the grid drops ways (emergency power). If we set the multi to
	// 'charge only', it will not start inverting so the system on AC-In and
	// AC-Out will lose power.
	// mMulti->setMode(feedbackDisabled ? MultiChargerOnly : MultiOn);
	if (std::isfinite(pMultiNew))
		mMulti->setAcPowerSetPoint(-pMultiNew);
}

double ControlLoop::computeSetpoint() const
{
	PowerInfo *pi = mAcSensor->getPowerInfo(mPhase);
	double pNet = pi->power();
	MultiPhaseData *mpd = mMulti->getPhaseData(mPhase);
	double pMulti = -mpd->acPowerIn();
	double pLoad = pNet + pMulti - mSettings->acPowerSetPoint();
	if (!std::isfinite(pNet))
		return NaN;
	// EV: EM340 seems to work better with Alpha = 0.6. Possibly due to lower
	// update rate.
	const double Alpha = 0.8;
	double pMultiNew = Alpha * pLoad + (1 - Alpha) * pMulti;
	QLOG_TRACE() << pNet << '\t' << pLoad << '\t' << pMulti << '\t' << pMultiNew;
	return pMultiNew;
}

bool ControlLoop::isMultiCharged() const
{
	return mMulti->state() == MultiStateFloat || mMulti->state() == MultiStateStorage;
}

void ControlLoop::updateMaintenanceDate()
{
	QDateTime nextCharge = mClock->now();
	nextCharge.setTime(QTime(1, 0, 0));
	mSettings->setMaintenanceDate(nextCharge);
	QLOG_INFO() << "Next maintenance base date set at:" << nextCharge;
}

void ControlLoop::setHub4State(Hub4State state)
{
	if (mSettings->state() == state)
		return;
	QLOG_INFO() << "Changing Hub4 state from"
				<< getStateName(mSettings->state())
				<< "to" << getStateName(state);
	mSettings->setState(state);
}

const char *ControlLoop::getStateName(int state)
{
	static const char *StateNames[] = { "SelfConsumption", "ChargeFromGrid", "Charged", "Storage", "External" };
	static const int StateNameCount	= static_cast<int>(sizeof(StateNames)/sizeof(StateNames[0]));
	if (state < 0 || state >= StateNameCount)
		return "Unknown";
	return StateNames[state];
}
<commit_msg>Changed sign of computed AC power setpoint improving readability.<commit_after>#include <cmath>
#include <QDateTime>
#include <QsLog.h>
#include <QTimer>
#include "ac_sensor.h"
#include "control_loop.h"
#include "multi.h"
#include "multi_phase_data.h"
#include "power_info.h"
#include "settings.h"

ControlLoop::ControlLoop(Multi *multi, Phase phase, AcSensor *AcSensor,
						 Settings *settings, Clock *clock, QObject *parent):
	QObject(parent),
	mMulti(multi),
	mAcSensor(AcSensor),
	mSettings(settings),
	mTimer(new QTimer(this)),
	mClock(clock),
	mPhase(phase),
	mMultiUpdate(false),
	mMeterUpdate(false)
{
	Q_ASSERT(multi != 0);
	Q_ASSERT(multi->getPhaseData(phase) != 0);
	Q_ASSERT(AcSensor != 0);
	Q_ASSERT(settings != 0);
	if (mClock == 0)
		mClock.reset(new DefaultClock());
	mTimer->setInterval(5000);
	mTimer->start();
	connect(mMulti, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mMulti->getPhaseData(mPhase), SIGNAL(acPowerOutChanged()),
			this, SLOT(onPowerFromMulti()));
	connect(mAcSensor, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mAcSensor->getPowerInfo(mPhase), SIGNAL(powerChanged()),
			this, SLOT(onPowerFromMeter()));
	connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
}

Phase ControlLoop::phase() const
{
	return mPhase;
}

void ControlLoop::onDestroyed()
{
	deleteLater();
}

void ControlLoop::onTimer()
{
	QLOG_DEBUG() << "Update timeout";
	mMultiUpdate = true;
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMulti()
{
	mMultiUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMeter()
{
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::checkStep()
{
	if (!mMeterUpdate || !mMultiUpdate)
		return;
	performStep();
	mMeterUpdate = false;
	mMultiUpdate = false;
	mTimer->start();
}

void ControlLoop::performStep()
{
	// pMultiNew < 0: battery is discharging
	// pMultiNew > 0: battery is charging
	double pMultiNew = 0;
	double maxChargePct = qMax(0.0, qMin(100.0, mSettings->maxChargePercentage()));
	double maxDischargePct = qMax(0.0, qMin(100.0, mSettings->maxDischargePercentage()));
	double maxPower =
			maxChargePct *
			mMulti->maxChargeCurrent() *
			mMulti->dcVoltage() / 100;

	if (mSettings->maintenanceInterval() == 0) {
		setHub4State(Hub4External);
	} else if (mSettings->state() == Hub4External) {
		setHub4State(Hub4SelfConsumption);
	}

	switch (mSettings->state()) {
	case Hub4SelfConsumption:
	{
		pMultiNew = computeSetpoint();
		QDateTime nextCharge = mSettings->maintenanceDate();
		if (!nextCharge.isValid()) {
			updateMaintenanceDate();
			nextCharge = mSettings->maintenanceDate();
		}
		nextCharge = nextCharge.addDays(mSettings->maintenanceInterval());
		QDateTime now = mClock->now();
		if (now >= nextCharge && now.time().hour() == 12) {
			setHub4State(Hub4ChargeFromGrid);
		}

		if (isMultiCharged()) {
			setHub4State(Hub4Charged);
		}
		break;
	}
	case Hub4Charged:
		pMultiNew = computeSetpoint();
		if (!isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4External:
		pMultiNew = computeSetpoint();
		break;
	case Hub4ChargeFromGrid:
		pMultiNew = maxPower;
		if (isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4Storage:
		pMultiNew = maxPower;
		if (isMultiCharged())
			mSettings->setMaintenanceDate(QDateTime());
		break;
	}

	bool feedbackDisabled = maxDischargePct < 50;
	// It seems that enabling the the ChargeDisabled flag disables both charge
	// and discharge. So we're only setting the flags when we are not
	// discharging.
	bool chargeDisabled = maxChargePct <= 0 &&
						  (feedbackDisabled || pMultiNew > 30);
	pMultiNew = qMin(maxPower, pMultiNew);

	// Ugly workaround: the value of pMultiNew must always be sent over the
	// D-Bus, even when it does not change, because the multi will reset its
	// power setpoint if no value has been set during the last 10 seconds.
	// So we add a random value to ensure pMultiNew changes. Other solution
	// would involve changes in velib (VBusItem and friends).
	pMultiNew -= qrand() / (10.0 * RAND_MAX);

	mMulti->setIsChargeDisabled(chargeDisabled);
	mMulti->setIsFeedbackDisabled(feedbackDisabled);
	// If feedback is disabled and the multi is 'on', it will start inverting
	// when the grid drops ways (emergency power). If we set the multi to
	// 'charge only', it will not start inverting so the system on AC-In and
	// AC-Out will lose power.
	// mMulti->setMode(feedbackDisabled ? MultiChargerOnly : MultiOn);
	if (std::isfinite(pMultiNew))
		mMulti->setAcPowerSetPoint(pMultiNew);
}

double ControlLoop::computeSetpoint() const
{
	PowerInfo *pi = mAcSensor->getPowerInfo(mPhase);
	double pNet = pi->power();
	if (!std::isfinite(pNet))
		return NaN;
	MultiPhaseData *mpd = mMulti->getPhaseData(mPhase);
	double pMulti = mpd->acPowerIn();
	double pTarget = mSettings->acPowerSetPoint();
	// EV: EM340 seems to work better with Alpha = 0.6. Possibly due to lower
	// update rate.
	const double Alpha = 0.8;
	return pMulti + Alpha * (pTarget - pNet);
}

bool ControlLoop::isMultiCharged() const
{
	return mMulti->state() == MultiStateFloat || mMulti->state() == MultiStateStorage;
}

void ControlLoop::updateMaintenanceDate()
{
	QDateTime nextCharge = mClock->now();
	nextCharge.setTime(QTime(1, 0, 0));
	mSettings->setMaintenanceDate(nextCharge);
	QLOG_INFO() << "Next maintenance base date set at:" << nextCharge;
}

void ControlLoop::setHub4State(Hub4State state)
{
	if (mSettings->state() == state)
		return;
	QLOG_INFO() << "Changing Hub4 state from"
				<< getStateName(mSettings->state())
				<< "to" << getStateName(state);
	mSettings->setState(state);
}

const char *ControlLoop::getStateName(int state)
{
	static const char *StateNames[] = { "SelfConsumption", "ChargeFromGrid", "Charged", "Storage", "External" };
	static const int StateNameCount	= static_cast<int>(sizeof(StateNames)/sizeof(StateNames[0]));
	if (state < 0 || state >= StateNameCount)
		return "Unknown";
	return StateNames[state];
}
<|endoftext|>


<commit_before>/// \file ROOT/RPageStorage.hxx
/// \ingroup NTuple ROOT7
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2018-07-19
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_RPageStorage
#define ROOT7_RPageStorage

#include <ROOT/RNTupleDescriptor.hxx>
#include <ROOT/RNTupleOptions.hxx>
#include <ROOT/RNTupleUtil.hxx>
#include <ROOT/RPage.hxx>
#include <ROOT/RPageAllocator.hxx>
#include <ROOT/RStringView.hxx>

#include <atomic>
#include <cstddef>
#include <functional>
#include <memory>
#include <unordered_set>

namespace ROOT {
namespace Experimental {

class RNTupleModel;
// TODO(jblomer): factory methods to create tree sinks and sources outside Detail namespace

namespace Detail {

class RCluster;
class RColumn;
class RColumnElementBase;
class RNTupleCompressor;
class RNTupleDecompressor;
class RPagePool;
class RFieldBase;
class RNTupleMetrics;

enum class EPageStorageType {
   kSink,
   kSource,
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageStorage
\ingroup NTuple
\brief Common functionality of an ntuple storage for both reading and writing

The RPageStore provides access to a storage container that keeps the bits of pages and clusters comprising
an ntuple.  Concrete implementations can use a TFile, a raw file, an object store, and so on.
*/
// clang-format on
class RPageStorage {
public:
   /// The interface of a task scheduler to schedule page (de)compression tasks
   class RTaskScheduler {
   public:
      virtual ~RTaskScheduler() = default;
      /// Start a new set of tasks
      virtual void Reset() = 0;
      /// Take a callable that represents a task
      virtual void AddTask(const std::function<void(void)> &taskFunc) = 0;
      /// Blocks until all scheduled tasks finished
      virtual void Wait() = 0;
   };

   /// A sealed page contains the bytes of a page as written to storage (packed & compressed).  It is used
   /// as an input to UnsealPages() as well as to transfer pages between different storage media.
   struct RSealedPage {
      const void *fBuffer = nullptr;
      std::uint32_t fSize = 0;
      std::uint32_t fNElements = 0;

      RSealedPage() = default;
      RSealedPage(const RSealedPage &other) = delete;
      RSealedPage& operator =(const RSealedPage &other) = delete;
      RSealedPage(RSealedPage &&other) = default;
      RSealedPage& operator =(RSealedPage &&other) = default;
   };

protected:
   std::string fNTupleName;
   RTaskScheduler *fTaskScheduler = nullptr;

public:
   explicit RPageStorage(std::string_view name);
   RPageStorage(const RPageStorage &other) = delete;
   RPageStorage& operator =(const RPageStorage &other) = delete;
   RPageStorage(RPageStorage &&other) = default;
   RPageStorage& operator =(RPageStorage &&other) = default;
   virtual ~RPageStorage();

   /// Whether the concrete implementation is a sink or a source
   virtual EPageStorageType GetType() = 0;

   struct RColumnHandle {
      DescriptorId_t fId = kInvalidDescriptorId;
      const RColumn *fColumn = nullptr;

      /// Returns true for a valid column handle; fColumn and fId should always either both
      /// be valid or both be invalid.
      operator bool() const { return fId != kInvalidDescriptorId && fColumn; }
   };
   /// The column handle identifies a column with the current open page storage
   using ColumnHandle_t = RColumnHandle;

   /// Register a new column.  When reading, the column must exist in the ntuple on disk corresponding to the meta-data.
   /// When writing, every column can only be attached once.
   virtual ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) = 0;
   /// Unregisters a column.  A page source decreases the reference counter for the corresponding active column.
   /// For a page sink, dropping columns is currently a no-op.
   virtual void DropColumn(ColumnHandle_t columnHandle) = 0;

   /// Every page store needs to be able to free pages it handed out.  But Sinks and sources have different means
   /// of allocating pages.
   virtual void ReleasePage(RPage &page) = 0;

   /// Returns an empty metrics.  Page storage implementations usually have their own metrics.
   virtual RNTupleMetrics &GetMetrics();

   void SetTaskScheduler(RTaskScheduler *taskScheduler) { fTaskScheduler = taskScheduler; }
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSink
\ingroup NTuple
\brief Abstract interface to write data into an ntuple

The page sink takes the list of columns and afterwards a series of page commits and cluster commits.
The user is responsible to commit clusters at a consistent point, i.e. when all pages corresponding to data
up to the given entry number are committed.
*/
// clang-format on
class RPageSink : public RPageStorage {
protected:
   RNTupleWriteOptions fOptions;

   /// Helper to zip pages and header/footer; comprises a 16MB unzip buffer.
   /// There could be concrete page sinks that don't need a compressor.  Therefore, and in order to stay consitent
   /// with the page source, we leave it up to the derived class whether or not the compressor gets constructed.
   std::unique_ptr<RNTupleCompressor> fCompressor;

   /// Building the ntuple descriptor while writing is done in the same way for all the storage sink implementations.
   /// Field, column, cluster ids and page indexes per cluster are issued sequentially starting with 0
   DescriptorId_t fLastFieldId = 0;
   DescriptorId_t fLastColumnId = 0;
   DescriptorId_t fLastClusterId = 0;
   NTupleSize_t fPrevClusterNEntries = 0;
   /// Keeps track of the number of elements in the currently open cluster. Indexed by column id.
   std::vector<RClusterDescriptor::RColumnRange> fOpenColumnRanges;
   /// Keeps track of the written pages in the currently open cluster. Indexed by column id.
   std::vector<RClusterDescriptor::RPageRange> fOpenPageRanges;
   RNTupleDescriptorBuilder fDescriptorBuilder;

   virtual void CreateImpl(const RNTupleModel &model) = 0;
   virtual RClusterDescriptor::RLocator CommitPageImpl(ColumnHandle_t columnHandle, const RPage &page) = 0;
   virtual RClusterDescriptor::RLocator CommitClusterImpl(NTupleSize_t nEntries) = 0;
   virtual void CommitDatasetImpl() = 0;

   /// Helper for streaming a page. This is commonly used in derived, concrete page sinks. Note that if
   /// compressionSetting is 0 (uncompressed) and the page is mappable, the returned sealed page will
   /// point directly to the input page buffer.  Otherwise, the sealed page references an internal buffer
   /// of fCompressor.  Thus, the buffer pointed to by sealed page should never be freed.
   /// Usage of this method requires construction of fCompressor.
   RSealedPage SealPage(const RPage &page, const RColumnElementBase &element, int compressionSetting);

public:
   RPageSink(std::string_view ntupleName, const RNTupleWriteOptions &options);

   RPageSink(const RPageSink&) = delete;
   RPageSink& operator=(const RPageSink&) = delete;
   RPageSink(RPageSink&&) = default;
   RPageSink& operator=(RPageSink&&) = default;
   virtual ~RPageSink();

   /// Guess the concrete derived page source from the file name (location)
   static std::unique_ptr<RPageSink> Create(std::string_view ntupleName, std::string_view location,
                                            const RNTupleWriteOptions &options = RNTupleWriteOptions());
   EPageStorageType GetType() final { return EPageStorageType::kSink; }

   ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) final;
   void DropColumn(ColumnHandle_t /*columnHandle*/) final {}

   /// Physically creates the storage container to hold the ntuple (e.g., a keys a TFile or an S3 bucket)
   /// To do so, Create() calls CreateImpl() after updating the descriptor.
   /// Create() associates column handles to the columns referenced by the model
   void Create(RNTupleModel &model);
   /// Write a page to the storage. The column must have been added before.
   void CommitPage(ColumnHandle_t columnHandle, const RPage &page);
   /// Finalize the current cluster and create a new one for the following data.
   void CommitCluster(NTupleSize_t nEntries);
   /// Finalize the current cluster and the entrire data set.
   void CommitDataset() { CommitDatasetImpl(); }

   /// Get a new, empty page for the given column that can be filled with up to nElements.  If nElements is zero,
   /// the page sink picks an appropriate size.
   virtual RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements = 0) = 0;
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSource
\ingroup NTuple
\brief Abstract interface to read data from an ntuple

The page source is initialized with the columns of interest. Pages from those columns can then be
mapped into memory. The page source also gives access to the ntuple's meta-data.
*/
// clang-format on
class RPageSource : public RPageStorage {
public:
   /// Derived from the model (fields) that are actually being requested at a given point in time
   using ColumnSet_t = std::unordered_set<DescriptorId_t>;

protected:
   RNTupleReadOptions fOptions;
   RNTupleDescriptor fDescriptor;
   /// The active columns are implicitly defined by the model fields or views
   ColumnSet_t fActiveColumns;

   /// Helper to unzip pages and header/footer; comprises a 16MB unzip buffer.
   /// Not all page sources need a decompressor (e.g. virtual ones for chains and friends don't), thus we
   /// leave it up to the derived class whether or not the decompressor gets constructed.
   std::unique_ptr<RNTupleDecompressor> fDecompressor;

   virtual RNTupleDescriptor AttachImpl() = 0;
   // Only called if a task scheduler is set. No-op be default.
   virtual void UnzipClusterImpl(RCluster * /* cluster */)
      { }

   /// Helper for unstreaming a page. This is commonly used in derived, concrete page sources.  The implementation
   /// currently always makes a memory copy, even if the sealed page is uncompressed and in the final memory layout.
   /// The optimization of directly mapping pages is left to the concrete page source implementations.
   /// Usage of this method requires construction of fDecompressor.
   unsigned char *UnsealPage(const RSealedPage &sealedPage, const RColumnElementBase &element);

public:
   RPageSource(std::string_view ntupleName, const RNTupleReadOptions &fOptions);
   RPageSource(const RPageSource&) = delete;
   RPageSource& operator=(const RPageSource&) = delete;
   RPageSource(RPageSource&&) = default;
   RPageSource& operator=(RPageSource&&) = default;
   virtual ~RPageSource();
   /// Guess the concrete derived page source from the file name (location)
   static std::unique_ptr<RPageSource> Create(std::string_view ntupleName, std::string_view location,
                                              const RNTupleReadOptions &options = RNTupleReadOptions());
   /// Open the same storage multiple time, e.g. for reading in multiple threads
   virtual std::unique_ptr<RPageSource> Clone() const = 0;

   EPageStorageType GetType() final { return EPageStorageType::kSource; }
   const RNTupleDescriptor &GetDescriptor() const { return fDescriptor; }
   ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) final;
   void DropColumn(ColumnHandle_t columnHandle) final;

   /// Open the physical storage container for the tree
   void Attach() { fDescriptor = AttachImpl(); }
   NTupleSize_t GetNEntries();
   NTupleSize_t GetNElements(ColumnHandle_t columnHandle);
   ColumnId_t GetColumnId(ColumnHandle_t columnHandle);

   /// Allocates and fills a page that contains the index-th element
   virtual RPage PopulatePage(ColumnHandle_t columnHandle, NTupleSize_t globalIndex) = 0;
   /// Another version of PopulatePage that allows to specify cluster-relative indexes
   virtual RPage PopulatePage(ColumnHandle_t columnHandle, const RClusterIndex &clusterIndex) = 0;

   /// Populates all the pages of the given cluster id and columns; it is possible that some columns do not
   /// contain any pages.  The pages source may load more columns than the minimal necessary set from `columns`.
   /// To indicate which columns have been loaded, LoadCluster() must mark them with SetColumnAvailable().
   /// That includes the ones from the `columns` that don't have pages; otherwise subsequent requests
   /// for the cluster would assume an incomplete cluster and trigger loading again.
   /// LoadCluster() is typically called from the I/O thread of a cluster pool, i.e. the method runs
   /// concurrently to other methods of the page source.
   virtual std::unique_ptr<RCluster> LoadCluster(DescriptorId_t clusterId, const ColumnSet_t &columns) = 0;

   /// Parallel decompression and unpacking of the pages in the given cluster. The unzipped pages are supposed
   /// to be preloaded in a page pool attached to the source. The method is triggered by the cluster pool's
   /// unzip thread. It is an optional optimization, the method can safely do nothing. In particular, the
   /// actual implementation will only run if a task scheduler is set. In practice, a task scheduler is set
   /// if implicit multi-threading is turned on.
   void UnzipCluster(RCluster *cluster);
};

} // namespace Detail

} // namespace Experimental
} // namespace ROOT

#endif
<commit_msg>[ntuple] Fix typo in code comment (NFC)<commit_after>/// \file ROOT/RPageStorage.hxx
/// \ingroup NTuple ROOT7
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2018-07-19
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_RPageStorage
#define ROOT7_RPageStorage

#include <ROOT/RNTupleDescriptor.hxx>
#include <ROOT/RNTupleOptions.hxx>
#include <ROOT/RNTupleUtil.hxx>
#include <ROOT/RPage.hxx>
#include <ROOT/RPageAllocator.hxx>
#include <ROOT/RStringView.hxx>

#include <atomic>
#include <cstddef>
#include <functional>
#include <memory>
#include <unordered_set>

namespace ROOT {
namespace Experimental {

class RNTupleModel;
// TODO(jblomer): factory methods to create tree sinks and sources outside Detail namespace

namespace Detail {

class RCluster;
class RColumn;
class RColumnElementBase;
class RNTupleCompressor;
class RNTupleDecompressor;
class RPagePool;
class RFieldBase;
class RNTupleMetrics;

enum class EPageStorageType {
   kSink,
   kSource,
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageStorage
\ingroup NTuple
\brief Common functionality of an ntuple storage for both reading and writing

The RPageStore provides access to a storage container that keeps the bits of pages and clusters comprising
an ntuple.  Concrete implementations can use a TFile, a raw file, an object store, and so on.
*/
// clang-format on
class RPageStorage {
public:
   /// The interface of a task scheduler to schedule page (de)compression tasks
   class RTaskScheduler {
   public:
      virtual ~RTaskScheduler() = default;
      /// Start a new set of tasks
      virtual void Reset() = 0;
      /// Take a callable that represents a task
      virtual void AddTask(const std::function<void(void)> &taskFunc) = 0;
      /// Blocks until all scheduled tasks finished
      virtual void Wait() = 0;
   };

   /// A sealed page contains the bytes of a page as written to storage (packed & compressed).  It is used
   /// as an input to UnsealPages() as well as to transfer pages between different storage media.
   struct RSealedPage {
      const void *fBuffer = nullptr;
      std::uint32_t fSize = 0;
      std::uint32_t fNElements = 0;

      RSealedPage() = default;
      RSealedPage(const RSealedPage &other) = delete;
      RSealedPage& operator =(const RSealedPage &other) = delete;
      RSealedPage(RSealedPage &&other) = default;
      RSealedPage& operator =(RSealedPage &&other) = default;
   };

protected:
   std::string fNTupleName;
   RTaskScheduler *fTaskScheduler = nullptr;

public:
   explicit RPageStorage(std::string_view name);
   RPageStorage(const RPageStorage &other) = delete;
   RPageStorage& operator =(const RPageStorage &other) = delete;
   RPageStorage(RPageStorage &&other) = default;
   RPageStorage& operator =(RPageStorage &&other) = default;
   virtual ~RPageStorage();

   /// Whether the concrete implementation is a sink or a source
   virtual EPageStorageType GetType() = 0;

   struct RColumnHandle {
      DescriptorId_t fId = kInvalidDescriptorId;
      const RColumn *fColumn = nullptr;

      /// Returns true for a valid column handle; fColumn and fId should always either both
      /// be valid or both be invalid.
      operator bool() const { return fId != kInvalidDescriptorId && fColumn; }
   };
   /// The column handle identifies a column with the current open page storage
   using ColumnHandle_t = RColumnHandle;

   /// Register a new column.  When reading, the column must exist in the ntuple on disk corresponding to the meta-data.
   /// When writing, every column can only be attached once.
   virtual ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) = 0;
   /// Unregisters a column.  A page source decreases the reference counter for the corresponding active column.
   /// For a page sink, dropping columns is currently a no-op.
   virtual void DropColumn(ColumnHandle_t columnHandle) = 0;

   /// Every page store needs to be able to free pages it handed out.  But Sinks and sources have different means
   /// of allocating pages.
   virtual void ReleasePage(RPage &page) = 0;

   /// Returns an empty metrics.  Page storage implementations usually have their own metrics.
   virtual RNTupleMetrics &GetMetrics();

   void SetTaskScheduler(RTaskScheduler *taskScheduler) { fTaskScheduler = taskScheduler; }
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSink
\ingroup NTuple
\brief Abstract interface to write data into an ntuple

The page sink takes the list of columns and afterwards a series of page commits and cluster commits.
The user is responsible to commit clusters at a consistent point, i.e. when all pages corresponding to data
up to the given entry number are committed.
*/
// clang-format on
class RPageSink : public RPageStorage {
protected:
   RNTupleWriteOptions fOptions;

   /// Helper to zip pages and header/footer; comprises a 16MB unzip buffer.
   /// There could be concrete page sinks that don't need a compressor.  Therefore, and in order to stay consistent
   /// with the page source, we leave it up to the derived class whether or not the compressor gets constructed.
   std::unique_ptr<RNTupleCompressor> fCompressor;

   /// Building the ntuple descriptor while writing is done in the same way for all the storage sink implementations.
   /// Field, column, cluster ids and page indexes per cluster are issued sequentially starting with 0
   DescriptorId_t fLastFieldId = 0;
   DescriptorId_t fLastColumnId = 0;
   DescriptorId_t fLastClusterId = 0;
   NTupleSize_t fPrevClusterNEntries = 0;
   /// Keeps track of the number of elements in the currently open cluster. Indexed by column id.
   std::vector<RClusterDescriptor::RColumnRange> fOpenColumnRanges;
   /// Keeps track of the written pages in the currently open cluster. Indexed by column id.
   std::vector<RClusterDescriptor::RPageRange> fOpenPageRanges;
   RNTupleDescriptorBuilder fDescriptorBuilder;

   virtual void CreateImpl(const RNTupleModel &model) = 0;
   virtual RClusterDescriptor::RLocator CommitPageImpl(ColumnHandle_t columnHandle, const RPage &page) = 0;
   virtual RClusterDescriptor::RLocator CommitClusterImpl(NTupleSize_t nEntries) = 0;
   virtual void CommitDatasetImpl() = 0;

   /// Helper for streaming a page. This is commonly used in derived, concrete page sinks. Note that if
   /// compressionSetting is 0 (uncompressed) and the page is mappable, the returned sealed page will
   /// point directly to the input page buffer.  Otherwise, the sealed page references an internal buffer
   /// of fCompressor.  Thus, the buffer pointed to by sealed page should never be freed.
   /// Usage of this method requires construction of fCompressor.
   RSealedPage SealPage(const RPage &page, const RColumnElementBase &element, int compressionSetting);

public:
   RPageSink(std::string_view ntupleName, const RNTupleWriteOptions &options);

   RPageSink(const RPageSink&) = delete;
   RPageSink& operator=(const RPageSink&) = delete;
   RPageSink(RPageSink&&) = default;
   RPageSink& operator=(RPageSink&&) = default;
   virtual ~RPageSink();

   /// Guess the concrete derived page source from the file name (location)
   static std::unique_ptr<RPageSink> Create(std::string_view ntupleName, std::string_view location,
                                            const RNTupleWriteOptions &options = RNTupleWriteOptions());
   EPageStorageType GetType() final { return EPageStorageType::kSink; }

   ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) final;
   void DropColumn(ColumnHandle_t /*columnHandle*/) final {}

   /// Physically creates the storage container to hold the ntuple (e.g., a keys a TFile or an S3 bucket)
   /// To do so, Create() calls CreateImpl() after updating the descriptor.
   /// Create() associates column handles to the columns referenced by the model
   void Create(RNTupleModel &model);
   /// Write a page to the storage. The column must have been added before.
   void CommitPage(ColumnHandle_t columnHandle, const RPage &page);
   /// Finalize the current cluster and create a new one for the following data.
   void CommitCluster(NTupleSize_t nEntries);
   /// Finalize the current cluster and the entrire data set.
   void CommitDataset() { CommitDatasetImpl(); }

   /// Get a new, empty page for the given column that can be filled with up to nElements.  If nElements is zero,
   /// the page sink picks an appropriate size.
   virtual RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements = 0) = 0;
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSource
\ingroup NTuple
\brief Abstract interface to read data from an ntuple

The page source is initialized with the columns of interest. Pages from those columns can then be
mapped into memory. The page source also gives access to the ntuple's meta-data.
*/
// clang-format on
class RPageSource : public RPageStorage {
public:
   /// Derived from the model (fields) that are actually being requested at a given point in time
   using ColumnSet_t = std::unordered_set<DescriptorId_t>;

protected:
   RNTupleReadOptions fOptions;
   RNTupleDescriptor fDescriptor;
   /// The active columns are implicitly defined by the model fields or views
   ColumnSet_t fActiveColumns;

   /// Helper to unzip pages and header/footer; comprises a 16MB unzip buffer.
   /// Not all page sources need a decompressor (e.g. virtual ones for chains and friends don't), thus we
   /// leave it up to the derived class whether or not the decompressor gets constructed.
   std::unique_ptr<RNTupleDecompressor> fDecompressor;

   virtual RNTupleDescriptor AttachImpl() = 0;
   // Only called if a task scheduler is set. No-op be default.
   virtual void UnzipClusterImpl(RCluster * /* cluster */)
      { }

   /// Helper for unstreaming a page. This is commonly used in derived, concrete page sources.  The implementation
   /// currently always makes a memory copy, even if the sealed page is uncompressed and in the final memory layout.
   /// The optimization of directly mapping pages is left to the concrete page source implementations.
   /// Usage of this method requires construction of fDecompressor.
   unsigned char *UnsealPage(const RSealedPage &sealedPage, const RColumnElementBase &element);

public:
   RPageSource(std::string_view ntupleName, const RNTupleReadOptions &fOptions);
   RPageSource(const RPageSource&) = delete;
   RPageSource& operator=(const RPageSource&) = delete;
   RPageSource(RPageSource&&) = default;
   RPageSource& operator=(RPageSource&&) = default;
   virtual ~RPageSource();
   /// Guess the concrete derived page source from the file name (location)
   static std::unique_ptr<RPageSource> Create(std::string_view ntupleName, std::string_view location,
                                              const RNTupleReadOptions &options = RNTupleReadOptions());
   /// Open the same storage multiple time, e.g. for reading in multiple threads
   virtual std::unique_ptr<RPageSource> Clone() const = 0;

   EPageStorageType GetType() final { return EPageStorageType::kSource; }
   const RNTupleDescriptor &GetDescriptor() const { return fDescriptor; }
   ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) final;
   void DropColumn(ColumnHandle_t columnHandle) final;

   /// Open the physical storage container for the tree
   void Attach() { fDescriptor = AttachImpl(); }
   NTupleSize_t GetNEntries();
   NTupleSize_t GetNElements(ColumnHandle_t columnHandle);
   ColumnId_t GetColumnId(ColumnHandle_t columnHandle);

   /// Allocates and fills a page that contains the index-th element
   virtual RPage PopulatePage(ColumnHandle_t columnHandle, NTupleSize_t globalIndex) = 0;
   /// Another version of PopulatePage that allows to specify cluster-relative indexes
   virtual RPage PopulatePage(ColumnHandle_t columnHandle, const RClusterIndex &clusterIndex) = 0;

   /// Populates all the pages of the given cluster id and columns; it is possible that some columns do not
   /// contain any pages.  The pages source may load more columns than the minimal necessary set from `columns`.
   /// To indicate which columns have been loaded, LoadCluster() must mark them with SetColumnAvailable().
   /// That includes the ones from the `columns` that don't have pages; otherwise subsequent requests
   /// for the cluster would assume an incomplete cluster and trigger loading again.
   /// LoadCluster() is typically called from the I/O thread of a cluster pool, i.e. the method runs
   /// concurrently to other methods of the page source.
   virtual std::unique_ptr<RCluster> LoadCluster(DescriptorId_t clusterId, const ColumnSet_t &columns) = 0;

   /// Parallel decompression and unpacking of the pages in the given cluster. The unzipped pages are supposed
   /// to be preloaded in a page pool attached to the source. The method is triggered by the cluster pool's
   /// unzip thread. It is an optional optimization, the method can safely do nothing. In particular, the
   /// actual implementation will only run if a task scheduler is set. In practice, a task scheduler is set
   /// if implicit multi-threading is turned on.
   void UnzipCluster(RCluster *cluster);
};

} // namespace Detail

} // namespace Experimental
} // namespace ROOT

#endif
<|endoftext|>


<commit_before>#include "xchainer/reduction_kernel_arg.h"

#include <cassert>
#include <cstdint>
#include <tuple>

#include "xchainer/shape.h"
#include "xchainer/strides.h"

namespace xchainer {
namespace internal {

// Squashes dimensions of reduction
//
// Example (in the case of a contiguous array):
// - in_shape:     (2, 3, 4, 5, 6)
// - out_shape:    (2, 3, 4)
// - reduce_shape: (5, 6)
// - in_squashed_shape: (24, 30)
// - out_squashed_shape: (24)
// - reduce_squashed_shape: (30)
//
// TODO(sonots): squash input and output dimensions individually to achieve best performance
SquashReductionArg SquashReductionShapeAndStrides(const SquashReductionArg& arg) {
#ifndef NDEBUG
    assert(arg.in_shape.ndim() == arg.out_shape.ndim() + arg.reduce_shape.ndim());
    assert(arg.in_shape.ndim() == arg.in_strides.ndim());
    assert(arg.out_shape.ndim() == arg.out_strides.ndim());

    for (int8_t i = 0; i < arg.out_shape.ndim(); ++i) {
        assert(arg.in_shape[i] == arg.out_shape[i]);
    }
    for (int8_t i = 0; i < arg.reduce_shape.ndim(); ++i) {
        assert(arg.in_shape[arg.out_shape.ndim() + i] == arg.reduce_shape[i]);
    }
#endif

    // Skip squashing for apparent cases:
    // if (in_shape.ndim() == 0) {
    //     assert(out_shape.ndim() == 0 && reduce_shape.ndim() == 0);
    //     return std::make_tuple(in_shape, in_strides, reduce_shape);
    // } else if (in_shape.ndim() == 1) {
    //     assert((out_shape.ndim() == 1 && reduce_shape.ndim() == 0) || (out.shape.ndim() == 0 && reduce_shape.ndim == 1));
    //     return std::make_tuple(in_shape, in_strides, reduce_shape);
    // } else if (in_shape.ndim() == 2 && out_shape.ndim() == 1) {
    //     assert(reduce_shape.ndim() == 1);
    //     return std::make_tuple(in_shape, in_strides, reduce_shape);
    // }

    // Squash out
    // Only out_shape.ndim() elements in in_strides are seen in SquashShape
    std::tuple<Shape, Axes> out_squashed_result = SquashShape(arg.out_shape, arg.in_strides, arg.out_strides);
    const Shape& out_squashed_shape = std::get<0>(out_squashed_result);
    const Axes& out_keep_axes = std::get<1>(out_squashed_result);
    Strides out_squashed_strides = GetSquashedStrides(arg.out_strides, out_keep_axes);

    // Squash reduce
    Strides reduce_strides{};
    for (int8_t i = arg.out_strides.ndim(); i < arg.in_strides.ndim(); ++i) {
        reduce_strides.emplace_back(arg.in_strides[i]);
    }
    std::tuple<Shape, Axes> reduce_squashed_result = SquashShape(arg.reduce_shape, reduce_strides);
    const Shape& reduce_squashed_shape = std::get<0>(reduce_squashed_result);
    const Axes& reduce_keep_axes = std::get<1>(reduce_squashed_result);
    Strides reduce_squashed_strides = GetSquashedStrides(reduce_strides, reduce_keep_axes);

    // Merge out and reduce into input
    Shape in_squashed_shape{out_squashed_shape};
    Strides in_squashed_strides = GetSquashedStrides(arg.in_strides, out_keep_axes);
    for (int8_t i = 0; i < reduce_squashed_shape.ndim(); ++i) {
        in_squashed_shape.emplace_back(reduce_squashed_shape[i]);
        in_squashed_strides.emplace_back(reduce_squashed_strides[i]);
    }

#ifndef NDEBUG
    assert(in_squashed_shape.ndim() == out_squashed_shape.ndim() + reduce_squashed_shape.ndim());
    assert(in_squashed_shape.ndim() == in_squashed_strides.ndim());
    assert(out_squashed_shape.ndim() == out_squashed_strides.ndim());

    for (int8_t i = 0; i < out_squashed_shape.ndim(); ++i) {
        assert(in_squashed_shape[i] == out_squashed_shape[i]);
    }
    for (int8_t i = 0; i < reduce_squashed_shape.ndim(); ++i) {
        assert(in_squashed_shape[out_squashed_shape.ndim() + i] == reduce_squashed_shape[i]);
    }
#endif

    return SquashReductionArg{std::move(in_squashed_strides),
                              std::move(out_squashed_strides),
                              std::move(in_squashed_shape),
                              std::move(out_squashed_shape),
                              std::move(reduce_squashed_shape)};
}  // namespace internal

}  // namespace internal
}  // namespace xchainer
<commit_msg>It is possible to skip squashing for some cases<commit_after>#include "xchainer/reduction_kernel_arg.h"

#include <cassert>
#include <cstdint>
#include <tuple>

#include "xchainer/shape.h"
#include "xchainer/strides.h"

namespace xchainer {
namespace internal {

// Squashes dimensions of reduction
//
// Example (in the case of a contiguous array):
// - in_shape:     (2, 3, 4, 5, 6)
// - out_shape:    (2, 3, 4)
// - reduce_shape: (5, 6)
// - in_squashed_shape: (24, 30)
// - out_squashed_shape: (24)
// - reduce_squashed_shape: (30)
SquashReductionArg SquashReductionShapeAndStrides(const SquashReductionArg& arg) {
#ifndef NDEBUG
    assert(arg.in_shape.ndim() == arg.out_shape.ndim() + arg.reduce_shape.ndim());
    assert(arg.in_shape.ndim() == arg.in_strides.ndim());
    assert(arg.out_shape.ndim() == arg.out_strides.ndim());

    for (int8_t i = 0; i < arg.out_shape.ndim(); ++i) {
        assert(arg.in_shape[i] == arg.out_shape[i]);
    }
    for (int8_t i = 0; i < arg.reduce_shape.ndim(); ++i) {
        assert(arg.in_shape[arg.out_shape.ndim() + i] == arg.reduce_shape[i]);
    }
#endif

    // Some cases we can not squash further
    if (arg.in_shape.ndim() == 0) {
        assert(arg.out_shape.ndim() == 0 && arg.reduce_shape.ndim() == 0);
        return arg;
    } else if (arg.in_shape.ndim() == 1) {
        assert((arg.out_shape.ndim() == 1 && arg.reduce_shape.ndim() == 0) || (arg.out_shape.ndim() == 0 && arg.reduce_shape.ndim() == 1));
        return arg;
    } else if (arg.in_shape.ndim() == 2 && arg.out_shape.ndim() == 1) {
        assert(arg.reduce_shape.ndim() == 1);
        return arg;
    }

    // Squash out
    // Note that only out_shape.ndim() elements in in_strides are seen in SquashShape, thus we can skip copying former parts of in_strides
    std::tuple<Shape, Axes> out_squashed_result = SquashShape(arg.out_shape, arg.in_strides, arg.out_strides);
    const Shape& out_squashed_shape = std::get<0>(out_squashed_result);
    const Axes& out_keep_axes = std::get<1>(out_squashed_result);
    Strides out_squashed_strides = GetSquashedStrides(arg.out_strides, out_keep_axes);

    // Squash reduce
    Strides reduce_strides{};
    for (int8_t i = arg.out_strides.ndim(); i < arg.in_strides.ndim(); ++i) {
        reduce_strides.emplace_back(arg.in_strides[i]);
    }
    std::tuple<Shape, Axes> reduce_squashed_result = SquashShape(arg.reduce_shape, reduce_strides);
    const Shape& reduce_squashed_shape = std::get<0>(reduce_squashed_result);
    const Axes& reduce_keep_axes = std::get<1>(reduce_squashed_result);
    Strides reduce_squashed_strides = GetSquashedStrides(reduce_strides, reduce_keep_axes);

    // Merge out and reduce into input
    Shape in_squashed_shape{out_squashed_shape};
    Strides in_squashed_strides = GetSquashedStrides(arg.in_strides, out_keep_axes);
    for (int8_t i = 0; i < reduce_squashed_shape.ndim(); ++i) {
        in_squashed_shape.emplace_back(reduce_squashed_shape[i]);
        in_squashed_strides.emplace_back(reduce_squashed_strides[i]);
    }

#ifndef NDEBUG
    assert(in_squashed_shape.ndim() == out_squashed_shape.ndim() + reduce_squashed_shape.ndim());
    assert(in_squashed_shape.ndim() == in_squashed_strides.ndim());
    assert(out_squashed_shape.ndim() == out_squashed_strides.ndim());

    for (int8_t i = 0; i < out_squashed_shape.ndim(); ++i) {
        assert(in_squashed_shape[i] == out_squashed_shape[i]);
    }
    for (int8_t i = 0; i < reduce_squashed_shape.ndim(); ++i) {
        assert(in_squashed_shape[out_squashed_shape.ndim() + i] == reduce_squashed_shape[i]);
    }
#endif

    return SquashReductionArg{std::move(in_squashed_strides),
                              std::move(out_squashed_strides),
                              std::move(in_squashed_shape),
                              std::move(out_squashed_shape),
                              std::move(reduce_squashed_shape)};
}  // namespace internal

}  // namespace internal
}  // namespace xchainer
<|endoftext|>