<commit_before>/*
 * This is part of the FL library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2014 Jan Issac (jan.issac@gmail.com)
 * Copyright (c) 2014 Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 * Max-Planck Institute for Intelligent Systems, AMD Lab
 * University of Southern California, CLMC Lab
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file gaussian_filter_kf.hpp
 * \date October 2014
 * \author Jan Issac (jan.issac@gmail.com)
 */

#ifndef FL__FILTER__GAUSSIAN__GAUSSIAN_FILTER_KF_HPP
#define FL__FILTER__GAUSSIAN__GAUSSIAN_FILTER_KF_HPP

#include <utility>

#include <fl/util/meta.hpp>
#include <fl/util/traits.hpp>
#include <fl/util/profiling.hpp>

#include <fl/exception/exception.hpp>
#include <fl/filter/filter_interface.hpp>

#include <fl/model/process/linear_state_transition_model.hpp>
#include <fl/model/observation/linear_gaussian_observation_model.hpp>

namespace fl
{

/**
 * \defgroup linear_gaussian_filter Linear Gaussian Filter (Kalman Filter)
 * \ingroup filters
 */

// Gaussian filter forward declaration
template <typename...> class GaussianFilter;

/**
 * \internal
 * \ingroup linear_gaussian_filter
 *
 * Traits of the Linear GaussianFilter (KalmanFilter)
 */
template <typename LinearStateTransitionModel, typename LinearObservationModel>
struct Traits<
           GaussianFilter<
               LinearStateTransitionModel,
               LinearObservationModel>>
{
    typedef typename LinearStateTransitionModel::State State;
    typedef typename LinearStateTransitionModel::Input Input;
    typedef typename LinearObservationModel::Obsrv Obsrv;
    typedef Gaussian<State> Belief;
};

/**
 * \ingroup linear_gaussian_filter
 *
 * \brief GaussianFilter resembles the Kalman filter.
 *
 * \tparam State  State type defining the state space
 * \tparam Input  Process model input type
 * \tparam Obsrv  Observation type of the linear observation Gaussian model
 *
 * The KalmanFilter type is represented by the GaussianFilter using
 * the linear Gaussian Models.
 *
 */
template <
    typename LinearStateTransitionModel,
    typename LinearObservationModel
>
class GaussianFilter<
          LinearStateTransitionModel,
          LinearObservationModel>
    :
    /* Implement the conceptual filter interface */
    public
    FilterInterface<
        GaussianFilter<
            typename ForwardLinearModelOnly<LinearStateTransitionModel>::Type,
            typename ForwardLinearModelOnly<LinearObservationModel>::Type>>
{
public:
    typedef typename LinearStateTransitionModel::State State;
    typedef typename LinearStateTransitionModel::Input Input;
    typedef typename LinearObservationModel::Obsrv Obsrv;

    /**
     * \brief Represents the underlying distribution of the estimated state.
     * In the case of the Kalman filter, the distribution is a simple Gaussian
     * with the dimension of the \c State
     */
    typedef Gaussian<State> Belief;

public:
    /**
     * Creates a linear Gaussian filter (a KalmanFilter)
     *
     * \param process_model         Process model instance
     * \param obsrv_model           Obsrv model instance
     */
    GaussianFilter(const LinearStateTransitionModel& process_model,
                   const LinearObservationModel& obsrv_model)
        : process_model_(process_model),
          obsrv_model_(obsrv_model)
    { }

    /**
     * \brief Overridable default destructor
     */
    virtual ~GaussianFilter() { }

    /**
     * \copydoc FilterInterface::predict
     *
     * KalmanFilter prediction step
     *
     * Given the following matrices
     *
     *  - \f$ A \f$: Dynamics Matrix
     *  - \f$ B \f$: Dynamics Noise Covariance
     *
     * and the current state distribution
     * \f$ {\cal N}(x_t\mid \hat{x}_t, \hat{\Sigma}_{t}) \f$,
     *
     * the prediction steps is the discrete linear mapping
     *
     * \f$ \bar{x}_{t} =  A \hat{x}_t\f$ and
     *
     * \f$ \bar{\Sigma}_{t} = A\hat{\Sigma}_{t}A^T + Q \f$
     */
    virtual void predict(const Belief& prior_belief,
                         const Input& input,
                         Belief& predicted_belief)
    {
        auto A = process_model_.dynamics_matrix();
        auto B = process_model_.input_matrix();
        auto Q = process_model_.noise_covariance();

        predicted_belief.mean(
            A * prior_belief.mean() + B * input);

        predicted_belief.covariance(
            A * prior_belief.covariance() * A.transpose() + Q);
    }

    /**
     * \copydoc FilterInterface::update
     *
     * Given the following matrices
     *
     *  - \f$ H \f$: Sensor Matrix
     *  - \f$ R \f$: Sensor Noise Covariance
     *
     * and the current predicted state distribution
     * \f$ {\cal N}(x_t\mid \bar{x}_t, \bar{\Sigma}_{t}) \f$,
     *
     * the update steps is the discrete linear mapping
     *
     * \f$ \hat{x}_{t+1} =  \bar{x}_t + K (y - H\bar{x}_t)\f$ and
     *
     * \f$ \hat{\Sigma}_{t} = (I - KH) \bar{\Sigma}_{t}\f$
     *
     * with the KalmanGain
     *
     * \f$ K = \bar{\Sigma}_{t}H^T (H\bar{\Sigma}_{t}H^T+R)^{-1}\f$.
     */
    virtual void update(const Belief& predicted_belief,
                        const Obsrv& y,
                        Belief& posterior_belief)
    {
        auto H = obsrv_model_.sensor_matrix();
        auto R = obsrv_model_.noise_covariance();

        auto&& mean = predicted_belief.mean();
        auto&& cov_xx = predicted_belief.covariance();

        auto S = (H * cov_xx * H.transpose() + R).eval();
        auto K = (cov_xx * H.transpose() * S.inverse()).eval();

        posterior_belief.mean(mean + K * (y - H * mean));
        posterior_belief.covariance(cov_xx - K * H * cov_xx);
    }

    virtual Belief create_belief() const
    {
        auto belief = Belief(process_model().state_dimension());
        return belief;
    }

    virtual std::string name() const
    {
        return "GaussianFilter<"
                + this->list_arguments(
                            process_model().name(),
                            obsrv_model().name())
                + ">";
    }

    virtual std::string description() const
    {
        return "Linear Gaussian filter (the Kalman Filter) with"
                + this->list_descriptions(
                            process_model().description(),
                            obsrv_model().description());
    }

    LinearStateTransitionModel& process_model() { return process_model_; }
    LinearObservationModel& obsrv_model() { return obsrv_model_; }
    const LinearStateTransitionModel& process_model() const { return process_model_; }
    const LinearObservationModel& obsrv_model() const { return obsrv_model_; }

protected:
    /** \cond internal */
    LinearStateTransitionModel process_model_;
    LinearObservationModel obsrv_model_;
    /** \endcond */
};

}

#endif
<commit_msg>pure auto for the moment<commit_after>/*
 * This is part of the FL library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2014 Jan Issac (jan.issac@gmail.com)
 * Copyright (c) 2014 Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 * Max-Planck Institute for Intelligent Systems, AMD Lab
 * University of Southern California, CLMC Lab
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file gaussian_filter_kf.hpp
 * \date October 2014
 * \author Jan Issac (jan.issac@gmail.com)
 */

#ifndef FL__FILTER__GAUSSIAN__GAUSSIAN_FILTER_KF_HPP
#define FL__FILTER__GAUSSIAN__GAUSSIAN_FILTER_KF_HPP

#include <utility>

#include <fl/util/meta.hpp>
#include <fl/util/traits.hpp>
#include <fl/util/profiling.hpp>

#include <fl/exception/exception.hpp>
#include <fl/filter/filter_interface.hpp>

#include <fl/model/process/linear_state_transition_model.hpp>
#include <fl/model/observation/linear_gaussian_observation_model.hpp>

