<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Vesa-Matti Hartikainen <vesa-matti.hartikainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <QObject>
#include <QtTest>
#include <QtQml>
#include <QFile>
#include <QDesktopServices>
#include <QGuiApplication>
#include "declarativebookmarkmodel.h"
#include "testobject.h"
#include "bookmarkmanager.h"

static const QByteArray QML_SNIPPET = \
        "import QtQuick 2.0\n" \
        "import Sailfish.Browser 1.0\n" \
        "Item {\n" \
        "   width: 100; height: 100\n" \
        "   property alias bookmarkModel: model\n" \
        "   BookmarkModel { id: model }\n" \
        "}\n";

class tst_declarativebookmarkmodel : public TestObject
{
    Q_OBJECT

public:
    tst_declarativebookmarkmodel();

private slots:
    void initTestCase();
    void cleanupTestCase();

    void addBookmark();
    void removeBookmark();
    void contains();
    void editBookmark();
    void clearBookmarks();

private:
    DeclarativeBookmarkModel *bookmarkModel;
};

tst_declarativebookmarkmodel::tst_declarativebookmarkmodel()
    : TestObject(QML_SNIPPET)
{
    bookmarkModel = TestObject::qmlObject<DeclarativeBookmarkModel>("bookmarkModel");
}

void tst_declarativebookmarkmodel::initTestCase()
{
    QVERIFY(bookmarkModel);

    // Preload bookmarks
    QCOMPARE(bookmarkModel->rowCount(), 6);
}

void tst_declarativebookmarkmodel::addBookmark()
{
    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    int count = bookmarkModel->rowCount() + 1;
    bookmarkModel->addBookmark("http://www.test1.jolla.com", "jolla", "");

    waitSignals(countChangeSpy, 1);
    QCOMPARE(bookmarkModel->rowCount(), count);
}

void tst_declarativebookmarkmodel::removeBookmark()
{
    bookmarkModel->addBookmark("http://www.test2.jolla.com", "jolla", "");
    int count = bookmarkModel->rowCount();

    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    bookmarkModel->removeBookmark("http://www.test2.jolla.com");
    waitSignals(countChangeSpy, 1);

    QCOMPARE(bookmarkModel->rowCount(), count-1);

    bookmarkModel->removeBookmark("http://www.test3.that.is.not.there.jolla.com");
    QCOMPARE(bookmarkModel->rowCount(), count-1);
}

void tst_declarativebookmarkmodel::contains()
{
    bookmarkModel->addBookmark("http://www.test4.contains.jolla.com", "jolla", "");

    QVERIFY(bookmarkModel->contains("http://www.test4.contains.jolla.com"));
    QVERIFY(!bookmarkModel->contains("http://www.test4.that.is.not.there.jolla.com"));
}

void tst_declarativebookmarkmodel::editBookmark()
{
    bookmarkModel->addBookmark("http://www.test5.jolla.com", "jolla", "");
    bookmarkModel->addBookmark("http://www.test5.jolla.com/2", "jolla2", "");
    int count = bookmarkModel->rowCount();

    QModelIndex index = bookmarkModel->index(0);
    // Let's make sure that first bookmark stays normal.
    QString constUrl = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();
    QString constTitle = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();

    // Edit only the last entry
    index = bookmarkModel->index(count-1);
    QString originalUrl = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();
    QString newTitle("New title");
    QString newUrl("http://www.test5.jolla.com/edited");

    bookmarkModel->editBookmark(index.row(), originalUrl, newTitle);
    QString title = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();
    QString url = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();

    QCOMPARE(title, newTitle);
    QCOMPARE(url, originalUrl);

    bookmarkModel->editBookmark(index.row(), newUrl, title);
    title = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();
    url = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();

    QCOMPARE(title, newTitle);
    QCOMPARE(url, newUrl);

    index = bookmarkModel->index(0);
    url = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();
    title = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();

    QCOMPARE(title, constTitle);
    QCOMPARE(url, constUrl);

    QCOMPARE(count, bookmarkModel->rowCount());
}

void tst_declarativebookmarkmodel::cleanupTestCase()
{
    QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/bookmarks.json");
    QVERIFY(file.remove());
}

void tst_declarativebookmarkmodel::clearBookmarks()
{
    bookmarkModel->addBookmark("http://www.test6.jolla.com", "jolla", "");

    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    BookmarkManager::instance()->clear();

    waitSignals(countChangeSpy, 1);
    QCOMPARE(bookmarkModel->rowCount(), 0);
    QVERIFY(!bookmarkModel->contains("http://www.test6.jolla.com"));
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    app.setApplicationName("bookmarks-tests");
    app.setOrganizationName("org.sailfishos");
    app.setAttribute(Qt::AA_Use96Dpi, true);
    qmlRegisterType<DeclarativeBookmarkModel>("Sailfish.Browser", 1, 0, "BookmarkModel");
    tst_declarativebookmarkmodel testcase;
    return QTest::qExec(&testcase, argc, argv); \
}

#include "tst_declarativebookmarkmodel.moc"
<commit_msg>[sailfish-browser] Fix dummy error from bookmark model tests. Contributes to JB#25678<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Vesa-Matti Hartikainen <vesa-matti.hartikainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <QObject>
#include <QtTest>
#include <QtQml>
#include <QFile>
#include <QDesktopServices>
#include <QGuiApplication>
#include "declarativebookmarkmodel.h"
#include "testobject.h"
#include "bookmarkmanager.h"

static const QByteArray QML_SNIPPET = \
        "import QtQuick 2.0\n" \
        "import Sailfish.Browser 1.0\n" \
        "Item {\n" \
        "   width: 100; height: 100\n" \
        "   property alias bookmarkModel: model\n" \
        "   BookmarkModel { id: model }\n" \
        "}\n";

class tst_declarativebookmarkmodel : public TestObject
{
    Q_OBJECT

public:
    tst_declarativebookmarkmodel();

private slots:
    void initTestCase();
    void cleanupTestCase();

    void addBookmark();
    void removeBookmark();
    void contains();
    void editBookmark();
    void clearBookmarks();

private:
    DeclarativeBookmarkModel *bookmarkModel;
};

tst_declarativebookmarkmodel::tst_declarativebookmarkmodel()
    : TestObject(QML_SNIPPET)
{
    bookmarkModel = TestObject::qmlObject<DeclarativeBookmarkModel>("bookmarkModel");
}

void tst_declarativebookmarkmodel::initTestCase()
{
    QVERIFY(bookmarkModel);

    // Preload bookmarks
    QCOMPARE(bookmarkModel->rowCount(), 4);
}

void tst_declarativebookmarkmodel::addBookmark()
{
    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    int count = bookmarkModel->rowCount() + 1;
    bookmarkModel->addBookmark("http://www.test1.jolla.com", "jolla", "");

    waitSignals(countChangeSpy, 1);
    QCOMPARE(bookmarkModel->rowCount(), count);
}

void tst_declarativebookmarkmodel::removeBookmark()
{
    bookmarkModel->addBookmark("http://www.test2.jolla.com", "jolla", "");
    int count = bookmarkModel->rowCount();

    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    bookmarkModel->removeBookmark("http://www.test2.jolla.com");
    waitSignals(countChangeSpy, 1);

    QCOMPARE(bookmarkModel->rowCount(), count-1);

    bookmarkModel->removeBookmark("http://www.test3.that.is.not.there.jolla.com");
    QCOMPARE(bookmarkModel->rowCount(), count-1);
}

void tst_declarativebookmarkmodel::contains()
{
    bookmarkModel->addBookmark("http://www.test4.contains.jolla.com", "jolla", "");

    QVERIFY(bookmarkModel->contains("http://www.test4.contains.jolla.com"));
    QVERIFY(!bookmarkModel->contains("http://www.test4.that.is.not.there.jolla.com"));
}

void tst_declarativebookmarkmodel::editBookmark()
{
    bookmarkModel->addBookmark("http://www.test5.jolla.com", "jolla", "");
    bookmarkModel->addBookmark("http://www.test5.jolla.com/2", "jolla2", "");
    int count = bookmarkModel->rowCount();

    QModelIndex index = bookmarkModel->index(0);
    // Let's make sure that first bookmark stays normal.
    QString constUrl = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();
    QString constTitle = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();

    // Edit only the last entry
    index = bookmarkModel->index(count-1);
    QString originalUrl = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();
    QString newTitle("New title");
    QString newUrl("http://www.test5.jolla.com/edited");

    bookmarkModel->editBookmark(index.row(), originalUrl, newTitle);
    QString title = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();
    QString url = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();

    QCOMPARE(title, newTitle);
    QCOMPARE(url, originalUrl);

    bookmarkModel->editBookmark(index.row(), newUrl, title);
    title = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();
    url = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();

    QCOMPARE(title, newTitle);
    QCOMPARE(url, newUrl);

    index = bookmarkModel->index(0);
    url = bookmarkModel->data(index, DeclarativeBookmarkModel::UrlRole).toString();
    title = bookmarkModel->data(index, DeclarativeBookmarkModel::TitleRole).toString();

    QCOMPARE(title, constTitle);
    QCOMPARE(url, constUrl);

    QCOMPARE(count, bookmarkModel->rowCount());
}

void tst_declarativebookmarkmodel::cleanupTestCase()
{
    QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/bookmarks.json");
    QVERIFY(file.remove());
}

void tst_declarativebookmarkmodel::clearBookmarks()
{
    bookmarkModel->addBookmark("http://www.test6.jolla.com", "jolla", "");

    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    BookmarkManager::instance()->clear();

    waitSignals(countChangeSpy, 1);
    QCOMPARE(bookmarkModel->rowCount(), 0);
    QVERIFY(!bookmarkModel->contains("http://www.test6.jolla.com"));
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    app.setApplicationName("bookmarks-tests");
    app.setOrganizationName("org.sailfishos");
    app.setAttribute(Qt::AA_Use96Dpi, true);
    qmlRegisterType<DeclarativeBookmarkModel>("Sailfish.Browser", 1, 0, "BookmarkModel");
    tst_declarativebookmarkmodel testcase;
    return QTest::qExec(&testcase, argc, argv); \
}

#include "tst_declarativebookmarkmodel.moc"
<|endoftext|>


<commit_before><commit_msg>__uuidof comentario adicionado<commit_after><|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_SCAL_PROB_BETA_BINOMIAL_LCDF_HPP
#define STAN_MATH_PRIM_SCAL_PROB_BETA_BINOMIAL_LCDF_HPP

#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/OperandsAndPartials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/binomial_coefficient_log.hpp>
#include <stan/math/prim/scal/fun/lbeta.hpp>
#include <stan/math/prim/scal/fun/digamma.hpp>
#include <stan/math/prim/scal/fun/lgamma.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <stan/math/prim/scal/meta/contains_nonconstant_struct.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/scal/prob/beta_rng.hpp>
#include <stan/math/prim/scal/fun/F32.hpp>
#include <stan/math/prim/scal/fun/grad_F32.hpp>
#include <cmath>

