text:
//
// Copyright (c) 2019 Miguel Portilla (miguelportilla64 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

// Test that header file is self-contained
#include <nudb/context.hpp>
#include <nudb/_experimental/test/temp_dir.hpp>
#include <nudb/nudb.hpp>

#include "suite.hpp"

#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/filesystem.hpp>

namespace nudb {
namespace test {

class context_test : public boost::beast::unit_test::suite
{
public:
    class tmp_store : public store
    {
    public:
        tmp_store(context& ctx, std::function<void(std::thread::id)>& f,
            error_code& ec)
            : store(ctx)
            , td_(boost::filesystem::path{})
            , f_(f)
        {
            nudb::create<nudb::xxhasher>(
                td_.file("nudb.dat"),
                td_.file("nudb.key"),
                td_.file("nudb.log"),
                1,
                nudb::make_salt(),
                4,
                4096,
                0.5f,
                ec);
        }

        ~tmp_store()
        {
            error_code ec;
            close(ec);
        }

        void
        flush() override
        {
            store::flush();
            f_(std::this_thread::get_id());
        }

        temp_dir td_;
        std::function<void(std::thread::id)>& f_;
    };

    std::size_t
    size(context::list const& v)
    {
        std::size_t size = 0;
        for (auto node = v.head_; node; node = node->next_)
            ++size;
        return size;
    }

    void
    test_list()
    {
        context::list v[2];
        store s[2];

        v[0].push_back(&s[0]);
        v[1].push_back(&s[1]);
        BEAST_EXPECT(size(v[0]) == 1);

        v[1].splice(v[0]);
        BEAST_EXPECT(size(v[1]) == 2);
        BEAST_EXPECT(v[1].head_ == &s[1]);
        BEAST_EXPECT(v[0].empty());

        v[1].splice(v[0]);
        BEAST_EXPECT(size(v[1]) == 2);
        BEAST_EXPECT(v[0].empty());

        v[0].splice(v[1]);
        BEAST_EXPECT(size(v[0]) == 2);
        BEAST_EXPECT(v[0].head_ == &s[1]);
        BEAST_EXPECT(v[1].empty());

        v[0].erase(&s[1]);
        BEAST_EXPECT(size(v[0]) == 1);
        BEAST_EXPECT(v[0].head_ == &s[0]);

        v[0].push_back(&s[1]);
        v[0].erase(&s[1]);
        BEAST_EXPECT(size(v[0]) == 1);

        v[0].erase(&s[0]);
        BEAST_EXPECT(v[0].empty());
        BEAST_EXPECT(v[0].head_ == nullptr);
    }

    void
    test_context()
    {
        using state = detail::store_base::state;

        context ctx;
        BEAST_EXPECT(ctx.num_threads_ == 0);
        BEAST_EXPECT(! ctx.t_.joinable());
        BEAST_EXPECT(! ctx.stop_);

        error_code ec;
        std::vector<std::unique_ptr<tmp_store>> dbs;
        std::set<std::thread::id> ids;
        std::mutex ids_mutex;
        std::function<void(std::thread::id)> f = [&](std::thread::id id) {
            std::lock_guard<std::mutex> lock(ids_mutex);
            ids.erase(id);
        };
        for (int i = 0; i < 3; ++i)
        {
            dbs.emplace_back(std::make_unique<tmp_store>(ctx, f, ec));
            if(! BEAST_EXPECTS(! ec, ec.message()))
                return;
        }

        BEAST_EXPECT(ctx.waiting_.empty());
        BEAST_EXPECT(ctx.flushing_.empty());
        for (auto& db : dbs)
        {
            BEAST_EXPECT(db->state_ ==  state::none);
            db->open(
                db->td_.file("nudb.dat"),
                db->td_.file("nudb.key"),
                db->td_.file("nudb.log"),
                ec);
            if(! BEAST_EXPECTS(! ec, ec.message()))
                return;
            BEAST_EXPECT(db->state_ == state::waiting);
        }
        BEAST_EXPECT(size(ctx.waiting_) == dbs.size());

        for (auto s = ctx.waiting_.head_; s; s = s->next_)
            s->state_ = state::flushing;
        ctx.flushing_.splice(ctx.waiting_);
        BEAST_EXPECT(size(ctx.flushing_) == dbs.size());

        while (ctx.flush_one()) {}
        BEAST_EXPECT(size(ctx.waiting_) == dbs.size());
        for (auto s = ctx.waiting_.head_; s; s = s->next_)
            BEAST_EXPECT(s->state_ == state::waiting);
        BEAST_EXPECT(ctx.flushing_.empty());

        // context store erase
        {
            // state waiting
            auto s = ctx.waiting_.head_;
            ctx.erase(*s);
            BEAST_EXPECT(s->state_ == state::none);

            // state flushing
            ctx.flushing_.push_back(s);
            s->state_ = state::flushing;
            ctx.erase(*s);
            BEAST_EXPECT(s->state_ == state::none);

            // state intermediate flush
            ctx.flushing_.push_back(s);
            s->state_ = state::intermediate;
            {
                std::thread t([&]() {
                    ctx.erase(*s);
                });
                s->state_ = state::flushing;
                ctx.cv_w_.notify_all();
                t.join();
            }
            BEAST_EXPECT(s->state_ == state::none);

            // state intermediate waiting
            ctx.waiting_.push_back(s);
            s->state_ = state::intermediate;
            {
                std::thread t([&]() {
                    ctx.erase(*s);
                });
                s->state_ = state::waiting;
                ctx.cv_w_.notify_all();
                t.join();
            }
            BEAST_EXPECT(s->state_ == state::none);

            ctx.waiting_.push_back(s);
            s->state_ = state::waiting;
        }

        // thread function
        {
            ctx.start();
            {
                std::unique_lock<std::mutex> lock(ctx.m_);
                BEAST_EXPECT(ctx.t_.joinable());
                ctx.cv_f_.wait(lock,
                    [&]
                    {
                        return ctx.num_threads_ != 0;
                    });
                ids.emplace(ctx.t_.get_id());

                for (int i = 0; i < 3; ++i)
                {
                    auto t = std::thread([&] {
                        ctx.run();
                    });
                    ids.emplace(t.get_id());
                    t.detach();
                }

                ctx.cv_f_.wait(lock,
                    [&]
                    {
                        std::lock_guard<std::mutex> lock(ids_mutex);
                        return ids.empty();
                    });
            }
            BEAST_EXPECT(ctx.num_threads_ == 4);
            ctx.stop_all();
            BEAST_EXPECT(ctx.num_threads_ == 0);
            BEAST_EXPECT(! ctx.t_.joinable());
            BEAST_EXPECT(! ctx.stop_);
        }

        dbs.clear();
        BEAST_EXPECT(ctx.waiting_.empty());
        BEAST_EXPECT(ctx.flushing_.empty());
    }

    void
    test_context_flush()
    {
        using state = detail::store_base::state;

        context ctx;

        BEAST_EXPECT(ctx.num_threads_ == 0);
        BEAST_EXPECT(ctx.waiting_.empty());
        BEAST_EXPECT(ctx.flushing_.empty());
        BEAST_EXPECT(! ctx.stop_);

        error_code ec;
        std::function<void(std::thread::id)> f = [&](std::thread::id id) {};
        auto db = std::make_unique<tmp_store>(ctx, f, ec);
        if(! BEAST_EXPECTS(! ec, ec.message()))
            return;

        BEAST_EXPECT(db->state_ ==  state::none);
        BEAST_EXPECT(ctx.num_threads_ == 0);
        BEAST_EXPECT(ctx.waiting_.empty());
        BEAST_EXPECT(ctx.flushing_.empty());
        BEAST_EXPECT(! ctx.stop_);

        db->open(
            db->td_.file("nudb.dat"),
            db->td_.file("nudb.key"),
            db->td_.file("nudb.log"),
            ec);
        if(! BEAST_EXPECTS(! ec, ec.message()))
            return;

        BEAST_EXPECT(db->state_ == state::waiting);
        BEAST_EXPECT(ctx.num_threads_ == 0);
        BEAST_EXPECT(size(ctx.waiting_) == 1);
        BEAST_EXPECT(ctx.flushing_.empty());
        BEAST_EXPECT(! ctx.stop_);

        for (int i = 0; i < 3; i++)
        {
            ctx.flush();

            BEAST_EXPECT(db->state_ == state::waiting);
            BEAST_EXPECT(ctx.num_threads_ == 0);
            BEAST_EXPECT(size(ctx.waiting_) == 1);
            BEAST_EXPECT(ctx.flushing_.empty());
            BEAST_EXPECT(! ctx.stop_);
        }

        db.reset();

        BEAST_EXPECT(ctx.num_threads_ == 0);
        BEAST_EXPECT(ctx.waiting_.empty());
        BEAST_EXPECT(ctx.flushing_.empty());
        BEAST_EXPECT(! ctx.stop_);
    }

    void
    run() override
    {
        BOOST_STATIC_ASSERT(! std::is_copy_constructible<context>{});
        BOOST_STATIC_ASSERT(! std::is_copy_assignable<context>{});
        BOOST_STATIC_ASSERT(! std::is_move_constructible<context>{});
        BOOST_STATIC_ASSERT(! std::is_move_assignable<context>{});

        test_list();
        test_context();
        test_context_flush();
    }
};

DEFINE_TESTSUITE(nudb,test,context);

} // test
} // nudb

max_stars_repo_name: legleux/NuDB

max_issues_repo_name: legleux/NuDB

max_forks_repo_name: legleux/NuDB


text: #include <boost/metaparse/v1/unless_error.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
// Copyright Ricardo Calheiros de Miranda Cosme 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#pragma once

#include <coruja/boost_optional.hpp>

#include <boost/serialization/split_free.hpp>
#include <boost/serialization/serialization.hpp>

namespace coruja { namespace serialization {

struct optional
{
    template<typename Archive, typename T>
    static void save(Archive& ar,
                     const coruja::optional<T>& o,
                     unsigned int /*version*/)
    {
        ar << o.observed();
    }

    template<typename Archive, typename T>
    static void load(Archive& ar,
                     coruja::optional<T>& o,
                     unsigned int version)
    {
        typename coruja::optional<T>::observed_t tmp;
        ar >> tmp;
        o._observed = std::move(tmp);
    }
};

}}

namespace boost { namespace serialization {

template<typename Archive, typename T>
inline void save(Archive& ar,
                 const coruja::optional<T>& o,
                 unsigned int version)
{
    ::coruja::serialization::optional::save(ar, o, version);
}

template<typename Archive, typename T>
inline void load(Archive& ar,
                 coruja::optional<T>& o,
                 unsigned int version)
{
    ::coruja::serialization::optional::load(ar, o, version);
}

template<typename Archive, typename T>
inline void serialize(Archive& ar, coruja::optional<T>& o,
                      unsigned int version)
{
    split_free(ar, o, version);
}

}}

max_stars_repo_name: romariorios/coruja

max_issues_repo_name: romariorios/coruja

max_forks_repo_name: romariorios/coruja


text:
// 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.

#include "exprs/scalar-fn-call.h"

#include <vector>
#include <gutil/strings/substitute.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/Attributes.h>

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>

#include "codegen/codegen-anyval.h"
#include "codegen/llvm-codegen.h"
#include "exprs/anyval-util.h"
#include "exprs/scalar-expr-evaluator.h"
#include "runtime/hdfs-fs-cache.h"
#include "runtime/lib-cache.h"
#include "runtime/runtime-state.h"
#include "runtime/types.h"
#include "udf/udf-internal.h"
#include "util/debug-util.h"

#include "common/names.h"

using namespace impala;
using namespace impala_udf;
using namespace strings;
using llvm::ArrayType;
using llvm::BasicBlock;
using llvm::Function;
using llvm::GlobalVariable;
using llvm::PointerType;
using llvm::Type;
using llvm::Value;
using std::move;
using std::pair;

// Maximum number of arguments the interpretation path supports.
#define MAX_INTERP_ARGS 20

ScalarFnCall::ScalarFnCall(const TExprNode& node)
  : ScalarExpr(node),
    vararg_start_idx_(node.__isset.vararg_start_idx ? node.vararg_start_idx : -1),
    scalar_fn_wrapper_(NULL),
    prepare_fn_(NULL),
    close_fn_(NULL),
    scalar_fn_(NULL) {
  DCHECK_NE(fn_.binary_type, TFunctionBinaryType::JAVA);
}