namespace fl
{

/**
 * \defgroup linear_gaussian_filter Linear Gaussian Filter (Kalman Filter)
 * \ingroup filters
 */

// Gaussian filter forward declaration
template <typename...> class GaussianFilter;

/**
 * \internal
 * \ingroup linear_gaussian_filter
 *
 * Traits of the Linear GaussianFilter (KalmanFilter)
 */
template <typename LinearStateTransitionModel, typename LinearObservationModel>
struct Traits<
           GaussianFilter<
               LinearStateTransitionModel,
               LinearObservationModel>>
{
    typedef typename LinearStateTransitionModel::State State;
    typedef typename LinearStateTransitionModel::Input Input;
    typedef typename LinearObservationModel::Obsrv Obsrv;
    typedef Gaussian<State> Belief;
};

/**
 * \ingroup linear_gaussian_filter
 *
 * \brief GaussianFilter resembles the Kalman filter.
 *
 * \tparam State  State type defining the state space
 * \tparam Input  Process model input type
 * \tparam Obsrv  Observation type of the linear observation Gaussian model
 *
 * The KalmanFilter type is represented by the GaussianFilter using
 * the linear Gaussian Models.
 *
 */
template <
    typename LinearStateTransitionModel,
    typename LinearObservationModel
>
class GaussianFilter<
          LinearStateTransitionModel,
          LinearObservationModel>
    :
    /* Implement the conceptual filter interface */
    public
    FilterInterface<
        GaussianFilter<
            typename ForwardLinearModelOnly<LinearStateTransitionModel>::Type,
            typename ForwardLinearModelOnly<LinearObservationModel>::Type>>
{
public:
    typedef typename LinearStateTransitionModel::State State;
    typedef typename LinearStateTransitionModel::Input Input;
    typedef typename LinearObservationModel::Obsrv Obsrv;

    /**
     * \brief Represents the underlying distribution of the estimated state.
     * In the case of the Kalman filter, the distribution is a simple Gaussian
     * with the dimension of the \c State
     */
    typedef Gaussian<State> Belief;

public:
    /**
     * Creates a linear Gaussian filter (a KalmanFilter)
     *
     * \param process_model         Process model instance
     * \param obsrv_model           Obsrv model instance
     */
    GaussianFilter(const LinearStateTransitionModel& process_model,
                   const LinearObservationModel& obsrv_model)
        : process_model_(process_model),
          obsrv_model_(obsrv_model)
    { }

    /**
     * \brief Overridable default destructor
     */
    virtual ~GaussianFilter() { }

    /**
     * \copydoc FilterInterface::predict
     *
     * KalmanFilter prediction step
     *
     * Given the following matrices
     *
     *  - \f$ A \f$: Dynamics Matrix
     *  - \f$ B \f$: Dynamics Noise Covariance
     *
     * and the current state distribution
     * \f$ {\cal N}(x_t\mid \hat{x}_t, \hat{\Sigma}_{t}) \f$,
     *
     * the prediction steps is the discrete linear mapping
     *
     * \f$ \bar{x}_{t} =  A \hat{x}_t\f$ and
     *
     * \f$ \bar{\Sigma}_{t} = A\hat{\Sigma}_{t}A^T + Q \f$
     */
    virtual void predict(const Belief& prior_belief,
                         const Input& input,
                         Belief& predicted_belief)
    {
        auto A = process_model_.dynamics_matrix();
        auto B = process_model_.input_matrix();
        auto Q = process_model_.noise_covariance();

        predicted_belief.mean(
            A * prior_belief.mean() + B * input);

        predicted_belief.covariance(
            A * prior_belief.covariance() * A.transpose() + Q);
    }

    /**
     * \copydoc FilterInterface::update
     *
     * Given the following matrices
     *
     *  - \f$ H \f$: Sensor Matrix
     *  - \f$ R \f$: Sensor Noise Covariance
     *
     * and the current predicted state distribution
     * \f$ {\cal N}(x_t\mid \bar{x}_t, \bar{\Sigma}_{t}) \f$,
     *
     * the update steps is the discrete linear mapping
     *
     * \f$ \hat{x}_{t+1} =  \bar{x}_t + K (y - H\bar{x}_t)\f$ and
     *
     * \f$ \hat{\Sigma}_{t} = (I - KH) \bar{\Sigma}_{t}\f$
     *
     * with the KalmanGain
     *
     * \f$ K = \bar{\Sigma}_{t}H^T (H\bar{\Sigma}_{t}H^T+R)^{-1}\f$.
     */
    virtual void update(const Belief& predicted_belief,
                        const Obsrv& y,
                        Belief& posterior_belief)
    {
        auto H = obsrv_model_.sensor_matrix();
        auto R = obsrv_model_.noise_covariance();

        auto mean = predicted_belief.mean();
        auto cov_xx = predicted_belief.covariance();

        auto S = (H * cov_xx * H.transpose() + R).eval();
        auto K = (cov_xx * H.transpose() * S.inverse()).eval();

//        PV(cov_xx);
//        PV(K);
//        PV((y - H * mean));

        posterior_belief.mean(mean + K * (y - H * mean));
        posterior_belief.covariance(cov_xx - K * H * cov_xx);
    }

    virtual Belief create_belief() const
    {
        auto belief = Belief(process_model().state_dimension());
        return belief;
    }

    virtual std::string name() const
    {
        return "GaussianFilter<"
                + this->list_arguments(
                            process_model().name(),
                            obsrv_model().name())
                + ">";
    }

    virtual std::string description() const
    {
        return "Linear Gaussian filter (the Kalman Filter) with"
                + this->list_descriptions(
                            process_model().description(),
                            obsrv_model().description());
    }

    LinearStateTransitionModel& process_model() { return process_model_; }
    LinearObservationModel& obsrv_model() { return obsrv_model_; }
    const LinearStateTransitionModel& process_model() const { return process_model_; }
    const LinearObservationModel& obsrv_model() const { return obsrv_model_; }

protected:
    /** \cond internal */
    LinearStateTransitionModel process_model_;
    LinearObservationModel obsrv_model_;
    /** \endcond */
};

}

#endif
<|endoftext|>