namespace stan {
  namespace math {

    /**
     * Returns the log CDF of the Beta-Binomial distribution with given population
     * size, prior success, and prior failure parameters. Given containers of
     * matching sizes, returns the log sum of probabilities.
     *
     * @tparam T_n type of success parameter
     * @tparam T_N type of population size parameter
     * @tparam T_size1 type of prior success parameter
     * @tparam T_size2 type of prior failure parameter
     * @param n success parameter
     * @param N population size parameter
     * @param alpha prior success parameter
     * @param beta prior failure parameter
     * @return log probability or log sum of probabilities
     * @throw std::domain_error if N, alpha, or beta fails to be positive
     * @throw std::invalid_argument if container sizes mismatch
     */
    template <typename T_n, typename T_N,
              typename T_size1, typename T_size2>
    typename return_type<T_size1, T_size2>::type
    beta_binomial_lcdf(const T_n& n, const T_N& N, const T_size1& alpha,
                          const T_size2& beta) {
      static const char* function("beta_binomial_lcdf");
      typedef typename stan::partials_return_type<T_n, T_N, T_size1,
                                                  T_size2>::type
        T_partials_return;

      if (!(stan::length(n) && stan::length(N) && stan::length(alpha)
            && stan::length(beta)))
        return 0.0;

      T_partials_return P(0.0);

      check_nonnegative(function, "Population size parameter", N);
      check_positive_finite(function,
                            "First prior sample size parameter", alpha);
      check_positive_finite(function,
                            "Second prior sample size parameter", beta);
      check_consistent_sizes(function,
                             "Successes variable", n,
                             "Population size parameter", N,
                             "First prior sample size parameter", alpha,
                             "Second prior sample size parameter", beta);

      scalar_seq_view<const T_n> n_vec(n);
      scalar_seq_view<const T_N> N_vec(N);
      scalar_seq_view<const T_size1> alpha_vec(alpha);
      scalar_seq_view<const T_size2> beta_vec(beta);
      size_t size = max_size(n, N, alpha, beta);

      using std::exp;
      using std::log;
      using std::exp;

      OperandsAndPartials<T_size1, T_size2>
        operands_and_partials(alpha, beta);

      // Explicit return for extreme values
      // The gradients are technically ill-defined, but treated as neg infinity
      for (size_t i = 0; i < stan::length(n); i++) {
        if (value_of(n_vec[i]) <= 0)
          return operands_and_partials.value(negative_infinity());
      }

      for (size_t i = 0; i < size; i++) {
        // Explicit results for extreme values
        // The gradients are technically ill-defined, but treated as zero
        if (value_of(n_vec[i]) >= value_of(N_vec[i])) {
          continue;
        }

        const T_partials_return n_dbl = value_of(n_vec[i]);
        const T_partials_return N_dbl = value_of(N_vec[i]);
        const T_partials_return alpha_dbl = value_of(alpha_vec[i]);
        const T_partials_return beta_dbl = value_of(beta_vec[i]);

        const T_partials_return mu = alpha_dbl + n_dbl + 1;
        const T_partials_return nu = beta_dbl + N_dbl - n_dbl - 1;

        T_partials_return F;
        F = F32((T_partials_return)1, mu, -N_dbl + n_dbl + 1, n_dbl + 2,
          1 - nu, (T_partials_return)1);

        T_partials_return C = lgamma(nu) - lgamma(N_dbl - n_dbl);
        C += lgamma(mu) - lgamma(n_dbl + 2);
        C += lgamma(N_dbl + 2) - lgamma(N_dbl + alpha_dbl + beta_dbl);
        C = exp(C);

        C *= F / exp(lbeta(alpha_dbl, beta_dbl));
        C /= N_dbl + 1;

        const T_partials_return Pi = 1 - C;

        P += log(Pi);

        T_partials_return dF[6];
        T_partials_return digammaOne = 0;
        T_partials_return digammaTwo = 0;

        if (contains_nonconstant_struct<T_size1, T_size2>::value) {
          digammaOne = digamma(mu + nu);
          digammaTwo = digamma(alpha_dbl + beta_dbl);
          grad_F32(dF, (T_partials_return)1, mu, -N_dbl + n_dbl + 1,
                               n_dbl + 2, 1 - nu, (T_partials_return)1);
        }
        if (!is_constant_struct<T_size1>::value) {
          const T_partials_return g
            = - C * (digamma(mu) - digammaOne + dF[1] / F
                     - digamma(alpha_dbl) + digammaTwo);
          operands_and_partials.d_x1[i] += g / Pi;
        }
        if (!is_constant_struct<T_size2>::value) {
          const T_partials_return g
            = - C * (digamma(nu) - digammaOne - dF[4] / F - digamma(beta_dbl)
                     + digammaTwo);
          operands_and_partials.d_x2[i] += g / Pi;
        }
      }

      return operands_and_partials.value(P);
    }

  }
}
#endif
<commit_msg>Pass cpplint.<commit_after>#ifndef STAN_MATH_PRIM_SCAL_PROB_BETA_BINOMIAL_LCDF_HPP
#define STAN_MATH_PRIM_SCAL_PROB_BETA_BINOMIAL_LCDF_HPP

#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/OperandsAndPartials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/binomial_coefficient_log.hpp>
#include <stan/math/prim/scal/fun/lbeta.hpp>
#include <stan/math/prim/scal/fun/digamma.hpp>
#include <stan/math/prim/scal/fun/lgamma.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <stan/math/prim/scal/meta/contains_nonconstant_struct.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/scal/prob/beta_rng.hpp>
#include <stan/math/prim/scal/fun/F32.hpp>
#include <stan/math/prim/scal/fun/grad_F32.hpp>
#include <cmath>

namespace stan {
  namespace math {

    /**
     * Returns the log CDF of the Beta-Binomial distribution with given population
     * size, prior success, and prior failure parameters. Given containers of
     * matching sizes, returns the log sum of probabilities.
     *
     * @tparam T_n type of success parameter
     * @tparam T_N type of population size parameter
     * @tparam T_size1 type of prior success parameter
     * @tparam T_size2 type of prior failure parameter
     * @param n success parameter
     * @param N population size parameter
     * @param alpha prior success parameter
     * @param beta prior failure parameter
     * @return log probability or log sum of probabilities
     * @throw std::domain_error if N, alpha, or beta fails to be positive
     * @throw std::invalid_argument if container sizes mismatch
     */
    template <typename T_n, typename T_N,
              typename T_size1, typename T_size2>
    typename return_type<T_size1, T_size2>::type
    beta_binomial_lcdf(const T_n& n, const T_N& N, const T_size1& alpha,
                          const T_size2& beta) {
      static const char* function("beta_binomial_lcdf");
      typedef typename stan::partials_return_type<T_n, T_N, T_size1,
                                                  T_size2>::type
        T_partials_return;

      if (!(stan::length(n) && stan::length(N) && stan::length(alpha)
            && stan::length(beta)))
        return 0.0;

      T_partials_return P(0.0);

      check_nonnegative(function, "Population size parameter", N);
      check_positive_finite(function,
                            "First prior sample size parameter", alpha);
      check_positive_finite(function,
                            "Second prior sample size parameter", beta);
      check_consistent_sizes(function,
                             "Successes variable", n,
                             "Population size parameter", N,
                             "First prior sample size parameter", alpha,
                             "Second prior sample size parameter", beta);

      scalar_seq_view<const T_n> n_vec(n);
      scalar_seq_view<const T_N> N_vec(N);
      scalar_seq_view<const T_size1> alpha_vec(alpha);
      scalar_seq_view<const T_size2> beta_vec(beta);
      size_t size = max_size(n, N, alpha, beta);

      using std::exp;
      using std::log;
      using std::exp;

      OperandsAndPartials<T_size1, T_size2>
        operands_and_partials(alpha, beta);

      // Explicit return for extreme values
      // The gradients are technically ill-defined, but treated as neg infinity
      for (size_t i = 0; i < stan::length(n); i++) {
        if (value_of(n_vec[i]) <= 0)
          return operands_and_partials.value(negative_infinity());
      }

      for (size_t i = 0; i < size; i++) {
        // Explicit results for extreme values
        // The gradients are technically ill-defined, but treated as zero
        if (value_of(n_vec[i]) >= value_of(N_vec[i])) {
          continue;
        }

        const T_partials_return n_dbl = value_of(n_vec[i]);
        const T_partials_return N_dbl = value_of(N_vec[i]);
        const T_partials_return alpha_dbl = value_of(alpha_vec[i]);
        const T_partials_return beta_dbl = value_of(beta_vec[i]);

        const T_partials_return mu = alpha_dbl + n_dbl + 1;
        const T_partials_return nu = beta_dbl + N_dbl - n_dbl - 1;

        T_partials_return F;
        F = F32((T_partials_return)1, mu, -N_dbl + n_dbl + 1, n_dbl + 2,
          1 - nu, (T_partials_return)1);

        T_partials_return C = lgamma(nu) - lgamma(N_dbl - n_dbl);
        C += lgamma(mu) - lgamma(n_dbl + 2);
        C += lgamma(N_dbl + 2) - lgamma(N_dbl + alpha_dbl + beta_dbl);
        C = exp(C);

        C *= F / exp(lbeta(alpha_dbl, beta_dbl));
        C /= N_dbl + 1;

        const T_partials_return Pi = 1 - C;

        P += log(Pi);

        T_partials_return dF[6];
        T_partials_return digammaOne = 0;
        T_partials_return digammaTwo = 0;

        if (contains_nonconstant_struct<T_size1, T_size2>::value) {
          digammaOne = digamma(mu + nu);
          digammaTwo = digamma(alpha_dbl + beta_dbl);
          grad_F32(dF, (T_partials_return)1, mu, -N_dbl + n_dbl + 1,
                               n_dbl + 2, 1 - nu, (T_partials_return)1);
        }
        if (!is_constant_struct<T_size1>::value) {
          const T_partials_return g
            = - C * (digamma(mu) - digammaOne + dF[1] / F
                     - digamma(alpha_dbl) + digammaTwo);
          operands_and_partials.d_x1[i] += g / Pi;
        }
        if (!is_constant_struct<T_size2>::value) {
          const T_partials_return g
            = - C * (digamma(nu) - digammaOne - dF[4] / F - digamma(beta_dbl)
                     + digammaTwo);
          operands_and_partials.d_x2[i] += g / Pi;
        }
      }

      return operands_and_partials.value(P);
    }

  }
}
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 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.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * 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 OR 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.
 */

#include "security_manager/crypto_manager_impl.h"

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <ctime>
#include "security_manager/security_manager.h"

#include "utils/logger.h"
#include "utils/atomic.h"
#include "utils/macro.h"
#include "utils/scope_guard.h"
#include "utils/date_time.h"

