<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/llvm/compile_cache.h>
#include <vespa/eval/eval/key_gen.h>
#include <vespa/eval/eval/test/eval_spec.h>
#include <vespa/vespalib/util/time.h>
#include <vespa/vespalib/util/threadstackexecutor.h>
#include <thread>
#include <set>

using namespace vespalib;
using namespace vespalib::eval;

//-----------------------------------------------------------------------------

TEST("require that parameter passing selection affects function key") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("a+b"), PassParams::SEPARATE),
                     gen_key(*Function::parse("a+b"), PassParams::ARRAY));
}

TEST("require that the number of parameters affects function key") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::SEPARATE),
                     gen_key(*Function::parse({"a", "b", "c"}, "a+b"), PassParams::SEPARATE));
    EXPECT_NOT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::ARRAY),
                     gen_key(*Function::parse({"a", "b", "c"}, "a+b"), PassParams::ARRAY));
}

TEST("require that implicit and explicit parameters give the same function key") {
    EXPECT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::SEPARATE),
                 gen_key(*Function::parse("a+b"), PassParams::SEPARATE));
    EXPECT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::ARRAY),
                 gen_key(*Function::parse("a+b"), PassParams::ARRAY));
}

TEST("require that symbol names does not affect function key") {
    EXPECT_EQUAL(gen_key(*Function::parse("a+b"), PassParams::SEPARATE),
                 gen_key(*Function::parse("x+y"), PassParams::SEPARATE));
    EXPECT_EQUAL(gen_key(*Function::parse("a+b"), PassParams::ARRAY),
                 gen_key(*Function::parse("x+y"), PassParams::ARRAY));
}

TEST("require that different values give different function keys") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("1"), PassParams::SEPARATE),
                     gen_key(*Function::parse("2"), PassParams::SEPARATE));
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("1"), PassParams::ARRAY),
                     gen_key(*Function::parse("2"), PassParams::ARRAY));
}

TEST("require that different strings give different function keys") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("\"a\""), PassParams::SEPARATE),
                     gen_key(*Function::parse("\"b\""), PassParams::SEPARATE));
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("\"a\""), PassParams::ARRAY),
                     gen_key(*Function::parse("\"b\""), PassParams::ARRAY));
}

//-----------------------------------------------------------------------------

struct CheckKeys : test::EvalSpec::EvalTest {
    bool failed = false;
    std::set<vespalib::string> seen_keys;
    bool check_key(const vespalib::string &key) {
        bool seen = (seen_keys.count(key) > 0);
        seen_keys.insert(key);
        return seen;
    }
    virtual void next_expression(const std::vector<vespalib::string> &param_names,
                                 const vespalib::string &expression) override
    {
        auto function = Function::parse(param_names, expression);
        if (!CompiledFunction::detect_issues(*function)) {
            if (check_key(gen_key(*function, PassParams::ARRAY)) ||
                check_key(gen_key(*function, PassParams::SEPARATE)) ||
                check_key(gen_key(*function, PassParams::LAZY)))
            {
                failed = true;
                fprintf(stderr, "key collision for: %s\n", expression.c_str());
            }
        }
    }
    virtual void handle_case(const std::vector<vespalib::string> &,
                             const std::vector<double> &,
                             const vespalib::string &,
                             double) override {}
};

TEST_FF("require that all conformance expressions have different function keys",
        CheckKeys(), test::EvalSpec())
{
    f2.add_all_cases();
    f2.each_case(f1);
    EXPECT_TRUE(!f1.failed);
    EXPECT_GREATER(f1.seen_keys.size(), 100u);
}

//-----------------------------------------------------------------------------

void verify_cache(size_t expect_cached, size_t expect_refs) {
    EXPECT_EQUAL(expect_cached, CompileCache::num_cached());
    EXPECT_EQUAL(expect_refs, CompileCache::count_refs());
}

TEST("require that cache is initially empty") {
    TEST_DO(verify_cache(0, 0));
}

TEST("require that unused functions are evicted from the cache") {
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    TEST_DO(verify_cache(1, 1));
    token_a.reset();
    TEST_DO(verify_cache(0, 0));
}

TEST("require that agents can have separate functions in the cache") {
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    CompileCache::Token::UP token_b = CompileCache::compile(*Function::parse("x*y"), PassParams::ARRAY);
    TEST_DO(verify_cache(2, 2));
}

TEST("require that agents can share functions in the cache") {
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    CompileCache::Token::UP token_b = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    TEST_DO(verify_cache(1, 2));
}

TEST("require that cache usage works") {
    TEST_DO(verify_cache(0, 0));
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::SEPARATE);
    EXPECT_EQUAL(5.0, token_a->get().get_function<2>()(2.0, 3.0));
    TEST_DO(verify_cache(1, 1));
    CompileCache::Token::UP token_b = CompileCache::compile(*Function::parse("x*y"), PassParams::SEPARATE);
    EXPECT_EQUAL(6.0, token_b->get().get_function<2>()(2.0, 3.0));
    TEST_DO(verify_cache(2, 2));
    CompileCache::Token::UP token_c = CompileCache::compile(*Function::parse("x+y"), PassParams::SEPARATE);
    EXPECT_EQUAL(5.0, token_c->get().get_function<2>()(2.0, 3.0));
    TEST_DO(verify_cache(2, 3));
    token_a.reset();
    TEST_DO(verify_cache(2, 2));
    token_b.reset();
    TEST_DO(verify_cache(1, 1));
    token_c.reset();
    TEST_DO(verify_cache(0, 0));
}

struct CompileCheck : test::EvalSpec::EvalTest {
    struct Entry {
        CompileCache::Token::UP fun;
        std::vector<double> params;
        double expect;
        Entry(CompileCache::Token::UP fun_in, const std::vector<double> &params_in, double expect_in)
            : fun(std::move(fun_in)), params(params_in), expect(expect_in) {}
    };
    std::vector<Entry> list;
    void next_expression(const std::vector<vespalib::string> &,
                         const vespalib::string &) override {}
    void handle_case(const std::vector<vespalib::string> &param_names,
                     const std::vector<double> &param_values,
                     const vespalib::string &expression,
                     double expected_result) override
    {
        auto function = Function::parse(param_names, expression);
        ASSERT_TRUE(!function->has_error());
        bool has_issues = CompiledFunction::detect_issues(*function);
        if (!has_issues) {
            list.emplace_back(CompileCache::compile(*function, PassParams::ARRAY), param_values, expected_result);
        }
    }
    void verify() {
        for (const Entry &entry: list) {
            auto fun = entry.fun->get().get_function();
            if (std::isnan(entry.expect)) {
                EXPECT_TRUE(std::isnan(fun(&entry.params[0])));
            } else {
                EXPECT_EQUAL(fun(&entry.params[0]), entry.expect);
            }
        }
    }
};

TEST_F("compile sequentially, then run all conformance tests", test::EvalSpec()) {
    f1.add_all_cases();
    for (size_t i = 0; i < 4; ++i) {
        CompileCheck test;
        auto t0 = steady_clock::now();
        f1.each_case(test);
        auto t1 = steady_clock::now();
        auto t2 = steady_clock::now();
        test.verify();
        auto t3 = steady_clock::now();
        fprintf(stderr, "sequential (run %zu): setup: %zu ms, wait: %zu ms, verify: %zu us, total: %zu ms\n",
                i, count_ms(t1 - t0), count_ms(t2 - t1), count_us(t3 - t2), count_ms(t3 - t0));
    }
}

TEST_FF("compile concurrently (8 threads), then run all conformance tests", test::EvalSpec(), TimeBomb(60)) {
    f1.add_all_cases();
    ThreadStackExecutor executor(8, 256*1024);
    CompileCache::attach_executor(executor);
    while (executor.num_idle_workers() < 8) {
        std::this_thread::sleep_for(1ms);
    }
    for (size_t i = 0; i < 4; ++i) {
        CompileCheck test;
        auto t0 = steady_clock::now();
        f1.each_case(test);
        auto t1 = steady_clock::now();
        executor.sync();
        auto t2 = steady_clock::now();
        test.verify();
        auto t3 = steady_clock::now();
        fprintf(stderr, "concurrent (run %zu): setup: %zu ms, wait: %zu ms, verify: %zu us, total: %zu ms\n",
                i, count_ms(t1 - t0), count_ms(t2 - t1), count_us(t3 - t2), count_ms(t3 - t0));
    }
    CompileCache::detach_executor();
}

//-----------------------------------------------------------------------------

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>give valgrind more time<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/llvm/compile_cache.h>
#include <vespa/eval/eval/key_gen.h>
#include <vespa/eval/eval/test/eval_spec.h>
#include <vespa/vespalib/util/time.h>
#include <vespa/vespalib/util/threadstackexecutor.h>
#include <thread>
#include <set>

using namespace vespalib;
using namespace vespalib::eval;

//-----------------------------------------------------------------------------

TEST("require that parameter passing selection affects function key") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("a+b"), PassParams::SEPARATE),
                     gen_key(*Function::parse("a+b"), PassParams::ARRAY));
}

TEST("require that the number of parameters affects function key") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::SEPARATE),
                     gen_key(*Function::parse({"a", "b", "c"}, "a+b"), PassParams::SEPARATE));
    EXPECT_NOT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::ARRAY),
                     gen_key(*Function::parse({"a", "b", "c"}, "a+b"), PassParams::ARRAY));
}

TEST("require that implicit and explicit parameters give the same function key") {
    EXPECT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::SEPARATE),
                 gen_key(*Function::parse("a+b"), PassParams::SEPARATE));
    EXPECT_EQUAL(gen_key(*Function::parse({"a", "b"}, "a+b"), PassParams::ARRAY),
                 gen_key(*Function::parse("a+b"), PassParams::ARRAY));
}

TEST("require that symbol names does not affect function key") {
    EXPECT_EQUAL(gen_key(*Function::parse("a+b"), PassParams::SEPARATE),
                 gen_key(*Function::parse("x+y"), PassParams::SEPARATE));
    EXPECT_EQUAL(gen_key(*Function::parse("a+b"), PassParams::ARRAY),
                 gen_key(*Function::parse("x+y"), PassParams::ARRAY));
}

TEST("require that different values give different function keys") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("1"), PassParams::SEPARATE),
                     gen_key(*Function::parse("2"), PassParams::SEPARATE));
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("1"), PassParams::ARRAY),
                     gen_key(*Function::parse("2"), PassParams::ARRAY));
}

TEST("require that different strings give different function keys") {
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("\"a\""), PassParams::SEPARATE),
                     gen_key(*Function::parse("\"b\""), PassParams::SEPARATE));
    EXPECT_NOT_EQUAL(gen_key(*Function::parse("\"a\""), PassParams::ARRAY),
                     gen_key(*Function::parse("\"b\""), PassParams::ARRAY));
}

//-----------------------------------------------------------------------------

struct CheckKeys : test::EvalSpec::EvalTest {
    bool failed = false;
    std::set<vespalib::string> seen_keys;
    bool check_key(const vespalib::string &key) {
        bool seen = (seen_keys.count(key) > 0);
        seen_keys.insert(key);
        return seen;
    }
    virtual void next_expression(const std::vector<vespalib::string> &param_names,
                                 const vespalib::string &expression) override
    {
        auto function = Function::parse(param_names, expression);
        if (!CompiledFunction::detect_issues(*function)) {
            if (check_key(gen_key(*function, PassParams::ARRAY)) ||
                check_key(gen_key(*function, PassParams::SEPARATE)) ||
                check_key(gen_key(*function, PassParams::LAZY)))
            {
                failed = true;
                fprintf(stderr, "key collision for: %s\n", expression.c_str());
            }
        }
    }
    virtual void handle_case(const std::vector<vespalib::string> &,
                             const std::vector<double> &,
                             const vespalib::string &,
                             double) override {}
};

TEST_FF("require that all conformance expressions have different function keys",
        CheckKeys(), test::EvalSpec())
{
    f2.add_all_cases();
    f2.each_case(f1);
    EXPECT_TRUE(!f1.failed);
    EXPECT_GREATER(f1.seen_keys.size(), 100u);
}