<commit_before>/** \file    create_match_db.cc
 *  \brief   Creates mapping databases from normalised author names and titles to control numbers.
 *  \author  Dr. Johannes Ruscheinski
 *
 *  Copyright (C) 2018, Library of the University of Tübingen
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <set>
#include <unordered_set>
#include <cctype>
#include <cstdlib>
#include "DbConnection.h"
#include "Compiler.h"
#include "MARC.h"
#include "StringUtil.h"
#include "TextUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " marc_titles\n";
    std::exit(EXIT_FAILURE);
}

std::string NormaliseAuthorName(std::string author_name) {
    author_name = StringUtil::TrimWhite(author_name);
    const auto comma_pos(author_name.find(','));
    if (comma_pos != std::string::npos)
        author_name = StringUtil::TrimWhite(author_name.substr(comma_pos + 1) + " " + author_name.substr(0, comma_pos));

    std::string normalised_author_name;
    bool space_seen(false);
    for (const char ch : author_name) {
        switch (ch) {
        case '.':
            normalised_author_name += ch;
            normalised_author_name += ' ';
            space_seen = true;
            break;
        case ' ':
            if (not space_seen)
                normalised_author_name += ' ';
            space_seen = true;
            break;
        default:
            normalised_author_name += ch;
            space_seen = false;
        }
    }

    return normalised_author_name;
}

void ExtractAuthors(const MARC::Record &record, const std::string &tag, std::unordered_set<std::string> * const normlised_author_names) {
    for (const auto &field : record.getTagRange(tag)) {
        for (const auto &subfield : field.getSubfields()) {
            if (subfield.code_ == 'a')
                normlised_author_names->emplace(TextUtil::UTF8ToLower(NormaliseAuthorName(subfield.value_)));
        }
    }
}

std::string NormaliseTitle(const std::string &title) {
    std::string normalised_title;
    bool space_seen(false);
    for (const char ch : title) {
        if (std::ispunct(ch) or std::isspace(ch)) {
            if (not space_seen)
                normalised_title += ' ';
            space_seen = true;
        } else {
            space_seen = false;
            normalised_title += ch;
        }
    }

    return StringUtil::TrimWhite(&normalised_title);
}

void ExtractAllAuthors(const MARC::Record &record, std::unordered_set<std::string> * const normalised_author_names) {
    ExtractAuthors(record, "100", normalised_author_names);
    ExtractAuthors(record, "700", normalised_author_names);
}

void PopulateTables(DbConnection * const db_connection, MARC::Reader * const reader) {
    unsigned count(0);
    std::unordered_set<std::string> already_seen_control_numbers;
    while (const auto record = reader->read()) {
        const auto control_number(record.getControlNumber());
        if (already_seen_control_numbers.find(control_number) != already_seen_control_numbers.cend()) {
            LOG_WARNING("Duplicate control number: " + control_number);
            continue;
        }
        already_seen_control_numbers.emplace(control_number);

        std::unordered_set<std::string> normalised_author_names;
        ExtractAllAuthors(record, &normalised_author_names);
        for (const auto normalised_author_name : normalised_author_names)
            db_connection->queryOrDie("INSERT INTO normalised_authors SET author='" + db_connection->escapeString(normalised_author_name)
                                      + "', ppn='" + control_number + "'");

        const auto normalised_title(TextUtil::UTF8ToLower(NormaliseTitle(record.getFirstFieldContents("245"))));
        if (likely(not normalised_title.empty()))
            db_connection->queryOrDie("INSERT INTO normalised_titles SET title='" + db_connection->escapeString(normalised_title)
                                      + "', ppn='" + control_number + "'");
    }

    LOG_INFO("Processed " + std::to_string(count) + " records.");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 2)
        Usage();

    auto reader(MARC::Reader::Factory(argv[1]));
    DbConnection ub_tools_db;
    ub_tools_db.queryOrDie("TRUNCATE TABLE normalised_authors");
    ub_tools_db.queryOrDie("TRUNCATE TABLE normalised_titles");
    PopulateTables(&ub_tools_db, reader.get());

    return EXIT_SUCCESS;
}
<commit_msg>Fixed title handling.<commit_after>/** \file    create_match_db.cc
 *  \brief   Creates mapping databases from normalised author names and titles to control numbers.
 *  \author  Dr. Johannes Ruscheinski
 *
 *  Copyright (C) 2018, Library of the University of Tübingen
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <set>
#include <unordered_set>
#include <cctype>
#include <cstdlib>
#include "DbConnection.h"
#include "Compiler.h"
#include "MARC.h"
#include "StringUtil.h"
#include "TextUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " marc_titles\n";
    std::exit(EXIT_FAILURE);
}

std::string NormaliseAuthorName(std::string author_name) {
    author_name = StringUtil::TrimWhite(author_name);
    const auto comma_pos(author_name.find(','));
    if (comma_pos != std::string::npos)
        author_name = StringUtil::TrimWhite(author_name.substr(comma_pos + 1) + " " + author_name.substr(0, comma_pos));

    std::string normalised_author_name;
    bool space_seen(false);
    for (const char ch : author_name) {
        switch (ch) {
        case '.':
            normalised_author_name += ch;
            normalised_author_name += ' ';
            space_seen = true;
            break;
        case ' ':
            if (not space_seen)
                normalised_author_name += ' ';
            space_seen = true;
            break;
        default:
            normalised_author_name += ch;
            space_seen = false;
        }
    }

    return normalised_author_name;
}

void ExtractAuthors(const MARC::Record &record, const std::string &tag, std::unordered_set<std::string> * const normlised_author_names) {
    for (const auto &field : record.getTagRange(tag)) {
        for (const auto &subfield : field.getSubfields()) {
            if (subfield.code_ == 'a')
                normlised_author_names->emplace(TextUtil::UTF8ToLower(NormaliseAuthorName(subfield.value_)));
        }
    }
}

std::string NormaliseTitle(const std::string &title) {
    std::string normalised_title;
    bool space_seen(false);
    for (const char ch : title) {
        if (std::ispunct(ch) or std::isspace(ch)) {
            if (not space_seen)
                normalised_title += ' ';
            space_seen = true;
        } else {
            space_seen = false;
            normalised_title += ch;
        }
    }

    return StringUtil::TrimWhite(&normalised_title);
}

void ExtractAllAuthors(const MARC::Record &record, std::unordered_set<std::string> * const normalised_author_names) {
    ExtractAuthors(record, "100", normalised_author_names);
    ExtractAuthors(record, "700", normalised_author_names);
}

void PopulateTables(DbConnection * const db_connection, MARC::Reader * const reader) {
    unsigned count(0);
    std::unordered_set<std::string> already_seen_control_numbers;
    while (const auto record = reader->read()) {
        const auto control_number(record.getControlNumber());
        if (already_seen_control_numbers.find(control_number) != already_seen_control_numbers.cend()) {
            LOG_WARNING("Duplicate control number: " + control_number);
            continue;
        }
        already_seen_control_numbers.emplace(control_number);

        std::unordered_set<std::string> normalised_author_names;
        ExtractAllAuthors(record, &normalised_author_names);
        for (const auto normalised_author_name : normalised_author_names)
            db_connection->queryOrDie("INSERT INTO normalised_authors SET author='" + db_connection->escapeString(normalised_author_name)
                                      + "', ppn='" + control_number + "'");

        const auto normalised_title(TextUtil::UTF8ToLower(NormaliseTitle(record.getMainTitle())));
        if (unlikely(normalised_title.empty()))
            LOG_WARNING("Empty normalised title in record w/ control number: " + control_number);
        else
            db_connection->queryOrDie("INSERT INTO normalised_titles SET title='" + db_connection->escapeString(normalised_title)
                                      + "', ppn='" + control_number + "'");
    }

    LOG_INFO("Processed " + std::to_string(count) + " records.");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 2)
        Usage();

    auto reader(MARC::Reader::Factory(argv[1]));
    DbConnection ub_tools_db;
    ub_tools_db.queryOrDie("TRUNCATE TABLE normalised_authors");
    ub_tools_db.queryOrDie("TRUNCATE TABLE normalised_titles");
    PopulateTables(&ub_tools_db, reader.get());

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h"

#include <gtk/gtk.h>
#include <algorithm>

namespace {

enum {
  PROP_0,
  PROP_HIDE_CHILD_DIRECTLY
};

struct SizeAllocateData {
  GtkChromeShrinkableHBox* box;
  GtkAllocation* allocation;
  GtkTextDirection direction;
  bool homogeneous;
  int border_width;

  // Maximum child width when |homogeneous| is TRUE.
  int homogeneous_child_width;
};

void CountVisibleChildren(GtkWidget* child, gpointer userdata) {
  if (GTK_WIDGET_VISIBLE(child))
    ++(*reinterpret_cast<int*>(userdata));
}

void SumChildrenWidthRequisition(GtkWidget* child, gpointer userdata) {
  if (GTK_WIDGET_VISIBLE(child)) {
    GtkRequisition req;
    gtk_widget_get_child_requisition(child, &req);
    (*reinterpret_cast<int*>(userdata)) += std::max(req.width, 0);
  }
}

void ShowInvisibleChildren(GtkWidget* child, gpointer userdata) {
  if (!GTK_WIDGET_VISIBLE(child)) {
    gtk_widget_show(child);
    ++(*reinterpret_cast<int*>(userdata));
  }
}

void ChildSizeAllocate(GtkWidget* child, gpointer userdata) {
  if (!GTK_WIDGET_VISIBLE(child))
    return;

  SizeAllocateData* data = reinterpret_cast<SizeAllocateData*>(userdata);
  GtkAllocation child_allocation = child->allocation;

  if (data->homogeneous) {
    // Make sure the child is not overlapped with others' boundary.
    if (child_allocation.width > data->homogeneous_child_width) {
      child_allocation.x +=
          (child_allocation.width - data->homogeneous_child_width) / 2;
      child_allocation.width = data->homogeneous_child_width;
    }
  } else {
    guint padding;
    GtkPackType pack_type;
    gtk_box_query_child_packing(GTK_BOX(data->box), child, NULL, NULL,
                                &padding, &pack_type);

    if ((data->direction == GTK_TEXT_DIR_RTL && pack_type == GTK_PACK_START) ||
        (data->direction != GTK_TEXT_DIR_RTL && pack_type == GTK_PACK_END)) {
      // All children are right aligned, so make sure the child won't overflow
      // its parent's left edge.
      int overflow = (data->allocation->x + data->border_width + padding -
                      child_allocation.x);
      if (overflow > 0) {
        child_allocation.width -= overflow;
        child_allocation.x += overflow;
      }
    } else {
      // All children are left aligned, so make sure the child won't overflow
      // its parent's right edge.
      int overflow = (child_allocation.x + child_allocation.width + padding -
          (data->allocation->x + data->allocation->width - data->border_width));
      if (overflow > 0)
        child_allocation.width -= overflow;
    }
  }

  if (child_allocation.width != child->allocation.width) {
    if (data->box->hide_child_directly || child_allocation.width <= 1)
      gtk_widget_hide(child);
    else
      gtk_widget_size_allocate(child, &child_allocation);
  }
}

}  // namespace

G_BEGIN_DECLS

static void gtk_chrome_shrinkable_hbox_set_property(GObject* object,
                                             guint prop_id,
                                             const GValue* value,
                                             GParamSpec* pspec);
static void gtk_chrome_shrinkable_hbox_get_property(GObject* object,
                                             guint prop_id,
                                             GValue* value,
                                             GParamSpec* pspec);
static void gtk_chrome_shrinkable_hbox_size_allocate(GtkWidget* widget,
                                              GtkAllocation* allocation);

G_DEFINE_TYPE(GtkChromeShrinkableHBox, gtk_chrome_shrinkable_hbox,
              GTK_TYPE_HBOX)

static void gtk_chrome_shrinkable_hbox_class_init(
    GtkChromeShrinkableHBoxClass *klass) {
  GObjectClass* object_class = G_OBJECT_CLASS(klass);
  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);

  object_class->set_property = gtk_chrome_shrinkable_hbox_set_property;
  object_class->get_property = gtk_chrome_shrinkable_hbox_get_property;

  widget_class->size_allocate = gtk_chrome_shrinkable_hbox_size_allocate;

  g_object_class_install_property(object_class, PROP_HIDE_CHILD_DIRECTLY,
      g_param_spec_boolean("hide-child-directly",
                           "Hide child directly",
                           "Whether the children should be hid directly, "
                           "if there is no enough space in its parent",
                           FALSE,
                           static_cast<GParamFlags>(
                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
}

static void gtk_chrome_shrinkable_hbox_init(GtkChromeShrinkableHBox* box) {
  box->hide_child_directly = FALSE;
  box->children_width_requisition = 0;
}

static void gtk_chrome_shrinkable_hbox_set_property(GObject* object,
                                                    guint prop_id,
                                                    const GValue* value,
                                                    GParamSpec* pspec) {
  GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(object);

  switch (prop_id) {
    case PROP_HIDE_CHILD_DIRECTLY:
      gtk_chrome_shrinkable_hbox_set_hide_child_directly(
          box, g_value_get_boolean(value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void gtk_chrome_shrinkable_hbox_get_property(GObject* object,
                                                    guint prop_id,
                                                    GValue* value,
                                                    GParamSpec* pspec) {
  GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(object);

  switch (prop_id) {
    case PROP_HIDE_CHILD_DIRECTLY:
      g_value_set_boolean(value, box->hide_child_directly);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void gtk_chrome_shrinkable_hbox_size_allocate(
    GtkWidget* widget, GtkAllocation* allocation) {
  GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(widget);
  gint children_width_requisition = 0;
  gtk_container_foreach(GTK_CONTAINER(widget), SumChildrenWidthRequisition,
                        &children_width_requisition);

  // If we are allocated to more width or some children are removed or shrunk,
  // then we need to show all invisible children before calling parent class's
  // size_allocate method, because the new width may be enough to show those
  // hidden children.
  if (widget->allocation.width < allocation->width ||
      box->children_width_requisition > children_width_requisition) {
    gint count = 0;
    gtk_container_foreach(GTK_CONTAINER(widget), ShowInvisibleChildren, &count);

    // If there were any invisible children, showing them will trigger another
    // allocate, so we can just return here.
    if (count > 0) return;
  }

  // Let the parent class do size allocation first. After that all children will
  // be allocated with reasonable position and size according to their size
  // request.
  (GTK_WIDGET_CLASS(gtk_chrome_shrinkable_hbox_parent_class)->size_allocate)
      (widget, allocation);

  gint visible_children_count =
      gtk_chrome_shrinkable_hbox_get_visible_child_count(
          GTK_CHROME_SHRINKABLE_HBOX(widget));

  box->children_width_requisition = 0;
  if (visible_children_count == 0)
    return;

  SizeAllocateData data;
  data.box = GTK_CHROME_SHRINKABLE_HBOX(widget);
  data.allocation = allocation;
  data.direction = gtk_widget_get_direction(widget);
  data.homogeneous = gtk_box_get_homogeneous(GTK_BOX(widget));
  data.border_width = gtk_container_get_border_width(GTK_CONTAINER(widget));
  data.homogeneous_child_width =
      (allocation->width - data.border_width * 2 -
       (visible_children_count - 1) * gtk_box_get_spacing(GTK_BOX(widget))) /
      visible_children_count;

  // Shrink or hide children if necessary.
  gtk_container_foreach(GTK_CONTAINER(widget), ChildSizeAllocate, &data);

  // Record current width requisition of visible children, so we can know if
  // it's necessary to show invisible children next time.
  gtk_container_foreach(GTK_CONTAINER(widget), SumChildrenWidthRequisition,
                        &box->children_width_requisition);
}

GtkWidget* gtk_chrome_shrinkable_hbox_new(gboolean hide_child_directly,
                                          gboolean homogeneous,
                                          gint spacing) {
  return GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_SHRINKABLE_HBOX,
                                 "hide-child-directly", hide_child_directly,
                                 "homogeneous", homogeneous,
                                 "spacing", spacing,
                                 NULL));
}

void gtk_chrome_shrinkable_hbox_set_hide_child_directly(
    GtkChromeShrinkableHBox* box, gboolean hide_child_directly) {
  g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box));

  if (hide_child_directly != box->hide_child_directly) {
    box->hide_child_directly = hide_child_directly;
    g_object_notify(G_OBJECT(box), "hide-child-directly");
    gtk_widget_queue_resize(GTK_WIDGET(box));
  }
}

gboolean gtk_chrome_shrinkable_hbox_get_hide_child_directly(
    GtkChromeShrinkableHBox* box) {
  g_return_val_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box), FALSE);

  return box->hide_child_directly;
}

void gtk_chrome_shrinkable_hbox_pack_start(GtkChromeShrinkableHBox* box,
                                           GtkWidget* child,
                                           guint padding) {
  g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box));
  g_return_if_fail(GTK_IS_WIDGET(child));

  gtk_box_pack_start(GTK_BOX(box), child, FALSE, FALSE, 0);
}

void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox* box,
                                         GtkWidget* child,
                                         guint padding) {
  g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box));
  g_return_if_fail(GTK_IS_WIDGET(child));

  gtk_box_pack_end(GTK_BOX(box), child, FALSE, FALSE, 0);
}

gint gtk_chrome_shrinkable_hbox_get_visible_child_count(
    GtkChromeShrinkableHBox* box) {
  gint visible_children_count = 0;
  gtk_container_foreach(GTK_CONTAINER(box), CountVisibleChildren,
                        &visible_children_count);
  return visible_children_count;
}

G_END_DECLS
<commit_msg>[Linux]Fix a redraw issue when expanding a GtkChromeShrinkableHBox.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h"

#include <gtk/gtk.h>
#include <algorithm>

namespace {

enum {
  PROP_0,
  PROP_HIDE_CHILD_DIRECTLY
};

struct SizeAllocateData {
  GtkChromeShrinkableHBox* box;
  GtkAllocation* allocation;
  GtkTextDirection direction;
  bool homogeneous;
  int border_width;

  // Maximum child width when |homogeneous| is TRUE.
  int homogeneous_child_width;
};

void CountVisibleChildren(GtkWidget* child, gpointer userdata) {
  if (GTK_WIDGET_VISIBLE(child))
    ++(*reinterpret_cast<int*>(userdata));
}

void SumChildrenWidthRequisition(GtkWidget* child, gpointer userdata) {
  if (GTK_WIDGET_VISIBLE(child)) {
    GtkRequisition req;
    gtk_widget_get_child_requisition(child, &req);
    (*reinterpret_cast<int*>(userdata)) += std::max(req.width, 0);
  }
}

void ShowInvisibleChildren(GtkWidget* child, gpointer userdata) {
  if (!GTK_WIDGET_VISIBLE(child)) {
    gtk_widget_show(child);
    ++(*reinterpret_cast<int*>(userdata));
  }
}

void ChildSizeAllocate(GtkWidget* child, gpointer userdata) {
  if (!GTK_WIDGET_VISIBLE(child))
    return;

  SizeAllocateData* data = reinterpret_cast<SizeAllocateData*>(userdata);
  GtkAllocation child_allocation = child->allocation;

  if (data->homogeneous) {
    // Make sure the child is not overlapped with others' boundary.
    if (child_allocation.width > data->homogeneous_child_width) {
      child_allocation.x +=
          (child_allocation.width - data->homogeneous_child_width) / 2;
      child_allocation.width = data->homogeneous_child_width;
    }
  } else {
    guint padding;
    GtkPackType pack_type;
    gtk_box_query_child_packing(GTK_BOX(data->box), child, NULL, NULL,
                                &padding, &pack_type);

    if ((data->direction == GTK_TEXT_DIR_RTL && pack_type == GTK_PACK_START) ||
        (data->direction != GTK_TEXT_DIR_RTL && pack_type == GTK_PACK_END)) {
      // All children are right aligned, so make sure the child won't overflow
      // its parent's left edge.
      int overflow = (data->allocation->x + data->border_width + padding -
                      child_allocation.x);
      if (overflow > 0) {
        child_allocation.width -= overflow;
        child_allocation.x += overflow;
      }
    } else {
      // All children are left aligned, so make sure the child won't overflow
      // its parent's right edge.
      int overflow = (child_allocation.x + child_allocation.width + padding -
          (data->allocation->x + data->allocation->width - data->border_width));
      if (overflow > 0)
        child_allocation.width -= overflow;
    }
  }

  if (child_allocation.width != child->allocation.width) {
    if (data->box->hide_child_directly || child_allocation.width <= 1)
      gtk_widget_hide(child);
    else
      gtk_widget_size_allocate(child, &child_allocation);
  }
}

}  // namespace

G_BEGIN_DECLS

static void gtk_chrome_shrinkable_hbox_set_property(GObject* object,
                                             guint prop_id,
                                             const GValue* value,
                                             GParamSpec* pspec);
static void gtk_chrome_shrinkable_hbox_get_property(GObject* object,
                                             guint prop_id,
                                             GValue* value,
                                             GParamSpec* pspec);
static void gtk_chrome_shrinkable_hbox_size_allocate(GtkWidget* widget,
                                              GtkAllocation* allocation);

G_DEFINE_TYPE(GtkChromeShrinkableHBox, gtk_chrome_shrinkable_hbox,
              GTK_TYPE_HBOX)

static void gtk_chrome_shrinkable_hbox_class_init(
    GtkChromeShrinkableHBoxClass *klass) {
  GObjectClass* object_class = G_OBJECT_CLASS(klass);
  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);

  object_class->set_property = gtk_chrome_shrinkable_hbox_set_property;
  object_class->get_property = gtk_chrome_shrinkable_hbox_get_property;

  widget_class->size_allocate = gtk_chrome_shrinkable_hbox_size_allocate;

  g_object_class_install_property(object_class, PROP_HIDE_CHILD_DIRECTLY,
      g_param_spec_boolean("hide-child-directly",
                           "Hide child directly",
                           "Whether the children should be hid directly, "
                           "if there is no enough space in its parent",
                           FALSE,
                           static_cast<GParamFlags>(
                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
}

static void gtk_chrome_shrinkable_hbox_init(GtkChromeShrinkableHBox* box) {
  box->hide_child_directly = FALSE;
  box->children_width_requisition = 0;
}

static void gtk_chrome_shrinkable_hbox_set_property(GObject* object,
                                                    guint prop_id,
                                                    const GValue* value,
                                                    GParamSpec* pspec) {
  GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(object);

  switch (prop_id) {
    case PROP_HIDE_CHILD_DIRECTLY:
      gtk_chrome_shrinkable_hbox_set_hide_child_directly(
          box, g_value_get_boolean(value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void gtk_chrome_shrinkable_hbox_get_property(GObject* object,
                                                    guint prop_id,
                                                    GValue* value,
                                                    GParamSpec* pspec) {
  GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(object);

  switch (prop_id) {
    case PROP_HIDE_CHILD_DIRECTLY:
      g_value_set_boolean(value, box->hide_child_directly);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void gtk_chrome_shrinkable_hbox_size_allocate(
    GtkWidget* widget, GtkAllocation* allocation) {
  GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(widget);
  gint children_width_requisition = 0;
  gtk_container_foreach(GTK_CONTAINER(widget), SumChildrenWidthRequisition,
                        &children_width_requisition);

  // If we are allocated to more width or some children are removed or shrunk,
  // then we need to show all invisible children before calling parent class's
  // size_allocate method, because the new width may be enough to show those
  // hidden children.
  if (widget->allocation.width < allocation->width ||
      box->children_width_requisition > children_width_requisition) {
    gtk_container_foreach(GTK_CONTAINER(widget),
                          reinterpret_cast<GtkCallback>(gtk_widget_show), NULL);

    // If there were any invisible children, showing them will trigger another
    // allocate. But we still need to go through the size allocate process
    // in this iteration, otherwise before the next allocate iteration, the
    // children may be redrawn on the screen with incorrect size allocation.
  }

  // Let the parent class do size allocation first. After that all children will
  // be allocated with reasonable position and size according to their size
  // request.
  (GTK_WIDGET_CLASS(gtk_chrome_shrinkable_hbox_parent_class)->size_allocate)
      (widget, allocation);

  gint visible_children_count =
      gtk_chrome_shrinkable_hbox_get_visible_child_count(
          GTK_CHROME_SHRINKABLE_HBOX(widget));

  box->children_width_requisition = 0;
  if (visible_children_count == 0)
    return;

  SizeAllocateData data;
  data.box = GTK_CHROME_SHRINKABLE_HBOX(widget);
  data.allocation = allocation;
  data.direction = gtk_widget_get_direction(widget);
  data.homogeneous = gtk_box_get_homogeneous(GTK_BOX(widget));
  data.border_width = gtk_container_get_border_width(GTK_CONTAINER(widget));
  data.homogeneous_child_width =
      (allocation->width - data.border_width * 2 -
       (visible_children_count - 1) * gtk_box_get_spacing(GTK_BOX(widget))) /
      visible_children_count;

  // Shrink or hide children if necessary.
  gtk_container_foreach(GTK_CONTAINER(widget), ChildSizeAllocate, &data);

  // Record current width requisition of visible children, so we can know if
  // it's necessary to show invisible children next time.
  gtk_container_foreach(GTK_CONTAINER(widget), SumChildrenWidthRequisition,
                        &box->children_width_requisition);
}

GtkWidget* gtk_chrome_shrinkable_hbox_new(gboolean hide_child_directly,
                                          gboolean homogeneous,
                                          gint spacing) {
  return GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_SHRINKABLE_HBOX,
                                 "hide-child-directly", hide_child_directly,
                                 "homogeneous", homogeneous,
                                 "spacing", spacing,
                                 NULL));
}

void gtk_chrome_shrinkable_hbox_set_hide_child_directly(
    GtkChromeShrinkableHBox* box, gboolean hide_child_directly) {
  g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box));

  if (hide_child_directly != box->hide_child_directly) {
    box->hide_child_directly = hide_child_directly;
    g_object_notify(G_OBJECT(box), "hide-child-directly");
    gtk_widget_queue_resize(GTK_WIDGET(box));
  }
}

gboolean gtk_chrome_shrinkable_hbox_get_hide_child_directly(
    GtkChromeShrinkableHBox* box) {
  g_return_val_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box), FALSE);

  return box->hide_child_directly;
}

void gtk_chrome_shrinkable_hbox_pack_start(GtkChromeShrinkableHBox* box,
                                           GtkWidget* child,
                                           guint padding) {
  g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box));
  g_return_if_fail(GTK_IS_WIDGET(child));

  gtk_box_pack_start(GTK_BOX(box), child, FALSE, FALSE, 0);
}

void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox* box,
                                         GtkWidget* child,
                                         guint padding) {
  g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box));
  g_return_if_fail(GTK_IS_WIDGET(child));

  gtk_box_pack_end(GTK_BOX(box), child, FALSE, FALSE, 0);
}

gint gtk_chrome_shrinkable_hbox_get_visible_child_count(
    GtkChromeShrinkableHBox* box) {
  gint visible_children_count = 0;
  gtk_container_foreach(GTK_CONTAINER(box), CountVisibleChildren,
                        &visible_children_count);
  return visible_children_count;
}

G_END_DECLS
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2009-2010 Digital Bazaar, Inc. All rights reserved.
 */
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS

#include "bitmunk/node/BtpServer.h"

#include "bitmunk/common/Tools.h"
#include "bitmunk/node/CertificateCreator.h"
#include "monarch/crypto/AsymmetricKeyFactory.h"
#include "monarch/data/json/JsonWriter.h"
#include "monarch/net/NullSocketDataPresenter.h"
#include "monarch/net/SslSocketDataPresenter.h"

using namespace std;
using namespace monarch::config;
using namespace monarch::crypto;
using namespace monarch::data::json;
using namespace monarch::event;
using namespace monarch::http;
using namespace monarch::io;
using namespace monarch::net;
using namespace monarch::rt;
using namespace monarch::util;
using namespace bitmunk::common;
using namespace bitmunk::protocol;
using namespace bitmunk::node;

BtpServer::BtpServer(Node* node) :
mNode(node),
mHostAddress(NULL),
mSslContext(NULL),
mSocketDataPresenterList(NULL),
mServiceId(Server::sInvalidServiceId)
{
}

BtpServer::~BtpServer()
{
}

bool BtpServer::initialize(Config& cfg)
{
   bool rval = true;

   // create SSL server context ("TLS" is most secure and recent SSL but we
   // must use "ALL" to handle browsers that hit the api using earlier SSL
   // versions ... TLS is only required for BTP and not all SSL traffic to
   // the BtpServer must be BTP)
   mSslContext = new SslContext("ALL", false);

   // setup certificate file and private key
   File certFile(cfg["sslCertificate"]->getString());
   File pkeyFile(cfg["sslPrivateKey"]->getString());

   // generate new private key and self-signed certificate if requested
   // and if no private key or certificate file already exists
   if(cfg["sslGenerate"]->getBoolean() &&
      (!pkeyFile->exists() || !certFile->exists()))
   {
      MO_CAT_INFO(BM_NODE_CAT,
         "SSL X.509 self-signed certificate creation requested, creating...");

      CertificateCreator certCreator(mNode);
      rval = certCreator.createCertificate(pkeyFile, certFile);
      if(rval)
      {
         MO_CAT_INFO(BM_NODE_CAT, "SSL X.509 self-signed certificate created.");
      }
   }

   if(rval)
   {
      // set certificate and private key for SSL context
      rval =
         mSslContext->setCertificate(certFile) &&
         mSslContext->setPrivateKey(pkeyFile);
      if(rval)
      {
         // set default virtual host based on certificate common name
         ByteBuffer b(certFile->getLength());
         rval = certFile.readBytes(&b);
         if(rval)
         {
            AsymmetricKeyFactory afk;
            X509CertificateRef cert = afk.loadCertificateFromPem(
               b.data(), b.length());
            rval = !cert.isNull();
            if(rval)
            {
               DynamicObject subject = cert->getSubject();
               string commonName = cert->getField(subject, "CN");
               mSslContext->setVirtualHost(commonName.c_str());
            }
         }
      }
   }

   if(rval)
   {
      // setup host address for node
      const char* host = cfg["host"]->getString();
      uint32_t port = cfg["port"]->getUInt32();
      mHostAddress = new InternetAddress(host, port);

      // handle socket presentation layer that is SSL or non-SSL
      mSocketDataPresenterList = new SocketDataPresenterList(true);
      SslSocketDataPresenter* ssdp =
         new SslSocketDataPresenter(&(*mSslContext));
      NullSocketDataPresenter* nsdp = new NullSocketDataPresenter();
      mSocketDataPresenterList->add(ssdp);
      mSocketDataPresenterList->add(nsdp);

      // get the list of default domains
      if(cfg->hasMember("domains"))
      {
         mDefaultDomains = cfg["domains"].clone();
         if(mDefaultDomains->length() == 0)
         {
            // add wildcard if no domains specified
            mDefaultDomains->append() = "*";
         }
      }
      else
      {
         // no specified default domains, so use "*"
         mDefaultDomains = DynamicObject();
         mDefaultDomains->append() = "*";
      }

      MO_CAT_INFO(BM_NODE_CAT, "Running services on domains: %s",
         JsonWriter::writeToString(mDefaultDomains, false, false).c_str());
   }

   return rval;
}