Status ScalarFnCall::LoadPrepareAndCloseFn(LlvmCodeGen* codegen) {
  if (fn_.scalar_fn.__isset.prepare_fn_symbol) {
    RETURN_IF_ERROR(GetFunction(codegen, fn_.scalar_fn.prepare_fn_symbol,
        reinterpret_cast<void**>(&prepare_fn_)));
  }
  if (fn_.scalar_fn.__isset.close_fn_symbol) {
    RETURN_IF_ERROR(GetFunction(codegen, fn_.scalar_fn.close_fn_symbol,
        reinterpret_cast<void**>(&close_fn_)));
  }
  return Status::OK();
}

Status ScalarFnCall::Init(const RowDescriptor& desc, RuntimeState* state) {
  // Initialize children first.
  RETURN_IF_ERROR(ScalarExpr::Init(desc, state));

  if (fn_.scalar_fn.symbol.empty()) {
    // This path is intended to only be used during development to test FE
    // code before the BE has implemented the function.
    // Having the failure in the BE (rather than during analysis) allows for
    // better FE testing.
    DCHECK_EQ(fn_.binary_type, TFunctionBinaryType::BUILTIN);
    stringstream ss;
    ss << "Function " << fn_.name.function_name << " is not implemented.";
    return Status(ss.str());
  }

  // Check if the function takes CHAR as input or returns CHAR.
  bool has_char_arg_or_result = type_.type == TYPE_CHAR;
  for (int i = 0; !has_char_arg_or_result && i < children_.size(); ++i) {
    has_char_arg_or_result |= children_[i]->type_.type == TYPE_CHAR;
  }

  // Use the interpreted path and call the builtin without codegen if any of the
  // followings is true:
  // 1. codegen is disabled by query option
  // 2. there are char arguments (as they aren't supported yet)
  // 3. there is an optimization hint to disable codegen and UDF can be interpreted.
  //    IR UDF or UDF with more than MAX_INTERP_ARGS number of fixed arguments
  //    cannot be interpreted.
  //
  // TODO: codegen for char arguments
  bool is_ir_udf = fn_.binary_type == TFunctionBinaryType::IR;
  bool too_many_args_to_interp = NumFixedArgs() > MAX_INTERP_ARGS;
  bool udf_interpretable = !is_ir_udf && !too_many_args_to_interp;
  if (state->CodegenDisabledByQueryOption() || has_char_arg_or_result ||
      (state->CodegenHasDisableHint() && udf_interpretable)) {
    if (is_ir_udf) {
      // CHAR or VARCHAR are not supported as input arguments or return values for UDFs.
      DCHECK(!has_char_arg_or_result && state->CodegenDisabledByQueryOption());
      return Status(Substitute("Cannot interpret LLVM IR UDF '$0': Codegen is needed. "
          "Please set DISABLE_CODEGEN to false.", fn_.name.function_name));
    }

    // The templates for builtin or native UDFs used in the interpretation path
    // support up to MAX_INTERP_ARGS number of arguments only.
    if (too_many_args_to_interp) {
      DCHECK_EQ(fn_.binary_type, TFunctionBinaryType::NATIVE);
      // CHAR or VARCHAR are not supported as input arguments or return values for UDFs.
      DCHECK(!has_char_arg_or_result && state->CodegenDisabledByQueryOption());
      return Status(Substitute("Cannot interpret native UDF '$0': number of arguments is "
          "more than $1. Codegen is needed. Please set DISABLE_CODEGEN to false.",
          fn_.name.function_name, MAX_INTERP_ARGS));
    }

    Status status = LibCache::instance()->GetSoFunctionPtr(
        fn_.hdfs_location, fn_.scalar_fn.symbol, &scalar_fn_, &cache_entry_);
    if (!status.ok()) {
      if (fn_.binary_type == TFunctionBinaryType::BUILTIN) {
        // Builtins symbols should exist unless there is a version mismatch.
        return Status(TErrorCode::MISSING_BUILTIN, fn_.name.function_name,
            fn_.scalar_fn.symbol);
      } else {
        DCHECK_EQ(fn_.binary_type, TFunctionBinaryType::NATIVE);
        return Status(Substitute("Problem loading UDF '$0':\n$1",
            fn_.name.function_name, status.GetDetail()));
      }
    }
  } else {
    // Add the expression to the list of expressions to codegen in the codegen phase.
    state->AddScalarFnToCodegen(this);
  }

  // For IR UDF, the loading of the Init() and CloseContext() functions is deferred until
  // first time GetCodegendComputeFn() is invoked.
  if (!is_ir_udf) RETURN_IF_ERROR(LoadPrepareAndCloseFn(NULL));
  return Status::OK();
}

Status ScalarFnCall::OpenEvaluator(FunctionContext::FunctionStateScope scope,
    RuntimeState* state, ScalarExprEvaluator* eval) const {
  // Opens and inits children
  RETURN_IF_ERROR(ScalarExpr::OpenEvaluator(scope, state, eval));
  DCHECK_GE(fn_ctx_idx_, 0);
  FunctionContext* fn_ctx = eval->fn_context(fn_ctx_idx_);
  bool is_interpreted = scalar_fn_wrapper_ == nullptr;

  if (is_interpreted) {
    // We're in the interpreted path (i.e. no JIT). Populate our FunctionContext's
    // staging_input_vals, which will be reused across calls to scalar_fn_.
    DCHECK(scalar_fn_ != nullptr);
    vector<AnyVal*>* input_vals = fn_ctx->impl()->staging_input_vals();
    for (int i = 0; i < NumFixedArgs(); ++i) {
      AnyVal* input_val;
      RETURN_IF_ERROR(AllocateAnyVal(state, eval->mem_pool(), children_[i]->type(),
          "Could not allocate expression value", &input_val));
      input_vals->push_back(input_val);
    }
  }

  // Only evaluate constant arguments at the top level of function contexts.
  // If 'eval' was cloned, the constant values were copied from the parent.
  if (scope == FunctionContext::FRAGMENT_LOCAL) {
    vector<AnyVal*> constant_args;
    for (const ScalarExpr* child : children()) {
      AnyVal* const_val;
      RETURN_IF_ERROR(eval->GetConstValue(state, *child, &const_val));
      constant_args.push_back(const_val);
    }
    fn_ctx->impl()->SetConstantArgs(move(constant_args));

    // If we're calling MathFunctions::RoundUpTo(), we need to set output_scale_
    // which determines how many decimal places are printed.
    // TODO: Move this to Expr initialization.
    if (this == &eval->root()) {
      if (fn_.name.function_name == "round" && type_.type == TYPE_DOUBLE) {
        DCHECK_EQ(children_.size(), 2);
        IntVal* scale_arg = reinterpret_cast<IntVal*>(constant_args[1]);
        if (scale_arg != nullptr) eval->output_scale_ = scale_arg->val;
      }
    }
  }

  if (is_interpreted) {
    // Now we have the constant values, cache them so that the interpreted path can
    // call the UDF without reevaluating the arguments. 'staging_input_vals' and
    // 'varargs_buffer' in the FunctionContext are used to pass fixed and variable-length
    // arguments respectively. 'non_constant_args()' in the FunctionContext will contain
    // pointers to the remaining (non-constant) children that are evaluated for every row.
    vector<pair<ScalarExpr*, AnyVal*>> non_constant_args;
    uint8_t* varargs_buffer = fn_ctx->impl()->varargs_buffer();
    for (int i = 0; i < children_.size(); ++i) {
      AnyVal* input_arg;
      int arg_bytes = AnyValUtil::AnyValSize(children_[i]->type());
      if (i < NumFixedArgs()) {
        input_arg = (*fn_ctx->impl()->staging_input_vals())[i];
      } else {
        input_arg = reinterpret_cast<AnyVal*>(varargs_buffer);
        varargs_buffer += arg_bytes;
      }
      // IMPALA-4586: Cache constant arguments only if the frontend has rewritten them
      // into literal expressions. This gives the frontend control over how expressions
      // are evaluated. This means that setting enable_expr_rewrites=false will also
      // disable caching of non-literal constant expressions, which gives the old
      // behaviour (before this caching optimisation was added) of repeatedly evaluating
      // exprs that are constant according to is_constant(). For exprs that are not truly
      // constant (yet is_constant() returns true for) e.g. non-deterministic UDFs, this
      // means that setting enable_expr_rewrites=false works as a safety valve to get
      // back the old behaviour, before constant expr folding or caching was added.
      // TODO: once we can annotate UDFs as non-deterministic (IMPALA-4606), we should
      // be able to trust is_constant() and switch back to that.
      if (children_[i]->IsLiteral()) {
        const AnyVal* constant_arg = fn_ctx->impl()->constant_args()[i];
        DCHECK(constant_arg != nullptr);
        memcpy(input_arg, constant_arg, arg_bytes);
      } else {
        non_constant_args.emplace_back(children_[i], input_arg);
      }
    }
    fn_ctx->impl()->SetNonConstantArgs(move(non_constant_args));
  }

  if (prepare_fn_ != nullptr) {
    if (scope == FunctionContext::FRAGMENT_LOCAL) {
      prepare_fn_(fn_ctx, FunctionContext::FRAGMENT_LOCAL);
      if (fn_ctx->has_error()) return Status(fn_ctx->error_msg());
    }
    prepare_fn_(fn_ctx, FunctionContext::THREAD_LOCAL);
    if (fn_ctx->has_error()) return Status(fn_ctx->error_msg());
  }

  return Status::OK();
}

void ScalarFnCall::CloseEvaluator(FunctionContext::FunctionStateScope scope,
    RuntimeState* state, ScalarExprEvaluator* eval) const {
  DCHECK_GE(fn_ctx_idx_, 0);
  if (close_fn_ != NULL) {
    FunctionContext* fn_ctx = eval->fn_context(fn_ctx_idx_);
    close_fn_(fn_ctx, FunctionContext::THREAD_LOCAL);
    if (scope == FunctionContext::FRAGMENT_LOCAL) {
      close_fn_(fn_ctx, FunctionContext::FRAGMENT_LOCAL);
    }
  }
  ScalarExpr::CloseEvaluator(scope, state, eval);
}