//-----------------------------------------------------------------------------

void verify_cache(size_t expect_cached, size_t expect_refs) {
    EXPECT_EQUAL(expect_cached, CompileCache::num_cached());
    EXPECT_EQUAL(expect_refs, CompileCache::count_refs());
}

TEST("require that cache is initially empty") {
    TEST_DO(verify_cache(0, 0));
}

TEST("require that unused functions are evicted from the cache") {
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    TEST_DO(verify_cache(1, 1));
    token_a.reset();
    TEST_DO(verify_cache(0, 0));
}

TEST("require that agents can have separate functions in the cache") {
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    CompileCache::Token::UP token_b = CompileCache::compile(*Function::parse("x*y"), PassParams::ARRAY);
    TEST_DO(verify_cache(2, 2));
}

TEST("require that agents can share functions in the cache") {
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    CompileCache::Token::UP token_b = CompileCache::compile(*Function::parse("x+y"), PassParams::ARRAY);
    TEST_DO(verify_cache(1, 2));
}

TEST("require that cache usage works") {
    TEST_DO(verify_cache(0, 0));
    CompileCache::Token::UP token_a = CompileCache::compile(*Function::parse("x+y"), PassParams::SEPARATE);
    EXPECT_EQUAL(5.0, token_a->get().get_function<2>()(2.0, 3.0));
    TEST_DO(verify_cache(1, 1));
    CompileCache::Token::UP token_b = CompileCache::compile(*Function::parse("x*y"), PassParams::SEPARATE);
    EXPECT_EQUAL(6.0, token_b->get().get_function<2>()(2.0, 3.0));
    TEST_DO(verify_cache(2, 2));
    CompileCache::Token::UP token_c = CompileCache::compile(*Function::parse("x+y"), PassParams::SEPARATE);
    EXPECT_EQUAL(5.0, token_c->get().get_function<2>()(2.0, 3.0));
    TEST_DO(verify_cache(2, 3));
    token_a.reset();
    TEST_DO(verify_cache(2, 2));
    token_b.reset();
    TEST_DO(verify_cache(1, 1));
    token_c.reset();
    TEST_DO(verify_cache(0, 0));
}

struct CompileCheck : test::EvalSpec::EvalTest {
    struct Entry {
        CompileCache::Token::UP fun;
        std::vector<double> params;
        double expect;
        Entry(CompileCache::Token::UP fun_in, const std::vector<double> &params_in, double expect_in)
            : fun(std::move(fun_in)), params(params_in), expect(expect_in) {}
    };
    std::vector<Entry> list;
    void next_expression(const std::vector<vespalib::string> &,
                         const vespalib::string &) override {}
    void handle_case(const std::vector<vespalib::string> &param_names,
                     const std::vector<double> &param_values,
                     const vespalib::string &expression,
                     double expected_result) override
    {
        auto function = Function::parse(param_names, expression);
        ASSERT_TRUE(!function->has_error());
        bool has_issues = CompiledFunction::detect_issues(*function);
        if (!has_issues) {
            list.emplace_back(CompileCache::compile(*function, PassParams::ARRAY), param_values, expected_result);
        }
    }
    void verify() {
        for (const Entry &entry: list) {
            auto fun = entry.fun->get().get_function();
            if (std::isnan(entry.expect)) {
                EXPECT_TRUE(std::isnan(fun(&entry.params[0])));
            } else {
                EXPECT_EQUAL(fun(&entry.params[0]), entry.expect);
            }
        }
    }
};

TEST_F("compile sequentially, then run all conformance tests", test::EvalSpec()) {
    f1.add_all_cases();
    for (size_t i = 0; i < 4; ++i) {
        CompileCheck test;
        auto t0 = steady_clock::now();
        f1.each_case(test);
        auto t1 = steady_clock::now();
        auto t2 = steady_clock::now();
        test.verify();
        auto t3 = steady_clock::now();
        fprintf(stderr, "sequential (run %zu): setup: %zu ms, wait: %zu ms, verify: %zu us, total: %zu ms\n",
                i, count_ms(t1 - t0), count_ms(t2 - t1), count_us(t3 - t2), count_ms(t3 - t0));
    }
}

TEST_F("compile concurrently (8 threads), then run all conformance tests", test::EvalSpec()) {
    f1.add_all_cases();
    ThreadStackExecutor executor(8, 256*1024);
    CompileCache::attach_executor(executor);
    while (executor.num_idle_workers() < 8) {
        std::this_thread::sleep_for(1ms);
    }
    for (size_t i = 0; i < 4; ++i) {
        CompileCheck test;
        auto t0 = steady_clock::now();
        f1.each_case(test);
        auto t1 = steady_clock::now();
        executor.sync();
        auto t2 = steady_clock::now();
        test.verify();
        auto t3 = steady_clock::now();
        fprintf(stderr, "concurrent (run %zu): setup: %zu ms, wait: %zu ms, verify: %zu us, total: %zu ms\n",
                i, count_ms(t1 - t0), count_ms(t2 - t1), count_us(t3 - t2), count_ms(t3 - t0));
    }
    CompileCache::detach_executor();
}

//-----------------------------------------------------------------------------

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license.

#include "SpriteFont.hpp"
#include "TrueTypeFont.hpp"
#include "SpriteBatchRenderer.hpp"
#include "Pomdog/Math/Color.hpp"
#include "Pomdog/Math/Matrix4x4.hpp"
#include "Pomdog/Math/Vector2.hpp"
#include "Pomdog/Math/Vector3.hpp"
#include "Pomdog/Math/Radian.hpp"
#include "Pomdog/Graphics/Texture2D.hpp"
#include "Pomdog/Utility/Assert.hpp"

#ifndef _MSC_VER
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#endif
#include <utfcpp/source/utf8.h>
#ifndef _MSC_VER
#pragma clang diagnostic pop
#endif

#include <unordered_map>

namespace Pomdog {
namespace {

std::vector<std::uint8_t> ConvertTextureDataByteToByte4(const std::uint8_t* source, size_t size)
{
    std::vector<std::uint8_t> output;
    output.reserve(size * 4);

    for (size_t i = 0; i < size; ++i) {
        output.push_back(255);
        output.push_back(255);
        output.push_back(255);
        output.push_back(source[i]);
    }
    return std::move(output);
}

} // unnamed namespace

// MARK: - SpriteFont::Impl

class SpriteFont::Impl {
public:
    typedef Detail::SpriteFonts::Glyph Glyph;

    static constexpr int TextureWidth = 512;
    static constexpr int TextureHeight = 512;

    std::unordered_map<char32_t, Glyph> spriteFontMap;

    char32_t defaultCharacter;
    float lineSpacing;
    std::uint16_t spacing;

    Impl(
        std::vector<std::shared_ptr<Texture2D>> && textures,
        const std::vector<Detail::SpriteFonts::Glyph>& glyphs,
        char32_t defaultCharacter,
        std::int16_t spacing,
        std::int16_t lineSpacing);

    Impl(
        const std::shared_ptr<GraphicsDevice>& graphicsDevice,
        const std::shared_ptr<TrueTypeFont>& font,
        char32_t defaultCharacter,
        std::int16_t lineSpacing);

    Vector2 MeasureString(const std::string& text) const;

    void Draw(
        SpriteBatchRenderer & spriteBatch,
        const std::string& text,
        const Vector2& position,
        const Color& color);

    void Draw(
        SpriteBatchRenderer & spriteBatch,
        const std::string& text,
        const Vector2& position,
        const Color& color,
        const Radian<float>& rotation,
        //const Vector2& originPivot,
        const Vector2& scale);

    void PrepareFonts(const std::string& text);

private:
    std::vector<std::shared_ptr<Texture2D>> textures;
    std::shared_ptr<GraphicsDevice> graphicsDevice;
    std::shared_ptr<TrueTypeFont> font;
    std::vector<std::uint8_t> pixelData;
    Point2D currentPoint;
    int bottomY;
};
constexpr int SpriteFont::Impl::TextureWidth;
constexpr int SpriteFont::Impl::TextureHeight;

SpriteFont::Impl::Impl(
    std::vector<std::shared_ptr<Texture2D>> && texturesIn,
    const std::vector<Detail::SpriteFonts::Glyph>& glyphsIn,
    char32_t defaultCharacterIn,
    std::int16_t spacingIn,
    std::int16_t lineSpacingIn)
    : textures(std::move(texturesIn))
    , defaultCharacter(defaultCharacterIn)
    , spacing(spacingIn)
    , lineSpacing(lineSpacingIn)
{
    for (auto & glyph: glyphsIn) {
        spriteFontMap.emplace(glyph.Character, glyph);
    }
}

SpriteFont::Impl::Impl(
    const std::shared_ptr<GraphicsDevice>& graphicsDeviceIn,
    const std::shared_ptr<TrueTypeFont>& fontIn,
    char32_t defaultCharacterIn,
    std::int16_t lineSpacingIn)
    : graphicsDevice(graphicsDeviceIn)
    , font(fontIn)
    , defaultCharacter(defaultCharacterIn)
    , spacing(0)
    , lineSpacing(lineSpacingIn)
{
    POMDOG_ASSERT(font);

    pixelData.resize(TextureWidth * TextureHeight, 0);
    currentPoint = {1, 1};
    bottomY = 1;

    auto texture = std::make_shared<Texture2D>(graphicsDevice,
        TextureWidth, TextureHeight, false, SurfaceFormat::R8G8B8A8_UNorm);
    textures.push_back(texture);
}

void SpriteFont::Impl::PrepareFonts(const std::string& text)
{
    POMDOG_ASSERT(!text.empty());

    if (!graphicsDevice || !font) {
        return;
    }

    float fontSize = lineSpacing;

    bool needToFetchPixelData = false;

    auto fetchTextureData = [&] {
        if (needToFetchPixelData) {
            auto texture = textures.back();
            texture->SetData(ConvertTextureDataByteToByte4(pixelData.data(), pixelData.size()).data());
            needToFetchPixelData = false;
        }
    };

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd) {
        const auto character = utf8::next(textIter, textIterEnd);

        if (spriteFontMap.find(character) != std::end(spriteFontMap)) {
            continue;
        }

        auto glyph = font->RasterizeGlyph(character, fontSize, TextureWidth,
        [&](int glyphWidth, int glyphHeight, Point2D & pointOut, std::uint8_t* & output) {
            if (currentPoint.X + glyphWidth + 1 >= TextureWidth) {
                // advance to next row
                currentPoint.Y = bottomY;
                currentPoint.X = 1;
            }
            if (currentPoint.Y + glyphHeight + 1 >= TextureHeight) {
                fetchTextureData();
                std::fill(std::begin(pixelData), std::end(pixelData), 0);

                auto textureNew = std::make_shared<Texture2D>(graphicsDevice,
                    TextureWidth, TextureHeight, false, SurfaceFormat::R8G8B8A8_UNorm);
                textures.push_back(textureNew);
                currentPoint = {1, 1};
                bottomY = 1;
            }

            POMDOG_ASSERT(currentPoint.X + glyphWidth < TextureWidth);
            POMDOG_ASSERT(currentPoint.Y + glyphHeight < TextureHeight);

            pointOut = currentPoint;
            output = pixelData.data();
        });

        if (!glyph) {
            continue;
        }

        currentPoint.X = currentPoint.X + glyph->Subrect.Width + 1;
        bottomY = std::max(bottomY, currentPoint.Y + glyph->Subrect.Height + 1);

        POMDOG_ASSERT(!textures.empty() && textures.size() > 0);
        glyph->TexturePage = textures.size() - 1;

        spriteFontMap.emplace(glyph->Character, *glyph);
        needToFetchPixelData = true;
    }

    fetchTextureData();
}