void BtpServer::cleanup()
{
   // clean up services
   for(DomainMap::iterator i = mServices.begin(); i != mServices.end(); ++i)
   {
      free((char*)i->first);
      delete i->second;
   }

   // clean up
   mHostAddress.setNull();
   mSslContext.setNull();
   mSocketDataPresenterList.setNull();
   mDefaultDomains.setNull();
}

bool BtpServer::start()
{
   bool rval = true;

   // add http connection service
   mServiceId = mNode->getServer()->addConnectionService(
      &(*mHostAddress), &mHttpConnectionServicer,
      &(*mSocketDataPresenterList), "MainHttpService");
   if(mServiceId == Server::sInvalidServiceId)
   {
      // could not add connection service
      rval = false;
   }
   else
   {
      MO_CAT_INFO(BM_NODE_CAT, "Serving on %s",
         mHostAddress->toString(false).c_str());
   }

   return rval;
}

void BtpServer::stop()
{
   // remove http connection service
   if(mServiceId != Server::sInvalidServiceId)
   {
      mNode->getServer()->removePortService(mServiceId);
      mServiceId = Server::sInvalidServiceId;
   }
}

/**
 * Makes sure a btp service is added in both secure and non-secure mode (if
 * specified) or it isn't added at all.
 *
 * @param bs the service to add.
 * @param ssl the ssl mode.
 * @param hcs the servicer to add the service to.
 * @param domain the domain to add the service to.
 *
 * @return true if added, false if not.
 */
static bool _addService(
   BtpServiceRef& bs, Node::SslStatus ssl, HttpConnectionServicer* hcs,
   const char* domain)
{
   bool rval = true;

   // try to add secure service if applicable
   bool added = false;
   if(ssl == Node::SslOn || ssl == Node::SslAny)
   {
      // try to add the service
      rval = hcs->addRequestServicer(&(*bs), true, domain);
      added = true;
   }

   // if success, add non-secure service if applicable
   if(rval && (ssl == Node::SslOff || ssl == Node::SslAny))
   {
      // if non-secure service could not be added, remove the secure one
      // it was added
      if(!hcs->addRequestServicer(&(*bs), false, domain))
      {
         rval = false;
         if(added)
         {
            hcs->removeRequestServicer(&(*bs), true, domain);
         }
      }
   }

   return rval;
}

bool BtpServer::addService(
   BtpServiceRef& service, Node::SslStatus ssl, bool initialize,
   const char* domain)
{
   bool rval = true;

   if(initialize)
   {
      rval = service->initialize();
   }

   if(rval)
   {
      // get the domain list
      DynamicObject dl(NULL);
      if(domain == NULL)
      {
         dl = mDefaultDomains;
      }
      else
      {
         dl = DynamicObject();
         dl->append() = domain;
      }

      mBtpServiceLock.lock();
      {
         // keep track of domains with added services
         DynamicObject added;
         added->setType(Array);

         // for each domain, add service
         BtpServiceMaps* bsm = NULL;
         DynamicObjectIterator dli = dl.getIterator();
         while(rval && dli->hasNext())
         {
            const char* dom = dli->next()->getString();
            DomainMap::iterator di = mServices.find(dom);
            if(di == mServices.end())
            {
               // add a new maps entry
               bsm = new BtpServiceMaps;
               mServices[strdup(dom)] = bsm;
            }
            else
            {
               // use existing maps entry
               bsm = di->second;
            }

            // try to add the service
            rval = _addService(service, ssl, &mHttpConnectionServicer, dom);
            if(rval)
            {
               // added to the given domain
               added->append() = dom;

               // save reference to service
               const char* path = service->getPath();
               if(ssl == Node::SslOn || ssl == Node::SslAny)
               {
                  bsm->secure[path] = service;
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Added SSL BTP service: %s%s", dom, path);
               }
               if(ssl == Node::SslOff || ssl == Node::SslAny)
               {
                  bsm->nonSecure[path] = service;
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Added non-SSL BTP service: %s%s", dom, path);
               }
            }
         }

         // if service didn't add to a domain, remove it from all the
         // ones it was added to
         if(!rval)
         {
            dli = added.getIterator();
            while(dli->hasNext())
            {
               const char* dom = dli->next()->getString();
               removeService(service->getPath(), ssl, false, dom);
            }
         }
      }
      mBtpServiceLock.unlock();

      // failed to add service
      if(!rval)
      {
         if(initialize)
         {
            // clean up service
            service->cleanup();
         }

         // set exception
         ExceptionRef e = new Exception(
            "Could not add btp service.",
            "bitmunk.node.AddBtpServiceFailure");
         Exception::push(e);
      }
   }

   return rval;
}