#define TLS1_1_MINIMAL_VERSION 0x1000103fL
#define CONST_SSL_METHOD_MINIMAL_VERSION 0x00909000L

namespace security_manager {

CREATE_LOGGERPTR_GLOBAL(logger_, "SecurityManager")

uint32_t CryptoManagerImpl::instance_count_ = 0;
sync_primitives::Lock CryptoManagerImpl::instance_lock_;

namespace {
int debug_callback(int preverify_ok, X509_STORE_CTX* ctx) {
  if (!preverify_ok) {
    const int error = X509_STORE_CTX_get_error(ctx);
    UNUSED(error);
    LOG4CXX_WARN(logger_,
                 "Certificate verification failed with error "
                     << error << " \"" << X509_verify_cert_error_string(error)
                     << '"');
  }
  return preverify_ok;
}

void free_ctx(SSL_CTX** ctx) {
  if (ctx) {
    SSL_CTX_free(*ctx);
    *ctx = NULL;
  }
}
}

CryptoManagerImpl::CryptoManagerImpl(
    const utils::SharedPtr<const CryptoManagerSettings> set)
    : settings_(set), context_(NULL) {
  LOG4CXX_AUTO_TRACE(logger_);
  sync_primitives::AutoLock lock(instance_lock_);
  instance_count_++;
  if (instance_count_ == 1) {
    LOG4CXX_DEBUG(logger_, "Openssl engine initialization");
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    OpenSSL_add_all_algorithms();
    SSL_library_init();
  }
}

CryptoManagerImpl::~CryptoManagerImpl() {
  LOG4CXX_AUTO_TRACE(logger_);
  sync_primitives::AutoLock lock(instance_lock_);
  LOG4CXX_DEBUG(logger_, "Deinitilization");
  if (!context_) {
    LOG4CXX_WARN(logger_, "Manager is not initialized");
  } else {
    SSL_CTX_free(context_);
  }
  instance_count_--;
  if (instance_count_ == 0) {
    LOG4CXX_DEBUG(logger_, "Openssl engine deinitialization");
    EVP_cleanup();
    ERR_free_strings();
  }
}

bool CryptoManagerImpl::Init() {
  LOG4CXX_AUTO_TRACE(logger_);

  const Mode mode = get_settings().security_manager_mode();
  const bool is_server = (mode == SERVER);
  if (is_server) {
    LOG4CXX_DEBUG(logger_, "Server mode");
  } else {
    LOG4CXX_DEBUG(logger_, "Client mode");
  }
  LOG4CXX_DEBUG(logger_,
                "Peer verification "
                    << (get_settings().verify_peer() ? "enabled" : "disabled"));
  LOG4CXX_DEBUG(logger_,
                "CA certificate file is \"" << get_settings().ca_cert_path()
                                            << '"');

#if OPENSSL_VERSION_NUMBER < CONST_SSL_METHOD_MINIMAL_VERSION
  SSL_METHOD* method;
#else
  const SSL_METHOD* method;
#endif
  switch (get_settings().security_manager_protocol_name()) {
    case SSLv3:
      method = is_server ? SSLv3_server_method() : SSLv3_client_method();
      break;
    case TLSv1:
      method = is_server ? TLSv1_server_method() : TLSv1_client_method();
      break;
    case TLSv1_1:
#if OPENSSL_VERSION_NUMBER < TLS1_1_MINIMAL_VERSION
      LOG4CXX_WARN(
          logger_,
          "OpenSSL has no TLSv1.1 with version lower 1.0.1, set TLSv1.0");
      method = is_server ? TLSv1_server_method() : TLSv1_client_method();
#else
      method = is_server ? TLSv1_1_server_method() : TLSv1_1_client_method();
#endif
      break;
    case TLSv1_2:
#if OPENSSL_VERSION_NUMBER < TLS1_1_MINIMAL_VERSION
      LOG4CXX_WARN(
          logger_,
          "OpenSSL has no TLSv1.2 with version lower 1.0.1, set TLSv1.0");
      method = is_server ? TLSv1_server_method() : TLSv1_client_method();
#else
      method = is_server ? TLSv1_2_server_method() : TLSv1_2_client_method();
#endif
      break;
    default:
      LOG4CXX_ERROR(logger_,
                    "Unknown protocol: "
                        << get_settings().security_manager_protocol_name());
      return false;
  }
  if (context_) {
    free_ctx(&context_);
  }
  context_ = SSL_CTX_new(method);

  utils::ScopeGuard guard = utils::MakeGuard(free_ctx, &context_);

  // Disable SSL2 as deprecated
  SSL_CTX_set_options(context_, SSL_OP_NO_SSLv2);

  set_certificate(get_settings().certificate_data());

  if (get_settings().ciphers_list().empty()) {
    LOG4CXX_WARN(logger_, "Empty ciphers list");
  } else {
    LOG4CXX_DEBUG(logger_, "Cipher list: " << get_settings().ciphers_list());
    if (!SSL_CTX_set_cipher_list(context_,
                                 get_settings().ciphers_list().c_str())) {
      LOG4CXX_ERROR(
          logger_,
          "Could not set cipher list: " << get_settings().ciphers_list());
      return false;
    }
  }

  if (get_settings().ca_cert_path().empty()) {
    LOG4CXX_WARN(logger_, "Setting up empty CA certificate location");
  }

  LOG4CXX_DEBUG(logger_, "Setting up CA certificate location");
  const int result = SSL_CTX_load_verify_locations(
      context_, NULL, get_settings().ca_cert_path().c_str());

  if (!result) {
    const unsigned long error = ERR_get_error();
    UNUSED(error);
    LOG4CXX_WARN(logger_,
                 "Wrong certificate file '"
                     << get_settings().ca_cert_path() << "', err 0x" << std::hex
                     << error << " \"" << ERR_reason_error_string(error)
                     << '"');
  }

  guard.Dismiss();

  const int verify_mode =
      get_settings().verify_peer()
          ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
          : SSL_VERIFY_NONE;
  LOG4CXX_DEBUG(logger_,
                "Setting up peer verification in mode: " << verify_mode);
  SSL_CTX_set_verify(context_, verify_mode, &debug_callback);
  return true;
}

bool CryptoManagerImpl::OnCertificateUpdated(const std::string& data) {
  LOG4CXX_AUTO_TRACE(logger_);
  if (!context_) {
    LOG4CXX_WARN(logger_, "Not initialized");
    return false;
  }

  return set_certificate(data);
}

SSLContext* CryptoManagerImpl::CreateSSLContext() {
  if (context_ == NULL) {
    return NULL;
  }

  SSL* conn = SSL_new(context_);
  if (conn == NULL)
    return NULL;

  if (get_settings().security_manager_mode() == SERVER) {
    SSL_set_accept_state(conn);
  } else {
    SSL_set_connect_state(conn);
  }
  return new SSLContextImpl(conn,
                            get_settings().security_manager_mode(),
                            get_settings().maximum_payload_size());
}

void CryptoManagerImpl::ReleaseSSLContext(SSLContext* context) {
  delete context;
}

std::string CryptoManagerImpl::LastError() const {
  if (!context_) {
    return std::string("Initialization is not completed");
  }
  const char* reason = ERR_reason_error_string(ERR_get_error());
  return std::string(reason ? reason : "");
}

bool CryptoManagerImpl::IsCertificateUpdateRequired() const {
  LOG4CXX_AUTO_TRACE(logger_);

  const time_t cert_date = mktime(&expiration_time_);

  if (cert_date == -1) {
    LOG4CXX_WARN(logger_,
                 "The certifiacte expiration time cannot be represented.");
    return false;
  }
  const time_t now = time(NULL);
  const double seconds = difftime(cert_date, now);

  LOG4CXX_DEBUG(logger_,
                "Certificate expiration time: " << asctime(&expiration_time_));
  LOG4CXX_DEBUG(logger_,
                "Host time: " << asctime(localtime(&now))
                              << ". Seconds before expiration: " << seconds);
  if (seconds < 0) {
    LOG4CXX_WARN(logger_, "Certificate is already expired.");
    return true;
  }

  return seconds <= (get_settings().update_before_hours() *
                     date_time::DateTime::SECONDS_IN_HOUR);
}

const CryptoManagerSettings& CryptoManagerImpl::get_settings() const {
  return *settings_;
}

bool CryptoManagerImpl::set_certificate(const std::string& cert_data) {
  if (cert_data.empty()) {
    LOG4CXX_WARN(logger_, "Empty certificate");
    return false;
  }

  BIO* bio_cert = BIO_new_mem_buf(const_cast<char*>(cert_data.c_str()), cert_data.length());

  utils::ScopeGuard bio_guard = utils::MakeGuard(BIO_free, bio_cert);
  UNUSED(bio_guard)

  X509* cert = NULL;
  PEM_read_bio_X509(bio_cert, &cert,0, 0);

  EVP_PKEY* pkey = NULL;
  if (1 == BIO_reset(bio_cert)) {
    PEM_read_bio_PrivateKey(bio_cert, &pkey, 0,0);
  } else {
    LOG4CXX_WARN(logger_, "Unabled to reset BIO in order to read private key, " << LastError());
  }

  if (NULL == cert || NULL == pkey) {
    LOG4CXX_WARN(logger_, "Either certificate or key not valid.");
    return false;
  }

  if (!SSL_CTX_use_certificate(context_, cert)) {
    LOG4CXX_WARN(logger_, "Could not use certificate");
    return false;
  }

  asn1_time_to_tm(X509_get_notAfter(cert));

  if (!SSL_CTX_use_PrivateKey(context_, pkey)) {
    LOG4CXX_ERROR(logger_, "Could not use key");
    return false;
  }
  if (!SSL_CTX_check_private_key(context_)) {
    LOG4CXX_ERROR(logger_, "Could not use certificate ");
    return false;
  }
  return true;
}

int CryptoManagerImpl::pull_number_from_buf(char* buf, int* idx) {
  if (!idx) {
    return 0;
  }
  const int val = ((buf[*idx] - '0') * 10) + buf[(*idx) + 1] - '0';
  *idx = *idx + 2;
  return val;
}

void CryptoManagerImpl::asn1_time_to_tm(ASN1_TIME* time) {
  char* buf = (char*)time->data;
  int index = 0;
  const int year = pull_number_from_buf(buf, &index);
  if (V_ASN1_GENERALIZEDTIME == time->type) {
    expiration_time_.tm_year =
        (year * 100 - 1900) + pull_number_from_buf(buf, &index);
  } else {
    expiration_time_.tm_year = year < 50 ? year + 100 : year;
  }

  const int mon = pull_number_from_buf(buf, &index);
  const int day = pull_number_from_buf(buf, &index);
  const int hour = pull_number_from_buf(buf, &index);
  const int mn = pull_number_from_buf(buf, &index);

  expiration_time_.tm_mon = mon - 1;
  expiration_time_.tm_mday = day;
  expiration_time_.tm_hour = hour;
  expiration_time_.tm_min = mn;

  if (buf[index] == 'Z') {
    expiration_time_.tm_sec = 0;
  }
  if ((buf[index] == '+') || (buf[index] == '-')) {
    const int mn = pull_number_from_buf(buf, &index);
    const int mn1 = pull_number_from_buf(buf, &index);
    expiration_time_.tm_sec = (mn * 3600) + (mn1 * 60);
  } else {
    const int sec = pull_number_from_buf(buf, &index);
    expiration_time_.tm_sec = sec;
  }
}

}  // namespace security_manager
<commit_msg>Fix code style<commit_after>/*
 * Copyright (c) 2014, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 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.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * 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 OR 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.
 */