Vector2 SpriteFont::Impl::MeasureString(const std::string& text) const
{
    POMDOG_ASSERT(!text.empty());

    Vector2 result = Vector2::Zero;
    Vector2 currentPosition = Vector2::Zero;

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd)
    {
        const auto character = utf8::next(textIter, textIterEnd);

        if (character == U'\n') {
            currentPosition.X = 0;
            currentPosition.Y -= lineSpacing;
            continue;
        }

        auto iter = spriteFontMap.find(character);
        if (iter == std::end(spriteFontMap)) {
            iter = spriteFontMap.find(defaultCharacter);
            POMDOG_ASSERT(iter != std::end(spriteFontMap));
        }

        POMDOG_ASSERT(iter != std::end(spriteFontMap));
        auto const & glyph = iter->second;

        currentPosition.X += (glyph.XAdvance - spacing);

        result.X = std::max(result.X, currentPosition.X);
        result.Y = std::max(result.Y, currentPosition.Y);
    }

    return std::move(result);
}

void SpriteFont::Impl::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color)
{
    if (text.empty()) {
        return;
    }

    if (textures.empty()) {
        return;
    }

    Vector2 currentPosition = position;

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd)
    {
        const auto character = utf8::next(textIter, textIterEnd);

        if (character == U'\n') {
            currentPosition.X = position.X;
            currentPosition.Y -= lineSpacing;
            continue;
        }

        auto iter = spriteFontMap.find(character);
        if (iter == std::end(spriteFontMap)) {
            iter = spriteFontMap.find(defaultCharacter);
            if (iter == std::end(spriteFontMap)) {
                continue;
            }
        }

        POMDOG_ASSERT(iter != std::end(spriteFontMap));
        auto const & glyph = iter->second;

        if (glyph.Subrect.Width > 0 && glyph.Subrect.Height > 0)
        {
            POMDOG_ASSERT(glyph.TexturePage >= 0);
            POMDOG_ASSERT(glyph.TexturePage < static_cast<int>(textures.size()));

            spriteBatch.Draw(textures[glyph.TexturePage],
                currentPosition + Vector2(glyph.XOffset, -glyph.YOffset),
                glyph.Subrect, color, 0.0f, Vector2{0.0f, 1.0f}, Vector2{1.0f, 1.0f});
        }

        currentPosition.X += (glyph.XAdvance - spacing);
    }
}

void SpriteFont::Impl::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color,
    const Radian<float>& rotation,
    //const Vector2& originPivot,
    const Vector2& scale)
{
    if (text.empty()) {
        return;
    }

    if (textures.empty()) {
        return;
    }

    Vector2 currentPosition = position;

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd)
    {
        const auto character = utf8::next(textIter, textIterEnd);

        if (character == U'\n') {
            currentPosition.X = position.X;
            currentPosition.Y -= lineSpacing * scale.Y;
            continue;
        }

        auto iter = spriteFontMap.find(character);
        if (iter == std::end(spriteFontMap)) {
            iter = spriteFontMap.find(defaultCharacter);
            if (iter == std::end(spriteFontMap)) {
                continue;
            }
        }

        POMDOG_ASSERT(iter != std::end(spriteFontMap));
        auto const & glyph = iter->second;

        if (glyph.Subrect.Width > 0 && glyph.Subrect.Height > 0)
        {
            POMDOG_ASSERT(glyph.TexturePage >= 0);
            POMDOG_ASSERT(glyph.TexturePage < static_cast<int>(textures.size()));

            spriteBatch.Draw(textures[glyph.TexturePage],
                currentPosition + Vector2(glyph.XOffset, -glyph.YOffset) * scale,
                glyph.Subrect, color, 0.0f, Vector2{0.0f, 1.0f}, scale);
        }

        currentPosition.X += ((glyph.XAdvance - spacing) * scale.X);
    }
}

// MARK: - SpriteFont

SpriteFont::SpriteFont(
    std::vector<std::shared_ptr<Texture2D>> && textures,
    const std::vector<Detail::SpriteFonts::Glyph>& glyphs,
    char32_t defaultCharacter,
    std::int16_t spacing,
    std::int16_t lineSpacing)
    : impl(std::make_unique<Impl>(std::move(textures), glyphs, defaultCharacter, spacing, lineSpacing))
{
}

SpriteFont::SpriteFont(
    const std::shared_ptr<GraphicsDevice>& graphicsDevice,
    const std::shared_ptr<TrueTypeFont>& font,
    char32_t defaultCharacter,
    std::int16_t lineSpacing)
    : impl(std::make_unique<Impl>(graphicsDevice, font, defaultCharacter, lineSpacing))
{
}

SpriteFont::~SpriteFont() = default;

Vector2 SpriteFont::MeasureString(const std::string& utf8String) const
{
    if (utf8String.empty()) {
        return Vector2::Zero;
    }
    return impl->MeasureString(utf8String);
}

char32_t SpriteFont::GetDefaultCharacter() const
{
    POMDOG_ASSERT(impl);
    return impl->defaultCharacter;
}

void SpriteFont::SetDefaultCharacter(char32_t character)
{
    POMDOG_ASSERT(impl);
    POMDOG_ASSERT(ContainsCharacter(character));
    impl->defaultCharacter = character;
}

float SpriteFont::GetLineSpacing() const
{
    POMDOG_ASSERT(impl);
    return impl->lineSpacing;
}

void SpriteFont::SetLineSpacing(float lineSpacingIn)
{
    POMDOG_ASSERT(impl);
    impl->lineSpacing = lineSpacingIn;
}

bool SpriteFont::ContainsCharacter(char32_t character) const
{
    POMDOG_ASSERT(impl);
    return impl->spriteFontMap.find(character) != std::end(impl->spriteFontMap);
}

void SpriteFont::Begin(
    const std::shared_ptr<GraphicsCommandList>& commandList,
    SpriteBatchRenderer & spriteBatch,
    const Matrix4x4& transformMatrix)
{
    POMDOG_ASSERT(impl);
    POMDOG_ASSERT(commandList);

    spriteBatch.Begin(commandList, transformMatrix);

//    spriteBatch.Begin(SpriteSortMode::Deferred, Matrix4x4::CreateRotationZ(rotation)
//        * Matrix4x4::CreateScale({scale, 1.0f})
//        * Matrix4x4::CreateTranslation({position, 0.0f})
//        * transformMatrix);
}

void SpriteFont::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color)
{
    if (text.empty()) {
        return;
    }

    impl->PrepareFonts(text);
    impl->Draw(spriteBatch, text, position, color);
}

void SpriteFont::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color,
    const Radian<float>& rotation,
    //const Vector2& originPivot,
    float scale)
{
    this->Draw(spriteBatch, text, position, color,
        rotation, Vector2{scale, scale});
}

void SpriteFont::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color,
    const Radian<float>& rotation,
    //const Vector2& originPivot,
    const Vector2& scale)
{
    if (text.empty()) {
        return;
    }

    impl->PrepareFonts(text);
    impl->Draw(spriteBatch, text, position, color,
        rotation, scale);
}

void SpriteFont::End(SpriteBatchRenderer & spriteBatch)
{
    spriteBatch.End();
}

} // namespace Pomdog
<commit_msg>Change font texture width to 1024<commit_after>// Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license.

#include "SpriteFont.hpp"
#include "TrueTypeFont.hpp"
#include "SpriteBatchRenderer.hpp"
#include "Pomdog/Math/Color.hpp"
#include "Pomdog/Math/Matrix4x4.hpp"
#include "Pomdog/Math/Vector2.hpp"
#include "Pomdog/Math/Vector3.hpp"
#include "Pomdog/Math/Radian.hpp"
#include "Pomdog/Graphics/Texture2D.hpp"
#include "Pomdog/Utility/Assert.hpp"

#ifndef _MSC_VER
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#endif
#include <utfcpp/source/utf8.h>
#ifndef _MSC_VER
#pragma clang diagnostic pop
#endif

#include <unordered_map>

namespace Pomdog {
namespace {

std::vector<std::uint8_t> ConvertTextureDataByteToByte4(const std::uint8_t* source, size_t size)
{
    std::vector<std::uint8_t> output;
    output.reserve(size * 4);

    for (size_t i = 0; i < size; ++i) {
        output.push_back(255);
        output.push_back(255);
        output.push_back(255);
        output.push_back(source[i]);
    }
    return std::move(output);
}

} // unnamed namespace

// MARK: - SpriteFont::Impl

class SpriteFont::Impl {
public:
    typedef Detail::SpriteFonts::Glyph Glyph;

    static constexpr int TextureWidth = 1024;
    static constexpr int TextureHeight = 1024;

    std::unordered_map<char32_t, Glyph> spriteFontMap;

    char32_t defaultCharacter;
    float lineSpacing;
    std::uint16_t spacing;

    Impl(
        std::vector<std::shared_ptr<Texture2D>> && textures,
        const std::vector<Detail::SpriteFonts::Glyph>& glyphs,
        char32_t defaultCharacter,
        std::int16_t spacing,
        std::int16_t lineSpacing);

    Impl(
        const std::shared_ptr<GraphicsDevice>& graphicsDevice,
        const std::shared_ptr<TrueTypeFont>& font,
        char32_t defaultCharacter,
        std::int16_t lineSpacing);

    Vector2 MeasureString(const std::string& text) const;

    void Draw(
        SpriteBatchRenderer & spriteBatch,
        const std::string& text,
        const Vector2& position,
        const Color& color);

    void Draw(
        SpriteBatchRenderer & spriteBatch,
        const std::string& text,
        const Vector2& position,
        const Color& color,
        const Radian<float>& rotation,
        //const Vector2& originPivot,
        const Vector2& scale);

    void PrepareFonts(const std::string& text);

private:
    std::vector<std::shared_ptr<Texture2D>> textures;
    std::shared_ptr<GraphicsDevice> graphicsDevice;
    std::shared_ptr<TrueTypeFont> font;
    std::vector<std::uint8_t> pixelData;
    Point2D currentPoint;
    int bottomY;
};
constexpr int SpriteFont::Impl::TextureWidth;
constexpr int SpriteFont::Impl::TextureHeight;

SpriteFont::Impl::Impl(
    std::vector<std::shared_ptr<Texture2D>> && texturesIn,
    const std::vector<Detail::SpriteFonts::Glyph>& glyphsIn,
    char32_t defaultCharacterIn,
    std::int16_t spacingIn,
    std::int16_t lineSpacingIn)
    : textures(std::move(texturesIn))
    , defaultCharacter(defaultCharacterIn)
    , spacing(spacingIn)
    , lineSpacing(lineSpacingIn)
{
    for (auto & glyph: glyphsIn) {
        spriteFontMap.emplace(glyph.Character, glyph);
    }
}

SpriteFont::Impl::Impl(
    const std::shared_ptr<GraphicsDevice>& graphicsDeviceIn,
    const std::shared_ptr<TrueTypeFont>& fontIn,
    char32_t defaultCharacterIn,
    std::int16_t lineSpacingIn)
    : graphicsDevice(graphicsDeviceIn)
    , font(fontIn)
    , defaultCharacter(defaultCharacterIn)
    , spacing(0)
    , lineSpacing(lineSpacingIn)
{
    POMDOG_ASSERT(font);

    pixelData.resize(TextureWidth * TextureHeight, 0);
    currentPoint = {1, 1};
    bottomY = 1;

    auto texture = std::make_shared<Texture2D>(graphicsDevice,
        TextureWidth, TextureHeight, false, SurfaceFormat::R8G8B8A8_UNorm);
    textures.push_back(texture);
}

void SpriteFont::Impl::PrepareFonts(const std::string& text)
{
    POMDOG_ASSERT(!text.empty());

    if (!graphicsDevice || !font) {
        return;
    }

    float fontSize = lineSpacing;

    bool needToFetchPixelData = false;

    auto fetchTextureData = [&] {
        if (needToFetchPixelData) {
            auto texture = textures.back();
            texture->SetData(ConvertTextureDataByteToByte4(pixelData.data(), pixelData.size()).data());
            needToFetchPixelData = false;
        }
    };

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd) {
        const auto character = utf8::next(textIter, textIterEnd);

        if (spriteFontMap.find(character) != std::end(spriteFontMap)) {
            continue;
        }

        auto glyph = font->RasterizeGlyph(character, fontSize, TextureWidth,
        [&](int glyphWidth, int glyphHeight, Point2D & pointOut, std::uint8_t* & output) {
            if (currentPoint.X + glyphWidth + 1 >= TextureWidth) {
                // advance to next row
                currentPoint.Y = bottomY;
                currentPoint.X = 1;
            }
            if (currentPoint.Y + glyphHeight + 1 >= TextureHeight) {
                fetchTextureData();
                std::fill(std::begin(pixelData), std::end(pixelData), 0);

                auto textureNew = std::make_shared<Texture2D>(graphicsDevice,
                    TextureWidth, TextureHeight, false, SurfaceFormat::R8G8B8A8_UNorm);
                textures.push_back(textureNew);
                currentPoint = {1, 1};
                bottomY = 1;
            }

            POMDOG_ASSERT(currentPoint.X + glyphWidth < TextureWidth);
            POMDOG_ASSERT(currentPoint.Y + glyphHeight < TextureHeight);

            pointOut = currentPoint;
            output = pixelData.data();
        });

        if (!glyph) {
            continue;
        }

        currentPoint.X = currentPoint.X + glyph->Subrect.Width + 1;
        bottomY = std::max(bottomY, currentPoint.Y + glyph->Subrect.Height + 1);

        POMDOG_ASSERT(!textures.empty() && textures.size() > 0);
        glyph->TexturePage = textures.size() - 1;

        spriteFontMap.emplace(glyph->Character, *glyph);
        needToFetchPixelData = true;
    }