void BtpServer::removeService(
   const char* path, Node::SslStatus ssl, bool cleanup,
   const char* domain)
{
   // get the domain list
   DynamicObject dl(NULL);
   if(domain == NULL)
   {
      dl = mDefaultDomains;
   }
   else
   {
      dl = DynamicObject();
      dl->append() = domain;
   }

   // build a unique list of services to cleanup
   UniqueList<BtpServiceRef> cleanupList;

   mBtpServiceLock.lock();
   {
      // for each domain, remove service and store it in cleanup list
      DynamicObjectIterator dli = dl.getIterator();
      while(dli->hasNext())
      {
         const char* dom = dli->next()->getString();
         DomainMap::iterator di = mServices.find(dom);
         if(di != mServices.end())
         {
            if(ssl == Node::SslOn || ssl == Node::SslAny)
            {
               BtpServiceMap::iterator i = di->second->secure.find(path);
               if(i != di->second->secure.end())
               {
                  cleanupList.add(i->second);
                  mHttpConnectionServicer.removeRequestServicer(
                     path, true, dom);
                  di->second->secure.erase(i);
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Removed SSL BTP service: %s%s", dom, path);
               }
            }

            if(ssl == Node::SslOff || ssl == Node::SslAny)
            {
               BtpServiceMap::iterator i = di->second->nonSecure.find(path);
               if(i != di->second->nonSecure.end())
               {
                  cleanupList.add(i->second);
                  mHttpConnectionServicer.removeRequestServicer(
                     path, false, dom);
                  di->second->nonSecure.erase(i);
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Removed non-SSL BTP service: %s%s", dom, path);
               }
            }
         }
      }
   }
   mBtpServiceLock.unlock();

   // clean up services
   if(cleanup)
   {
      IteratorRef<BtpServiceRef> i = cleanupList.getIterator();
      while(i->hasNext())
      {
         BtpServiceRef& bs = i->next();
         bs->cleanup();
      }
   }
}

BtpServiceRef BtpServer::getService(
   const char* path, Node::SslStatus ssl, const char* domain)
{
   BtpServiceRef rval;

   // get the domain list
   DynamicObject dl(NULL);
   if(domain == NULL)
   {
      dl = mDefaultDomains;
   }
   else
   {
      dl = DynamicObject();
      dl->append() = domain;
   }
   domain = dl.first()->getString();

   // look in services map for service
   mBtpServiceLock.lock();
   {
      DomainMap::iterator di = mServices.find(domain);
      if(di != mServices.end())
      {
         if(ssl == Node::SslOff || ssl == Node::SslAny)
         {
            BtpServiceMap::iterator i = di->second->nonSecure.find(path);
            if(i != di->second->nonSecure.end())
            {
               rval = i->second;
            }
         }

         if(rval.isNull() && ssl != Node::SslOff)
         {
            BtpServiceMap::iterator i = di->second->secure.find(path);
            if(i != di->second->secure.end())
            {
               rval = i->second;
            }
         }
      }
   }
   mBtpServiceLock.unlock();

   return rval;
}

InternetAddressRef BtpServer::getHostAddress()
{
   return mHostAddress;
}

bool BtpServer::addVirtualHost(ProfileRef& profile)
{
   bool rval = false;

   // FIXME: for future support of client-side certificates, enable peer
   // authentication (pass true instead of false to SslContext constructor)
   // and add same trusted certs used by BtpClient, then add a way to expose
   // the user in the client cert (currently taken from common name) to the
   // application as the bitmunk user ID of the caller, keep in mind agent
   // headers (acting on behalf of another bitmunk user ID) must still be
   // checked

   // create an SSL context for the user's virtual host
   SslContextRef ctx = new SslContext("TLS", false);
   rval =
      ctx->setCertificate(profile->getCertificate()) &&
      ctx->setPrivateKey(profile->getPrivateKey());

   if(rval)
   {
      string name = Tools::getBitmunkUserCommonName(profile->getUserId());
      ctx->setVirtualHost(name.c_str());
      MO_CAT_DEBUG(BM_NODE_CAT, "Adding virtual host '%s'", name.c_str());

      // add the virtual host
      rval = mSslContext->addVirtualHost(ctx);
   }

   return rval;
}

void BtpServer::removeVirtualHost(UserId userId, SslContextRef* ctx)
{
   string name = Tools::getBitmunkUserCommonName(userId);
   MO_CAT_DEBUG(BM_NODE_CAT, "Removing virtual host '%s'", name.c_str());
   mSslContext->removeVirtualHost(name.c_str(), ctx);
}
<commit_msg>Added code to expand cert/pkey paths.<commit_after>/*
 * Copyright (c) 2009-2010 Digital Bazaar, Inc. All rights reserved.
 */
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS

#include "bitmunk/node/BtpServer.h"

#include "bitmunk/common/Tools.h"
#include "bitmunk/node/CertificateCreator.h"
#include "monarch/crypto/AsymmetricKeyFactory.h"
#include "monarch/data/json/JsonWriter.h"
#include "monarch/net/NullSocketDataPresenter.h"
#include "monarch/net/SslSocketDataPresenter.h"

using namespace std;
using namespace monarch::config;
using namespace monarch::crypto;
using namespace monarch::data::json;
using namespace monarch::event;
using namespace monarch::http;
using namespace monarch::io;
using namespace monarch::net;
using namespace monarch::rt;
using namespace monarch::util;
using namespace bitmunk::common;
using namespace bitmunk::protocol;
using namespace bitmunk::node;

BtpServer::BtpServer(Node* node) :
mNode(node),
mHostAddress(NULL),
mSslContext(NULL),
mSocketDataPresenterList(NULL),
mServiceId(Server::sInvalidServiceId)
{
}

BtpServer::~BtpServer()
{
}

bool BtpServer::initialize(Config& cfg)
{
   bool rval = true;

   // create SSL server context ("TLS" is most secure and recent SSL but we
   // must use "ALL" to handle browsers that hit the api using earlier SSL
   // versions ... TLS is only required for BTP and not all SSL traffic to
   // the BtpServer must be BTP)
   mSslContext = new SslContext("ALL", false);

   // setup certificate file and private key
   string certPath;
   string pkeyPath;
   mNode->getConfigManager()->expandBitmunkHomePath(
      cfg["sslCertificate"]->getString(), certPath);
   mNode->getConfigManager()->expandBitmunkHomePath(
      cfg["sslPrivateKey"]->getString(), pkeyPath);
   File certFile(certPath.c_str());
   File pkeyFile(pkeyPath.c_str());

   // generate new private key and self-signed certificate if requested
   // and if no private key or certificate file already exists
   if(rval && cfg["sslGenerate"]->getBoolean() &&
      (!pkeyFile->exists() || !certFile->exists()))
   {
      MO_CAT_INFO(BM_NODE_CAT,
         "SSL X.509 self-signed certificate creation requested, creating...");

      CertificateCreator certCreator(mNode);
      rval = certCreator.createCertificate(pkeyFile, certFile);
      if(rval)
      {
         MO_CAT_INFO(BM_NODE_CAT, "SSL X.509 self-signed certificate created.");
      }
   }

   if(rval)
   {
      // set certificate and private key for SSL context
      rval =
         mSslContext->setCertificate(certFile) &&
         mSslContext->setPrivateKey(pkeyFile);
      if(rval)
      {
         // set default virtual host based on certificate common name
         ByteBuffer b(certFile->getLength());
         rval = certFile.readBytes(&b);
         if(rval)
         {
            AsymmetricKeyFactory afk;
            X509CertificateRef cert = afk.loadCertificateFromPem(
               b.data(), b.length());
            rval = !cert.isNull();
            if(rval)
            {
               DynamicObject subject = cert->getSubject();
               string commonName = cert->getField(subject, "CN");
               mSslContext->setVirtualHost(commonName.c_str());
            }
         }
      }
   }

   if(rval)
   {
      // setup host address for node
      const char* host = cfg["host"]->getString();
      uint32_t port = cfg["port"]->getUInt32();
      mHostAddress = new InternetAddress(host, port);

      // handle socket presentation layer that is SSL or non-SSL
      mSocketDataPresenterList = new SocketDataPresenterList(true);
      SslSocketDataPresenter* ssdp =
         new SslSocketDataPresenter(&(*mSslContext));
      NullSocketDataPresenter* nsdp = new NullSocketDataPresenter();
      mSocketDataPresenterList->add(ssdp);
      mSocketDataPresenterList->add(nsdp);

      // get the list of default domains
      if(cfg->hasMember("domains"))
      {
         mDefaultDomains = cfg["domains"].clone();
         if(mDefaultDomains->length() == 0)
         {
            // add wildcard if no domains specified
            mDefaultDomains->append() = "*";
         }
      }
      else
      {
         // no specified default domains, so use "*"
         mDefaultDomains = DynamicObject();
         mDefaultDomains->append() = "*";
      }

      MO_CAT_INFO(BM_NODE_CAT, "Running services on domains: %s",
         JsonWriter::writeToString(mDefaultDomains, false, false).c_str());
   }

   return rval;
}

void BtpServer::cleanup()
{
   // clean up services
   for(DomainMap::iterator i = mServices.begin(); i != mServices.end(); ++i)
   {
      free((char*)i->first);
      delete i->second;
   }

   // clean up
   mHostAddress.setNull();
   mSslContext.setNull();
   mSocketDataPresenterList.setNull();
   mDefaultDomains.setNull();
}

bool BtpServer::start()
{
   bool rval = true;

   // add http connection service
   mServiceId = mNode->getServer()->addConnectionService(
      &(*mHostAddress), &mHttpConnectionServicer,
      &(*mSocketDataPresenterList), "MainHttpService");
   if(mServiceId == Server::sInvalidServiceId)
   {
      // could not add connection service
      rval = false;
   }
   else
   {
      MO_CAT_INFO(BM_NODE_CAT, "Serving on %s",
         mHostAddress->toString(false).c_str());
   }

   return rval;
}

void BtpServer::stop()
{
   // remove http connection service
   if(mServiceId != Server::sInvalidServiceId)
   {
      mNode->getServer()->removePortService(mServiceId);
      mServiceId = Server::sInvalidServiceId;
   }
}