#include "security_manager/crypto_manager_impl.h"

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <ctime>
#include "security_manager/security_manager.h"

#include "utils/logger.h"
#include "utils/atomic.h"
#include "utils/macro.h"
#include "utils/scope_guard.h"
#include "utils/date_time.h"

#define TLS1_1_MINIMAL_VERSION 0x1000103fL
#define CONST_SSL_METHOD_MINIMAL_VERSION 0x00909000L

namespace security_manager {

CREATE_LOGGERPTR_GLOBAL(logger_, "SecurityManager")

uint32_t CryptoManagerImpl::instance_count_ = 0;
sync_primitives::Lock CryptoManagerImpl::instance_lock_;

namespace {
int debug_callback(int preverify_ok, X509_STORE_CTX* ctx) {
  if (!preverify_ok) {
    const int error = X509_STORE_CTX_get_error(ctx);
    UNUSED(error);
    LOG4CXX_WARN(logger_,
                 "Certificate verification failed with error "
                     << error << " \"" << X509_verify_cert_error_string(error)
                     << '"');
  }
  return preverify_ok;
}

void free_ctx(SSL_CTX** ctx) {
  if (ctx) {
    SSL_CTX_free(*ctx);
    *ctx = NULL;
  }
}
}

CryptoManagerImpl::CryptoManagerImpl(
    const utils::SharedPtr<const CryptoManagerSettings> set)
    : settings_(set), context_(NULL) {
  LOG4CXX_AUTO_TRACE(logger_);
  sync_primitives::AutoLock lock(instance_lock_);
  instance_count_++;
  if (instance_count_ == 1) {
    LOG4CXX_DEBUG(logger_, "Openssl engine initialization");
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    OpenSSL_add_all_algorithms();
    SSL_library_init();
  }
}

CryptoManagerImpl::~CryptoManagerImpl() {
  LOG4CXX_AUTO_TRACE(logger_);
  sync_primitives::AutoLock lock(instance_lock_);
  LOG4CXX_DEBUG(logger_, "Deinitilization");
  if (!context_) {
    LOG4CXX_WARN(logger_, "Manager is not initialized");
  } else {
    SSL_CTX_free(context_);
  }
  instance_count_--;
  if (instance_count_ == 0) {
    LOG4CXX_DEBUG(logger_, "Openssl engine deinitialization");
    EVP_cleanup();
    ERR_free_strings();
  }
}

bool CryptoManagerImpl::Init() {
  LOG4CXX_AUTO_TRACE(logger_);

  const Mode mode = get_settings().security_manager_mode();
  const bool is_server = (mode == SERVER);
  if (is_server) {
    LOG4CXX_DEBUG(logger_, "Server mode");
  } else {
    LOG4CXX_DEBUG(logger_, "Client mode");
  }
  LOG4CXX_DEBUG(logger_,
                "Peer verification "
                    << (get_settings().verify_peer() ? "enabled" : "disabled"));
  LOG4CXX_DEBUG(logger_,
                "CA certificate file is \"" << get_settings().ca_cert_path()
                                            << '"');

#if OPENSSL_VERSION_NUMBER < CONST_SSL_METHOD_MINIMAL_VERSION
  SSL_METHOD* method;
#else
  const SSL_METHOD* method;
#endif
  switch (get_settings().security_manager_protocol_name()) {
    case SSLv3:
      method = is_server ? SSLv3_server_method() : SSLv3_client_method();
      break;
    case TLSv1:
      method = is_server ? TLSv1_server_method() : TLSv1_client_method();
      break;
    case TLSv1_1:
#if OPENSSL_VERSION_NUMBER < TLS1_1_MINIMAL_VERSION
      LOG4CXX_WARN(
          logger_,
          "OpenSSL has no TLSv1.1 with version lower 1.0.1, set TLSv1.0");
      method = is_server ? TLSv1_server_method() : TLSv1_client_method();
#else
      method = is_server ? TLSv1_1_server_method() : TLSv1_1_client_method();
#endif
      break;
    case TLSv1_2:
#if OPENSSL_VERSION_NUMBER < TLS1_1_MINIMAL_VERSION
      LOG4CXX_WARN(
          logger_,
          "OpenSSL has no TLSv1.2 with version lower 1.0.1, set TLSv1.0");
      method = is_server ? TLSv1_server_method() : TLSv1_client_method();
#else
      method = is_server ? TLSv1_2_server_method() : TLSv1_2_client_method();
#endif
      break;
    default:
      LOG4CXX_ERROR(logger_,
                    "Unknown protocol: "
                        << get_settings().security_manager_protocol_name());
      return false;
  }
  if (context_) {
    free_ctx(&context_);
  }
  context_ = SSL_CTX_new(method);

  utils::ScopeGuard guard = utils::MakeGuard(free_ctx, &context_);

  // Disable SSL2 as deprecated
  SSL_CTX_set_options(context_, SSL_OP_NO_SSLv2);

  set_certificate(get_settings().certificate_data());

  if (get_settings().ciphers_list().empty()) {
    LOG4CXX_WARN(logger_, "Empty ciphers list");
  } else {
    LOG4CXX_DEBUG(logger_, "Cipher list: " << get_settings().ciphers_list());
    if (!SSL_CTX_set_cipher_list(context_,
                                 get_settings().ciphers_list().c_str())) {
      LOG4CXX_ERROR(
          logger_,
          "Could not set cipher list: " << get_settings().ciphers_list());
      return false;
    }
  }

  if (get_settings().ca_cert_path().empty()) {
    LOG4CXX_WARN(logger_, "Setting up empty CA certificate location");
  }

  LOG4CXX_DEBUG(logger_, "Setting up CA certificate location");
  const int result = SSL_CTX_load_verify_locations(
      context_, NULL, get_settings().ca_cert_path().c_str());

  if (!result) {
    const unsigned long error = ERR_get_error();
    UNUSED(error);
    LOG4CXX_WARN(logger_,
                 "Wrong certificate file '"
                     << get_settings().ca_cert_path() << "', err 0x" << std::hex
                     << error << " \"" << ERR_reason_error_string(error)
                     << '"');
  }

  guard.Dismiss();

  const int verify_mode =
      get_settings().verify_peer()
          ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
          : SSL_VERIFY_NONE;
  LOG4CXX_DEBUG(logger_,
                "Setting up peer verification in mode: " << verify_mode);
  SSL_CTX_set_verify(context_, verify_mode, &debug_callback);
  return true;
}

bool CryptoManagerImpl::OnCertificateUpdated(const std::string& data) {
  LOG4CXX_AUTO_TRACE(logger_);
  if (!context_) {
    LOG4CXX_WARN(logger_, "Not initialized");
    return false;
  }

  return set_certificate(data);
}

SSLContext* CryptoManagerImpl::CreateSSLContext() {
  if (context_ == NULL) {
    return NULL;
  }

  SSL* conn = SSL_new(context_);
  if (conn == NULL)
    return NULL;

  if (get_settings().security_manager_mode() == SERVER) {
    SSL_set_accept_state(conn);
  } else {
    SSL_set_connect_state(conn);
  }
  return new SSLContextImpl(conn,
                            get_settings().security_manager_mode(),
                            get_settings().maximum_payload_size());
}

void CryptoManagerImpl::ReleaseSSLContext(SSLContext* context) {
  delete context;
}

std::string CryptoManagerImpl::LastError() const {
  if (!context_) {
    return std::string("Initialization is not completed");
  }
  const char* reason = ERR_reason_error_string(ERR_get_error());
  return std::string(reason ? reason : "");
}

bool CryptoManagerImpl::IsCertificateUpdateRequired() const {
  LOG4CXX_AUTO_TRACE(logger_);

  const time_t cert_date = mktime(&expiration_time_);

  if (cert_date == -1) {
    LOG4CXX_WARN(logger_,
                 "The certifiacte expiration time cannot be represented.");
    return false;
  }
  const time_t now = time(NULL);
  const double seconds = difftime(cert_date, now);

  LOG4CXX_DEBUG(logger_,
                "Certificate expiration time: " << asctime(&expiration_time_));
  LOG4CXX_DEBUG(logger_,
                "Host time: " << asctime(localtime(&now))
                              << ". Seconds before expiration: " << seconds);
  if (seconds < 0) {
    LOG4CXX_WARN(logger_, "Certificate is already expired.");
    return true;
  }

  return seconds <= (get_settings().update_before_hours() *
                     date_time::DateTime::SECONDS_IN_HOUR);
}

const CryptoManagerSettings& CryptoManagerImpl::get_settings() const {
  return *settings_;
}

bool CryptoManagerImpl::set_certificate(const std::string& cert_data) {
  if (cert_data.empty()) {
    LOG4CXX_WARN(logger_, "Empty certificate");
    return false;
  }

  BIO* bio_cert =
      BIO_new_mem_buf(const_cast<char*>(cert_data.c_str()), cert_data.length());

  utils::ScopeGuard bio_guard = utils::MakeGuard(BIO_free, bio_cert);
  UNUSED(bio_guard)

  X509* cert = NULL;
  PEM_read_bio_X509(bio_cert, &cert, 0, 0);

  EVP_PKEY* pkey = NULL;
  if (1 == BIO_reset(bio_cert)) {
    PEM_read_bio_PrivateKey(bio_cert, &pkey, 0, 0);
  } else {
    LOG4CXX_WARN(logger_,
                 "Unabled to reset BIO in order to read private key, "
                     << LastError());
  }

  if (NULL == cert || NULL == pkey) {
    LOG4CXX_WARN(logger_, "Either certificate or key not valid.");
    return false;
  }

  if (!SSL_CTX_use_certificate(context_, cert)) {
    LOG4CXX_WARN(logger_, "Could not use certificate");
    return false;
  }

  asn1_time_to_tm(X509_get_notAfter(cert));

  if (!SSL_CTX_use_PrivateKey(context_, pkey)) {
    LOG4CXX_ERROR(logger_, "Could not use key");
    return false;
  }
  if (!SSL_CTX_check_private_key(context_)) {
    LOG4CXX_ERROR(logger_, "Could not use certificate ");
    return false;
  }
  return true;
}

int CryptoManagerImpl::pull_number_from_buf(char* buf, int* idx) {
  if (!idx) {
    return 0;
  }
  const int val = ((buf[*idx] - '0') * 10) + buf[(*idx) + 1] - '0';
  *idx = *idx + 2;
  return val;
}

void CryptoManagerImpl::asn1_time_to_tm(ASN1_TIME* time) {
  char* buf = (char*)time->data;
  int index = 0;
  const int year = pull_number_from_buf(buf, &index);
  if (V_ASN1_GENERALIZEDTIME == time->type) {
    expiration_time_.tm_year =
        (year * 100 - 1900) + pull_number_from_buf(buf, &index);
  } else {
    expiration_time_.tm_year = year < 50 ? year + 100 : year;
  }

  const int mon = pull_number_from_buf(buf, &index);
  const int day = pull_number_from_buf(buf, &index);
  const int hour = pull_number_from_buf(buf, &index);
  const int mn = pull_number_from_buf(buf, &index);

  expiration_time_.tm_mon = mon - 1;
  expiration_time_.tm_mday = day;
  expiration_time_.tm_hour = hour;
  expiration_time_.tm_min = mn;

  if (buf[index] == 'Z') {
    expiration_time_.tm_sec = 0;
  }
  if ((buf[index] == '+') || (buf[index] == '-')) {
    const int mn = pull_number_from_buf(buf, &index);
    const int mn1 = pull_number_from_buf(buf, &index);
    expiration_time_.tm_sec = (mn * 3600) + (mn1 * 60);
  } else {
    const int sec = pull_number_from_buf(buf, &index);
    expiration_time_.tm_sec = sec;
  }
}

}  // namespace security_manager
<|endoftext|>