    fetchTextureData();
}

Vector2 SpriteFont::Impl::MeasureString(const std::string& text) const
{
    POMDOG_ASSERT(!text.empty());

    Vector2 result = Vector2::Zero;
    Vector2 currentPosition = Vector2::Zero;

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd)
    {
        const auto character = utf8::next(textIter, textIterEnd);

        if (character == U'\n') {
            currentPosition.X = 0;
            currentPosition.Y -= lineSpacing;
            continue;
        }

        auto iter = spriteFontMap.find(character);
        if (iter == std::end(spriteFontMap)) {
            iter = spriteFontMap.find(defaultCharacter);
            POMDOG_ASSERT(iter != std::end(spriteFontMap));
        }

        POMDOG_ASSERT(iter != std::end(spriteFontMap));
        auto const & glyph = iter->second;

        currentPosition.X += (glyph.XAdvance - spacing);

        result.X = std::max(result.X, currentPosition.X);
        result.Y = std::max(result.Y, currentPosition.Y);
    }

    return std::move(result);
}

void SpriteFont::Impl::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color)
{
    if (text.empty()) {
        return;
    }

    if (textures.empty()) {
        return;
    }

    Vector2 currentPosition = position;

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd)
    {
        const auto character = utf8::next(textIter, textIterEnd);

        if (character == U'\n') {
            currentPosition.X = position.X;
            currentPosition.Y -= lineSpacing;
            continue;
        }

        auto iter = spriteFontMap.find(character);
        if (iter == std::end(spriteFontMap)) {
            iter = spriteFontMap.find(defaultCharacter);
            if (iter == std::end(spriteFontMap)) {
                continue;
            }
        }

        POMDOG_ASSERT(iter != std::end(spriteFontMap));
        auto const & glyph = iter->second;

        if (glyph.Subrect.Width > 0 && glyph.Subrect.Height > 0)
        {
            POMDOG_ASSERT(glyph.TexturePage >= 0);
            POMDOG_ASSERT(glyph.TexturePage < static_cast<int>(textures.size()));

            spriteBatch.Draw(textures[glyph.TexturePage],
                currentPosition + Vector2(glyph.XOffset, -glyph.YOffset),
                glyph.Subrect, color, 0.0f, Vector2{0.0f, 1.0f}, Vector2{1.0f, 1.0f});
        }

        currentPosition.X += (glyph.XAdvance - spacing);
    }
}

void SpriteFont::Impl::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color,
    const Radian<float>& rotation,
    //const Vector2& originPivot,
    const Vector2& scale)
{
    if (text.empty()) {
        return;
    }

    if (textures.empty()) {
        return;
    }

    Vector2 currentPosition = position;

    auto textIter = std::begin(text);
    auto textIterEnd = std::end(text);

    while (textIter != textIterEnd)
    {
        const auto character = utf8::next(textIter, textIterEnd);

        if (character == U'\n') {
            currentPosition.X = position.X;
            currentPosition.Y -= lineSpacing * scale.Y;
            continue;
        }

        auto iter = spriteFontMap.find(character);
        if (iter == std::end(spriteFontMap)) {
            iter = spriteFontMap.find(defaultCharacter);
            if (iter == std::end(spriteFontMap)) {
                continue;
            }
        }

        POMDOG_ASSERT(iter != std::end(spriteFontMap));
        auto const & glyph = iter->second;

        if (glyph.Subrect.Width > 0 && glyph.Subrect.Height > 0)
        {
            POMDOG_ASSERT(glyph.TexturePage >= 0);
            POMDOG_ASSERT(glyph.TexturePage < static_cast<int>(textures.size()));

            spriteBatch.Draw(textures[glyph.TexturePage],
                currentPosition + Vector2(glyph.XOffset, -glyph.YOffset) * scale,
                glyph.Subrect, color, 0.0f, Vector2{0.0f, 1.0f}, scale);
        }

        currentPosition.X += ((glyph.XAdvance - spacing) * scale.X);
    }
}

// MARK: - SpriteFont

SpriteFont::SpriteFont(
    std::vector<std::shared_ptr<Texture2D>> && textures,
    const std::vector<Detail::SpriteFonts::Glyph>& glyphs,
    char32_t defaultCharacter,
    std::int16_t spacing,
    std::int16_t lineSpacing)
    : impl(std::make_unique<Impl>(std::move(textures), glyphs, defaultCharacter, spacing, lineSpacing))
{
}

SpriteFont::SpriteFont(
    const std::shared_ptr<GraphicsDevice>& graphicsDevice,
    const std::shared_ptr<TrueTypeFont>& font,
    char32_t defaultCharacter,
    std::int16_t lineSpacing)
    : impl(std::make_unique<Impl>(graphicsDevice, font, defaultCharacter, lineSpacing))
{
}

SpriteFont::~SpriteFont() = default;

Vector2 SpriteFont::MeasureString(const std::string& utf8String) const
{
    if (utf8String.empty()) {
        return Vector2::Zero;
    }
    return impl->MeasureString(utf8String);
}

char32_t SpriteFont::GetDefaultCharacter() const
{
    POMDOG_ASSERT(impl);
    return impl->defaultCharacter;
}

void SpriteFont::SetDefaultCharacter(char32_t character)
{
    POMDOG_ASSERT(impl);
    POMDOG_ASSERT(ContainsCharacter(character));
    impl->defaultCharacter = character;
}

float SpriteFont::GetLineSpacing() const
{
    POMDOG_ASSERT(impl);
    return impl->lineSpacing;
}

void SpriteFont::SetLineSpacing(float lineSpacingIn)
{
    POMDOG_ASSERT(impl);
    impl->lineSpacing = lineSpacingIn;
}

bool SpriteFont::ContainsCharacter(char32_t character) const
{
    POMDOG_ASSERT(impl);
    return impl->spriteFontMap.find(character) != std::end(impl->spriteFontMap);
}

void SpriteFont::Begin(
    const std::shared_ptr<GraphicsCommandList>& commandList,
    SpriteBatchRenderer & spriteBatch,
    const Matrix4x4& transformMatrix)
{
    POMDOG_ASSERT(impl);
    POMDOG_ASSERT(commandList);

    spriteBatch.Begin(commandList, transformMatrix);

//    spriteBatch.Begin(SpriteSortMode::Deferred, Matrix4x4::CreateRotationZ(rotation)
//        * Matrix4x4::CreateScale({scale, 1.0f})
//        * Matrix4x4::CreateTranslation({position, 0.0f})
//        * transformMatrix);
}

void SpriteFont::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color)
{
    if (text.empty()) {
        return;
    }

    impl->PrepareFonts(text);
    impl->Draw(spriteBatch, text, position, color);
}

void SpriteFont::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color,
    const Radian<float>& rotation,
    //const Vector2& originPivot,
    float scale)
{
    this->Draw(spriteBatch, text, position, color,
        rotation, Vector2{scale, scale});
}

void SpriteFont::Draw(
    SpriteBatchRenderer & spriteBatch,
    const std::string& text,
    const Vector2& position,
    const Color& color,
    const Radian<float>& rotation,
    //const Vector2& originPivot,
    const Vector2& scale)
{
    if (text.empty()) {
        return;
    }

    impl->PrepareFonts(text);
    impl->Draw(spriteBatch, text, position, color,
        rotation, scale);
}

void SpriteFont::End(SpriteBatchRenderer & spriteBatch)
{
    spriteBatch.End();
}

} // namespace Pomdog
<|endoftext|>


<commit_before>/// \file
/// \ingroup tutorial_eve
/// Demonstrates usage of TEveBoxSet class.
///
/// \image html eve_boxset.png
/// \macro_code
///
/// \author Matevz Tadel