/**
 * Makes sure a btp service is added in both secure and non-secure mode (if
 * specified) or it isn't added at all.
 *
 * @param bs the service to add.
 * @param ssl the ssl mode.
 * @param hcs the servicer to add the service to.
 * @param domain the domain to add the service to.
 *
 * @return true if added, false if not.
 */
static bool _addService(
   BtpServiceRef& bs, Node::SslStatus ssl, HttpConnectionServicer* hcs,
   const char* domain)
{
   bool rval = true;

   // try to add secure service if applicable
   bool added = false;
   if(ssl == Node::SslOn || ssl == Node::SslAny)
   {
      // try to add the service
      rval = hcs->addRequestServicer(&(*bs), true, domain);
      added = true;
   }

   // if success, add non-secure service if applicable
   if(rval && (ssl == Node::SslOff || ssl == Node::SslAny))
   {
      // if non-secure service could not be added, remove the secure one
      // it was added
      if(!hcs->addRequestServicer(&(*bs), false, domain))
      {
         rval = false;
         if(added)
         {
            hcs->removeRequestServicer(&(*bs), true, domain);
         }
      }
   }

   return rval;
}

bool BtpServer::addService(
   BtpServiceRef& service, Node::SslStatus ssl, bool initialize,
   const char* domain)
{
   bool rval = true;

   if(initialize)
   {
      rval = service->initialize();
   }

   if(rval)
   {
      // get the domain list
      DynamicObject dl(NULL);
      if(domain == NULL)
      {
         dl = mDefaultDomains;
      }
      else
      {
         dl = DynamicObject();
         dl->append() = domain;
      }

      mBtpServiceLock.lock();
      {
         // keep track of domains with added services
         DynamicObject added;
         added->setType(Array);

         // for each domain, add service
         BtpServiceMaps* bsm = NULL;
         DynamicObjectIterator dli = dl.getIterator();
         while(rval && dli->hasNext())
         {
            const char* dom = dli->next()->getString();
            DomainMap::iterator di = mServices.find(dom);
            if(di == mServices.end())
            {
               // add a new maps entry
               bsm = new BtpServiceMaps;
               mServices[strdup(dom)] = bsm;
            }
            else
            {
               // use existing maps entry
               bsm = di->second;
            }

            // try to add the service
            rval = _addService(service, ssl, &mHttpConnectionServicer, dom);
            if(rval)
            {
               // added to the given domain
               added->append() = dom;

               // save reference to service
               const char* path = service->getPath();
               if(ssl == Node::SslOn || ssl == Node::SslAny)
               {
                  bsm->secure[path] = service;
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Added SSL BTP service: %s%s", dom, path);
               }
               if(ssl == Node::SslOff || ssl == Node::SslAny)
               {
                  bsm->nonSecure[path] = service;
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Added non-SSL BTP service: %s%s", dom, path);
               }
            }
         }

         // if service didn't add to a domain, remove it from all the
         // ones it was added to
         if(!rval)
         {
            dli = added.getIterator();
            while(dli->hasNext())
            {
               const char* dom = dli->next()->getString();
               removeService(service->getPath(), ssl, false, dom);
            }
         }
      }
      mBtpServiceLock.unlock();

      // failed to add service
      if(!rval)
      {
         if(initialize)
         {
            // clean up service
            service->cleanup();
         }

         // set exception
         ExceptionRef e = new Exception(
            "Could not add btp service.",
            "bitmunk.node.AddBtpServiceFailure");
         Exception::push(e);
      }
   }

   return rval;
}

void BtpServer::removeService(
   const char* path, Node::SslStatus ssl, bool cleanup,
   const char* domain)
{
   // get the domain list
   DynamicObject dl(NULL);
   if(domain == NULL)
   {
      dl = mDefaultDomains;
   }
   else
   {
      dl = DynamicObject();
      dl->append() = domain;
   }

   // build a unique list of services to cleanup
   UniqueList<BtpServiceRef> cleanupList;

   mBtpServiceLock.lock();
   {
      // for each domain, remove service and store it in cleanup list
      DynamicObjectIterator dli = dl.getIterator();
      while(dli->hasNext())
      {
         const char* dom = dli->next()->getString();
         DomainMap::iterator di = mServices.find(dom);
         if(di != mServices.end())
         {
            if(ssl == Node::SslOn || ssl == Node::SslAny)
            {
               BtpServiceMap::iterator i = di->second->secure.find(path);
               if(i != di->second->secure.end())
               {
                  cleanupList.add(i->second);
                  mHttpConnectionServicer.removeRequestServicer(
                     path, true, dom);
                  di->second->secure.erase(i);
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Removed SSL BTP service: %s%s", dom, path);
               }
            }

            if(ssl == Node::SslOff || ssl == Node::SslAny)
            {
               BtpServiceMap::iterator i = di->second->nonSecure.find(path);
               if(i != di->second->nonSecure.end())
               {
                  cleanupList.add(i->second);
                  mHttpConnectionServicer.removeRequestServicer(
                     path, false, dom);
                  di->second->nonSecure.erase(i);
                  MO_CAT_DEBUG(BM_NODE_CAT,
                     "Removed non-SSL BTP service: %s%s", dom, path);
               }
            }
         }
      }
   }
   mBtpServiceLock.unlock();

   // clean up services
   if(cleanup)
   {
      IteratorRef<BtpServiceRef> i = cleanupList.getIterator();
      while(i->hasNext())
      {
         BtpServiceRef& bs = i->next();
         bs->cleanup();
      }
   }
}

BtpServiceRef BtpServer::getService(
   const char* path, Node::SslStatus ssl, const char* domain)
{
   BtpServiceRef rval;

   // get the domain list
   DynamicObject dl(NULL);
   if(domain == NULL)
   {
      dl = mDefaultDomains;
   }
   else
   {
      dl = DynamicObject();
      dl->append() = domain;
   }
   domain = dl.first()->getString();

   // look in services map for service
   mBtpServiceLock.lock();
   {
      DomainMap::iterator di = mServices.find(domain);
      if(di != mServices.end())
      {
         if(ssl == Node::SslOff || ssl == Node::SslAny)
         {
            BtpServiceMap::iterator i = di->second->nonSecure.find(path);
            if(i != di->second->nonSecure.end())
            {
               rval = i->second;
            }
         }

         if(rval.isNull() && ssl != Node::SslOff)
         {
            BtpServiceMap::iterator i = di->second->secure.find(path);
            if(i != di->second->secure.end())
            {
               rval = i->second;
            }
         }
      }
   }
   mBtpServiceLock.unlock();

   return rval;
}

InternetAddressRef BtpServer::getHostAddress()
{
   return mHostAddress;
}

bool BtpServer::addVirtualHost(ProfileRef& profile)
{
   bool rval = false;

   // FIXME: for future support of client-side certificates, enable peer
   // authentication (pass true instead of false to SslContext constructor)
   // and add same trusted certs used by BtpClient, then add a way to expose
   // the user in the client cert (currently taken from common name) to the
   // application as the bitmunk user ID of the caller, keep in mind agent
   // headers (acting on behalf of another bitmunk user ID) must still be
   // checked

   // create an SSL context for the user's virtual host
   SslContextRef ctx = new SslContext("TLS", false);
   rval =
      ctx->setCertificate(profile->getCertificate()) &&
      ctx->setPrivateKey(profile->getPrivateKey());

   if(rval)
   {
      string name = Tools::getBitmunkUserCommonName(profile->getUserId());
      ctx->setVirtualHost(name.c_str());
      MO_CAT_DEBUG(BM_NODE_CAT, "Adding virtual host '%s'", name.c_str());

      // add the virtual host
      rval = mSslContext->addVirtualHost(ctx);
   }

   return rval;
}

void BtpServer::removeVirtualHost(UserId userId, SslContextRef* ctx)
{
   string name = Tools::getBitmunkUserCommonName(userId);
   MO_CAT_DEBUG(BM_NODE_CAT, "Removing virtual host '%s'", name.c_str());
   mSslContext->removeVirtualHost(name.c_str(), ctx);
}
<|endoftext|>


<commit_before>#include "buffer_t.h"
#include "JITCompiledModule.h"
#include "CodeGen.h"
#include "LLVM_Headers.h"
#include "Debug.h"

#include <string>

namespace Halide {
namespace Internal {

using std::string;

using namespace llvm;

// Wraps an execution engine. Takes ownership of the given module and
// the memory for jit compiled code.
class JITModuleHolder {
public:
    mutable RefCount ref_count;

    JITModuleHolder(llvm::ExecutionEngine *ee, llvm::Module *m, void (*shutdown)()) :
        execution_engine(ee),
        module(m),
        context(&m->getContext()),
        shutdown_thread_pool(shutdown) {
    }

    ~JITModuleHolder() {
        for (size_t i = 0; i < cleanup_routines.size(); i++) {
            void *ptr = reinterpret_bits<void *>(cleanup_routines[i]);
            debug(1) << "Calling target specific cleanup routine at " << ptr << "\n";
            (*cleanup_routines[i])();
        }

        shutdown_thread_pool();
        delete execution_engine;
        delete context;
        // No need to delete the module - deleting the execution engine should take care of that.
    }

    ExecutionEngine *execution_engine;
    Module *module;
    LLVMContext *context;
    void (*shutdown_thread_pool)();