<commit_before>// Copyright 2017 The NXT Authors
//
// Licensed 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 "backend/vulkan/CommandBufferVk.h"

#include "backend/Commands.h"
#include "backend/vulkan/BufferVk.h"
#include "backend/vulkan/FramebufferVk.h"
#include "backend/vulkan/RenderPassVk.h"
#include "backend/vulkan/RenderPipelineVk.h"
#include "backend/vulkan/TextureVk.h"
#include "backend/vulkan/VulkanBackend.h"

namespace backend { namespace vulkan {

    namespace {

        VkBufferImageCopy ComputeBufferImageCopyRegion(uint32_t rowPitch,
                                                       const BufferCopyLocation& bufferLocation,
                                                       const TextureCopyLocation& textureLocation) {
            const Texture* texture = ToBackend(textureLocation.texture).Get();

            VkBufferImageCopy region;

            region.bufferOffset = bufferLocation.offset;
            // In Vulkan the row length is in texels while it is in bytes for NXT
            region.bufferRowLength = rowPitch / TextureFormatPixelSize(texture->GetFormat());
            region.bufferImageHeight = rowPitch * textureLocation.height;

            region.imageSubresource.aspectMask = texture->GetVkAspectMask();
            region.imageSubresource.mipLevel = textureLocation.level;
            region.imageSubresource.baseArrayLayer = 0;
            region.imageSubresource.layerCount = 1;

            region.imageOffset.x = textureLocation.x;
            region.imageOffset.y = textureLocation.y;
            region.imageOffset.z = textureLocation.z;

            region.imageExtent.width = textureLocation.width;
            region.imageExtent.height = textureLocation.height;
            region.imageExtent.depth = textureLocation.depth;

            return region;
        }

    }  // anonymous namespace

    CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
        : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
    }

    CommandBuffer::~CommandBuffer() {
        FreeCommands(&mCommands);
    }

    void CommandBuffer::RecordCommands(VkCommandBuffer commands) {
        Device* device = ToBackend(GetDevice());

        Command type;
        while (mCommands.NextCommandId(&type)) {
            switch (type) {
                case Command::CopyBufferToBuffer: {
                    CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBufferCopy region;
                    region.srcOffset = src.offset;
                    region.dstOffset = dst.offset;
                    region.size = copy->size;

                    VkBuffer srcHandle = ToBackend(src.buffer)->GetHandle();
                    VkBuffer dstHandle = ToBackend(dst.buffer)->GetHandle();
                    device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, &region);
                } break;

                case Command::CopyBufferToTexture: {
                    CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle();
                    VkImage dstImage = ToBackend(dst.texture)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, src, dst);

                    // The image is written to so the NXT guarantees make sure it is in the
                    // TRANSFER_DST_OPTIMAL layout
                    device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage,
                                                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
                                                    &region);
                } break;

                case Command::CopyTextureToBuffer: {
                    CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkImage srcImage = ToBackend(src.texture)->GetHandle();
                    VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, dst, src);

                    // The NXT TransferSrc usage is always mapped to GENERAL
                    device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL,
                                                    dstBuffer, 1, &region);
                } break;

                case Command::BeginRenderPass: {
                    BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
                    Framebuffer* framebuffer = ToBackend(cmd->framebuffer.Get());
                    RenderPass* renderPass = ToBackend(cmd->renderPass.Get());

                    // NXT has an implicit transition to color attachment on subpasses. Transition
                    // the attachments now before we start the render pass.
                    for (uint32_t i = 0; i < renderPass->GetAttachmentCount(); ++i) {
                        Texture* attachment =
                            ToBackend(framebuffer->GetTextureView(i)->GetTexture());

                        if (attachment->GetUsage() & nxt::TextureUsageBit::OutputAttachment) {
                            continue;
                        }

                        attachment->RecordBarrier(commands, attachment->GetUsage(),
                                                  nxt::TextureUsageBit::OutputAttachment);
                        attachment->UpdateUsageInternal(nxt::TextureUsageBit::OutputAttachment);
                    }

                    ASSERT(renderPass->GetSubpassCount() == 1);
                    ASSERT(renderPass->GetAttachmentCount() <= kMaxColorAttachments + 1);

                    std::array<VkClearValue, kMaxColorAttachments + 1> clearValues;
                    framebuffer->FillClearValues(clearValues.data());

                    VkRenderPassBeginInfo beginInfo;
                    beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
                    beginInfo.pNext = nullptr;
                    beginInfo.renderPass = renderPass->GetHandle();
                    beginInfo.framebuffer = framebuffer->GetHandle();
                    beginInfo.renderArea.offset.x = 0;
                    beginInfo.renderArea.offset.y = 0;
                    beginInfo.renderArea.extent.width = framebuffer->GetWidth();
                    beginInfo.renderArea.extent.height = framebuffer->GetHeight();
                    beginInfo.clearValueCount = renderPass->GetAttachmentCount();
                    beginInfo.pClearValues = clearValues.data();

                    device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);

                    // Set all the dynamic state just in case.
                    device->fn.CmdSetLineWidth(commands, 1.0f);
                    device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f);

                    device->fn.CmdSetStencilCompareMask(commands, VK_STENCIL_FRONT_AND_BACK, 0);
                    device->fn.CmdSetStencilWriteMask(commands, VK_STENCIL_FRONT_AND_BACK, 0);
                    device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0);

                    // The viewport and scissor default to cover all of the attachments
                    VkViewport viewport;
                    viewport.x = 0.0f;
                    viewport.y = 0.0f;
                    viewport.width = static_cast<float>(framebuffer->GetWidth());
                    viewport.height = static_cast<float>(framebuffer->GetHeight());
                    viewport.minDepth = 0.0f;
                    viewport.maxDepth = 1.0f;
                    device->fn.CmdSetViewport(commands, 0, 1, &viewport);

                    VkRect2D scissorRect;
                    scissorRect.offset.x = 0;
                    scissorRect.offset.y = 0;
                    scissorRect.extent.width = framebuffer->GetWidth();
                    scissorRect.extent.height = framebuffer->GetHeight();
                    device->fn.CmdSetScissor(commands, 0, 1, &scissorRect);
                } break;

                case Command::BeginRenderSubpass: {
                    mCommands.NextCommand<BeginRenderSubpassCmd>();
                    // Do nothing related to subpasses because the single subpass is started in
                    // vkBeginRenderPass

                    // Set up the default state
                    float blendConstants[4] = {
                        0.0f,
                        0.0f,
                        0.0f,
                        0.0f,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::DrawArrays: {
                    DrawArraysCmd* draw = mCommands.NextCommand<DrawArraysCmd>();

                    device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount,
                                       draw->firstVertex, draw->firstInstance);
                } break;

                case Command::DrawElements: {
                    DrawElementsCmd* draw = mCommands.NextCommand<DrawElementsCmd>();

                    uint32_t vertexOffset = 0;
                    device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount,
                                              draw->firstIndex, vertexOffset, draw->firstInstance);
                } break;

                case Command::EndRenderPass: {
                    mCommands.NextCommand<EndRenderPassCmd>();
                    device->fn.CmdEndRenderPass(commands);
                } break;

                case Command::EndRenderSubpass: {
                    mCommands.NextCommand<EndRenderSubpassCmd>();
                    // Do nothing because the single subpass is ended in vkEndRenderPass
                } break;

                case Command::SetBlendColor: {
                    SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>();
                    float blendConstants[4] = {
                        cmd->r,
                        cmd->g,
                        cmd->b,
                        cmd->a,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::SetIndexBuffer: {
                    SetIndexBufferCmd* cmd = mCommands.NextCommand<SetIndexBufferCmd>();
                    VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle();

                    // TODO(cwallez@chromium.org): get the index type from the last render pipeline
                    // and rebind if needed on pipeline change
                    device->fn.CmdBindIndexBuffer(commands, indexBuffer,
                                                  static_cast<VkDeviceSize>(cmd->offset),
                                                  VK_INDEX_TYPE_UINT16);
                } break;

                case Command::SetRenderPipeline: {
                    SetRenderPipelineCmd* cmd = mCommands.NextCommand<SetRenderPipelineCmd>();
                    RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get();

                    device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                               pipeline->GetHandle());
                } break;

                case Command::SetVertexBuffers: {
                    SetVertexBuffersCmd* cmd = mCommands.NextCommand<SetVertexBuffersCmd>();
                    auto buffers = mCommands.NextData<Ref<BufferBase>>(cmd->count);
                    auto offsets = mCommands.NextData<uint32_t>(cmd->count);

                    std::array<VkBuffer, kMaxVertexInputs> vkBuffers;
                    std::array<VkDeviceSize, kMaxVertexInputs> vkOffsets;

                    for (uint32_t i = 0; i < cmd->count; ++i) {
                        Buffer* buffer = ToBackend(buffers[i].Get());
                        vkBuffers[i] = buffer->GetHandle();
                        vkOffsets[i] = static_cast<VkDeviceSize>(offsets[i]);
                    }

                    device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count,
                                                    vkBuffers.data(), vkOffsets.data());
                } break;

                case Command::TransitionBufferUsage: {
                    TransitionBufferUsageCmd* cmd =
                        mCommands.NextCommand<TransitionBufferUsageCmd>();

                    Buffer* buffer = ToBackend(cmd->buffer.Get());
                    buffer->RecordBarrier(commands, buffer->GetUsage(), cmd->usage);
                    buffer->UpdateUsageInternal(cmd->usage);
                } break;

                case Command::TransitionTextureUsage: {
                    TransitionTextureUsageCmd* cmd =
                        mCommands.NextCommand<TransitionTextureUsageCmd>();

                    Texture* texture = ToBackend(cmd->texture.Get());
                    texture->RecordBarrier(commands, texture->GetUsage(), cmd->usage);
                    texture->UpdateUsageInternal(cmd->usage);
                } break;

                default: { UNREACHABLE(); } break;
            }
        }
    }

}}  // namespace backend::vulkan
<commit_msg>Vulkan: Implement SetBindGroup<commit_after>// Copyright 2017 The NXT Authors
//
// Licensed 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 "backend/vulkan/CommandBufferVk.h"