TEveBoxSet* boxset(Float_t x=0, Float_t y=0, Float_t z=0,
                   Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   TEveRGBAPalette* pal = new TEveRGBAPalette(0, 130);

   TEveFrameBox* frm = new TEveFrameBox();
   frm->SetAABoxCenterHalfSize(0, 0, 0, 12, 12, 12);
   frm->SetFrameColor(kCyan);
   frm->SetBackColorRGBA(120,120,120,20);
   frm->SetDrawBack(kTRUE);

   TEveBoxSet* q = new TEveBoxSet("BoxSet");
   q->SetPalette(pal);
   q->SetFrame(frm);
   q->Reset(TEveBoxSet::kBT_AABox, kFALSE, 64);
   for (Int_t i=0; i<num; ++i) {
      q->AddBox(r.Uniform(-10, 10), r.Uniform(-10, 10), r.Uniform(-10, 10),
                r.Uniform(0.2, 1),  r.Uniform(0.2, 1),  r.Uniform(0.2, 1));
      q->DigitValue(r.Uniform(0, 130));
   }
   q->RefitPlex();

   TEveTrans& t = q->RefMainTrans();
   t.SetPos(x, y, z);

   // Uncomment these two lines to get internal highlight / selection.
   // q->SetPickable(1);
   // q->SetAlwaysSecSelect(1);

   if (registerSet)
   {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}

TEveBoxSet* boxset_colisval(Float_t x=0, Float_t y=0, Float_t z=0,
                            Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   TEveBoxSet* q = new TEveBoxSet("BoxSet");
   q->Reset(TEveBoxSet::kBT_AABox, kTRUE, 64);
   for (Int_t i=0; i<num; ++i) {
      q->AddBox(r.Uniform(-10, 10), r.Uniform(-10, 10), r.Uniform(-10, 10),
                r.Uniform(0.2, 1),  r.Uniform(0.2, 1),  r.Uniform(0.2, 1));
      q->DigitColor(r.Uniform(20, 255), r.Uniform(20, 255),
                    r.Uniform(20, 255), r.Uniform(20, 255));
   }
   q->RefitPlex();

   TEveTrans& t = q->RefMainTrans();
   t.SetPos(x, y, z);

   if (registerSet)
   {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}

TEveBoxSet* boxset_single_color(Float_t x=0, Float_t y=0, Float_t z=0,
                                Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   TEveBoxSet* q = new TEveBoxSet("BoxSet");
   q->UseSingleColor();
   q->SetMainColor(kCyan-2);
   q->SetMainTransparency(50);
   q->Reset(TEveBoxSet::kBT_AABox, kFALSE, 64);
   for (Int_t i=0; i<num; ++i) {
      q->AddBox(r.Uniform(-10, 10), r.Uniform(-10, 10), r.Uniform(-10, 10),
                r.Uniform(0.2, 1),  r.Uniform(0.2, 1),  r.Uniform(0.2, 1));
   }
   q->RefitPlex();

   TEveTrans& t = q->RefMainTrans();
   t.SetPos(x, y, z);

   if (registerSet)
   {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}

TEveBoxSet* boxset_freebox(Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   TEveRGBAPalette* pal = new TEveRGBAPalette(0, 130);

   TEveBoxSet* q = new TEveBoxSet("BoxSet");
   q->SetPalette(pal);
   q->Reset(TEveBoxSet::kBT_FreeBox, kFALSE, 64);

#define RND_BOX(x) (Float_t)r.Uniform(-(x), (x))

   Float_t verts[24];
   for (Int_t i=0; i<num; ++i) {
      Float_t x = RND_BOX(10);
      Float_t y = RND_BOX(10);
      Float_t z = RND_BOX(10);
      Float_t a = r.Uniform(0.2, 0.5);
      Float_t d = 0.05;
      Float_t verts[24] = {
         x - a + RND_BOX(d), y - a + RND_BOX(d), z - a + RND_BOX(d),
         x - a + RND_BOX(d), y + a + RND_BOX(d), z - a + RND_BOX(d),
         x + a + RND_BOX(d), y + a + RND_BOX(d), z - a + RND_BOX(d),
         x + a + RND_BOX(d), y - a + RND_BOX(d), z - a + RND_BOX(d),
         x - a + RND_BOX(d), y - a + RND_BOX(d), z + a + RND_BOX(d),
         x - a + RND_BOX(d), y + a + RND_BOX(d), z + a + RND_BOX(d),
         x + a + RND_BOX(d), y + a + RND_BOX(d), z + a + RND_BOX(d),
         x + a + RND_BOX(d), y - a + RND_BOX(d), z + a + RND_BOX(d) };
      q->AddBox(verts);
      q->DigitValue(r.Uniform(0, 130));
   }
   q->RefitPlex();

#undef RND_BOX

   // Uncomment these two lines to get internal highlight / selection.
   // q->SetPickable(1);
   // q->SetAlwaysSecSelect(1);

   if (registerSet)
   {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}
<commit_msg>- use auto<commit_after>/// \file
/// \ingroup tutorial_eve
/// Demonstrates usage of TEveBoxSet class.
///
/// \image html eve_boxset.png
/// \macro_code
///
/// \author Matevz Tadel

TEveBoxSet* boxset(Float_t x=0, Float_t y=0, Float_t z=0,
                   Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   auto pal = new TEveRGBAPalette(0, 130);

   auto frm = new TEveFrameBox();
   frm->SetAABoxCenterHalfSize(0, 0, 0, 12, 12, 12);
   frm->SetFrameColor(kCyan);
   frm->SetBackColorRGBA(120,120,120,20);
   frm->SetDrawBack(kTRUE);

   auto q = new TEveBoxSet("BoxSet");
   q->SetPalette(pal);
   q->SetFrame(frm);
   q->Reset(TEveBoxSet::kBT_AABox, kFALSE, 64);
   for (Int_t i=0; i<num; ++i) {
      q->AddBox(r.Uniform(-10, 10), r.Uniform(-10, 10), r.Uniform(-10, 10),
                r.Uniform(0.2, 1),  r.Uniform(0.2, 1),  r.Uniform(0.2, 1));
      q->DigitValue(r.Uniform(0, 130));
   }
   q->RefitPlex();

   TEveTrans& t = q->RefMainTrans();
   t.SetPos(x, y, z);

   // Uncomment these two lines to get internal highlight / selection.
   // q->SetPickable(1);
   // q->SetAlwaysSecSelect(1);

   if (registerSet)
   {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}

TEveBoxSet* boxset_colisval(Float_t x=0, Float_t y=0, Float_t z=0,
                            Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   auto q = new TEveBoxSet("BoxSet");
   q->Reset(TEveBoxSet::kBT_AABox, kTRUE, 64);
   for (Int_t i=0; i<num; ++i) {
      q->AddBox(r.Uniform(-10, 10), r.Uniform(-10, 10), r.Uniform(-10, 10),
                r.Uniform(0.2, 1),  r.Uniform(0.2, 1),  r.Uniform(0.2, 1));
      q->DigitColor(r.Uniform(20, 255), r.Uniform(20, 255),
                    r.Uniform(20, 255), r.Uniform(20, 255));
   }
   q->RefitPlex();

   TEveTrans& t = q->RefMainTrans();
   t.SetPos(x, y, z);

   if (registerSet)
   {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}

TEveBoxSet* boxset_single_color(Float_t x=0, Float_t y=0, Float_t z=0,
                                Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   auto q = new TEveBoxSet("BoxSet");
   q->UseSingleColor();
   q->SetMainColor(kCyan-2);
   q->SetMainTransparency(50);
   q->Reset(TEveBoxSet::kBT_AABox, kFALSE, 64);
   for (Int_t i=0; i<num; ++i) {
      q->AddBox(r.Uniform(-10, 10), r.Uniform(-10, 10), r.Uniform(-10, 10),
                r.Uniform(0.2, 1),  r.Uniform(0.2, 1),  r.Uniform(0.2, 1));
   }
   q->RefitPlex();

   TEveTrans& t = q->RefMainTrans();
   t.SetPos(x, y, z);

   if (registerSet) {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}

TEveBoxSet* boxset_freebox(Int_t num=100, Bool_t registerSet=kTRUE)
{
   TEveManager::Create();

   TRandom r(0);

   auto pal = new TEveRGBAPalette(0, 130);

   auto q = new TEveBoxSet("BoxSet");
   q->SetPalette(pal);
   q->Reset(TEveBoxSet::kBT_FreeBox, kFALSE, 64);

#define RND_BOX(x) (Float_t)r.Uniform(-(x), (x))

   Float_t verts[24];
   for (Int_t i=0; i<num; ++i) {
      Float_t x = RND_BOX(10);
      Float_t y = RND_BOX(10);
      Float_t z = RND_BOX(10);
      Float_t a = r.Uniform(0.2, 0.5);
      Float_t d = 0.05;
      Float_t verts[24] = {
         x - a + RND_BOX(d), y - a + RND_BOX(d), z - a + RND_BOX(d),
         x - a + RND_BOX(d), y + a + RND_BOX(d), z - a + RND_BOX(d),
         x + a + RND_BOX(d), y + a + RND_BOX(d), z - a + RND_BOX(d),
         x + a + RND_BOX(d), y - a + RND_BOX(d), z - a + RND_BOX(d),
         x - a + RND_BOX(d), y - a + RND_BOX(d), z + a + RND_BOX(d),
         x - a + RND_BOX(d), y + a + RND_BOX(d), z + a + RND_BOX(d),
         x + a + RND_BOX(d), y + a + RND_BOX(d), z + a + RND_BOX(d),
         x + a + RND_BOX(d), y - a + RND_BOX(d), z + a + RND_BOX(d) };
      q->AddBox(verts);
      q->DigitValue(r.Uniform(0, 130));
   }
   q->RefitPlex();

#undef RND_BOX

   // Uncomment these two lines to get internal highlight / selection.
   // q->SetPickable(1);
   // q->SetAlwaysSecSelect(1);

   if (registerSet) {
      gEve->AddElement(q);
      gEve->Redraw3D(kTRUE);
   }

   return q;
}
<|endoftext|>


<commit_before>void loopdir() {
   // example of script to loop on all the objects of a ROOT file directory
   // and print on Postscript all TH1 derived objects
   // This script uses the file generated by tutorial hsimple.C
   //Author: Rene Brun

   TString dir = gSystem->UnixPathName(__FILE__);
   dir.ReplaceAll("loopdir.C","../hsimple.C");
   dir.ReplaceAll("/./","/");
   if (!gInterpreter->IsLoaded(dir.Data())) gInterpreter->LoadMacro(dir.Data());
   TFile *f1 = (TFile*)gROOT->ProcessLineFast("hsimple(1)");
   TIter next(f1->GetListOfKeys());
   TKey *key;
   TCanvas c1;
   c1.Print("hsimple.ps[");
   while ((key = (TKey*)next())) {
      TClass *cl = gROOT->GetClass(key->GetClassName());
      if (!cl->InheritsFrom("TH1")) continue;
      TH1 *h = (TH1*)key->ReadObj();
      h->Draw();
      c1.Print("hsimple.ps");
   }
   c1.Print("hsimple.ps]");
}

<commit_msg>Do not trigger the creation of hsimple.root, which may create clashes when running in parallel.<commit_after>void loopdir() {
   // example of script to loop on all the objects of a ROOT file directory
   // and print on Postscript all TH1 derived objects
   // This script uses the file generated by tutorial hsimple.C
   //Author: Rene Brun

   TFile *f1 = TFile::Open("hsimple.root");
   TIter next(f1->GetListOfKeys());
   TKey *key;
   TCanvas c1;
   c1.Print("hsimple.ps[");
   while ((key = (TKey*)next())) {
      TClass *cl = gROOT->GetClass(key->GetClassName());
      if (!cl->InheritsFrom("TH1")) continue;
      TH1 *h = (TH1*)key->ReadObj();
      h->Draw();
      c1.Print("hsimple.ps");
   }
   c1.Print("hsimple.ps]");
}

<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * DSIG_Reference := Class for checking and setting up reference nodes in a DSIG signature
 *
 * $Id$
 *
 */

// High level include
#include <xsec/framework/XSECDefs.hpp>

// Xerces INcludes

#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMNamedNodeMap.hpp>

// XSEC Includes
#include <xsec/utils/XSECSafeBufferFormatter.hpp>
#include <xsec/dsig/DSIGTransform.hpp>
#include <xsec/dsig/DSIGReferenceList.hpp>
#include <xsec/dsig/DSIGConstants.hpp>

class DSIGTransformList;
class DSIGTransformBase64;
class DSIGTransformC14n;
class DSIGTransformEnvelope;
class DSIGTransformXPath;
class DSIGTransformXPathFilter;
class DSIGTransformXSL;
class DSIGSignature;
class DSIGSignedInfo;

class TXFMBase;
class TXFMChain;
class XSECBinTXFMInputStream;
class XSECURIResolver;
class XSECEnv;

/**
 * @ingroup pubsig
 */

/**
 * @brief The class used for manipulating Reference Elements within a signature.
 *
 *
The DSIGReference class creates and manipulates (including hashing and validating)
 * \<Reference\> elements.

 *
 */

class XSEC_EXPORT DSIGReference {

public:

    /** @name Constructors and Destructors */
    //@{

    /**
	 * \brief Contructor for use with existing XML signatures or templates.
	 *
	 *
Create a DSIGReference object based on an already existing
	 * DSIG Reference XML node.  It is assumed that the underlying
	 * DOM structure is in place and works correctly.

	 *
	 * @note DSIGReference structures should only ever be created via calls to a
	 * DSIGSignature object.
	 *
	 * @param env The operating environment in which the Reference is operating
	 * @param dom The DOM node (within doc) that is to be used as the base of the reference.
	 * @see #load
	 * @see DSIGSignature#createReference
	 */

	DSIGReference(const XSECEnv * env, XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *dom);

    /**
	 * \brief Contructor for use when creating new Reference structures.
	 *
	 *
Create a DSIGReference object that can later be used to create
	 * a new Reference structure in the DOM document.

	 *
	 * @note DSIGReference structures should only ever be created via calls to a
	 * DSIGSignature object.
	 *
	 * @param env The environment object for this reference.
	 * @see #load
	 * @see DSIGSignature#createReference
	 */

	DSIGReference(const XSECEnv * env);

	/**
	 * \brief Destructor.
	 *
	 * @note Does not impact any created DOM structures when destroyed.
	 *
	 * @note DSIGReferences should never be destroyed/deleted by
	 * applications.  They are owned and managed by DSIGSignature structures.
	 */

	~DSIGReference();

	//@}

    /** @name Reference Construction and Manipulation */
    //@{

	/**
	 * \brief Load a DSIGReference from an existing DOM structure.
	 *
	 *
This function will load a Reference structure from the owner
	 * document.

	 *
	 */

	void load();

	/**
	 * \brief Create a Reference structure in the document.
	 *
	 *
This function will create a Reference structure in the owner
	 * document.  In some cases, a call to this function will be sufficient
	 * to put the required Reference in place.  In other cases, calls will
	 * also need to be made to the various append*Transform methods.

	 *
	 * @note The XSEC Library currently makes very little use of type
	 * attributes in \<Reference\> Elements.  However this may of use to calling
	 * applications.
	 *
	 * @param URI The URI (data source) for this reference.  Set to NULL for
	 * an anonymous reference.
	 * @param hashAlgorithmURI The type of Digest to be used (generally SHA-1)
	 * @param type A type string (as defined by XML Signature).
	 * @returns The root Reference element of the newly created DOM structure.
	 */

	XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *
		createBlankReference(const XMLCh * URI,
			const XMLCh * hashAlgorithmURI,
			const XMLCh * type);

	/**
	 * \brief Append an Enveloped Signature Transform to the Reference.
	 *
	 * Appends a simple enveloped-signature transform to the list of transforms
	 * in this element.
	 *
	 * @returns The newly created envelope transform.
	 *
	 */

	DSIGTransformEnvelope *  appendEnvelopedSignatureTransform();

	/**
	 * \brief Append a Base64 Transform to the Reference.
	 *
	 * @returns The newly created Base64 transform.
	 */

	DSIGTransformBase64 * appendBase64Transform();

	/**
	 * \brief Append an XPath Transform to the Reference.
	 *
	 *
 Append an XPath transform.  Namespaces can be added to the
	 * transform directly using the returned DSIGTransformXPath
	 * structure

	 *
	 * @param expr The XPath expression to be placed in the transform.
	 * @returns The newly created XPath transform
	 */

	DSIGTransformXPath * appendXPathTransform(const char * expr);

	/**
	 * \brief Append an XPath-Filter2 Transform to the Reference.
	 *
	 * The returned DSIGTransformXPathFilter will have no actual filter
	 * expressions loaded, but calls can be made to
	 * DSIGTransformXPathFilter::appendTransform to add them.
	 *
	 * @returns The newly created XPath Filter transform
	 */

	DSIGTransformXPathFilter * appendXPathFilterTransform(void);

	/**
	 * \brief Append an XSLT Transform to the Reference.
	 *
	 *
The caller must have already create the stylesheet and turned it into
	 * a DOM structure that is passed in as the stylesheet parameter.

	 *
	 * @param stylesheet The stylesheet DOM structure to be placed in the reference.
	 * @returns The newly create XSLT transform
	 */

	DSIGTransformXSL * appendXSLTransform(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *stylesheet);

	/**
	 * \brief Append a Canonicalization Transform to the Reference.
	 *
	 * @param canonicalizationAlgorithmURI The type of canonicalisation to be added.
	 * @returns The newly create canonicalisation transform
	 */

	DSIGTransformC14n * appendCanonicalizationTransform(
		const XMLCh * canonicalizationAlgorithmURI
	);

	/**
	 * \brief Append a "debug" transformer.
	 *
	 * This method allows applications to provide a TXFM that will be appended
	 * to the transform chain just prior to the application of the hash
	 * algorithm.
	 *
	 * @note This is primarily for debugging.  It should not be used to modify the
	 * contents of the byte stream.
	 *
	 * @param t The TXFM element to insert.
	 */

	void setPreHashTXFM(TXFMBase * t);

   /**
    * \brief Set the Id attribute of the DSIGReference
    *
    * This method allows applications to set the Id attribute of the DSIGReference
    * as described in http://www.w3.org/TR/xmldsig-core/#sec-Reference.
    *
    *
    * @param id The value for this reference.
    */
   void setId(const XMLCh *id);

   /**
    * \brief Set the Type attribute of the DSIGReference
    *
    * This method allows applications to set the Type attribute of the DSIGReference
    * as described in http://www.w3.org/TR/xmldsig-core/#sec-Reference.
    *
    *
    * @param type The value for this reference.
    */
   void setType(const XMLCh *type);
	//@}

	/** @name Getting Information */
	//@{

	/**
	 * \brief Create an input stream based on the digested byte stream.
	 *
	 * This method allows applications to read the fully canonicalised
	 * byte stream that is hashed for a reference.
	 *
	 * All transforms are performed up to the point where they would
	 * normally be fed into the Digest function.
	 *
	 * @returns A BinInputSource of the canonicalised SignedInfo
	 */

	XSECBinTXFMInputStream * makeBinInputStream(void) const;

	/**
	 * \brief Return the URI string of the Reference.
	 *
	 * @returns A pointer to the buffer (owned by the Reference) containing
	 * the value of the URI stored inthe reference
	 */

	const XMLCh * getURI() const;

    /**
     * \brief Get the Digest Algorithm URI
     *
     * @returns the URI associated with the Algorithm used to generate
     * the digest
     */

    const XMLCh * getAlgorithmURI() const {
        return mp_algorithmURI;
    }

	/**
	 * \brief Obtain the transforms for this reference
	 *
	 * Get the DSIGTransformList object for this reference.  Can be used to
	 * obtain information about the transforms and also change the the transforms
	 */

	DSIGTransformList * getTransforms(void) const {
		return mp_transformList;
	}

	/**
	 * \brief Determine whether the reference is a manifest
	 *
	 * @returns true iff the Reference element is a Manifest reference
	 */

	bool isManifest() const;

	/**
	 * \brief Get the Manifest
	 *
	 * @returns The ReferenceList containing the references in the Manifest
	 * list of this reference element.
	 */

	DSIGReferenceList * getManifestReferenceList() const;		// Return list of references for a manifest object

	//@}

	/** @name Message Digest/Hash manipulation */
	//@{

	/**
	 * \brief Calculate the Hash  value of a reference
	 *
	 * Takes the Reference URI, performs all the transforms and finally
	 * calculates the Hash value of the data using the Digest algorithm
	 * indicated in the reference
	 *
	 * @param toFill A Buffer that the raw hash will be copied into.
	 * @param maxToFill Maximum number of bytes to place in the buffer
	 * @returns The number of bytes copied into the buffer
	 */

	unsigned int calculateHash(XMLByte * toFill, unsigned int maxToFill) const;

	/**
	 * \brief Read the hash from the Reference element.
	 *
	 * Reads the Base64 encoded element from the Reference element.
	 * The hash is then translated from Base64 back into raw form and
	 * written into the indicated buffer.
	 *
	 * @param toFill Pointer to the buffer where the raw hash will be written
	 * @param maxToFill Maximum bytes to write to the buffer
	 * @returns Number of bytes written
	 */

	unsigned int readHash(XMLByte *toFill, unsigned int maxToFill) const;

	/**
	 * \brief Validate the Reference element
	 *
	 * Performs a #calculateHash() and a #readHash() and then compares the
	 * results.
	 *
	 * @returns true iff the hash of the data matches the hash stored
	 * in the reference.
	 */

	bool checkHash() const;

	/**
	 * \brief Set the value of the hash in the Reference
	 *
	 * Hashes the data referenced by the element and then writes
	 * the Base64 encoded hash value into the Reference.
	 *
	 */

	void setHash();

	//@}

	/** @name Helper (static) Functions */
	//@{

	/**
	 * \brief Create a Transformer chain
	 *
	 * Given a TransformList create the corresponding TXFM chain to allow
	 * the caller to read the reference byte stream
	 *
	 * @note This method is primarily for use within the XSEC library.
	 * Users wishing to get the byte stream should use the #makeBinInputStream
	 * method instead.
	 *
	 * @param input The input transformer to which the TXFMs will be applied to
	 * This is generally created from the URI attribute of the reference.
	 * @param lst The list of Transform elements from which to build the
	 * transformer list.
	 * @returns The end of the newly build TXFM chain.  This can be
	 * read from using TXFMBase::readBytes() to give the end result of the
	 * transforms.
	 */

	static TXFMChain * createTXFMChainFromList(TXFMBase * input,
							DSIGTransformList * lst);

	/**
	 * \brief Load a Transforms list from the \<Transforms\> DOMNode.
	 *
	 * Reads the data from the XML data stored in the DOM and create
	 * the associated DSIGTrasnformList.
	 *
	 * @param transformsNode Starting node in the DOM
	 * @param formatter The formatter to be used to move from XMLCh to strings
	 * @param env Environment in which to operate
	 * @returns A pointer to the created list.
	 */

	static DSIGTransformList * loadTransforms(
							XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *transformsNode,
							XSECSafeBufferFormatter * formatter,
							const XSECEnv * env);

	/**
	 * \brief Create a starting point for a TXFM Chain.
	 *
	 * Uses the provided URI to find the base data that the Transformer chain
	 * will be built upon.
	 *
	 * @param doc The document that the signature is based on (used for local URIs)
	 * @param URI The URI to build the base from
	 * @param env The environment the signature is operating in
	 * @returns A base TXFM element.
	 */

	static TXFMBase * getURIBaseTXFM(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * doc,
									const XMLCh * URI,
									const XSECEnv * env);

	/**
	 * \brief Load a series of references.
	 *
	 * Takes a series of \<Reference\> elements in a DOM structure
	 * and creates the corresponding ReferenceList object.
	 *
	 * @note Internal function - meant for use by the library
	 *
	 * @param env The environment in which this reference resides
	 * @param firstReference First reference in DOM structure
	 * @returns the created list.
	 */

	static DSIGReferenceList *loadReferenceListFromXML(const XSECEnv * env,
													   XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *firstReference);

	/**
	 * \brief Validate a list of references.
	 *
	 * Runs through a reference list, calling verify() on each and
	 * setting the ErrroStrings for any errors found
	 *
	 * @param lst The list to verify
	 * @param errorStr The string to append any errors found to
	 * @returns true iff all the references validate successfully.
	 */

	static bool verifyReferenceList(const DSIGReferenceList * lst, safeBuffer &errorStr);

	/**
	 * \brief Hash a reference list
	 *
	 * Run through a list of references and calculate the hash value of each
	 * element.  Finally set the Base64 encoded string according to the newly
	 * calcuated hash.
	 *
	 * @note This is an internal library function and should not be called directly.
	 *
	 * @param list The list of references
	 * @param interlocking If set to false, the library will assume there
	 * are no inter-related references.  The algorithm for determining this
	 * internally is very primitive and CPU intensive, so this is a method to
	 * bypass the checks.
	 */
	static void hashReferenceList(const DSIGReferenceList * list, bool interlocking = true);

	//@}

private:

	// Internal functions
	void createTransformList(void);
	void addTransform(
		DSIGTransform * txfm,
		XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * txfmElt
	);

	XSECSafeBufferFormatter		* mp_formatter;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode
								* mp_referenceNode;		// Points to start of document where reference node is
	mutable TXFMBase				* mp_preHash;			// To be used pre-hash
	DSIGReferenceList			* mp_manifestList;		// The list of references in a manifest
	const XMLCh					* mp_URI;				// The URI String
	bool						m_isManifest;			// Does this reference a manifest?
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode
								* mp_transformsNode;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode
								* mp_hashValueNode;		// Node where the Hash value is stored
	const XSECEnv				* mp_env;
	DSIGTransformList			* mp_transformList;		// List of transforms
	const XMLCh					* mp_algorithmURI;		// Hash algorithm for this reference

	bool                        m_loaded;

	DSIGReference();

	/*\@}*/

	friend class DSIGSignedInfo;
};

<commit_msg>SANTUARIO-568 - DSIGReference is missing a header guard<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * DSIG_Reference := Class for checking and setting up reference nodes in a DSIG signature
 *
 * $Id$
 *
 */

#ifndef DSIGREFERENCE_INCLUDE
#define DSIGREFERENCE_INCLUDE

// High level include
#include <xsec/framework/XSECDefs.hpp>

// Xerces INcludes

#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMNamedNodeMap.hpp>

// XSEC Includes
#include <xsec/utils/XSECSafeBufferFormatter.hpp>
#include <xsec/dsig/DSIGTransform.hpp>
#include <xsec/dsig/DSIGReferenceList.hpp>
#include <xsec/dsig/DSIGConstants.hpp>

class DSIGTransformList;
class DSIGTransformBase64;
class DSIGTransformC14n;
class DSIGTransformEnvelope;
class DSIGTransformXPath;
class DSIGTransformXPathFilter;
class DSIGTransformXSL;
class DSIGSignature;
class DSIGSignedInfo;

class TXFMBase;
class TXFMChain;
class XSECBinTXFMInputStream;
class XSECURIResolver;
class XSECEnv;

/**
 * @ingroup pubsig
 */

/**
 * @brief The class used for manipulating Reference Elements within a signature.
 *
 *
The DSIGReference class creates and manipulates (including hashing and validating)
 * \<Reference\> elements.

 *
 */

class XSEC_EXPORT DSIGReference {

public:

    /** @name Constructors and Destructors */
    //@{

    /**
	 * \brief Contructor for use with existing XML signatures or templates.
	 *
	 *
Create a DSIGReference object based on an already existing
	 * DSIG Reference XML node.  It is assumed that the underlying
	 * DOM structure is in place and works correctly.

	 *
	 * @note DSIGReference structures should only ever be created via calls to a
	 * DSIGSignature object.
	 *
	 * @param env The operating environment in which the Reference is operating
	 * @param dom The DOM node (within doc) that is to be used as the base of the reference.
	 * @see #load
	 * @see DSIGSignature#createReference
	 */

	DSIGReference(const XSECEnv * env, XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *dom);

    /**
	 * \brief Contructor for use when creating new Reference structures.
	 *
	 *
Create a DSIGReference object that can later be used to create
	 * a new Reference structure in the DOM document.

	 *
	 * @note DSIGReference structures should only ever be created via calls to a
	 * DSIGSignature object.
	 *
	 * @param env The environment object for this reference.
	 * @see #load
	 * @see DSIGSignature#createReference
	 */

	DSIGReference(const XSECEnv * env);

	/**
	 * \brief Destructor.
	 *
	 * @note Does not impact any created DOM structures when destroyed.
	 *
	 * @note DSIGReferences should never be destroyed/deleted by
	 * applications.  They are owned and managed by DSIGSignature structures.
	 */

	~DSIGReference();

	//@}

    /** @name Reference Construction and Manipulation */
    //@{

	/**
	 * \brief Load a DSIGReference from an existing DOM structure.
	 *
	 *
This function will load a Reference structure from the owner
	 * document.

	 *
	 */

	void load();

	/**
	 * \brief Create a Reference structure in the document.
	 *
	 *
This function will create a Reference structure in the owner
	 * document.  In some cases, a call to this function will be sufficient
	 * to put the required Reference in place.  In other cases, calls will
	 * also need to be made to the various append*Transform methods.

	 *
	 * @note The XSEC Library currently makes very little use of type
	 * attributes in \<Reference\> Elements.  However this may of use to calling
	 * applications.
	 *
	 * @param URI The URI (data source) for this reference.  Set to NULL for
	 * an anonymous reference.
	 * @param hashAlgorithmURI The type of Digest to be used (generally SHA-1)
	 * @param type A type string (as defined by XML Signature).
	 * @returns The root Reference element of the newly created DOM structure.
	 */

	XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *
		createBlankReference(const XMLCh * URI,
			const XMLCh * hashAlgorithmURI,
			const XMLCh * type);

	/**
	 * \brief Append an Enveloped Signature Transform to the Reference.
	 *
	 * Appends a simple enveloped-signature transform to the list of transforms
	 * in this element.
	 *
	 * @returns The newly created envelope transform.
	 *
	 */

	DSIGTransformEnvelope *  appendEnvelopedSignatureTransform();

	/**
	 * \brief Append a Base64 Transform to the Reference.
	 *
	 * @returns The newly created Base64 transform.
	 */

	DSIGTransformBase64 * appendBase64Transform();

	/**
	 * \brief Append an XPath Transform to the Reference.
	 *
	 *
 Append an XPath transform.  Namespaces can be added to the
	 * transform directly using the returned DSIGTransformXPath
	 * structure

	 *
	 * @param expr The XPath expression to be placed in the transform.
	 * @returns The newly created XPath transform
	 */

	DSIGTransformXPath * appendXPathTransform(const char * expr);

	/**
	 * \brief Append an XPath-Filter2 Transform to the Reference.
	 *
	 * The returned DSIGTransformXPathFilter will have no actual filter
	 * expressions loaded, but calls can be made to
	 * DSIGTransformXPathFilter::appendTransform to add them.
	 *
	 * @returns The newly created XPath Filter transform
	 */

	DSIGTransformXPathFilter * appendXPathFilterTransform(void);

	/**
	 * \brief Append an XSLT Transform to the Reference.
	 *
	 *
The caller must have already create the stylesheet and turned it into
	 * a DOM structure that is passed in as the stylesheet parameter.

	 *
	 * @param stylesheet The stylesheet DOM structure to be placed in the reference.
	 * @returns The newly create XSLT transform
	 */

	DSIGTransformXSL * appendXSLTransform(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *stylesheet);

	/**
	 * \brief Append a Canonicalization Transform to the Reference.
	 *
	 * @param canonicalizationAlgorithmURI The type of canonicalisation to be added.
	 * @returns The newly create canonicalisation transform
	 */

	DSIGTransformC14n * appendCanonicalizationTransform(
		const XMLCh * canonicalizationAlgorithmURI
	);

	/**
	 * \brief Append a "debug" transformer.
	 *
	 * This method allows applications to provide a TXFM that will be appended
	 * to the transform chain just prior to the application of the hash
	 * algorithm.
	 *
	 * @note This is primarily for debugging.  It should not be used to modify the
	 * contents of the byte stream.
	 *
	 * @param t The TXFM element to insert.
	 */

	void setPreHashTXFM(TXFMBase * t);

   /**
    * \brief Set the Id attribute of the DSIGReference
    *
    * This method allows applications to set the Id attribute of the DSIGReference
    * as described in http://www.w3.org/TR/xmldsig-core/#sec-Reference.
    *
    *
    * @param id The value for this reference.
    */
   void setId(const XMLCh *id);

   /**
    * \brief Set the Type attribute of the DSIGReference
    *
    * This method allows applications to set the Type attribute of the DSIGReference
    * as described in http://www.w3.org/TR/xmldsig-core/#sec-Reference.
    *
    *
    * @param type The value for this reference.
    */
   void setType(const XMLCh *type);
	//@}

	/** @name Getting Information */
	//@{

	/**
	 * \brief Create an input stream based on the digested byte stream.
	 *
	 * This method allows applications to read the fully canonicalised
	 * byte stream that is hashed for a reference.
	 *
	 * All transforms are performed up to the point where they would
	 * normally be fed into the Digest function.
	 *
	 * @returns A BinInputSource of the canonicalised SignedInfo
	 */

	XSECBinTXFMInputStream * makeBinInputStream(void) const;

	/**
	 * \brief Return the URI string of the Reference.
	 *
	 * @returns A pointer to the buffer (owned by the Reference) containing
	 * the value of the URI stored inthe reference
	 */

	const XMLCh * getURI() const;

    /**
     * \brief Get the Digest Algorithm URI
     *
     * @returns the URI associated with the Algorithm used to generate
     * the digest
     */

    const XMLCh * getAlgorithmURI() const {
        return mp_algorithmURI;
    }

	/**
	 * \brief Obtain the transforms for this reference
	 *
	 * Get the DSIGTransformList object for this reference.  Can be used to
	 * obtain information about the transforms and also change the the transforms
	 */

	DSIGTransformList * getTransforms(void) const {
		return mp_transformList;
	}

	/**
	 * \brief Determine whether the reference is a manifest
	 *
	 * @returns true iff the Reference element is a Manifest reference
	 */

	bool isManifest() const;

	/**
	 * \brief Get the Manifest
	 *
	 * @returns The ReferenceList containing the references in the Manifest
	 * list of this reference element.
	 */

	DSIGReferenceList * getManifestReferenceList() const;		// Return list of references for a manifest object

	//@}

	/** @name Message Digest/Hash manipulation */
	//@{

	/**
	 * \brief Calculate the Hash  value of a reference
	 *
	 * Takes the Reference URI, performs all the transforms and finally
	 * calculates the Hash value of the data using the Digest algorithm
	 * indicated in the reference
	 *
	 * @param toFill A Buffer that the raw hash will be copied into.
	 * @param maxToFill Maximum number of bytes to place in the buffer
	 * @returns The number of bytes copied into the buffer
	 */

	unsigned int calculateHash(XMLByte * toFill, unsigned int maxToFill) const;

	/**
	 * \brief Read the hash from the Reference element.
	 *
	 * Reads the Base64 encoded element from the Reference element.
	 * The hash is then translated from Base64 back into raw form and
	 * written into the indicated buffer.
	 *
	 * @param toFill Pointer to the buffer where the raw hash will be written
	 * @param maxToFill Maximum bytes to write to the buffer
	 * @returns Number of bytes written
	 */

	unsigned int readHash(XMLByte *toFill, unsigned int maxToFill) const;

	/**
	 * \brief Validate the Reference element
	 *
	 * Performs a #calculateHash() and a #readHash() and then compares the
	 * results.
	 *
	 * @returns true iff the hash of the data matches the hash stored
	 * in the reference.
	 */

	bool checkHash() const;

	/**
	 * \brief Set the value of the hash in the Reference
	 *
	 * Hashes the data referenced by the element and then writes
	 * the Base64 encoded hash value into the Reference.
	 *
	 */

	void setHash();

	//@}

	/** @name Helper (static) Functions */
	//@{

	/**
	 * \brief Create a Transformer chain
	 *
	 * Given a TransformList create the corresponding TXFM chain to allow
	 * the caller to read the reference byte stream
	 *
	 * @note This method is primarily for use within the XSEC library.
	 * Users wishing to get the byte stream should use the #makeBinInputStream
	 * method instead.
	 *
	 * @param input The input transformer to which the TXFMs will be applied to
	 * This is generally created from the URI attribute of the reference.
	 * @param lst The list of Transform elements from which to build the
	 * transformer list.
	 * @returns The end of the newly build TXFM chain.  This can be
	 * read from using TXFMBase::readBytes() to give the end result of the
	 * transforms.
	 */

	static TXFMChain * createTXFMChainFromList(TXFMBase * input,
							DSIGTransformList * lst);

	/**
	 * \brief Load a Transforms list from the \<Transforms\> DOMNode.
	 *
	 * Reads the data from the XML data stored in the DOM and create
	 * the associated DSIGTrasnformList.
	 *
	 * @param transformsNode Starting node in the DOM
	 * @param formatter The formatter to be used to move from XMLCh to strings
	 * @param env Environment in which to operate
	 * @returns A pointer to the created list.
	 */

	static DSIGTransformList * loadTransforms(
							XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *transformsNode,
							XSECSafeBufferFormatter * formatter,
							const XSECEnv * env);

	/**
	 * \brief Create a starting point for a TXFM Chain.
	 *
	 * Uses the provided URI to find the base data that the Transformer chain
	 * will be built upon.
	 *
	 * @param doc The document that the signature is based on (used for local URIs)
	 * @param URI The URI to build the base from
	 * @param env The environment the signature is operating in
	 * @returns A base TXFM element.
	 */

	static TXFMBase * getURIBaseTXFM(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * doc,
									const XMLCh * URI,
									const XSECEnv * env);

	/**
	 * \brief Load a series of references.
	 *
	 * Takes a series of \<Reference\> elements in a DOM structure
	 * and creates the corresponding ReferenceList object.
	 *
	 * @note Internal function - meant for use by the library
	 *
	 * @param env The environment in which this reference resides
	 * @param firstReference First reference in DOM structure
	 * @returns the created list.
	 */

	static DSIGReferenceList *loadReferenceListFromXML(const XSECEnv * env,
													   XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *firstReference);

	/**
	 * \brief Validate a list of references.
	 *
	 * Runs through a reference list, calling verify() on each and
	 * setting the ErrroStrings for any errors found
	 *
	 * @param lst The list to verify
	 * @param errorStr The string to append any errors found to
	 * @returns true iff all the references validate successfully.
	 */

	static bool verifyReferenceList(const DSIGReferenceList * lst, safeBuffer &errorStr);

	/**
	 * \brief Hash a reference list
	 *
	 * Run through a list of references and calculate the hash value of each
	 * element.  Finally set the Base64 encoded string according to the newly
	 * calcuated hash.
	 *
	 * @note This is an internal library function and should not be called directly.
	 *
	 * @param list The list of references
	 * @param interlocking If set to false, the library will assume there
	 * are no inter-related references.  The algorithm for determining this
	 * internally is very primitive and CPU intensive, so this is a method to
	 * bypass the checks.
	 */
	static void hashReferenceList(const DSIGReferenceList * list, bool interlocking = true);

	//@}

private:

	// Internal functions
	void createTransformList(void);
	void addTransform(
		DSIGTransform * txfm,
		XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * txfmElt
	);

	XSECSafeBufferFormatter		* mp_formatter;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode
								* mp_referenceNode;		// Points to start of document where reference node is
	mutable TXFMBase				* mp_preHash;			// To be used pre-hash
	DSIGReferenceList			* mp_manifestList;		// The list of references in a manifest
	const XMLCh					* mp_URI;				// The URI String
	bool						m_isManifest;			// Does this reference a manifest?
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode
								* mp_transformsNode;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode
								* mp_hashValueNode;		// Node where the Hash value is stored
	const XSECEnv				* mp_env;
	DSIGTransformList			* mp_transformList;		// List of transforms
	const XMLCh					* mp_algorithmURI;		// Hash algorithm for this reference

	bool                        m_loaded;

	DSIGReference();

	/*\@}*/

	friend class DSIGSignedInfo;
};

#endif /* #define DSIGREFERENCE_INCLUDE */
<|endoftext|>


<commit_before>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/test/unit_test.hpp>

#include "TestFunctions.hpp"

#include "CoreConstants.hpp"
#include "CoreGlobals.hpp"
#include "Exception.hpp"
#include "Model.hpp"
#include "Changer.hpp"
#include "Interpreter.hpp"

typedef void (CheckerFunc) (
    Implementation::Unit prev,
    Implementation::Unit curr,
    bool spec
);

static void eatChecker(
    Implementation::Unit prev,
    Implementation::Unit curr,
    bool spec
) {
    if (spec) {
        int max_mass = prev.mass + RANDOM_MAX_ACTIONS * EAT_MASS;
        BOOST_REQUIRE(curr.mass >= prev.mass && curr.mass < max_mass);
    } else {
        BOOST_REQUIRE(curr.mass == (prev.mass + EAT_MASS));
    }
}

static Implementation::Changer createChanger(
    ModelPtr model,
    bool spec,
    bool many_commands
) {
    int instructions;
    if (spec && many_commands) {
        // func; func r; func n
        instructions = 3;
    } else if (spec) {
        // func; func r
        instructions = 2;
    } else if (many_commands) {
        // func; func n
        instructions = 2;
    } else {
        // func
        instructions = 1;
    }
    return Implementation::Changer(model, 0, 0, instructions);
}

static void checkSpec(
    ModelPtr model,
    Implementation::Changer& changer,
    Implementation::ChangerMethod tested,
    CheckerFunc checker
) {
    Implementation::Unit prev = getFirstUnit(model);
    Abstract::Params params(-1, -1, true);
    changer.clearBeforeMove();
    (changer.*tested)(&params, 0);
    Implementation::Unit curr = getFirstUnit(model);
    checker(prev, curr, true);
}

static void checkManyCommands(
    ModelPtr model,
    Implementation::Changer& changer,
    Implementation::ChangerMethod tested,
    CheckerFunc checker
) {
    int commands = 5;
    Abstract::Params params(commands, -1, false);
    for (int i = 0; i < commands; i++) {
        Implementation::Unit prev = getFirstUnit(model);
        changer.clearBeforeMove();
        (changer.*tested)(&params, 0);
        Implementation::Unit curr = getFirstUnit(model);
        checker(prev, curr, false);
    }
    // range error
    params = Abstract::Params(10000, -1, false);
    changer.clearBeforeMove();
    BOOST_REQUIRE_THROW((changer.*tested)(&params, 0), Exception);
}

static void checkLogicalMethod(
    Implementation::ChangerMethod tested,
    CheckerFunc checker,
    bool spec,
    bool many_commands
) {
    ModelPtr model(createBaseModel(1, 1));
    Implementation::Changer changer =
        createChanger(model, spec, many_commands);
    Implementation::Unit prev = getFirstUnit(model);
    Abstract::Params params(-1, -1, false);
    (changer.*tested)(&params, 0);
    Implementation::Unit curr = getFirstUnit(model);
    checker(prev, curr, false);
    if (many_commands) {
        checkManyCommands(model, changer, tested, checker);
    }
    if (spec) {
        checkSpec(model, changer, tested, checker);
    }
}

BOOST_AUTO_TEST_CASE (get_bacteria_number_test) {
    ModelPtr model(createBaseModel(1, 1));
    Implementation::Changer changer(model, 0, 0, 0);
    BOOST_REQUIRE(changer.getBacteriaNumber() == 1);
}

BOOST_AUTO_TEST_CASE (get_team_test) {
    ModelPtr model(createBaseModel(1, 1));
    Implementation::Changer changer(model, 0, 0, 0);
    BOOST_REQUIRE(changer.getTeam() == 0);
}

BOOST_AUTO_TEST_CASE (changer_get_instruction_test) {
    ModelPtr model(createBaseModel(1, 1));
    // total number of instructions is 2
    Implementation::Changer changer(model, 0, 0, 2);
    // initial state
    BOOST_REQUIRE(changer.getInstruction(0) == 0);
    Abstract::Params params(-1, -1, false);
    changer.eat(&params, 0);
    // after execution of the first command
    BOOST_REQUIRE(changer.getInstruction(0) == 1);
    int commands = 5;
    params = Abstract::Params(commands, -1, false);
    for (int i = 0; i < commands; i++) {
        // it must execute the same command `commands` times
        BOOST_REQUIRE(changer.getInstruction(0) == 1);
        changer.clearBeforeMove();
        changer.eat(&params, 0);
    }
    // cycle: after execution of all commands it returns to initial state
    BOOST_REQUIRE(changer.getInstruction(0) == 0);
}

BOOST_AUTO_TEST_CASE (eat_test) {
    ModelPtr model(createBaseModel(1, 1));
    // number of instructions is 3: eat; eat 5; eat r
    Implementation::Changer changer(model, 0, 0, 3);
    Abstract::Params params(-1, -1, false);
    // eat
    changer.eat(&params, 0);
    BOOST_REQUIRE(model->getMass(0, 0) == DEFAULT_MASS + EAT_MASS);
    int commands = 5;
    params = Abstract::Params(commands, -1, false);
    for (int i = 0; i < commands; i++) {
        int expected_mass = EAT_MASS * (i + 1) + DEFAULT_MASS;
        BOOST_REQUIRE(model->getMass(0, 0) == expected_mass);
        changer.clearBeforeMove();
        // eat 5
        changer.eat(&params, 0);
    }
    int prev_mass = model->getMass(0, 0);
    params = Abstract::Params(-1, -1, true);
    changer.clearBeforeMove();
    // eat r
    changer.eat(&params, 0);
    int curr_mass = model->getMass(0, 0);
    int max_mass = prev_mass + RANDOM_MAX_ACTIONS * EAT_MASS;
    BOOST_REQUIRE(curr_mass >= prev_mass && curr_mass < max_mass);
    // range error
    params = Abstract::Params(10000, -1, false);
    BOOST_REQUIRE_THROW(changer.eat(&params, 0), Exception);
}
<commit_msg>Unit tests (changer): use checkLogicalMethod() in eat_test<commit_after>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/test/unit_test.hpp>

#include "TestFunctions.hpp"

#include "CoreConstants.hpp"
#include "CoreGlobals.hpp"
#include "Exception.hpp"
#include "Model.hpp"
#include "Changer.hpp"
#include "Interpreter.hpp"

typedef void (CheckerFunc) (
    Implementation::Unit prev,
    Implementation::Unit curr,
    bool spec
);

static void eatChecker(
    Implementation::Unit prev,
    Implementation::Unit curr,
    bool spec
) {
    if (spec) {
        int max_mass = prev.mass + RANDOM_MAX_ACTIONS * EAT_MASS;
        BOOST_REQUIRE(curr.mass >= prev.mass && curr.mass < max_mass);
    } else {
        BOOST_REQUIRE(curr.mass == (prev.mass + EAT_MASS));
    }
}

static Implementation::Changer createChanger(
    ModelPtr model,
    bool spec,
    bool many_commands
) {
    int instructions;
    if (spec && many_commands) {
        // func; func r; func n
        instructions = 3;
    } else if (spec) {
        // func; func r
        instructions = 2;
    } else if (many_commands) {
        // func; func n
        instructions = 2;
    } else {
        // func
        instructions = 1;
    }
    return Implementation::Changer(model, 0, 0, instructions);
}

static void checkSpec(
    ModelPtr model,
    Implementation::Changer& changer,
    Implementation::ChangerMethod tested,
    CheckerFunc checker
) {
    Implementation::Unit prev = getFirstUnit(model);
    Abstract::Params params(-1, -1, true);
    changer.clearBeforeMove();
    (changer.*tested)(&params, 0);
    Implementation::Unit curr = getFirstUnit(model);
    checker(prev, curr, true);
}

static void checkManyCommands(
    ModelPtr model,
    Implementation::Changer& changer,
    Implementation::ChangerMethod tested,
    CheckerFunc checker
) {
    int commands = 5;
    Abstract::Params params(commands, -1, false);
    for (int i = 0; i < commands; i++) {
        Implementation::Unit prev = getFirstUnit(model);
        changer.clearBeforeMove();
        (changer.*tested)(&params, 0);
        Implementation::Unit curr = getFirstUnit(model);
        checker(prev, curr, false);
    }
    // range error
    params = Abstract::Params(10000, -1, false);
    changer.clearBeforeMove();
    BOOST_REQUIRE_THROW((changer.*tested)(&params, 0), Exception);
}

static void checkLogicalMethod(
    Implementation::ChangerMethod tested,
    CheckerFunc checker,
    bool spec,
    bool many_commands
) {
    ModelPtr model(createBaseModel(1, 1));
    Implementation::Changer changer =
        createChanger(model, spec, many_commands);
    Implementation::Unit prev = getFirstUnit(model);
    Abstract::Params params(-1, -1, false);
    (changer.*tested)(&params, 0);
    Implementation::Unit curr = getFirstUnit(model);
    checker(prev, curr, false);
    if (many_commands) {
        checkManyCommands(model, changer, tested, checker);
    }
    if (spec) {
        checkSpec(model, changer, tested, checker);
    }
}

BOOST_AUTO_TEST_CASE (get_bacteria_number_test) {
    ModelPtr model(createBaseModel(1, 1));
    Implementation::Changer changer(model, 0, 0, 0);
    BOOST_REQUIRE(changer.getBacteriaNumber() == 1);
}

BOOST_AUTO_TEST_CASE (get_team_test) {
    ModelPtr model(createBaseModel(1, 1));
    Implementation::Changer changer(model, 0, 0, 0);
    BOOST_REQUIRE(changer.getTeam() == 0);
}

BOOST_AUTO_TEST_CASE (changer_get_instruction_test) {
    ModelPtr model(createBaseModel(1, 1));
    // total number of instructions is 2
    Implementation::Changer changer(model, 0, 0, 2);
    // initial state
    BOOST_REQUIRE(changer.getInstruction(0) == 0);
    Abstract::Params params(-1, -1, false);
    changer.eat(&params, 0);
    // after execution of the first command
    BOOST_REQUIRE(changer.getInstruction(0) == 1);
    int commands = 5;
    params = Abstract::Params(commands, -1, false);
    for (int i = 0; i < commands; i++) {
        // it must execute the same command `commands` times
        BOOST_REQUIRE(changer.getInstruction(0) == 1);
        changer.clearBeforeMove();
        changer.eat(&params, 0);
    }
    // cycle: after execution of all commands it returns to initial state
    BOOST_REQUIRE(changer.getInstruction(0) == 0);
}

BOOST_AUTO_TEST_CASE (eat_test) {
    checkLogicalMethod(
        &Abstract::Changer::eat,
        eatChecker,
        true,
        true
    );
}
<|endoftext|>