    /** Do any target-specific module cleanup. */
    std::vector<void (*)()> cleanup_routines;
};

template<>
EXPORT RefCount &ref_count<JITModuleHolder>(const JITModuleHolder *f) {return f->ref_count;}

template<>
EXPORT void destroy<JITModuleHolder>(const JITModuleHolder *f) {delete f;}

namespace {

// Retrieve a function pointer from an llvm module, possibly by
// compiling it. Returns it by assigning to the last argument.
template<typename FP>
void hook_up_function_pointer(ExecutionEngine *ee, Module *mod, const string &name, bool must_succeed, FP *result) {

    assert(mod && ee);

    debug(2) << "Retrieving " << name << " from module\n";
    llvm::Function *fn = mod->getFunction(name);
    if (!fn) {
        if (must_succeed) {
            std::cerr << "Could not find function " << name << " in module\n";
            assert(false);
        } else {
            *result = NULL;
            return;
        }
    }

    debug(2) << "JIT Compiling " << name << "\n";
    void *f = ee->getPointerToFunction(fn);
    if (!f && must_succeed) {
        std::cerr << "Compiling " << name << " returned NULL\n";
        assert(false);
    }
    debug(2) << "Function exists at " << f << "\n";

    *result = reinterpret_bits<FP>(f);

}

}

void JITCompiledModule::compile_module(CodeGen *cg, llvm::Module *m, const string &function_name) {

    // Make the execution engine
    debug(2) << "Creating new execution engine\n";
    string error_string;

    TargetOptions options;
    options.LessPreciseFPMADOption = true;
    options.NoFramePointerElim = false;
    options.AllowFPOpFusion = FPOpFusion::Fast;
    options.UnsafeFPMath = true;
    options.NoInfsFPMath = true;
    options.NoNaNsFPMath = true;
    options.HonorSignDependentRoundingFPMathOption = false;
    options.UseSoftFloat = false;
    options.FloatABIType =
        cg->use_soft_float_abi() ? FloatABI::Soft : FloatABI::Hard;
    options.NoZerosInBSS = false;
    options.GuaranteedTailCallOpt = false;
    options.DisableTailCalls = false;
    options.StackAlignmentOverride = 0;
    options.TrapFuncName = "";
    options.PositionIndependentExecutable = true;
    options.EnableSegmentedStacks = false;
    options.UseInitArray = false;

    EngineBuilder engine_builder(m);
    engine_builder.setTargetOptions(options);
    engine_builder.setErrorStr(&error_string);
    engine_builder.setEngineKind(EngineKind::JIT);
    #ifdef USE_MCJIT
    engine_builder.setUseMCJIT(true);
    engine_builder.setJITMemoryManager(JITMemoryManager::CreateDefaultMemManager());
    #else
    engine_builder.setUseMCJIT(false);
    #endif
    engine_builder.setOptLevel(CodeGenOpt::Aggressive);
    engine_builder.setMCPU(cg->mcpu());
    engine_builder.setMAttrs(vec<string>(cg->mattrs()));
    ExecutionEngine *ee = engine_builder.create();
    if (!ee) std::cerr << error_string << "\n";
    assert(ee && "Couldn't create execution engine");
    // TODO: I don't think this is necessary, we shouldn't have any static constructors
    // ee->runStaticConstructorsDestructors(...);

    // Do any target-specific initialization
    cg->jit_init(ee, m);

    // Retrieve function pointers from the compiled module (which also
    // triggers compilation)
    debug(1) << "JIT compiling...\n";

    hook_up_function_pointer(ee, m, function_name, true, &function);

    void *function_address = reinterpret_bits<void *>(function);
    debug(1) << "JIT compiled function pointer " << function_address << "\n";

    hook_up_function_pointer(ee, m, function_name + "_jit_wrapper", true, &wrapped_function);
    hook_up_function_pointer(ee, m, "halide_copy_to_host", false, &copy_to_host);
    hook_up_function_pointer(ee, m, "halide_copy_to_dev", false, &copy_to_dev);
    hook_up_function_pointer(ee, m, "halide_free_dev_buffer", false, &free_dev_buffer);
    hook_up_function_pointer(ee, m, "halide_set_error_handler", true, &set_error_handler);
    hook_up_function_pointer(ee, m, "halide_set_custom_allocator", true, &set_custom_allocator);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_par_for", true, &set_custom_do_par_for);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_task", true, &set_custom_do_task);
    hook_up_function_pointer(ee, m, "halide_shutdown_thread_pool", true, &shutdown_thread_pool);

    debug(2) << "Finalizing object\n";
    ee->finalizeObject();

    // Do any target-specific post-compilation module meddling
    cg->jit_finalize(ee, m, &module.ptr->cleanup_routines);

    // Stash the various objects that need to stay alive behind a reference-counted pointer.
    module = new JITModuleHolder(ee, m, shutdown_thread_pool);

    debug(2) << "Done with compilation\n";
}

}
}
<commit_msg>Moved jit_finalize back to where it was<commit_after>#include "buffer_t.h"
#include "JITCompiledModule.h"
#include "CodeGen.h"
#include "LLVM_Headers.h"
#include "Debug.h"

#include <string>

namespace Halide {
namespace Internal {

using std::string;

using namespace llvm;

// Wraps an execution engine. Takes ownership of the given module and
// the memory for jit compiled code.
class JITModuleHolder {
public:
    mutable RefCount ref_count;

    JITModuleHolder(llvm::ExecutionEngine *ee, llvm::Module *m, void (*shutdown)()) :
        execution_engine(ee),
        module(m),
        context(&m->getContext()),
        shutdown_thread_pool(shutdown) {
    }

    ~JITModuleHolder() {
        for (size_t i = 0; i < cleanup_routines.size(); i++) {
            void *ptr = reinterpret_bits<void *>(cleanup_routines[i]);
            debug(1) << "Calling target specific cleanup routine at " << ptr << "\n";
            (*cleanup_routines[i])();
        }

        shutdown_thread_pool();
        delete execution_engine;
        delete context;
        // No need to delete the module - deleting the execution engine should take care of that.
    }

    ExecutionEngine *execution_engine;
    Module *module;
    LLVMContext *context;
    void (*shutdown_thread_pool)();

    /** Do any target-specific module cleanup. */
    std::vector<void (*)()> cleanup_routines;
};

template<>
EXPORT RefCount &ref_count<JITModuleHolder>(const JITModuleHolder *f) {return f->ref_count;}

template<>
EXPORT void destroy<JITModuleHolder>(const JITModuleHolder *f) {delete f;}

namespace {

// Retrieve a function pointer from an llvm module, possibly by
// compiling it. Returns it by assigning to the last argument.
template<typename FP>
void hook_up_function_pointer(ExecutionEngine *ee, Module *mod, const string &name, bool must_succeed, FP *result) {

    assert(mod && ee);

    debug(2) << "Retrieving " << name << " from module\n";
    llvm::Function *fn = mod->getFunction(name);
    if (!fn) {
        if (must_succeed) {
            std::cerr << "Could not find function " << name << " in module\n";
            assert(false);
        } else {
            *result = NULL;
            return;
        }
    }

    debug(2) << "JIT Compiling " << name << "\n";
    void *f = ee->getPointerToFunction(fn);
    if (!f && must_succeed) {
        std::cerr << "Compiling " << name << " returned NULL\n";
        assert(false);
    }
    debug(2) << "Function exists at " << f << "\n";

    *result = reinterpret_bits<FP>(f);

}

}

void JITCompiledModule::compile_module(CodeGen *cg, llvm::Module *m, const string &function_name) {

    // Make the execution engine
    debug(2) << "Creating new execution engine\n";
    string error_string;

    TargetOptions options;
    options.LessPreciseFPMADOption = true;
    options.NoFramePointerElim = false;
    options.AllowFPOpFusion = FPOpFusion::Fast;
    options.UnsafeFPMath = true;
    options.NoInfsFPMath = true;
    options.NoNaNsFPMath = true;
    options.HonorSignDependentRoundingFPMathOption = false;
    options.UseSoftFloat = false;
    options.FloatABIType =
        cg->use_soft_float_abi() ? FloatABI::Soft : FloatABI::Hard;
    options.NoZerosInBSS = false;
    options.GuaranteedTailCallOpt = false;
    options.DisableTailCalls = false;
    options.StackAlignmentOverride = 0;
    options.TrapFuncName = "";
    options.PositionIndependentExecutable = true;
    options.EnableSegmentedStacks = false;
    options.UseInitArray = false;

    EngineBuilder engine_builder(m);
    engine_builder.setTargetOptions(options);
    engine_builder.setErrorStr(&error_string);
    engine_builder.setEngineKind(EngineKind::JIT);
    #ifdef USE_MCJIT
    engine_builder.setUseMCJIT(true);
    engine_builder.setJITMemoryManager(JITMemoryManager::CreateDefaultMemManager());
    #else
    engine_builder.setUseMCJIT(false);
    #endif
    engine_builder.setOptLevel(CodeGenOpt::Aggressive);
    engine_builder.setMCPU(cg->mcpu());
    engine_builder.setMAttrs(vec<string>(cg->mattrs()));
    ExecutionEngine *ee = engine_builder.create();
    if (!ee) std::cerr << error_string << "\n";
    assert(ee && "Couldn't create execution engine");
    // TODO: I don't think this is necessary, we shouldn't have any static constructors
    // ee->runStaticConstructorsDestructors(...);

    // Do any target-specific initialization
    cg->jit_init(ee, m);

    // Retrieve function pointers from the compiled module (which also
    // triggers compilation)
    debug(1) << "JIT compiling...\n";

    hook_up_function_pointer(ee, m, function_name, true, &function);

    void *function_address = reinterpret_bits<void *>(function);
    debug(1) << "JIT compiled function pointer " << function_address << "\n";

    hook_up_function_pointer(ee, m, function_name + "_jit_wrapper", true, &wrapped_function);
    hook_up_function_pointer(ee, m, "halide_copy_to_host", false, &copy_to_host);
    hook_up_function_pointer(ee, m, "halide_copy_to_dev", false, &copy_to_dev);
    hook_up_function_pointer(ee, m, "halide_free_dev_buffer", false, &free_dev_buffer);
    hook_up_function_pointer(ee, m, "halide_set_error_handler", true, &set_error_handler);
    hook_up_function_pointer(ee, m, "halide_set_custom_allocator", true, &set_custom_allocator);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_par_for", true, &set_custom_do_par_for);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_task", true, &set_custom_do_task);
    hook_up_function_pointer(ee, m, "halide_shutdown_thread_pool", true, &shutdown_thread_pool);

    debug(2) << "Finalizing object\n";
    ee->finalizeObject();

    // Stash the various objects that need to stay alive behind a reference-counted pointer.
    module = new JITModuleHolder(ee, m, shutdown_thread_pool);

    // Do any target-specific post-compilation module meddling
    cg->jit_finalize(ee, m, &module.ptr->cleanup_routines);

    debug(2) << "Done with compilation\n";
}

}
}
<|endoftext|>