// Dynamically loads the pre-compiled UDF and codegens a function that calls each child's
// codegen'd function, then passes those values to the UDF and returns the result.
// Example generated IR for a UDF with signature
//    create function Udf(double, int...) returns double
//    select Udf(1.0, 2, 3, 4, 5)
// define { i8, double } @UdfWrapper(i8* %context, %"class.impala::TupleRow"* %row) {
// entry:
//   %arg_val = call { i8, double }
//      @ExprWrapper(i8* %context, %"class.impala::TupleRow"* %row)
//   %arg_ptr = alloca { i8, double }
//   store { i8, double } %arg_val, { i8, double }* %arg_ptr
//   %arg_val1 = call i64 @ExprWrapper1(i8* %context, %"class.impala::TupleRow"* %row)
//   store i64 %arg_val1, i64* inttoptr (i64 89111072 to i64*)
//   %arg_val2 = call i64 @ExprWrapper2(i8* %context, %"class.impala::TupleRow"* %row)
//   store i64 %arg_val2, i64* inttoptr (i64 89111080 to i64*)
//   %arg_val3 = call i64 @ExprWrapper3(i8* %context, %"class.impala::TupleRow"* %row)
//   store i64 %arg_val3, i64* inttoptr (i64 89111088 to i64*)
//   %arg_val4 = call i64 @ExprWrapper4(i8* %context, %"class.impala::TupleRow"* %row)
//   store i64 %arg_val4, i64* inttoptr (i64 89111096 to i64*)
//   %result = call { i8, double }
//      @_Z14VarSumMultiplyPN10impala_udf15FunctionContextERKNS_9DoubleValEiPKNS_6IntValE(
//        %"class.impala_udf::FunctionContext"* inttoptr
//            (i64 37522464 to %"class.impala_udf::FunctionContext"*),
//        {i8, double }* %arg_ptr,
//        i32 4,
//        i64* inttoptr (i64 89111072 to i64*))
//   ret { i8, double } %result
Status ScalarFnCall::GetCodegendComputeFn(LlvmCodeGen* codegen, Function** fn) {
  if (ir_compute_fn_ != NULL) {
    *fn = ir_compute_fn_;
    return Status::OK();
  }
  if (type_.type == TYPE_CHAR) {
    return Status("ScalarFnCall Codegen not supported for CHAR");
  }
  for (int i = 0; i < GetNumChildren(); ++i) {
    if (children_[i]->type().type == TYPE_CHAR) {
      *fn = NULL;
      return Status("ScalarFnCall Codegen not supported for CHAR");
    }
  }

  vector<ColumnType> arg_types;
  for (const Expr* child : children_) arg_types.push_back(child->type());
  Function* udf;
  RETURN_IF_ERROR(codegen->LoadFunction(fn_, fn_.scalar_fn.symbol, &type_, arg_types,
      NumFixedArgs(), vararg_start_idx_ != -1, &udf, &cache_entry_));
  // Inline constants into the function if it has an IR body.
  if (!udf->isDeclaration()) {
    codegen->InlineConstFnAttrs(AnyValUtil::ColumnTypeToTypeDesc(type_),
        AnyValUtil::ColumnTypesToTypeDescs(arg_types), udf);
    udf = codegen->FinalizeFunction(udf);
    if (udf == NULL) {
      return Status(
          TErrorCode::UDF_VERIFY_FAILED, fn_.scalar_fn.symbol, fn_.hdfs_location);
    }
  }

  if (fn_.binary_type == TFunctionBinaryType::IR) {
    // LoadFunction() should have linked the IR module into 'codegen'. Now load the
    // Prepare() and Close() functions from 'codegen'.
    RETURN_IF_ERROR(LoadPrepareAndCloseFn(codegen));
  }

  // Create wrapper that computes args and calls UDF
  stringstream fn_name;
  fn_name << udf->getName().str() << "Wrapper";

  Value* args[2];
  *fn = CreateIrFunctionPrototype(fn_name.str(), codegen, &args);
  Value* eval = args[0];
  Value* row = args[1];
  BasicBlock* block = BasicBlock::Create(codegen->context(), "entry", *fn);
  LlvmBuilder builder(block);

  // Populate UDF arguments
  vector<Value*> udf_args;

  // First argument is always FunctionContext*.
  // Index into our registered offset in the ScalarFnEvaluator.
  Value* eval_gep = builder.CreateStructGEP(NULL, eval, 1, "eval_gep");
  Value* fn_ctxs_base = builder.CreateLoad(eval_gep, "fn_ctxs_base");
  // Use GEP to add our index to the base pointer
  Value* fn_ctx_ptr = builder.CreateConstGEP1_32(fn_ctxs_base, fn_ctx_idx_, "fn_ctx_ptr");
  Value* fn_ctx = builder.CreateLoad(fn_ctx_ptr, "fn_ctx");
  udf_args.push_back(fn_ctx);

  // Allocate a varargs array. The array's entry type is the appropriate AnyVal subclass.
  // E.g. if the vararg type is STRING, and the function is called with 10 arguments, we
  // allocate a StringVal[10] array. We allocate the buffer with Alloca so that LLVM can
  // optimise out the buffer once the function call is inlined.
  Value* varargs_buffer = NULL;
  if (vararg_start_idx_ != -1) {
    Type* unlowered_varargs_type =
        CodegenAnyVal::GetUnloweredType(codegen, VarArgsType());
    varargs_buffer = codegen->CreateEntryBlockAlloca(builder, unlowered_varargs_type,
        NumVarArgs(), FunctionContextImpl::VARARGS_BUFFER_ALIGNMENT, "varargs_buffer");
  }

  // Call children to populate remaining arguments
  for (int i = 0; i < GetNumChildren(); ++i) {
    Function* child_fn = NULL;
    vector<Value*> child_fn_args;
    // Set 'child_fn' to the codegen'd function, sets child_fn == NULL if codegen fails
    Status status = children_[i]->GetCodegendComputeFn(codegen, &child_fn);
    if (UNLIKELY(!status.ok())) {
      DCHECK(child_fn == NULL);
      // Set 'child_fn' to the interpreted function
      child_fn = GetStaticGetValWrapper(children_[i]->type(), codegen);
      // First argument to interpreted function is children_[i]
      Type* expr_ptr_type = codegen->GetPtrType(ScalarExpr::LLVM_CLASS_NAME);
      child_fn_args.push_back(codegen->CastPtrToLlvmPtr(expr_ptr_type, children_[i]));
    }
    child_fn_args.push_back(eval);
    child_fn_args.push_back(row);

    // Call 'child_fn', adding the result to either 'udf_args' or 'varargs_buffer'
    DCHECK(child_fn != NULL);
    Type* arg_type = CodegenAnyVal::GetUnloweredType(codegen, children_[i]->type());
    Value* arg_val_ptr;
    if (i < NumFixedArgs()) {
      // Allocate space to store 'child_fn's result so we can pass the pointer to the UDF.
      arg_val_ptr = codegen->CreateEntryBlockAlloca(builder, arg_type, "arg_val_ptr");
      udf_args.push_back(arg_val_ptr);
    } else {
      // Store the result of 'child_fn' in varargs_buffer[i].
      arg_val_ptr =
          builder.CreateConstGEP1_32(varargs_buffer, i - NumFixedArgs(), "arg_val_ptr");
    }
    DCHECK_EQ(arg_val_ptr->getType(), arg_type->getPointerTo());
    // The result of the call must be stored in a lowered AnyVal
    Value* lowered_arg_val_ptr = builder.CreateBitCast(arg_val_ptr,
        CodegenAnyVal::GetLoweredPtrType(codegen, children_[i]->type()),
        "lowered_arg_val_ptr");
    CodegenAnyVal::CreateCall(
        codegen, &builder, child_fn, child_fn_args, "arg_val", lowered_arg_val_ptr);
  }

  if (vararg_start_idx_ != -1) {
    // We've added the FunctionContext argument plus any non-variadic arguments
    DCHECK_EQ(udf_args.size(), vararg_start_idx_ + 1);
    DCHECK_GE(GetNumChildren(), 1);
    // Add the number of varargs
    udf_args.push_back(codegen->GetIntConstant(TYPE_INT, NumVarArgs()));
    // Add all the accumulated vararg inputs as one input argument.
    PointerType* vararg_type = CodegenAnyVal::GetUnloweredPtrType(codegen, VarArgsType());
    udf_args.push_back(builder.CreateBitCast(varargs_buffer, vararg_type, "varargs"));
  }

  // Call UDF
  Value* result_val =
      CodegenAnyVal::CreateCall(codegen, &builder, udf, udf_args, "result");
  builder.CreateRet(result_val);

  *fn = codegen->FinalizeFunction(*fn);
  if (*fn == NULL) {
    return Status(
        TErrorCode::UDF_VERIFY_FAILED, fn_.scalar_fn.symbol, fn_.hdfs_location);
  }
  ir_compute_fn_ = *fn;
  // TODO: don't do this for child exprs
  codegen->AddFunctionToJit(ir_compute_fn_, &scalar_fn_wrapper_);
  return Status::OK();
}

Status ScalarFnCall::GetFunction(LlvmCodeGen* codegen, const string& symbol, void** fn) {
  if (fn_.binary_type == TFunctionBinaryType::NATIVE
      || fn_.binary_type == TFunctionBinaryType::BUILTIN) {
    return LibCache::instance()->GetSoFunctionPtr(
        fn_.hdfs_location, symbol, fn, &cache_entry_);
  } else {
    DCHECK_EQ(fn_.binary_type, TFunctionBinaryType::IR);
    DCHECK(codegen != NULL);
    Function* ir_fn = codegen->GetFunction(symbol, false);
    if (ir_fn == NULL) {
      stringstream ss;
      ss << "Unable to locate function " << symbol << " from LLVM module "
         << fn_.hdfs_location;
      return Status(ss.str());
    }
    codegen->AddFunctionToJit(ir_fn, fn);
    return Status::OK();
  }
}

void ScalarFnCall::EvaluateNonConstantChildren(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  FunctionContext* fn_ctx = eval->fn_context(fn_ctx_idx_);
  for (pair<ScalarExpr*, AnyVal*> child : fn_ctx->impl()->non_constant_args()) {
    void* val = eval->GetValue(*(child.first), row);
    AnyValUtil::SetAnyVal(val, child.first->type(), child.second);
  }
}

template<typename RETURN_TYPE>
RETURN_TYPE ScalarFnCall::InterpretEval(ScalarExprEvaluator* eval,
    const TupleRow* row) const {
  DCHECK(scalar_fn_ != NULL) << DebugString();
  FunctionContext* fn_ctx = eval->fn_context(fn_ctx_idx_);
  vector<AnyVal*>* input_vals = fn_ctx->impl()->staging_input_vals();
  EvaluateNonConstantChildren(eval, row);

  if (vararg_start_idx_ == -1) {
    switch (children_.size()) {
#define ARG_DECL_ONE(z, n, data) BOOST_PP_COMMA_IF(n) const AnyVal&
#define ARG_DECL_LIST(n) \
  FunctionContext* BOOST_PP_COMMA_IF(n) BOOST_PP_REPEAT(n, ARG_DECL_ONE, unused)
#define ARG_ONE(z, n, data) BOOST_PP_COMMA_IF(n) *(*input_vals)[n]
#define ARG_LIST(n) fn_ctx BOOST_PP_COMMA_IF(n) BOOST_PP_REPEAT(n, ARG_ONE, unused)

   // Expands to code snippet like the following for X from 0 to 20:
   // case X:
   //     typedef RETURN_TYPE (*ScalarFnX)(FunctionContext*, const AnyVal& a1, ...,
   //         const AnyVal& aX);
   //     return reinterpret_cast<ScalarFnX>(scalar_fn_)(fn_ctx, *(*input_vals)[0], ...,
   //         *(*input_vals)[X-1]);
#define SCALAR_FN_TYPE(n) BOOST_PP_CAT(ScalarFn, n)
#define INTERP_SCALAR_FN(z, n, unused)                                       \
      case n:                                                                \
        typedef RETURN_TYPE (*SCALAR_FN_TYPE(n))(ARG_DECL_LIST(n));          \
        return reinterpret_cast<SCALAR_FN_TYPE(n)>(scalar_fn_)(ARG_LIST(n));

      // Support up to MAX_INTERP_ARGS arguments in the interpretation path
      BOOST_PP_REPEAT_FROM_TO(0, BOOST_PP_ADD(MAX_INTERP_ARGS, 1),
          INTERP_SCALAR_FN, unused)

      default:
        DCHECK(false) << "Interpreted path not implemented.";
    }
  } else {
    int num_varargs = children_.size() - NumFixedArgs();
    const AnyVal* varargs = reinterpret_cast<AnyVal*>(fn_ctx->impl()->varargs_buffer());
    switch (NumFixedArgs()) {

   // Expands to code snippet like the following for X from 0 to 20:
   // case X:
   //     typedef RETURN_TYPE (*VarargFnX)(FunctionContext*, const AnyVal& a1, ...,
   //         const AnyVal& aX, int num_varargs, const AnyVal* varargs);
   //     return reinterpret_cast<VarargFnX>(scalar_fn_)(fn_ctx, *(*input_vals)[0], ...,
   //         *(*input_vals)[X-1], num_varargs, varargs);
#define SCALAR_VARARG_FN_TYPE(n) BOOST_PP_CAT(VarargFn, n)
#define INTERP_SCALAR_VARARG_FN(z, n, text)                                        \
      case n:                                                                      \
        typedef RETURN_TYPE (*SCALAR_VARARG_FN_TYPE(n))(ARG_DECL_LIST(n), int,     \
            const AnyVal*);                                                        \
        return reinterpret_cast<SCALAR_VARARG_FN_TYPE(n)>(scalar_fn_)(ARG_LIST(n), \
            num_varargs, varargs);

      BOOST_PP_REPEAT_FROM_TO(0, BOOST_PP_ADD(MAX_INTERP_ARGS, 1),
         INTERP_SCALAR_VARARG_FN, unused)

      default:
        DCHECK(false) << "Interpreted path not implemented.";
    }
  }
  return RETURN_TYPE::null();
}