#include "backend/Commands.h"
#include "backend/vulkan/BindGroupVk.h"
#include "backend/vulkan/BufferVk.h"
#include "backend/vulkan/FramebufferVk.h"
#include "backend/vulkan/PipelineLayoutVk.h"
#include "backend/vulkan/RenderPassVk.h"
#include "backend/vulkan/RenderPipelineVk.h"
#include "backend/vulkan/TextureVk.h"
#include "backend/vulkan/VulkanBackend.h"

namespace backend { namespace vulkan {

    namespace {

        VkBufferImageCopy ComputeBufferImageCopyRegion(uint32_t rowPitch,
                                                       const BufferCopyLocation& bufferLocation,
                                                       const TextureCopyLocation& textureLocation) {
            const Texture* texture = ToBackend(textureLocation.texture).Get();

            VkBufferImageCopy region;

            region.bufferOffset = bufferLocation.offset;
            // In Vulkan the row length is in texels while it is in bytes for NXT
            region.bufferRowLength = rowPitch / TextureFormatPixelSize(texture->GetFormat());
            region.bufferImageHeight = rowPitch * textureLocation.height;

            region.imageSubresource.aspectMask = texture->GetVkAspectMask();
            region.imageSubresource.mipLevel = textureLocation.level;
            region.imageSubresource.baseArrayLayer = 0;
            region.imageSubresource.layerCount = 1;

            region.imageOffset.x = textureLocation.x;
            region.imageOffset.y = textureLocation.y;
            region.imageOffset.z = textureLocation.z;

            region.imageExtent.width = textureLocation.width;
            region.imageExtent.height = textureLocation.height;
            region.imageExtent.depth = textureLocation.depth;

            return region;
        }

    }  // anonymous namespace

    CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
        : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
    }

    CommandBuffer::~CommandBuffer() {
        FreeCommands(&mCommands);
    }

    void CommandBuffer::RecordCommands(VkCommandBuffer commands) {
        Device* device = ToBackend(GetDevice());

        RenderPipeline* lastRenderPipeline = nullptr;

        Command type;
        while (mCommands.NextCommandId(&type)) {
            switch (type) {
                case Command::CopyBufferToBuffer: {
                    CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBufferCopy region;
                    region.srcOffset = src.offset;
                    region.dstOffset = dst.offset;
                    region.size = copy->size;

                    VkBuffer srcHandle = ToBackend(src.buffer)->GetHandle();
                    VkBuffer dstHandle = ToBackend(dst.buffer)->GetHandle();
                    device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, &region);
                } break;

                case Command::CopyBufferToTexture: {
                    CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle();
                    VkImage dstImage = ToBackend(dst.texture)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, src, dst);

                    // The image is written to so the NXT guarantees make sure it is in the
                    // TRANSFER_DST_OPTIMAL layout
                    device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage,
                                                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
                                                    &region);
                } break;

                case Command::CopyTextureToBuffer: {
                    CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
                    auto& src = copy->source;
                    auto& dst = copy->destination;

                    VkImage srcImage = ToBackend(src.texture)->GetHandle();
                    VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle();
                    VkBufferImageCopy region =
                        ComputeBufferImageCopyRegion(copy->rowPitch, dst, src);

                    // The NXT TransferSrc usage is always mapped to GENERAL
                    device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL,
                                                    dstBuffer, 1, &region);
                } break;

                case Command::BeginRenderPass: {
                    BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
                    Framebuffer* framebuffer = ToBackend(cmd->framebuffer.Get());
                    RenderPass* renderPass = ToBackend(cmd->renderPass.Get());

                    // NXT has an implicit transition to color attachment on subpasses. Transition
                    // the attachments now before we start the render pass.
                    for (uint32_t i = 0; i < renderPass->GetAttachmentCount(); ++i) {
                        Texture* attachment =
                            ToBackend(framebuffer->GetTextureView(i)->GetTexture());

                        if (attachment->GetUsage() & nxt::TextureUsageBit::OutputAttachment) {
                            continue;
                        }

                        attachment->RecordBarrier(commands, attachment->GetUsage(),
                                                  nxt::TextureUsageBit::OutputAttachment);
                        attachment->UpdateUsageInternal(nxt::TextureUsageBit::OutputAttachment);
                    }

                    ASSERT(renderPass->GetSubpassCount() == 1);
                    ASSERT(renderPass->GetAttachmentCount() <= kMaxColorAttachments + 1);

                    std::array<VkClearValue, kMaxColorAttachments + 1> clearValues;
                    framebuffer->FillClearValues(clearValues.data());

                    VkRenderPassBeginInfo beginInfo;
                    beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
                    beginInfo.pNext = nullptr;
                    beginInfo.renderPass = renderPass->GetHandle();
                    beginInfo.framebuffer = framebuffer->GetHandle();
                    beginInfo.renderArea.offset.x = 0;
                    beginInfo.renderArea.offset.y = 0;
                    beginInfo.renderArea.extent.width = framebuffer->GetWidth();
                    beginInfo.renderArea.extent.height = framebuffer->GetHeight();
                    beginInfo.clearValueCount = renderPass->GetAttachmentCount();
                    beginInfo.pClearValues = clearValues.data();

                    device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);

                    // Set all the dynamic state just in case.
                    device->fn.CmdSetLineWidth(commands, 1.0f);
                    device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f);

                    device->fn.CmdSetStencilCompareMask(commands, VK_STENCIL_FRONT_AND_BACK, 0);
                    device->fn.CmdSetStencilWriteMask(commands, VK_STENCIL_FRONT_AND_BACK, 0);
                    device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0);

                    // The viewport and scissor default to cover all of the attachments
                    VkViewport viewport;
                    viewport.x = 0.0f;
                    viewport.y = 0.0f;
                    viewport.width = static_cast<float>(framebuffer->GetWidth());
                    viewport.height = static_cast<float>(framebuffer->GetHeight());
                    viewport.minDepth = 0.0f;
                    viewport.maxDepth = 1.0f;
                    device->fn.CmdSetViewport(commands, 0, 1, &viewport);

                    VkRect2D scissorRect;
                    scissorRect.offset.x = 0;
                    scissorRect.offset.y = 0;
                    scissorRect.extent.width = framebuffer->GetWidth();
                    scissorRect.extent.height = framebuffer->GetHeight();
                    device->fn.CmdSetScissor(commands, 0, 1, &scissorRect);
                } break;

                case Command::BeginRenderSubpass: {
                    mCommands.NextCommand<BeginRenderSubpassCmd>();
                    // Do nothing related to subpasses because the single subpass is started in
                    // vkBeginRenderPass

                    // Set up the default state
                    float blendConstants[4] = {
                        0.0f,
                        0.0f,
                        0.0f,
                        0.0f,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::DrawArrays: {
                    DrawArraysCmd* draw = mCommands.NextCommand<DrawArraysCmd>();

                    device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount,
                                       draw->firstVertex, draw->firstInstance);
                } break;

                case Command::DrawElements: {
                    DrawElementsCmd* draw = mCommands.NextCommand<DrawElementsCmd>();

                    uint32_t vertexOffset = 0;
                    device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount,
                                              draw->firstIndex, vertexOffset, draw->firstInstance);
                } break;

                case Command::EndRenderPass: {
                    mCommands.NextCommand<EndRenderPassCmd>();
                    device->fn.CmdEndRenderPass(commands);
                } break;

                case Command::EndRenderSubpass: {
                    mCommands.NextCommand<EndRenderSubpassCmd>();
                    // Do nothing because the single subpass is ended in vkEndRenderPass
                } break;

                case Command::SetBindGroup: {
                    SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
                    VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();

                    // TODO(cwallez@chromium.org): Add some dirty bits for this to allow setting
                    // before there is a pipeline layout
                    // TODO(cwallez@chromium.org): fix for compute passes
                    VkPipelineLayout layout =
                        ToBackend(lastRenderPipeline->GetLayout())->GetHandle();
                    device->fn.CmdBindDescriptorSets(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                                     layout, cmd->index, 1, &set, 0, nullptr);
                } break;

                case Command::SetBlendColor: {
                    SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>();
                    float blendConstants[4] = {
                        cmd->r,
                        cmd->g,
                        cmd->b,
                        cmd->a,
                    };
                    device->fn.CmdSetBlendConstants(commands, blendConstants);
                } break;

                case Command::SetIndexBuffer: {
                    SetIndexBufferCmd* cmd = mCommands.NextCommand<SetIndexBufferCmd>();
                    VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle();

                    // TODO(cwallez@chromium.org): get the index type from the last render pipeline
                    // and rebind if needed on pipeline change
                    device->fn.CmdBindIndexBuffer(commands, indexBuffer,
                                                  static_cast<VkDeviceSize>(cmd->offset),
                                                  VK_INDEX_TYPE_UINT16);
                } break;

                case Command::SetRenderPipeline: {
                    SetRenderPipelineCmd* cmd = mCommands.NextCommand<SetRenderPipelineCmd>();
                    RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get();

                    device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                               pipeline->GetHandle());
                    lastRenderPipeline = pipeline;
                } break;

                case Command::SetVertexBuffers: {
                    SetVertexBuffersCmd* cmd = mCommands.NextCommand<SetVertexBuffersCmd>();
                    auto buffers = mCommands.NextData<Ref<BufferBase>>(cmd->count);
                    auto offsets = mCommands.NextData<uint32_t>(cmd->count);

                    std::array<VkBuffer, kMaxVertexInputs> vkBuffers;
                    std::array<VkDeviceSize, kMaxVertexInputs> vkOffsets;

                    for (uint32_t i = 0; i < cmd->count; ++i) {
                        Buffer* buffer = ToBackend(buffers[i].Get());
                        vkBuffers[i] = buffer->GetHandle();
                        vkOffsets[i] = static_cast<VkDeviceSize>(offsets[i]);
                    }

                    device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count,
                                                    vkBuffers.data(), vkOffsets.data());
                } break;

                case Command::TransitionBufferUsage: {
                    TransitionBufferUsageCmd* cmd =
                        mCommands.NextCommand<TransitionBufferUsageCmd>();

                    Buffer* buffer = ToBackend(cmd->buffer.Get());
                    buffer->RecordBarrier(commands, buffer->GetUsage(), cmd->usage);
                    buffer->UpdateUsageInternal(cmd->usage);
                } break;

                case Command::TransitionTextureUsage: {
                    TransitionTextureUsageCmd* cmd =
                        mCommands.NextCommand<TransitionTextureUsageCmd>();

                    Texture* texture = ToBackend(cmd->texture.Get());
                    texture->RecordBarrier(commands, texture->GetUsage(), cmd->usage);
                    texture->UpdateUsageInternal(cmd->usage);
                } break;

                default: { UNREACHABLE(); } break;
            }
        }
    }

}}  // namespace backend::vulkan
<|endoftext|>


<commit_before>/**
 * @file
 * @brief Embox Framebuffer plugin
 *
 * @date 15.06.2019
 * @author Alexander Kalmuk
 */

#include "qgenericunixfontdatabase.h"
#include <QtGui/private/qpixmap_raster_p.h>
#include <qimage.h>
#include "emboxfbintegration.h"

#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/fb.h>

#define DPRINTF(fmt, ...) \
    do { \
        fprintf(stderr, "> QEmboxFB: " fmt, ## __VA_ARGS__); \
    } while (0)

void QEmboxFbIntegration::printFbInfo(void)
{
	DPRINTF("Framebuffer info:\n");
	DPRINTF("  Framebuffer start = 0x%x\n", (unsigned int) fbData);
	DPRINTF("  Width = %d, Height = %d\n", fbWidth, fbHeight);
	DPRINTF("  BytesPerLine = %d\n", fbBytesPerLine);
	DPRINTF("  Format = %d\n", fbFormat);
	DPRINTF("\n");
}

QEmboxFbIntegration::QEmboxFbIntegration()
	: fontDb(new QGenericUnixFontDatabase())
{
	struct fb_var_screeninfo vinfo;
	struct fb_fix_screeninfo finfo;
	const char *fbPath = "/dev/fb0";

	fbFd = open(fbPath, O_RDWR);
	if (fbFd < 0) {
		qFatal("QEmboxFbIntegration: Error open framebuffer %s", fbPath);
	}
	if (ioctl(fbFd, FBIOGET_FSCREENINFO, &finfo) == -1) {
		qFatal("QEmboxFbIntegration: Error ioctl framebuffer %s", fbPath);
	}
	if (ioctl(fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
		qFatal("QEmboxFbIntegration: Error ioctl framebuffer %s", fbPath);
	}
	fbWidth        = vinfo.xres;
	fbHeight       = vinfo.yres;
	fbBytesPerLine = finfo.line_length;
	fbSize         = fbBytesPerLine * fbHeight;
	fbFormat       = vinfo.fmt;
	fbData = (uint8_t *)mmap(0, fbSize, PROT_READ | PROT_WRITE,
				             MAP_SHARED, fbFd, 0);
	if (fbData == MAP_FAILED) {
		qFatal("QEmboxFbIntegration: Error mmap framebuffer %s", fbPath);
	}
	if (!fbData || !fbSize) {
		qFatal("QEmboxFbIntegration: Wrong framebuffer: base = %p,"
			   "size=%d", fbData, fbSize);
	}

	mPrimaryScreen = new QEmboxFbScreen(fbData, fbWidth,
							            fbHeight, fbBytesPerLine,
							            emboxFbFormatToQImageFormat(fbFormat));

	mPrimaryScreen->setPhysicalSize(QSize(fbWidth, fbHeight));
	mScreens.append(mPrimaryScreen);

	this->printFbInfo();
}

QEmboxFbIntegration::~QEmboxFbIntegration()
{
	delete mPrimaryScreen;
}

FbFormat QEmboxFbIntegration::emboxFbFormatToQImageFormat(
	int emboxFbFormat)
{
	FbFormat format;

	format.format = QImage::Format_Invalid;
	format.swapRgb = 0;

	switch (emboxFbFormat) {
	case EMBOX_BGR888:
		format.swapRgb = 1;
	case EMBOX_RGB888:
		format.format =  QImage::Format_RGB888;
		break;
	case EMBOX_BGRA8888:
		format.swapRgb = 1;
	case EMBOX_RGBA8888:
		format.format = QImage::Format_ARGB32;
		break;
	case EMBOX_BGR565:
		format.swapRgb = 1;
	case EMBOX_RGB565:
		format.format = QImage::Format_RGB16;
		break;
	default:
		qFatal("QEmboxFbIntegration: Unknown framebuffer format %d",
			emboxFbFormat);
		break;
	}
	return format;
}

bool QEmboxFbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
	switch (cap) {
	case ThreadedPixmaps:
		return true;
	default:
		return QPlatformIntegration::hasCapability(cap);
	}
}

QPixmapData *QEmboxFbIntegration::createPixmapData(QPixmapData::PixelType type) const
{
	return new QRasterPixmapData(type);
}

QPlatformWindow *QEmboxFbIntegration::createPlatformWindow(QWidget *widget, WId winId) const
{
	Q_UNUSED(winId);
	QFbWindow *w = new QFbWindow(widget);
	mPrimaryScreen->addWindow(w);
	return w;
}

QWindowSurface *QEmboxFbIntegration::createWindowSurface(QWidget *widget, WId winId) const
{
	Q_UNUSED(winId);
	return new QFbWindowSurface(mPrimaryScreen, widget);
}

QPlatformFontDatabase *QEmboxFbIntegration::fontDatabase() const
{
	return fontDb;
}

QEmboxFbScreen::QEmboxFbScreen(uint8_t *data, int width,
	int height, int bytesPerLine,
	FbFormat format) : compositePainter(0)
{
	mData         = data;
	mGeometry     = QRect(0, 0, width, height);
	mBytesPerLine = bytesPerLine;
	mFormat       = format.format;
	mSwapRgb      = format.swapRgb;
	mDepth        = 16;

	mScreenImage = new QImage(mGeometry.width(), mGeometry.height(), mFormat);
	mFbScreenImage = new QImage(mData, mGeometry.width(), mGeometry.height(),
						        mBytesPerLine, mFormat);
}

void QEmboxFbScreen::setGeometry(QRect rect)
{
	mGeometry = rect;

	delete mFbScreenImage;
	mFbScreenImage = new QImage(mData, mGeometry.width(), mGeometry.height(),
						        mBytesPerLine, mFormat);
	delete compositePainter;
	compositePainter = 0;
	delete mScreenImage;
	mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
					          mFormat);
}

void QEmboxFbScreen::setFormat(QImage::Format format)
{
	mFormat = format;

	delete mFbScreenImage;
	mFbScreenImage = new QImage(mData, mGeometry.width(), mGeometry.height(),
						        mBytesPerLine, mFormat);
	delete compositePainter;
	compositePainter = 0;
	delete mScreenImage;
	mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
						      mFormat);
}

QRegion QEmboxFbScreen::doRedraw()
{
	QVector<QRect> rects;
	QRegion touched = QFbScreen::doRedraw();

	if (!compositePainter) {
		compositePainter = new QPainter(mFbScreenImage);
	}

	rects = touched.rects();
	if (mSwapRgb) {
		for (int i = 0; i < rects.size(); i++) {
			compositePainter->drawImage(rects[i], mScreenImage->rgbSwapped(), rects[i]);
			}
	} else {
		for (int i = 0; i < rects.size(); i++) {
			compositePainter->drawImage(rects[i], *mScreenImage, rects[i]);
		}
	}
	return touched;
}
<commit_msg>qt: Add cursor to emboxfb plugin<commit_after>/**
 * @file
 * @brief Embox Framebuffer plugin
 *
 * @date 15.06.2019
 * @author Alexander Kalmuk
 */

#include "qgenericunixfontdatabase.h"
#include <QtGui/private/qpixmap_raster_p.h>
#include <qimage.h>
#include "emboxfbintegration.h"

#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/fb.h>

#define DPRINTF(fmt, ...) \
    do { \
        fprintf(stderr, "> QEmboxFB: " fmt, ## __VA_ARGS__); \
    } while (0)

void QEmboxFbIntegration::printFbInfo(void)
{
	DPRINTF("Framebuffer info:\n");
	DPRINTF("  Framebuffer start = 0x%x\n", (unsigned int) fbData);
	DPRINTF("  Width = %d, Height = %d\n", fbWidth, fbHeight);
	DPRINTF("  BytesPerLine = %d\n", fbBytesPerLine);
	DPRINTF("  Format = %d\n", fbFormat);
	DPRINTF("\n");
}

QEmboxFbIntegration::QEmboxFbIntegration()
	: fontDb(new QGenericUnixFontDatabase())
{
	struct fb_var_screeninfo vinfo;
	struct fb_fix_screeninfo finfo;
	const char *fbPath = "/dev/fb0";

	fbFd = open(fbPath, O_RDWR);
	if (fbFd < 0) {
		qFatal("QEmboxFbIntegration: Error open framebuffer %s", fbPath);
	}
	if (ioctl(fbFd, FBIOGET_FSCREENINFO, &finfo) == -1) {
		qFatal("QEmboxFbIntegration: Error ioctl framebuffer %s", fbPath);
	}
	if (ioctl(fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
		qFatal("QEmboxFbIntegration: Error ioctl framebuffer %s", fbPath);
	}
	fbWidth        = vinfo.xres;
	fbHeight       = vinfo.yres;
	fbBytesPerLine = finfo.line_length;
	fbSize         = fbBytesPerLine * fbHeight;
	fbFormat       = vinfo.fmt;
	fbData = (uint8_t *)mmap(0, fbSize, PROT_READ | PROT_WRITE,
				             MAP_SHARED, fbFd, 0);
	if (fbData == MAP_FAILED) {
		qFatal("QEmboxFbIntegration: Error mmap framebuffer %s", fbPath);
	}
	if (!fbData || !fbSize) {
		qFatal("QEmboxFbIntegration: Wrong framebuffer: base = %p,"
			   "size=%d", fbData, fbSize);
	}

	mPrimaryScreen = new QEmboxFbScreen(fbData, fbWidth,
							            fbHeight, fbBytesPerLine,
							            emboxFbFormatToQImageFormat(fbFormat));

	mPrimaryScreen->setPhysicalSize(QSize(fbWidth, fbHeight));
	mScreens.append(mPrimaryScreen);

	this->printFbInfo();
}

QEmboxFbIntegration::~QEmboxFbIntegration()
{
	delete mPrimaryScreen;
}

FbFormat QEmboxFbIntegration::emboxFbFormatToQImageFormat(
	int emboxFbFormat)
{
	FbFormat format;

	format.format = QImage::Format_Invalid;
	format.swapRgb = 0;

	switch (emboxFbFormat) {
	case EMBOX_BGR888:
		format.swapRgb = 1;
	case EMBOX_RGB888:
		format.format =  QImage::Format_RGB888;
		break;
	case EMBOX_BGRA8888:
		format.swapRgb = 1;
	case EMBOX_RGBA8888:
		format.format = QImage::Format_ARGB32;
		break;
	case EMBOX_BGR565:
		format.swapRgb = 1;
	case EMBOX_RGB565:
		format.format = QImage::Format_RGB16;
		break;
	default:
		qFatal("QEmboxFbIntegration: Unknown framebuffer format %d",
			emboxFbFormat);
		break;
	}
	return format;
}

bool QEmboxFbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
	switch (cap) {
	case ThreadedPixmaps:
		return true;
	default:
		return QPlatformIntegration::hasCapability(cap);
	}
}

QPixmapData *QEmboxFbIntegration::createPixmapData(QPixmapData::PixelType type) const
{
	return new QRasterPixmapData(type);
}

QPlatformWindow *QEmboxFbIntegration::createPlatformWindow(QWidget *widget, WId winId) const
{
	Q_UNUSED(winId);
	QFbWindow *w = new QFbWindow(widget);
	mPrimaryScreen->addWindow(w);
	return w;
}

QWindowSurface *QEmboxFbIntegration::createWindowSurface(QWidget *widget, WId winId) const
{
	Q_UNUSED(winId);
	return new QFbWindowSurface(mPrimaryScreen, widget);
}

QPlatformFontDatabase *QEmboxFbIntegration::fontDatabase() const
{
	return fontDb;
}

QEmboxFbScreen::QEmboxFbScreen(uint8_t *data, int width,
	int height, int bytesPerLine,
	FbFormat format) : compositePainter(0)
{
	mData         = data;
	mGeometry     = QRect(0, 0, width, height);
	mBytesPerLine = bytesPerLine;
	mFormat       = format.format;
	mSwapRgb      = format.swapRgb;
	mDepth        = 16;

	mScreenImage = new QImage(mGeometry.width(), mGeometry.height(), mFormat);
	mFbScreenImage = new QImage(mData, mGeometry.width(), mGeometry.height(),
						        mBytesPerLine, mFormat);
#ifndef QT_NO_CURSOR
    cursor = new QPlatformSoftwareCursor(this);
#endif
}

void QEmboxFbScreen::setGeometry(QRect rect)
{
	mGeometry = rect;

	delete mFbScreenImage;
	mFbScreenImage = new QImage(mData, mGeometry.width(), mGeometry.height(),
						        mBytesPerLine, mFormat);
	delete compositePainter;
	compositePainter = 0;
	delete mScreenImage;
	mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
					          mFormat);
}