typedef BooleanVal (*BooleanWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef TinyIntVal (*TinyIntWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef SmallIntVal (*SmallIntWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef IntVal (*IntWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef BigIntVal (*BigIntWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef FloatVal (*FloatWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef DoubleVal (*DoubleWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef StringVal (*StringWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef TimestampVal (*TimestampWrapper)(ScalarExprEvaluator*, const TupleRow*);
typedef DecimalVal (*DecimalWrapper)(ScalarExprEvaluator*, const TupleRow*);

// TODO: macroify this?
BooleanVal ScalarFnCall::GetBooleanVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_BOOLEAN);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<BooleanVal>(eval, row);
  BooleanWrapper fn = reinterpret_cast<BooleanWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

TinyIntVal ScalarFnCall::GetTinyIntVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_TINYINT);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<TinyIntVal>(eval, row);
  TinyIntWrapper fn = reinterpret_cast<TinyIntWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

SmallIntVal ScalarFnCall::GetSmallIntVal(
     ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_SMALLINT);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<SmallIntVal>(eval, row);
  SmallIntWrapper fn = reinterpret_cast<SmallIntWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

IntVal ScalarFnCall::GetIntVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_INT);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<IntVal>(eval, row);
  IntWrapper fn = reinterpret_cast<IntWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

BigIntVal ScalarFnCall::GetBigIntVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_BIGINT);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<BigIntVal>(eval, row);
  BigIntWrapper fn = reinterpret_cast<BigIntWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

FloatVal ScalarFnCall::GetFloatVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_FLOAT);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<FloatVal>(eval, row);
  FloatWrapper fn = reinterpret_cast<FloatWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

DoubleVal ScalarFnCall::GetDoubleVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_DOUBLE);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<DoubleVal>(eval, row);
  DoubleWrapper fn = reinterpret_cast<DoubleWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

StringVal ScalarFnCall::GetStringVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK(type_.IsStringType());
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<StringVal>(eval, row);
  StringWrapper fn = reinterpret_cast<StringWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

TimestampVal ScalarFnCall::GetTimestampVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_TIMESTAMP);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<TimestampVal>(eval, row);
  TimestampWrapper fn = reinterpret_cast<TimestampWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

DecimalVal ScalarFnCall::GetDecimalVal(
    ScalarExprEvaluator* eval, const TupleRow* row) const {
  DCHECK_EQ(type_.type, TYPE_DECIMAL);
  DCHECK(eval != NULL);
  if (scalar_fn_wrapper_ == NULL) return InterpretEval<DecimalVal>(eval, row);
  DecimalWrapper fn = reinterpret_cast<DecimalWrapper>(scalar_fn_wrapper_);
  return fn(eval, row);
}

string ScalarFnCall::DebugString() const {
  stringstream out;
  out << "ScalarFnCall(udf_type=" << fn_.binary_type << " location=" << fn_.hdfs_location
      << " symbol_name=" << fn_.scalar_fn.symbol <<  ScalarExpr::DebugString() << ")";
  return out.str();
}

int ScalarFnCall::ComputeVarArgsBufferSize() const {
  for (int i = NumFixedArgs(); i < children_.size(); ++i) {
    // All varargs should have same type.
    DCHECK_EQ(children_[i]->type(), VarArgsType());
  }
  return NumVarArgs() == 0 ? 0 : NumVarArgs() * AnyValUtil::AnyValSize(VarArgsType());
}

ext: cc


text:
#include "StdAfx.h"
#include "InstallManager.h"

#include <boost/algorithm/string.hpp>
#include <boost/thread/thread.hpp>
#include <boost/filesystem.hpp>

#include <fstream>

#include <algorithm>

#include "Zip/unzip.h"
#include "PackageFileNameUtils.h"
#include <unique_handle.h>
#include <lhWinAPI.h>
#include <lhException.h>
#include "DBHelper.h"
#include <KompexSQLiteStatement.h>
#include <KompexSQLiteException.h>

#include "FileSystemUtils.h"
#include "DeinstallManager.h"

namespace l3d
{
	namespace packageinstaller
	{
		using namespace std;
		using namespace Concurrency;
		using namespace insthelper;
		using namespace Kompex;
		using namespace l3d::packageinstaller::fs;

		InstallManager::InstallManager(const std::wstring& loksimPath, bool disableTransactions) :
			transactFs(NULL, 0, NULL, NULL, !disableTransactions),
			l3dPath(loksimPath), replaceOnlyOlder(true), alwaysAskBeforeOverwrite(false), installState(InstallPreparing), cntTotalFiles(0), cancelled(false), dbAgent(loksimPath), installInfoLock()
		{
			usedTransactions = transactFs.UseTransactedFunctions();
			dbAgent.SetUseTransaction(usedTransactions);
			rootBackupPath = l3dPath / L"\\Backup\\Install";
		}

		InstallManager::~InstallManager(void)
		{
			CancelAndWaitForTasks();
		}

		void InstallManager::WaitUntilFinished()
		{
			bgWork.wait();
		}

		void InstallManager::CancelAndWaitForTasks()
		{
			try
			{
				if (eventInstallFinished.wait(0) != 0)
				{
					workerTskGrp.cancel();
					bgWork.cancel();
					try
					{
						workerTskGrp.wait();
					} catch (const std::exception& ex)
					{
						// TODO log? kann zb passieren falls user auf abbrechen klickt und beim preinstallworker ex aufgetreten ist
						const char* s  = ex.what();
					}
					dbAgent.cancel();
					if (dbAgent.status() != agent_created)
					{
						agent::wait(&dbAgent);
					}
				}
			bgWork.wait();
			} catch (...)
			{
			}
		}

		void InstallManager::CancelInstallation()
		{
			//cancelled = true;
			workerTskGrp.cancel();
			//bgWork.cancel();
		}

		bool InstallManager::AddPackageToInstall(const wstring& fileName)
		{
			if (find(selectedFilesToInstall.begin(), selectedFilesToInstall.end(), fileName) == selectedFilesToInstall.end())
			{
				selectedFilesToInstall.push_back(fileName);
				PreInstallWorker *ptr = new PreInstallWorker(fileName);
				installData[fileName] = unique_ptr<PreInstallWorker>(ptr);
				workerTskGrp.run([ptr]
				{
					try
					{
						ptr->DoPreInstallWork();
						ptr->GetWorkFinishedEvent().set();
					} catch (...)
					{
						ptr->GetWorkFinishedEvent().set();
						throw;
					}
				});
				return true;
			}
			return false;
		}

		/**
		 * Entfernt ein Package aus der Liste zu installierender Packages

		 * Diese Methode darf nicht mehr ausgeführt werrden, wenn die Installation bereits begonnen wurde!
		 * @param fileName Pfad zum Package
		 * @return liefert true falls package entfernt wurde; false falls es nicth in der liste vorhanden ist
		 */
		bool InstallManager::RemovePackageToInstall(const std::wstring& fileName)
		{
			auto found = find(selectedFilesToInstall.begin(), selectedFilesToInstall.end(), fileName);
			if (found != selectedFilesToInstall.end())
			{
				selectedFilesToInstall.erase(found);
				return true;
			}
			return false;
		}

		bool InstallManager::IsPreInstallFinished(const wstring& packageFileName)
		{
			auto pkgData = installData.find(packageFileName);
			if (pkgData != installData.end())
			{
				return pkgData->second->GetWorkFinishedEvent().wait(0) == 0;
			}
			return false;
		}

		const wstring& InstallManager::GetReadmeForPackage(const wstring& packageFileName)
		{
			return installData[packageFileName]->GetReadme();
		}

		bool isReadmeDeleteOrPackFile(const wstring& name)
		{
			return boost::iequals(name, L"readme.txt") || boost::iequals(name, L"DeleteFiles.txt") || boost::iends_with(name, L".l3dpack") ||
				boost::iequals(name, L"PackageInfo.xml");
		}

		void InstallManager::ThrowZipException(ZRESULT zipRes, const PackageInfo& pkgInfo, const char *exMsg, const std::wstring& curFile)
		{
			wchar_t buf[256];
			FormatZipMessage(ZR_RECENT, buf, sizeof buf / sizeof buf[0]);
			InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorZipOccurred, pkgInfo, buf, curFile);
			throw InstallerException(errInfo, exMsg);
		}

		void InstallManager::BackupFile(const boost::filesystem::path& l3dRelativeFilePath, insthelper::InstallInformation* installInfo)
		{
			boost::filesystem::path curPath = l3dPath / l3dRelativeFilePath;
			boost::filesystem::path pkgPath = installInfo->packageName;
			boost::filesystem::path newPath = currentBackupPath / l3dRelativeFilePath;
			if (!FileExistsTransacted(transactFs, newPath.c_str()))
			{
				CreateDirectoriesTransacted(transactFs, newPath.parent_path());
				transactFs.CopyFileEx(curPath.c_str(), newPath.c_str(), nullptr, nullptr, nullptr, COPY_FILE_ALLOW_DECRYPTED_DESTINATION);
			}
		}

		bool InstallManager::IsInDeleteList(const wstring& fname, const insthelper::InstallInformation& installInfo)
		{
			boost::filesystem::path bpath = fname;
			if (!fname.empty() && fname[0] != '/' && fname[0] != '\\')
			{
				bpath = L"/" / bpath;
			}
			bpath = bpath.make_preferred();
			auto func = [&bpath](const insthelper::FileInstallInfo& inf) -> bool
			{
				auto p = boost::filesystem::path(inf.fileName).make_preferred();
				return bpath == p;
			};

			bool found = find_if(begin(installInfo.filesToDelete), end(installInfo.filesToDelete), func) != end(installInfo.filesToDelete);
			return found || find_if(begin(installInfo.writeProtectedFilesToDelete), end(installInfo.writeProtectedFilesToDelete), func) != end(installInfo.writeProtectedFilesToDelete);
		}

		void InstallManager::InstallFiles(size_t startIndex, size_t end, const RootPackageInfo& rootPkg, const PackageInfo& pkgInfo, insthelper::InstallInformation* installInfo)
		{
			size_t zipBufSize = 131072;
			unique_ptr<char[]> zipBuf(new char[zipBufSize]);

			const size_t fileBufSize = 16384;
			unique_ptr<char[]> fileBuf(new char[fileBufSize]);

			ZRESULT zipRes;

			UserSelection *userInstSel = nullptr;
			if (installState == InstallPassTwoRunning)
			{
				userInstSel = &(*userSelectedFiles)[installInfo->packageName];
			}

			unique_handle<HZIP, hzip_traits> hZip = unique_handle<HZIP, hzip_traits>(OpenZip(pkgInfo.packageName.c_str(), nullptr));
			if (!hZip)
			{
				ThrowZipException(ZR_RECENT, pkgInfo, "Error opening zip file", pkgInfo.packageName);
			}

			ZIPENTRY ze;
			size_t processed = 0;
			for (size_t i = startIndex; i < end && !workerTskGrp.is_canceling(); ++i)
			{
				zipRes = GetZipItem(hZip.get(), i, &ze);
				if (zipRes != ZR_OK)
				{
					ThrowZipException(zipRes, pkgInfo, "Error calling GetZipItem", pkgInfo.packageName);
				}
				std::wstring fname = PackageFileNameToFileName(ze.name);

				if ((ze.attr & FILE_ATTRIBUTE_DIRECTORY) != 0 || isReadmeDeleteOrPackFile(fname))
				{
					continue;
				}

				boost::filesystem::path diskPath = l3dPath;
				diskPath /= fname;
				bool fileExists = FileExistsTransacted(transactFs, diskPath.c_str());

				WIN32_FILE_ATTRIBUTE_DATA diskFileAttr = { 0 };
				if (fileExists)
				{
					transactFs.GetFileAttributesExW(diskPath.c_str(), GetFileExInfoStandard, &diskFileAttr);
				}

				++processed;
				if (processed > 30)
				{
					InterlockedExchangeAdd(&installInfo->cntProcessedFiles, processed);
					processed = 0;
				}

				if (userInstSel != nullptr && !binary_search(userInstSel->filesToInstall.begin(), userInstSel->filesToInstall.end(), fname))
				{
					// wir befinden uns im pass two und der user hat unsere datei nicht ausgewählt => continue
					if (fileExists)
					{
						send(dbAgent.GetFileInfoTarget(), make_shared<db::DbInstallFileInfo>(FileInstallInfo(fname, ze.mtime, diskFileAttr.ftLastWriteTime), false));
					}
					else
					{
						send(dbAgent.GetFileInfoTarget(), make_shared<db::DbInstallFileInfo>(FileInstallInfo(fname, ze.mtime), false));
					}
					continue;
				}

				bool installFile = true;
				if (fileExists && userInstSel == nullptr && !IsInDeleteList(fname, *installInfo))
				{
					// Falls nur ältere Dateien ersetzen werden sollen, Modified Times von Disk und Zip prüfen
					if(replaceOnlyOlder)
					{
						FILETIME diskTime = diskFileAttr.ftLastWriteTime;
						FILETIME zipTime = ze.mtime;
						installFile = CompareFileTime(&diskTime, &zipTime) < 0;
					}
					if (installFile)
					{
						if (static_cast<size_t>(ze.unc_size) > zipBufSize)
						{
							zipBufSize = max(131072, ze.unc_size);
							zipBuf.reset(new char[zipBufSize]);
						}
						zipRes = UnzipItem(hZip.get(), i, zipBuf.get(), zipBufSize);

						if ((diskFileAttr.nFileSizeHigh << sizeof (DWORD) | diskFileAttr.nFileSizeLow) == ze.unc_size)
						{
							// Falls filesize gleich, müssen wir den Inhalt vergleichen
							handle h (transactFs.CreateFile(diskPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
							if (h.get() == INVALID_HANDLE_VALUE)
							{
								InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, pkgInfo, lhWinAPI::GetLastErrorS(),
									diskPath.wstring());
								throw InstallerException(errInfo, "Error open file while file compare");
							}

							installFile = false;
							DWORD bytesRead;
							size_t overallRead = 0;
							do
							{
								if (ReadFile(h.get(), fileBuf.get(), fileBufSize, &bytesRead, nullptr))
								{
									if (overallRead + bytesRead > static_cast<size_t>(ze.unc_size) || memcmp(zipBuf.get() + overallRead, fileBuf.get(), bytesRead) != 0)
									{
										overallRead += bytesRead;
										installFile = true;
										break;
									}
									overallRead += bytesRead;
								}
								else
								{
									InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, pkgInfo, lhWinAPI::GetLastErrorS(),
										diskPath.wstring());
									throw InstallerException(errInfo, "Error reading file while file compare");
								}
							} while (bytesRead > 0);
							if (!installFile)
							{
								// prüfen ob gelesene anzahl bytes != bytes in zipfile sind
								installFile = overallRead != ze.unc_size;
							}
						}
					}
				}
				else
				{
					if (static_cast<size_t>(ze.unc_size) > zipBufSize)
					{
						zipBufSize = max(131072, ze.unc_size);
						zipBuf.reset(new char[zipBufSize]);
					}
					zipRes = UnzipItem(hZip.get(), i, zipBuf.get(), zipBufSize);
					if (zipRes != ZR_OK)
					{
						ThrowZipException(zipRes, pkgInfo, "Error calling GetZipItem", pkgInfo.packageName);
					}
				}
				if (installFile)
				{
					if (userInstSel == nullptr)
					{
						if (fileExists && (diskFileAttr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
						{
							// File is write protected
							installInfo->writeProtectedFilesToInstall.push_back(FileInstallInfo(fname, ze.mtime, diskFileAttr.ftLastWriteTime));
						}
						else if (fileExists)
						{
							// File existiert, ist aber nicht schreibgeschützt
							installInfo->filesToInstall.push_back(FileInstallInfo(fname, ze.mtime, diskFileAttr.ftLastWriteTime));
						}
						else
						{
							// File existiert nicht auf Disk
							installInfo->filesToInstall.push_back(FileInstallInfo(fname, ze.mtime));
						}

						if (alwaysAskBeforeOverwrite)
						{
							// Benutzer will für jede Datei explizit gefragt werden =>
							// nur in Liste aufnehmen
							continue;
						}
					}

					if (!FileExistsTransacted(transactFs, diskPath.parent_path().c_str()))
					{
						if (!CreateDirectoriesTransacted(transactFs, diskPath.parent_path()) && !FileExistsTransacted(transactFs, diskPath.parent_path().c_str()))
						{
							InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, pkgInfo, lhWinAPI::GetLastErrorS(), diskPath.wstring());
							throw InstallerException(errInfo, "Error create directory");
						}
					}

					if (fileExists && (diskFileAttr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
					{
						// Schreibgeschützt => Schreibschutz aufheben
						transactFs.SetFileAttributes(diskPath.c_str(), diskFileAttr.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
					}

					if (fileExists)
					{
						BackupFile(fname, installInfo);
						send(dbAgent.GetFileInfoTarget(), make_shared<db::DbInstallFileInfo>(FileInstallInfo(fname, ze.mtime, diskFileAttr.ftLastWriteTime), true));
					}
					else
					{
						send(dbAgent.GetFileInfoTarget(), make_shared<db::DbInstallFileInfo>(FileInstallInfo(fname, ze.mtime), true));
					}

					handle h (transactFs.CreateFileW(diskPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
					if (h.get() != INVALID_HANDLE_VALUE)
					{
						DWORD bytesWritten;
						if (!WriteFile(h.get(), zipBuf.get(), ze.unc_size, &bytesWritten, nullptr))
						{
							InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, pkgInfo, lhWinAPI::GetLastErrorS(), diskPath.wstring());
							throw InstallerException(errInfo, "Error writing output file");
						}
						SetFileTime(h.get(), nullptr, nullptr, &ze.mtime);
						if (fileExists && (diskFileAttr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
						{
							// Datei war schreibgeschützt != Schreibschutz wieder setzen
							transactFs.SetFileAttributes(diskPath.c_str(), diskFileAttr.dwFileAttributes);
						}
					}
					else
					{
						InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, pkgInfo, lhWinAPI::GetLastErrorS(), diskPath.wstring());
						throw InstallerException(errInfo, "Error creating output file");
					}
				}
				else if (!alwaysAskBeforeOverwrite || installState == InstallPassTwoRunning)
				{
					if (fileExists)
					{
						send(dbAgent.GetFileInfoTarget(), make_shared<db::DbInstallFileInfo>(FileInstallInfo(fname, ze.mtime, diskFileAttr.ftLastWriteTime), false));
					}
					else
					{
						send(dbAgent.GetFileInfoTarget(), make_shared<db::DbInstallFileInfo>(FileInstallInfo(fname, ze.mtime), false));
					}
				}
			}
			InterlockedExchangeAdd(&installInfo->cntProcessedFiles, processed);
		}

		void InstallManager::InstallPackage(size_t numThreads, const RootPackageInfo& rootPkg, const PackageInfo& pkgInfo, InstallInformation* instInfo)
		{
			for (const auto& instData : installData) {
				auto itPkg = instData.second->GetPackagGuidsToDeinstall().find(L"@" + rootPkg.hash);
				if (itPkg != instData.second->GetPackagGuidsToDeinstall().end()) {
					// Package wuerde deinstalliert werden
					// => bei Installation ignorieren
					return;
				}
			}

			size_t itemsPerThread = pkgInfo.numElements / numThreads;
			for (size_t t = 0; t < numThreads; ++t)
			{
				size_t startIndex = t * itemsPerThread;
				size_t end = startIndex + itemsPerThread;
				if (t == numThreads - 1)
				{
					end = pkgInfo.numElements;
				}
				workerTskGrp.run([this, pkgInfo, startIndex, end, instInfo, rootPkg] () {
					InstallFiles(startIndex, end, rootPkg, pkgInfo, instInfo);
				});
			}
		}

		void InstallManager::DeleteFiles(const vector<wstring>& filesToDelete, InstallInformation* instInfo, const PackageInfo& pkgInfo)
		{
			UserSelection *userInstSel = nullptr;
			if (installState == InstallPassTwoRunning)
			{
				userInstSel = &(*userSelectedFiles)[instInfo->packageName];
			}
			for_each(filesToDelete.begin(), filesToDelete.end(), [this, &instInfo, &pkgInfo, userInstSel] (const wstring& fname) {
				boost::filesystem::path diskPath = l3dPath;
				diskPath /= fname;
				WIN32_FILE_ATTRIBUTE_DATA diskFileAttr = { 0 };

				if (transactFs.GetFileAttributesExW(diskPath.c_str(), GetFileExInfoStandard, &diskFileAttr))
				{
					if (installState != InstallPassTwoRunning)
					{
						if ((diskFileAttr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
						{
							instInfo->writeProtectedFilesToDelete.push_back(FileInstallInfo(fname, diskFileAttr.ftLastWriteTime, true));
						}
						else
						{
							instInfo->filesToDelete.push_back(FileInstallInfo(fname, diskFileAttr.ftLastWriteTime, true));
						}
					}

					if ((!alwaysAskBeforeOverwrite || installState == InstallPassTwoRunning) &&
						(userInstSel == nullptr || userInstSel->filesToDelete.find(fname) != userInstSel->filesToDelete.end()))
					{
						BackupFile(fname, instInfo);
						if ((diskFileAttr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
						{
							transactFs.SetFileAttributes(diskPath.c_str(), diskFileAttr.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
						}
						if ((diskFileAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
						{
							// Funktion entfernt nur leere Verzeichnisse => das wollen wir auch genauso
							transactFs.RemoveDirectory(diskPath.c_str());
						}
						else if (!transactFs.DeleteFileW(diskPath.c_str()))
						{
							InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, pkgInfo, lhWinAPI::GetLastErrorS(), diskPath.wstring());
							throw InstallerException(errInfo, "Error deleting file");
						}
					}
				}
			});

		}

		size_t InstallManager::GetInstallationProgress(size_t *cntProcessedFiles, size_t *cntTotalFiles)
		{
			if (cntTotalFiles != nullptr)
			{
				*cntTotalFiles = this->cntTotalFiles;
			}
			size_t processed = 0;
			if (!alwaysAskBeforeOverwrite || installState == InstallPassTwoRunning)
			{
				try_receive<size_t>(dbAgent.GetCntProcessedFilesSource(), processed);
			}
			else
			{
				reader_writer_lock::scoped_lock_read lck(installInfoLock);
				for (auto instInfo = installInformation.begin(); instInfo != installInformation.end(); ++instInfo)
				{
					processed += instInfo->second->cntProcessedFiles;
				}
			}
			if (cntProcessedFiles != nullptr)
			{
				*cntProcessedFiles = processed;
			}
			if (this->cntTotalFiles > 0)
			{
				return min(100, (100 * processed / this->cntTotalFiles));
			}
			return 0;
		}

		/**
		 * Startet zweiten Durchgang der Installation

		 * Installiert / Löscht alle Dateien die im übergebenen Argument gesetzt sind

		 * Setzt das EventInstallFinished zurück. Dieses Event kann nun wieder verwendet werden, um auf den Abschluss der Installation zu warten
		 * Aufruf nur gültig falls IsAlwaysAsk() true liefert
		 * @param userSelection Auswahl des Benutzers welche Dateien in welchem Package zu installieren sind
		 */
		void InstallManager::StartInstallUserSelection(std::shared_ptr<std::map<std::wstring, insthelper::UserSelection>> userSelection)
		{
			eventInstallFinished.reset();
			this->userSelectedFiles = userSelection;
			installState = InstallRunning;

			{
				reader_writer_lock::scoped_lock_read lck(installInfoLock);
				for (auto it = installInformation.begin(); it != installInformation.end(); ++it)
				{
					it->second->cntProcessedFiles = 0;
				}
			}

			dbAgent.start();

			InstallManager *mt = this;
			auto& outerSelFiles = selectedFilesToInstall;
			bgWork.run([mt, outerSelFiles] {
				// VS2010 SP1 Bug nested lambda Connect ID 537366
				InstallManager *m = mt;
				auto& selFiles = outerSelFiles;
				mt->DoExceptionHandledInstall([m, &selFiles]() {
					m->cancelled = m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled;

					m->installState = InstallManager::InstallPassTwoRunning;

					vector<int> deinstallPkgIds = m->DetermineFilesToDeinstall();

					// einen core explizit für db operationen "reservieren"
					size_t numThreads = max(1, boost::thread::hardware_concurrency() - 1);
					for (auto fileName = selFiles.begin(); fileName != selFiles.end() && !m->workerTskGrp.is_canceling(); ++fileName)
					{
						//UserSelection& uEntry = (*m->userSelectedFiles)[*fileName];
						InstallInformation *instInfo = nullptr;
						{
							reader_writer_lock::scoped_lock_read lck(m->installInfoLock);
							instInfo = m->installInformation[*fileName].get();
						}
						unique_ptr<PreInstallWorker>& refData = m->installData.at(*fileName);

						m->SetBackupDir(refData->GetPackageInfo());
						send(m->dbAgent.GetPkgBeginTarget(), &refData->GetPackageInfo());
						refData->GetPackageInfo().dbId = receive(m->dbAgent.GetPkgDbIdSource());
						m->dbAgent.ThrowExceptionIfAvailable();

						m->DeleteFiles(refData->GetFilesToDelete(), instInfo, refData->GetPackageInfo());

						m->InstallPackage(numThreads, refData->GetPackageInfo(), refData->GetPackageInfo(), instInfo);
						m->cancelled =  m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled;

						const std::vector<PackageInfo>& innerPackages = refData->GetContainedPackages();
						for (auto innerPkgs = innerPackages.begin(); innerPkgs != innerPackages.end() && !m->workerTskGrp.is_canceling(); ++innerPkgs)
						{
							m->InstallPackage(numThreads, refData->GetPackageInfo(), *innerPkgs, instInfo);
							m->cancelled = m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled;
						}
						// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
						send(m->dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					}

					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(m->dbAgent.GetPkgBeginTarget(), nullptr);

					m->cancelled = m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled || m->workerTskGrp.is_canceling();
					if (m->cancelled)
					{
						m->installState = InstallManager::InstallCancelled;
						if (m->transactFs.RollBack() && m->IsUsingTransaction())
						{
							m->dbAgent.SetAgentCommand(AgentCancelAndRollback);
						}

						// Falls filesystem rollback fehlschlägt, speichern wir die installation in der db
						// damit eine deinstallation möglich ist
						m->dbAgent.SetAgentCommand(AgentCancelAndCommit);
						agent::wait(&m->dbAgent);
						m->dbAgent.ThrowExceptionIfAvailable();
					}
					else
					{
						if (m->filesToDeinstall)
						{
							DeinstallAgent da(m->l3dPath, deinstallPkgIds, m->dbAgent.GetConnection(), &m->transactFs);
							da.start();
							agent::wait(&da);
							da.ThrowExceptionIfAvailable();
							m->deinstalledFiles = da.GetDeinstalledFiles();
						}

						m->dbAgent.SetAgentCommand(AgentStopAndCommit);
						agent::wait(&m->dbAgent);
						m->dbAgent.ThrowExceptionIfAvailable();

						if (!m->transactFs.Commit())
						{
							InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, PackageInfo(), lhWinAPI::GetLastErrorS());
							throw InstallerException(errInfo, "Transaction commit failed");
						}
						else
						{
							m->installState = InstallManager::InstallSuccessfullyFinished;
						}
					}
				});
			});
		}

		void InstallManager::SetBackupDir(RootPackageInfo& pkgInfo)
		{
			boost::filesystem::path pkgPath = pkgInfo.packageName;
			currentBackupPath = rootBackupPath / pkgPath.stem();
			int nr = 1;
			while (FileExistsTransacted(transactFs, currentBackupPath.c_str()))
			{
				currentBackupPath = rootBackupPath / (pkgPath.stem().wstring() + lhstd::String(nr));
				nr++;
			}
			pkgInfo.backupDirectory = currentBackupPath.wstring();
		}

		vector<int> InstallManager::DetermineFilesToDeinstall()
		{
			vector<int> deinstallPkgIds;

			if (installState == InstallPassTwoRunning)
			{
				for(auto& en = begin(*userSelectedFiles); en != end(*userSelectedFiles); ++en)
				{
					copy(begin(en->second.packagesToDeinstall), end(en->second.packagesToDeinstall), back_inserter(deinstallPkgIds));
				}
			}
			else
			{
				packagesToDeinstall = make_shared<map<int, db::DBPackageEntry>>();
				for(auto& en = begin(installData); en != end(installData); ++en)
				{
					std::transform(begin(en->second->GetPackagesToDeinstall()), end(en->second->GetPackagesToDeinstall()), back_inserter(deinstallPkgIds), [](const db::DBPackageEntry& dbEn)
					{
						return dbEn.GetId();
					});

					std::for_each(begin(en->second->GetPackagesToDeinstall()), end(en->second->GetPackagesToDeinstall()),
						[this](const db::DBPackageEntry& dbEn)
					{
						(*packagesToDeinstall)[dbEn.GetId()] = dbEn;
					});
				}
			}
			if (!deinstallPkgIds.empty())
			{
				FilesToDeinstallGathererAgent ga(l3dPath, deinstallPkgIds);
				ga.start();
				agent::wait(&ga);
				ga.ThrowExceptionIfAvailable();
				filesToDeinstall = ga.GetFilesToDeinstall();
			}
			else
			{
				filesToDeinstall = nullptr;
			}
			return deinstallPkgIds;
		}

		void InstallManager::StartInstallation()
		{
			if (alwaysAskBeforeOverwrite && selectedFilesToInstall.size() > 1)
			{
				throw std::invalid_argument("alwaysAskBeforeOverwrite only possible if installing one file");
			}

			InstallManager* mt = this;
			installState = InstallRunning;

			{
				wofstream outStr(GetFilenameInLocalSettingsDir(FILENAME_CRASHRPT_LOG), fstream::out | fstream::app);
				outStr << L"Files To Install:" << endl;
				std::for_each(begin(selectedFilesToInstall), end(selectedFilesToInstall), [&outStr](const wstring& f) {
					outStr << f << endl;
				});
			}

			if (!alwaysAskBeforeOverwrite)
			{
				dbAgent.start();
			}
			auto& outerSelFiles = selectedFilesToInstall;
			bgWork.run([mt, outerSelFiles] {
				// VS2010 SP1 Bug nested lambda Connect ID 537366
				InstallManager *m = mt;
				auto& selFiles = outerSelFiles;
				mt->DoExceptionHandledInstall([m, &selFiles]() {
					m->cancelled = m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled;

					m->cntTotalFiles = 0;
					for (auto fileName = selFiles.begin(); fileName != selFiles.end() && !m->workerTskGrp.is_canceling(); ++fileName)
					{
						PreInstallWorker *preInstallData = m->installData.at(*fileName).get();
						m->cntTotalFiles += preInstallData->GetPackageInfo().numFilesToInstall;
						const std::vector<PackageInfo>& innerPackages = preInstallData->GetContainedPackages();
						for (auto innerPkgs = innerPackages.begin(); innerPkgs != innerPackages.end() && !m->workerTskGrp.is_canceling(); ++innerPkgs)
						{
							m->cntTotalFiles += innerPkgs->numFilesToInstall;
						}
					}

					vector<int> deinstallPkgIds = m->DetermineFilesToDeinstall();

					// einen core explizit für db operationen "reservieren"
					// außer wir machen in diesem pass nix mit der db
					size_t numThreads = max(1, boost::thread::hardware_concurrency() - (m->alwaysAskBeforeOverwrite ? 0 : 1));
					for (auto fileName = selFiles.begin(); fileName != selFiles.end() && !m->workerTskGrp.is_canceling(); ++fileName)
					{
						unique_ptr<InstallInformation> instInf = unique_ptr<InstallInformation>(new InstallInformation());
						instInf->packageName = *fileName;
						instInf->cntProcessedFiles = 0;
						InstallInformation *pInf = instInf.get();
						{
							reader_writer_lock::scoped_lock lck(m->installInfoLock);
							m->installInformation[*fileName] = move(instInf);
						}
						unique_ptr<PreInstallWorker>& refData = m->installData.at(*fileName);

						if (!m->alwaysAskBeforeOverwrite)
						{
							m->SetBackupDir(refData->GetPackageInfo());
							send(m->dbAgent.GetPkgBeginTarget(), &refData->GetPackageInfo());
							refData->GetPackageInfo().dbId = receive(m->dbAgent.GetPkgDbIdSource());
							m->dbAgent.ThrowExceptionIfAvailable();
						}

						m->DeleteFiles(refData->GetFilesToDelete(), pInf, refData->GetPackageInfo());

						m->InstallPackage(numThreads, refData->GetPackageInfo(), refData->GetPackageInfo(), pInf);
						m->cancelled =  m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled;

						const std::vector<PackageInfo>& innerPackages = refData->GetContainedPackages();
						for (auto innerPkgs = innerPackages.begin(); innerPkgs != innerPackages.end() && !m->workerTskGrp.is_canceling(); ++innerPkgs)
						{
							m->InstallPackage(numThreads, refData->GetPackageInfo(), *innerPkgs, pInf);
							m->cancelled = m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled;
						}
						if (!m->alwaysAskBeforeOverwrite)
						{
							// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
							send(m->dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
						}
						if (!m->cancelled)
						{
							// Sorieren für den User, bzw in weiterer folge auch wichtig für die userSelection
							sort(pInf->filesToDelete.begin(), pInf->filesToDelete.end());
							sort(pInf->filesToInstall.begin(), pInf->filesToInstall.end());
							sort(pInf->writeProtectedFilesToDelete.begin(), pInf->writeProtectedFilesToDelete.end());
							sort(pInf->writeProtectedFilesToInstall.begin(), pInf->writeProtectedFilesToInstall.end());
						}
					}

					if (!m->alwaysAskBeforeOverwrite)
					{
						// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
						send<RootPackageInfo*>(m->dbAgent.GetPkgBeginTarget(), nullptr);
					}

					m->cancelled = m->workerTskGrp.wait() == Concurrency::canceled || m->cancelled || m->workerTskGrp.is_canceling();
					if (m->cancelled)
					{
						m->installState = InstallManager::InstallCancelled;
						if (m->transactFs.RollBack() && m->IsUsingTransaction() && !m->alwaysAskBeforeOverwrite)
						{
							m->dbAgent.SetAgentCommand(AgentCancelAndRollback);
						} else if (!m->alwaysAskBeforeOverwrite)
						{
							// Falls filesystem rollback fehlschlägt, speichern wir die installation in der db
							// damit eine deinstallation möglich ist
							m->dbAgent.SetAgentCommand(AgentCancelAndCommit);
							agent::wait(&m->dbAgent);
							m->dbAgent.ThrowExceptionIfAvailable();
						}
					}
					else if (m->alwaysAskBeforeOverwrite)
					{
						m->installState = InstallManager::InstallPassOneFinished;
						m->GenerateFilesToDeinstallList(deinstallPkgIds);
					}
					else
					{
						if (m->filesToDeinstall)
						{
							DeinstallAgent da(m->l3dPath, deinstallPkgIds, m->dbAgent.GetConnection(), &m->transactFs);
							da.start();
							agent::wait(&da);
							da.ThrowExceptionIfAvailable();
							m->deinstalledFiles = da.GetDeinstalledFiles();
						}

						m->dbAgent.SetAgentCommand(AgentStopAndCommit);
						agent::wait(&m->dbAgent);
						m->dbAgent.ThrowExceptionIfAvailable();

						if (!m->transactFs.Commit())
						{
							InstallerErrorInformation errInfo(InstallerErrorInformation::InstallErrorFileSystemOccurred, PackageInfo(), lhWinAPI::GetLastErrorS());
							throw InstallerException(errInfo, "Transaction commit failed");
						}
						else
						{
							m->installState = InstallManager::InstallSuccessfullyFinished;
						}
					}
				});
				if (!mt->alwaysAskBeforeOverwrite)
				{
					agent::wait(&mt->dbAgent);
				}
			});
		}

		template<class Func>
		void InstallManager::DoExceptionHandledInstall(Func f)
		{
			try
			{
				f();
			}
			catch (const InstallerException& ex)
			{
				installState = InstallErrorOccurred;
				errorEx.reset(new InstallerException(ex));
				if (transactFs.RollBack() && IsUsingTransaction() && dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndRollback);
				}
				else if (dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndCommit);
				}
				if (dbAgent.status() != Concurrency::agent_created)
				{
					// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
					send(dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(dbAgent.GetPkgBeginTarget(), nullptr);
				}
			}
			catch (const DeinstallException& ex)
			{
				installState = InstallErrorOccurred;
				errorEx.reset(new InstallerException(ex));
				if (transactFs.RollBack() && IsUsingTransaction() && dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndRollback);
				}
				else if (dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndCommit);
				}
				if (dbAgent.status() != Concurrency::agent_created)
				{
					// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
					send(dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(dbAgent.GetPkgBeginTarget(), nullptr);
				}
			}
			catch (const SQLiteException& ex)
			{
				installState = InstallErrorOccurred;
				std::wstring msg;
				lhWinAPI::CharToTchar(msg, ex.GetString());
				errorEx.reset(new InstallerException(InstallerErrorInformation(InstallerErrorInformation::InstallErrorFileSystemOccurred, PackageInfo(), msg)));
				if (transactFs.RollBack() && IsUsingTransaction() && dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndRollback);
				}
				else if (dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndCommit);
				}
				if (dbAgent.status() != Concurrency::agent_created)
				{
					// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
					send(dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(dbAgent.GetPkgBeginTarget(), nullptr);
				}
			}
			catch (const lhstd::exception& ex)
			{
				installState = InstallErrorOccurred;
				errorEx.reset(new InstallerException(InstallerErrorInformation(InstallerErrorInformation::InstallErrorFileSystemOccurred, PackageInfo(), ex.what())));
				if (transactFs.RollBack() && IsUsingTransaction() && dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndRollback);
				}
				else if (dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndCommit);
				}
				if (dbAgent.status() != Concurrency::agent_created)
				{
					// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
					send(dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(dbAgent.GetPkgBeginTarget(), nullptr);
				}
			}
			catch (const std::exception& ex)
			{
				installState = InstallErrorOccurred;
				wstring msg;
				lhWinAPI::CharToTchar(msg, ex.what());
				errorEx.reset(new InstallerException(InstallerErrorInformation(InstallerErrorInformation::InstallErrorFileSystemOccurred, PackageInfo(), wstring(L"std::exception ").append(msg))));
				if (transactFs.RollBack() && IsUsingTransaction() && dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndRollback);
				}
				else if (dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndCommit);
				}
				if (dbAgent.status() != Concurrency::agent_created)
				{
					// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
					send(dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(dbAgent.GetPkgBeginTarget(), nullptr);
				}
			}
			catch (...)
			{
				installState = InstallErrorOccurred;
				if (transactFs.RollBack() && IsUsingTransaction() && dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndRollback);
				}
				else if (dbAgent.status() != Concurrency::agent_created)
				{
					dbAgent.SetAgentCommand(AgentStopAndCommit);
				}
				if (dbAgent.status() != Concurrency::agent_created)
				{
					// Sende 'Code' (leerer shared_ptr), dass Package fertig abgearbeitet wurde
					send(dbAgent.GetFileInfoTarget(), shared_ptr<db::DbInstallFileInfo>());
					// Sende 'Code', dass es keine Packages mehr zum Abarbeiten gibt
					send<RootPackageInfo*>(dbAgent.GetPkgBeginTarget(), nullptr);
				}
			}
			eventInstallFinished.set();
		}

		std::shared_ptr<std::vector<pair<db::DBPackageEntry, std::vector<std::wstring>>>> InstallManager::GetDeinstalledFiles() const
		{
			std::shared_ptr<std::vector<pair<db::DBPackageEntry, std::vector<std::wstring>>>> ret;
			if (packagesToDeinstall && deinstalledFiles && !deinstalledFiles->empty())
			{
				ret = make_shared<std::vector<pair<db::DBPackageEntry, std::vector<std::wstring>>>>();
				for_each(begin(*deinstalledFiles), end(*deinstalledFiles), [&ret, this](const pair<int, std::vector<std::wstring>>& en)
				{
					ret->push_back(make_pair((*packagesToDeinstall)[en.first], en.second));
				});
			}
			return ret;
		}

		// Prüft ob eine Datei in installInformation drinnen ist
		bool InstallManager::IsInInstallList(const std::wstring& fname) const
		{
			boost::filesystem::path bpath = boost::to_lower_copy(fname);
			if (!fname.empty() && fname[0] != '/' && fname[0] != '\\')
			{
				bpath = L"/" / bpath;
			}
			bpath = bpath.make_preferred();
			for (auto en = begin(installInformation); en != end(installInformation); ++en)
			{
				const auto& bp = bpath;
				bool found = find_if(begin(en->second->filesToInstall), end(en->second->filesToInstall), [&bp](const FileInstallInfo& ftd) -> bool
				{
					auto p = boost::filesystem::path(boost::to_lower_copy(ftd.fileName)).make_preferred();
					return bp == p;
				}) != end(en->second->filesToInstall);

				found = found ||  find_if(begin(en->second->writeProtectedFilesToInstall), end(en->second->writeProtectedFilesToInstall), [&bp](const FileInstallInfo& ftd) -> bool
				{
					auto p = boost::filesystem::path(boost::to_lower_copy(ftd.fileName)).make_preferred();
					return bp == p;
				}) != end(en->second->writeProtectedFilesToInstall);

				if (found)
				{
					return true;
				}
			}
			return false;
		}

		// Erzeugt Liste zu deinstallierender Dateien zur Anzeige für User
		void InstallManager::GenerateFilesToDeinstallList(vector<int> deinstallPkgIds)
		{
			userFilesToDeinstall = make_shared<vector<pair<db::DBPackageEntry, vector<wstring>>>>();

			std::shared_ptr<std::map<int, std::vector<FileToDeinstall>>> files;
			if (filesToDeinstall)
			{
				FilesToDeinstallGathererAgent da(l3dPath, deinstallPkgIds);
				da.start();
				agent::wait(&da);
				da.ThrowExceptionIfAvailable();
				files = da.GetFilesToDeinstall();
			}

			if (files && !files->empty() && packagesToDeinstall)
			{
				for_each(begin(*files), end(*files), [this](const pair<int, vector<FileToDeinstall>>& en)
				{
					auto newEl = make_pair((*packagesToDeinstall)[en.first], vector<wstring>());
					InstallManager *m = this;
					for_each(begin(en.second), end(en.second), [&m, &newEl](const FileToDeinstall& f)
					{
						if (!m->IsInInstallList(f.filename))
						{
							newEl.second.push_back(f.filename);
						}
					});
					userFilesToDeinstall->push_back(move(newEl));
				});
			}
		}

		// Liste von Packages mit einem Pair<PackageName, Name zu deinstallierender Dateien>
		shared_ptr<vector<pair<db::DBPackageEntry, vector<wstring>>>> InstallManager::GetFilesToDeinstall() const
		{
			return userFilesToDeinstall;
		}
	}
}

ext: cpp


text:
/**
 * server.hpp
 */

#pragma once

#include "logger.hpp"
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/asio.hpp>
#include <cstdlib>
#include <string>
#include <iostream>
#include <memory>
#include <utility>

namespace ip = boost::asio::ip;

using boost::asio::ip::tcp;

namespace sqlproxy {

/// Class to represent client <-> server session
class session
    : public std::enable_shared_from_this<session>
{
public:
    /// Pointer type to session class
    typedef std::shared_ptr<session> ptr_type;

    /// Session constructor
    session(boost::asio::io_service& ios, const std::string& log_name)
        : client_socket_(ios)
        , server_socket_(ios)
        , logger_(ios, "SQL")
    {
        // Set the name of the file that all logger instances will use.
        logger_.use_file(log_name);
    }

    /// Client socket property
    tcp::socket& client_socket() {
        return client_socket_;
    }

    void start(const std::string& server_host,
               uint16_t server_port) {
        auto self = shared_from_this();
        // Connect to PostgreSQL server
        server_socket_.async_connect(
            tcp::endpoint(
                ip::address::from_string(server_host),
                server_port),
            [this, self](const boost::system::error_code& ec) {
                if (!ec) {
                    // std::cerr << "Connection to PostgreSQL\n";
                    on_server_connect();
                }
                else {
                    on_error(ec, "Connect");
                }
            });
    }

private:
    /// PostgreSQL server connected
    void on_server_connect() {
        auto self(shared_from_this());
        // Async read from PostgreSQL server
        server_socket_.async_read_some(
            boost::asio::buffer(server_data_, max_length),
            [this, self](const boost::system::error_code& ec, std::size_t length) {
                if (!ec) {
                    on_server_read(length);
                }
                else {
                    on_error(ec, "Server read");
                }
            });

        // Async read from client
        client_socket_.async_read_some(
            boost::asio::buffer(client_data_, max_length),
            [this, self](const boost::system::error_code& ec, std::size_t length) {
                if (!ec) {
                    on_client_read(length);
                }
                else {
                    on_error(ec, "Client read");
                }
            });
    }

    /// Send data from PostgreSQL server to client
    void on_server_read(std::size_t length) {
        auto self(shared_from_this());
        boost::asio::async_write(
            client_socket_,
            boost::asio::buffer(server_data_, length),
            [this, self](const boost::system::error_code& ec, std::size_t /*length*/) {
                if (!ec) {
                    on_client_write();
                }
                else {
                    on_error(ec, "Client write");
                }
            });
    }

    /// Send data to PostgreSQL server
    void on_client_read(const std::size_t& length) {
        if (client_data_[0] == 'Q') {
            // Log sql request
            // uint64_t p = (client_data_[1] << 24) +
            //              (client_data_[2] << 16) +
            //              (client_data_[3] << 8) +
            //              client_data_[4];
            // std::cout << p << "] ";
            std::string command(&client_data_[5], length - 6);
            logger_.log(command);
        }
        // else {
        //     // Log connection user and database
        //     std::string value(&client_data_[0], length);
        //     std::size_t pos = value.find("user");
        //     if (pos != std::string::npos) {
        //         std::size_t pos1 = value.find("database");
        //         if (pos1 != std::string::npos) {
        //             std::string str = format::fmt(
        //                 "Connection user: %s, database: %s",
        //                 value.substr(pos + 5, pos1 - pos - 6),
        //                 value.substr(pos1 + 9, value.size() - pos1 - 11));
        //             logger_.log(str);
        //         }
        //     }
        // }

        auto self(shared_from_this());
        boost::asio::async_write(
            server_socket_,
            boost::asio::buffer(client_data_, length),
            [this, self](const boost::system::error_code& ec, std::size_t /*length*/) {
                if (!ec) {
                    on_server_write();
                }
                else {
                    on_error(ec, "Server write");
                }
            });
    }

    /// Async read from PostgreSQL server
    void on_client_write() {
        auto self(shared_from_this());
        server_socket_.async_read_some(
            boost::asio::buffer(server_data_, max_length),
            [this, self](const boost::system::error_code& ec, std::size_t length) {
                if (!ec) {
                    on_server_read(length);
                }
                else {
                    on_error(ec, "Server read");
                }
            });
    }

    /// Async read from client
    void on_server_write() {
        auto self(shared_from_this());
        client_socket_.async_read_some(
            boost::asio::buffer(client_data_, max_length),
            [this, self](const boost::system::error_code& ec, std::size_t length) {
                if (!ec) {
                    on_client_read(length);
                }
                else {
                    on_error(ec, "Client read");
                }
            });
    }

    /// Close client and server sockets
    void close() {
        boost::mutex::scoped_lock lock(mutex_);

        if (client_socket_.is_open()) {
            client_socket_.close();
        }

        if (server_socket_.is_open()) {
            server_socket_.close();
        }
    }

    /// Log error and close sockets
    void on_error(const boost::system::error_code& ec,
                  const std::string& prefix) {
        auto code = boost::any_cast<int>(ec.value());
        if (!((code == 2) || (code == 125))) {
            logger_.log(format::fmt(
                "%s error: [%d] %s", prefix, code, ec.message()));
        }
        close();
    }

    tcp::socket client_socket_;
    tcp::socket server_socket_;
    services::logger logger_;

    enum { max_length = 4096 };
    std::array<char, max_length> client_data_;
    std::array<char, max_length> server_data_;

    boost::mutex mutex_;
};  // class session

/// Class to accept connections
class server
{
public:
    server(boost::asio::io_service& io_service,
           const std::string& local_host, uint16_t local_port,
           const std::string& server_host, uint16_t server_port,
           const std::string& log_name)
        : io_service_(io_service)
        , localhost_address_(
              boost::asio::ip::address_v4::from_string(local_host))
        , acceptor_(io_service_,
                    tcp::endpoint(localhost_address_, local_port))
        , server_port_(server_port)
        , server_host_(server_host)
        , log_name_(log_name)
    {
        on_accept();
    }

private:
    /// On accept connection
    void on_accept() {
        session_ = std::make_shared<session>(io_service_, log_name_);
        acceptor_.async_accept(
            session_->client_socket(),
            [this](const boost::system::error_code& ec) {
                if (!ec) {
                    // Start session
                    session_->start(server_host_, server_port_);
                    on_accept();
                }
                else {
                    std::cerr << "Error: [" << ec.value() << "] " << ec.message() << "\n";
                }
            });
    }

    boost::asio::io_service& io_service_;
    ip::address_v4 localhost_address_;
    tcp::acceptor acceptor_;
    session::ptr_type session_;
    uint16_t server_port_;
    std::string server_host_;
    std::string log_name_;
};  // class server

}  // namespace sqlproxy

max_stars_repo_name: i-wind/psqlproxy

max_issues_repo_name: i-wind/psqlproxy

max_forks_repo_name: i-wind/psqlproxy


text:
#pragma once

#include <string>

#include <boost/lexical_cast.hpp>

#include "all_type_variant.hpp"
#include "expression/correlated_parameter_expression.hpp"
#include "types.hpp"

namespace opossum {

namespace hana = boost::hana;

/**
 * AllParameterVariant holds either an AllTypeVariant, a ColumnID or a Placeholder.
 * It should be used to generalize Opossum operator calls.
 */

// This holds pairs of all types and their respective string representation
static constexpr auto parameter_types =
    hana::make_tuple(hana::make_pair("AllTypeVariant", hana::type_c<AllTypeVariant>),
                     hana::make_pair("ColumnID", hana::type_c<ColumnID>),       // NOLINT
                     hana::make_pair("Parameter", hana::type_c<ParameterID>));  // NOLINT

// This holds only the possible data types.
static constexpr auto parameter_types_as_hana_sequence = hana::transform(parameter_types, hana::second);

// Convert tuple to mpl vector
using ParameterTypesAsMplVector =
    decltype(hana::to<hana::ext::boost::mpl::vector_tag>(parameter_types_as_hana_sequence));

// Create boost::variant from mpl vector
using AllParameterVariant = typename boost::make_variant_over<ParameterTypesAsMplVector>::type;

// Function to check if AllParameterVariant is AllTypeVariant
inline bool is_variant(const AllParameterVariant& variant) { return (variant.type() == typeid(AllTypeVariant)); }

// Function to check if AllParameterVariant is a column id
inline bool is_column_id(const AllParameterVariant& variant) { return (variant.type() == typeid(ColumnID)); }

// Function to check if AllParameterVariant is a ParameterID
inline bool is_parameter_id(const AllParameterVariant& variant) { return (variant.type() == typeid(ParameterID)); }

std::ostream& operator<<(std::ostream& stream, const AllParameterVariant& variant);

}  // namespace opossum

max_stars_repo_name: mrcl-tst/hyrise

max_issues_repo_name: mrcl-tst/hyrise

max_forks_repo_name: mrcl-tst/hyrise


text:
/***********************************************************************************************************************
 *  OpenStudio(R), Copyright (c) 2008-2018, Alliance for Sustainable Energy, LLC. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 *  following conditions are met:
 *
 *  (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 *  disclaimer.
 *
 *  (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *  following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 *  (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote
 *  products derived from this software without specific prior written permission from the respective party.
 *
 *  (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative
 *  works may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without
 *  specific prior written permission from Alliance for Sustainable Energy, LLC.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **********************************************************************************************************************/

#ifndef MODEL_ATTRIBUTES_HPP
#define MODEL_ATTRIBUTES_HPP

#include <boost/preprocessor/control/if.hpp>
#include <boost/optional.hpp>
#include "../utilities/units/Quantity.hpp"
#include "../utilities/units/OSOptionalQuantity.hpp"

//ATTRIBUTE_DEFINITION(IS_OPTIONAL,IS_AUTOSIZEABLE,HAS_DEFAULT,ATTRIBUTE_NAME,ACCESSOR_BASE_NAME)
//ATTRIBUTE_IMPLEMENTATION(IS_OPTIONAL,HAS_DEFAULT,IS_AUTOSIZEABLE,
//                         ATTRIBUTE_NAME,ACCESSOR_BASE_NAME,
//                         CLASS_NAME,
//                         DEFAULT_IMPLEMENTATIOIN,IDD_OBJECT_NAME,IDD_FIELD_NAME)

// ATTRIBUTE_DEFINITION

#define ATTRIBUTE_DEFINITION(IS_OPTIONAL,HAS_DEFAULT,IS_AUTOSIZEABLE,ATTRIBUTE_NAME,ACCESSOR_BASE_NAME) \
\
BOOST_PP_IF(BOOST_PP_OR(IS_OPTIONAL,IS_AUTOSIZEABLE), \
\
bool set##ACCESSOR_BASE_NAME(boost::optional<double> value); \
bool set##ACCESSOR_BASE_NAME(const OSOptionalQuantity & value); \
OSOptionalQuantity ATTRIBUTE_NAME##_SI() const; \
OSOptionalQuantity ATTRIBUTE_NAME##_IP() const; \
OSOptionalQuantity get##ACCESSOR_BASE_NAME(bool returnIP = false) const; \
boost::optional<double> ATTRIBUTE_NAME() const; \
BOOST_PP_IF(HAS_DEFAULT, \
void reset##ACCESSOR_BASE_NAME(); \
bool is##ACCESSOR_BASE_NAME##Defaulted() const; \
,) \
BOOST_PP_IF(IS_AUTOSIZEABLE, \
void autosize##ACCESSOR_BASE_NAME(); \
bool is##ACCESSOR_BASE_NAME##Autosized() const; \
,) \
,\
bool set##ACCESSOR_BASE_NAME(double value); \
bool set##ACCESSOR_BASE_NAME(const Quantity & value); \
Quantity ATTRIBUTE_NAME##_SI() const; \
Quantity ATTRIBUTE_NAME##_IP() const; \
Quantity get##ACCESSOR_BASE_NAME(bool returnIP = false) const; \
double ATTRIBUTE_NAME() const; \
BOOST_PP_IF(HAS_DEFAULT, \
void reset##ACCESSOR_BASE_NAME(); \
bool is##ACCESSOR_BASE_NAME##Defaulted() const; \
,) \
)

// ATTRIBUTE_IMPLEMENTATION

#define ATTRIBUTE_IMPLEMENTATION(IS_OPTIONAL,HAS_DEFAULT,IS_AUTOSIZEABLE, \
                                 ATTRIBUTE_NAME,ACCESSOR_BASE_NAME, \
                                 CLASS_NAME, \
                                 DEFAULT_IMPLEMENTATIOIN,IDD_OBJECT_NAME,IDD_FIELD_NAME) \
\
BOOST_PP_IF(BOOST_PP_OR(IS_OPTIONAL,IS_AUTOSIZEABLE), \
\
OSOptionalQuantity CLASS_NAME##_Impl::ATTRIBUTE_NAME##_SI() const { \
  return get##ACCESSOR_BASE_NAME(false); \
} \
\
OSOptionalQuantity CLASS_NAME##_Impl::ATTRIBUTE_NAME##_IP() const { \
  return get##ACCESSOR_BASE_NAME(true); \
} \
\
OSOptionalQuantity CLASS_NAME##_Impl::get##ACCESSOR_BASE_NAME(bool returnIP) const { \
  OptionalDouble value = ATTRIBUTE_NAME(); \
  return getQuantityFromDouble(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, value, returnIP); \
} \
\
bool CLASS_NAME##_Impl::set##ACCESSOR_BASE_NAME(const OSOptionalQuantity& quantity) { \
  bool result(false); \
  OptionalDouble value; \
  if (quantity.isSet()) { \
    value = getDoubleFromQuantity(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME,quantity.get()); \
    if (value) { \
      result = set##ACCESSOR_BASE_NAME(value); \
    } \
  } \
  else { \
    result = set##ACCESSOR_BASE_NAME(value); \
  } \
  return result; \
} \
\
BOOST_PP_IF(DEFAULT_IMPLEMENTATIOIN, \
void CLASS_NAME##_Impl::set##ACCESSOR_BASE_NAME(boost::optional<double> value) { \
  bool result = false; \
  if (value) { \
    result = setDouble(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, value.get()); \
  } else { \
    result = setString(IDD_FIELD_NAME##Fields::IDD_FIELD_NAME, ""); \
  } \
  OS_ASSERT(result); \
} \
\
boost::optional<double> CLASS_NAME##_Impl::ATTRIBUTE_NAME() const { \
  OptionalDouble value = getDouble(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, true); \
  return value; \
} \
,)\
\
BOOST_PP_IF(HAS_DEFAULT, \
void CLASS_NAME##_Impl::reset##ATTRIBUTE_NAME() { \
  bool result = setString(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, ""); \
  OS_ASSERT(result); \
} \
,) \
BOOST_PP_IF(IS_AUTOSIZEABLE, \
bool CLASS_NAME##_Impl::is##ACCESSOR_BASE_NAME##Autosized() const { \
  bool result = false; \
  boost::optional<std::string> value = getString(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, true); \
  if (value) { \
    result = openstudio::istringEqual(value.get(), "autosize"); \
  } \
  return result; \
} \
void CLASS_NAME##_Impl::autosize##ACCESSOR_BASE_NAME() { \
  bool result = setString(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, "autosize"); \
  OS_ASSERT(result); \
} \
,) \
,\
Quantity CLASS_NAME##_Impl::get##ACCESSOR_BASE_NAME(bool returnIP) const { \
  OptionalDouble value = ATTRIBUTE_NAME(); \
  OSOptionalQuantity result = getQuantityFromDouble(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, value, returnIP); \
  OS_ASSERT(result.isSet()); \
  return result.get(); \
} \
\
bool CLASS_NAME##_Impl::set##ACCESSOR_BASE_NAME(const Quantity& quantity) { \
  OptionalDouble value = getDoubleFromQuantity(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME,quantity); \
  if (!value) { \
    return false; \
  } \
  return set##ACCESSOR_BASE_NAME(value.get()); \
} \
\
Quantity CLASS_NAME##_Impl::ATTRIBUTE_NAME##_SI() const { \
  return get##ACCESSOR_BASE_NAME(false); \
} \
\
Quantity CLASS_NAME##_Impl::ATTRIBUTE_NAME##_IP() const { \
  return get##ACCESSOR_BASE_NAME(true); \
} \
\
BOOST_PP_IF(DEFAULT_IMPLEMENTATIOIN, \
double CLASS_NAME##_Impl::ATTRIBUTE_NAME() const { \
  OptionalDouble value = getDouble(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, true); \
  OS_ASSERT(value); \
  return value.get(); \
} \
\
bool CLASS_NAME##_Impl::set##ACCESSOR_BASE_NAME(double value) { \
  bool result = false; \
  result = setDouble(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, value); \
  return result; \
} \
, ) \
\
BOOST_PP_IF(HAS_DEFAULT, \
void CLASS_NAME##_Impl::reset##ACCESSOR_BASE_NAME() { \
  bool result = setString(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME, ""); \
  OS_ASSERT(result); \
} \
bool CLASS_NAME##_Impl::is##ACCESSOR_BASE_NAME##Defaulted() const { \
  return isEmpty(IDD_OBJECT_NAME##Fields::IDD_FIELD_NAME); \
} \
,) \
)

#endif // MODEL_ATTRIBUTES_HPP

max_stars_repo_name: hongyuanjia/OpenStudio

max_issues_repo_name: hongyuanjia/OpenStudio

max_forks_repo_name: hongyuanjia/OpenStudio