void QEmboxFbScreen::setFormat(QImage::Format format)
{
	mFormat = format;

	delete mFbScreenImage;
	mFbScreenImage = new QImage(mData, mGeometry.width(), mGeometry.height(),
						        mBytesPerLine, mFormat);
	delete compositePainter;
	compositePainter = 0;
	delete mScreenImage;
	mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
						      mFormat);
}

QRegion QEmboxFbScreen::doRedraw()
{
	QVector<QRect> rects;
	QRegion touched = QFbScreen::doRedraw();

	if (!compositePainter) {
		compositePainter = new QPainter(mFbScreenImage);
	}

	rects = touched.rects();
	if (mSwapRgb) {
		for (int i = 0; i < rects.size(); i++) {
			compositePainter->drawImage(rects[i], mScreenImage->rgbSwapped(), rects[i]);
			}
	} else {
		for (int i = 0; i < rects.size(); i++) {
			compositePainter->drawImage(rects[i], *mScreenImage, rects[i]);
		}
	}
	return touched;
}
<|endoftext|>


<commit_before>/*
 * Launch and manage "Local HTTP" child processes.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "child_stock.hxx"
#include "child_socket.hxx"
#include "spawn/ExitListener.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "child_manager.hxx"
#include "gerrno.h"
#include "spawn/Direct.hxx"
#include "spawn/Prepared.hxx"
#include "pool.hxx"

#include <glib.h>

#include <string>

#include <assert.h>
#include <unistd.h>

struct ChildStockItem final : HeapStockItem, ExitListener {
    const int shutdown_signal;

    ChildSocket socket;
    pid_t pid = -1;

    bool busy = true;

    ChildStockItem(CreateStockItem c,
                   const ChildStockClass &_cls)
        :HeapStockItem(c),
         shutdown_signal(_cls.shutdown_signal) {}

    ~ChildStockItem() override;

    /* virtual methods from class StockItem */
    bool Borrow(gcc_unused void *ctx) override {
        assert(!busy);
        busy = true;

        return true;
    }

    bool Release(gcc_unused void *ctx) override {
        assert(busy);
        busy = false;

        /* reuse this item only if the child process hasn't exited */
        return pid > 0;
    }

    /* virtual methods from class ExitListener */
    void OnChildProcessExit(int status) override;
};

void
ChildStockItem::OnChildProcessExit(gcc_unused int status)
{
    pid = -1;

    if (!busy)
        InvokeIdleDisconnect();
}

/*
 * stock class
 *
 */

static void
child_stock_create(void *stock_ctx,
                   CreateStockItem c,
                   void *info,
                   gcc_unused struct pool &caller_pool,
                   gcc_unused struct async_operation_ref &async_ref)
{
    const auto *cls = (const ChildStockClass *)stock_ctx;

    GError *error = nullptr;

    auto *item = new ChildStockItem(c, *cls);

    int socket_type = cls->socket_type != nullptr
        ? cls->socket_type(info)
        : SOCK_STREAM;

    int fd = item->socket.Create(socket_type, &error);
    if (fd < 0) {
        item->InvokeCreateError(error);
        return;
    }

    PreparedChildProcess p;
    if (!cls->prepare(info, fd, p, &error)) {
        item->InvokeCreateError(error);
        return;
    }

    pid_t pid = item->pid = SpawnChildProcess(std::move(p));
    if (pid < 0) {
        item->InvokeCreateError(new_error_errno_msg2(-pid, "fork() failed"));
        return;
    }

    child_register(pid, item->GetStockName(), item);

    item->InvokeCreateSuccess();
}

ChildStockItem::~ChildStockItem()
{
    if (pid >= 0)
        child_kill_signal(pid, shutdown_signal);

    if (socket.IsDefined())
        socket.Unlink();
}

static constexpr StockClass child_stock_class = {
    .create = child_stock_create,
};

/*
 * interface
 *
 */

StockMap *
child_stock_new(unsigned limit, unsigned max_idle,
                const ChildStockClass *cls)
{
    assert(cls != nullptr);
    assert(cls->shutdown_signal != 0);
    assert(cls->prepare != nullptr);

    union {
        const ChildStockClass *in;
        void *out;
    } u = { .in = cls };

    return hstock_new(child_stock_class, u.out, limit, max_idle);
}

void
child_stock_free(StockMap *stock)
{
    hstock_free(stock);
}

int
child_stock_item_connect(const StockItem *_item, GError **error_r)
{
    const auto *item = (const ChildStockItem *)_item;

    return item->socket.Connect(error_r);
}
<commit_msg>child_stock: add class ChildStock<commit_after>/*
 * Launch and manage "Local HTTP" child processes.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "child_stock.hxx"
#include "child_socket.hxx"
#include "spawn/ExitListener.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "child_manager.hxx"
#include "gerrno.h"
#include "spawn/Direct.hxx"
#include "spawn/Prepared.hxx"
#include "pool.hxx"

#include <glib.h>

#include <string>

#include <assert.h>
#include <unistd.h>

struct ChildStockItem final : HeapStockItem, ExitListener {
    const int shutdown_signal;

    ChildSocket socket;
    pid_t pid = -1;

    bool busy = true;

    ChildStockItem(CreateStockItem c,
                   const ChildStockClass &_cls)
        :HeapStockItem(c),
         shutdown_signal(_cls.shutdown_signal) {}

    ~ChildStockItem() override;

    /* virtual methods from class StockItem */
    bool Borrow(gcc_unused void *ctx) override {
        assert(!busy);
        busy = true;

        return true;
    }

    bool Release(gcc_unused void *ctx) override {
        assert(busy);
        busy = false;

        /* reuse this item only if the child process hasn't exited */
        return pid > 0;
    }

    /* virtual methods from class ExitListener */
    void OnChildProcessExit(int status) override;
};

class ChildStock {
    const ChildStockClass &cls;

public:
    explicit ChildStock(const ChildStockClass &_cls)
        :cls(_cls) {}

    void Create(CreateStockItem c, void *info);

};

void
ChildStockItem::OnChildProcessExit(gcc_unused int status)
{
    pid = -1;

    if (!busy)
        InvokeIdleDisconnect();
}

/*
 * stock class
 *
 */

inline void
ChildStock::Create(CreateStockItem c, void *info)
{
    GError *error = nullptr;

    auto *item = new ChildStockItem(c, cls);

    int socket_type = cls.socket_type != nullptr
        ? cls.socket_type(info)
        : SOCK_STREAM;

    int fd = item->socket.Create(socket_type, &error);
    if (fd < 0) {
        item->InvokeCreateError(error);
        return;
    }

    PreparedChildProcess p;
    if (!cls.prepare(info, fd, p, &error)) {
        item->InvokeCreateError(error);
        return;
    }

    pid_t pid = item->pid = SpawnChildProcess(std::move(p));
    if (pid < 0) {
        item->InvokeCreateError(new_error_errno_msg2(-pid, "fork() failed"));
        return;
    }

    child_register(pid, item->GetStockName(), item);

    item->InvokeCreateSuccess();
}

static void
child_stock_create(void *stock_ctx,
                   CreateStockItem c,
                   void *info,
                   gcc_unused struct pool &caller_pool,
                   gcc_unused struct async_operation_ref &async_ref)
{
    auto &stock = *(ChildStock *)stock_ctx;

    stock.Create(c, info);
}

ChildStockItem::~ChildStockItem()
{
    if (pid >= 0)
        child_kill_signal(pid, shutdown_signal);

    if (socket.IsDefined())
        socket.Unlink();
}

static constexpr StockClass child_stock_class = {
    .create = child_stock_create,
};

/*
 * interface
 *
 */

StockMap *
child_stock_new(unsigned limit, unsigned max_idle,
                const ChildStockClass *cls)
{
    assert(cls != nullptr);
    assert(cls->shutdown_signal != 0);
    assert(cls->prepare != nullptr);

    auto *s = new ChildStock(*cls);
    return hstock_new(child_stock_class, s, limit, max_idle);
}

void
child_stock_free(StockMap *stock)
{
    auto *s = (ChildStock *)hstock_get_ctx(*stock);
    hstock_free(stock);
    delete s;
}

int
child_stock_item_connect(const StockItem *_item, GError **error_r)
{
    const auto *item = (const ChildStockItem *)_item;

    return item->socket.Connect(error_r);
}
<|endoftext|>


<commit_before><commit_msg>Refactor error printing<commit_after><|endoftext|>