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

#include "fcgi_stock.hxx"
#include "fcgi_quark.h"
#include "fcgi_launch.hxx"
#include "hstock.hxx"
#include "stock.hxx"
#include "child_stock.hxx"
#include "child_manager.hxx"
#include "ChildOptions.hxx"
#include "pevent.hxx"
#include "gerrno.h"
#include "pool.hxx"
#include "JailConfig.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cast.hxx"

#include <daemon/log.h>

#include <glib.h>

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

#ifdef __linux
#include <sched.h>
#endif

struct FcgiStock {
    StockMap *hstock;
    StockMap *child_stock;

    void FadeAll() {
        hstock_fade_all(*hstock);
        hstock_fade_all(*child_stock);
    }
};

struct FcgiChildParams {
    const char *executable_path;

    ConstBuffer<const char *> args;
    ConstBuffer<const char *> env;

    const ChildOptions *options;
};

struct FcgiConnection {
    StockItem base;

    JailParams jail_params;

    struct jail_config jail_config;

    StockItem *child;

    int fd;
    struct event event;

    /**
     * Is this a fresh connection to the FastCGI child process?
     */
    bool fresh;

    /**
     * Shall the FastCGI child process be killed?
     */
    bool kill;

    /**
     * Was the current request aborted by the fcgi_client caller?
     */
    bool aborted;
};

static const char *
fcgi_stock_key(struct pool *pool, const FcgiChildParams *params)
{
    const char *key = params->executable_path;

    for (auto i : params->args)
        key = p_strcat(pool, key, " ", i, nullptr);

    for (auto i : params->env)
        key = p_strcat(pool, key, "$", i, nullptr);

    char options_buffer[4096];
    *params->options->MakeId(options_buffer) = 0;
    if (*options_buffer != 0)
        key = p_strcat(pool, key, options_buffer, nullptr);

    return key;
}

gcc_pure
static const char *
fcgi_connection_key(const FcgiConnection *connection)
{
    return child_stock_item_key(connection->child);
}

/*
 * libevent callback
 *
 */

static void
fcgi_connection_event_callback(int fd, gcc_unused short event, void *ctx)
{
    auto *connection = (FcgiConnection *)ctx;

    assert(fd == connection->fd);

    p_event_consumed(&connection->event, connection->base.pool);

    if ((event & EV_TIMEOUT) == 0) {
        char buffer;
        ssize_t nbytes = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
        if (nbytes < 0)
            daemon_log(2, "error on idle FastCGI connection '%s': %s\n",
                       fcgi_connection_key(connection), strerror(errno));
        else if (nbytes > 0)
            daemon_log(2, "unexpected data from idle FastCGI connection '%s'\n",
                       fcgi_connection_key(connection));
    }

    stock_del(connection->base);
    pool_commit();
}

/*
 * child_stock class
 *
 */

static int
fcgi_child_stock_clone_flags(gcc_unused const char *key, void *info, int flags,
                             gcc_unused void *ctx)
{
    const FcgiChildParams *params =
        (const FcgiChildParams *)info;
    const ChildOptions *const options = params->options;

    return options->ns.GetCloneFlags(flags);
}

static int
fcgi_child_stock_run(gcc_unused struct pool *pool, gcc_unused const char *key,
                     void *info, gcc_unused void *ctx)
{
    const FcgiChildParams *params =
        (const FcgiChildParams *)info;
    const ChildOptions *const options = params->options;

    options->Apply(true);

    fcgi_run(&options->jail, params->executable_path,
             params->args, params->env);
}

static const struct child_stock_class fcgi_child_stock_class = {
    .shutdown_signal = SIGUSR1,
    .clone_flags = fcgi_child_stock_clone_flags,
    .run = fcgi_child_stock_run,
};

/*
 * stock class
 *
 */

static constexpr FcgiConnection &
ToFcgiConnection(StockItem &item)
{
    return ContainerCast2(item, &FcgiConnection::base);
}

static constexpr const FcgiConnection &
ToFcgiConnection(const StockItem &item)
{
    return ContainerCast2(item, &FcgiConnection::base);
}

static struct pool *
fcgi_stock_pool(void *ctx gcc_unused, struct pool &parent,
               const char *uri gcc_unused)
{
    return pool_new_linear(&parent, "fcgi_connection", 2048);
}

static void
fcgi_stock_create(void *ctx, StockItem &item,
                  const char *key, void *info,
                  gcc_unused struct pool &caller_pool,
                  gcc_unused struct async_operation_ref &async_ref)
{
    FcgiStock *fcgi_stock = (FcgiStock *)ctx;
    struct pool *pool = item.pool;
    FcgiChildParams *params = (FcgiChildParams *)info;
    auto *connection = &ToFcgiConnection(item);

    assert(key != nullptr);
    assert(params != nullptr);
    assert(params->executable_path != nullptr);

    const ChildOptions *const options = params->options;
    if (options->jail.enabled) {
        connection->jail_params.CopyFrom(*pool, options->jail);

        if (!jail_config_load(&connection->jail_config,
                              "/etc/cm4all/jailcgi/jail.conf", pool)) {
            GError *error = g_error_new(fcgi_quark(), 0,
                                        "Failed to load /etc/cm4all/jailcgi/jail.conf");
            stock_item_failed(item, error);
            return;
        }
    } else
        connection->jail_params.enabled = false;

    GError *error = nullptr;
    connection->child = hstock_get_now(*fcgi_stock->child_stock, *pool,
                                       key, params, &error);
    if (connection->child == nullptr) {
        g_prefix_error(&error, "failed to start to FastCGI server '%s': ",
                       key);

        stock_item_failed(item, error);
        return;
    }

    connection->fd = child_stock_item_connect(connection->child, &error);
    if (connection->fd < 0) {
        g_prefix_error(&error, "failed to connect to FastCGI server '%s': ",
                       key);

        child_stock_put(fcgi_stock->child_stock, connection->child, true);
        stock_item_failed(item, error);
        return;
    }

    connection->fresh = true;
    connection->kill = false;

    event_set(&connection->event, connection->fd, EV_READ|EV_TIMEOUT,
              fcgi_connection_event_callback, connection);

    stock_item_available(connection->base);
}

static bool
fcgi_stock_borrow(void *ctx gcc_unused, StockItem &item)
{
    auto *connection = &ToFcgiConnection(item);

    /* check the connection status before using it, just in case the
       FastCGI server has decided to close the connection before
       fcgi_connection_event_callback() got invoked */
    char buffer;
    ssize_t nbytes = recv(connection->fd, &buffer, sizeof(buffer),
                          MSG_DONTWAIT);
    if (nbytes > 0) {
        daemon_log(2, "unexpected data from idle FastCGI connection '%s'\n",
                   fcgi_connection_key(connection));
        return false;
    } else if (nbytes == 0) {
        /* connection closed (not worth a log message) */
        return false;
    } else if (errno != EAGAIN) {
        daemon_log(2, "error on idle FastCGI connection '%s': %s\n",
                   fcgi_connection_key(connection), strerror(errno));
        return false;
    }

    p_event_del(&connection->event, connection->base.pool);
    connection->aborted = false;
    return true;
}

static void
fcgi_stock_release(void *ctx gcc_unused, StockItem &item)
{
    auto *connection = &ToFcgiConnection(item);
    static const struct timeval tv = {
        .tv_sec = 300,
        .tv_usec = 0,
    };

    connection->fresh = false;

    p_event_add(&connection->event, &tv, connection->base.pool,
                "fcgi_connection_event");
}

static void
fcgi_stock_destroy(void *ctx, StockItem &item)
{
    FcgiStock *fcgi_stock = (FcgiStock *)ctx;
    auto *connection = &ToFcgiConnection(item);

    p_event_del(&connection->event, connection->base.pool);
    close(connection->fd);

    child_stock_put(fcgi_stock->child_stock, connection->child,
                    connection->kill);
}

static constexpr StockClass fcgi_stock_class = {
    .item_size = sizeof(FcgiConnection),
    .pool = fcgi_stock_pool,
    .create = fcgi_stock_create,
    .borrow = fcgi_stock_borrow,
    .release = fcgi_stock_release,
    .destroy = fcgi_stock_destroy,
};

/*
 * interface
 *
 */

FcgiStock *
fcgi_stock_new(struct pool *pool, unsigned limit, unsigned max_idle)
{
    auto fcgi_stock = NewFromPool<FcgiStock>(*pool);
    fcgi_stock->child_stock = child_stock_new(pool, limit, max_idle,
                                              &fcgi_child_stock_class);
    fcgi_stock->hstock = hstock_new(*pool, fcgi_stock_class, fcgi_stock,
                                    limit, max_idle);

    return fcgi_stock;
}

void
fcgi_stock_free(FcgiStock *fcgi_stock)
{
    hstock_free(fcgi_stock->hstock);
    hstock_free(fcgi_stock->child_stock);
}

void
fcgi_stock_fade_all(FcgiStock &fs)
{
    fs.FadeAll();
}

StockItem *
fcgi_stock_get(FcgiStock *fcgi_stock, struct pool *pool,
               const ChildOptions &options,
               const char *executable_path,
               ConstBuffer<const char *> args,
               ConstBuffer<const char *> env,
               GError **error_r)
{
    auto params = NewFromPool<FcgiChildParams>(*pool);
    params->executable_path = executable_path;
    params->args = args;
    params->env = env;
    params->options = &options;

    return hstock_get_now(*fcgi_stock->hstock, *pool,
                          fcgi_stock_key(pool, params), params,
                          error_r);
}

int
fcgi_stock_item_get_domain(gcc_unused const StockItem &item)
{
    return AF_UNIX;
}

int
fcgi_stock_item_get(const StockItem &item)
{
    const auto *connection = &ToFcgiConnection(item);

    assert(connection->fd >= 0);

    return connection->fd;
}

const char *
fcgi_stock_translate_path(const StockItem &item,
                          const char *path, struct pool *pool)
{
    const auto *connection = &ToFcgiConnection(item);

    if (!connection->jail_params.enabled)
        /* no JailCGI - application's namespace is the same as ours,
           no translation needed */
        return path;

    const char *jailed = jail_translate_path(&connection->jail_config, path,
                                             connection->jail_params.home_directory,
                                             pool);
    return jailed != nullptr ? jailed : path;
}

void
fcgi_stock_put(FcgiStock *fcgi_stock, StockItem &item,
               bool destroy)
{
    auto *connection = &ToFcgiConnection(item);

    if (connection->fresh && connection->aborted && destroy)
        /* the fcgi_client caller has aborted the request before the
           first response on a fresh connection was received: better
           kill the child process, it may be failing on us
           completely */
        connection->kill = true;

    hstock_put(*fcgi_stock->hstock, child_stock_item_key(connection->child),
               item, destroy);
}

void
fcgi_stock_aborted(StockItem &item)
{
    auto *connection = &ToFcgiConnection(item);

    connection->aborted = true;
}
<commit_msg>fcgi_stock: move functions into the structs<commit_after>/*
 * Launch and manage FastCGI child processes.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "fcgi_stock.hxx"
#include "fcgi_quark.h"
#include "fcgi_launch.hxx"
#include "hstock.hxx"
#include "stock.hxx"
#include "child_stock.hxx"
#include "child_manager.hxx"
#include "ChildOptions.hxx"
#include "pevent.hxx"
#include "gerrno.h"
#include "pool.hxx"
#include "JailConfig.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cast.hxx"

#include <daemon/log.h>

#include <glib.h>

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

#ifdef __linux
#include <sched.h>
#endif

struct FcgiStock {
    StockMap *hstock;
    StockMap *child_stock;

    void FadeAll() {
        hstock_fade_all(*hstock);
        hstock_fade_all(*child_stock);
    }
};

struct FcgiChildParams {
    const char *executable_path;

    ConstBuffer<const char *> args;
    ConstBuffer<const char *> env;

    const ChildOptions *options;

    const char *GetStockKey(struct pool &pool) const;
};

struct FcgiConnection {
    StockItem base;

    JailParams jail_params;

    struct jail_config jail_config;

    StockItem *child;

    int fd;
    struct event event;

    /**
     * Is this a fresh connection to the FastCGI child process?
     */
    bool fresh;

    /**
     * Shall the FastCGI child process be killed?
     */
    bool kill;

    /**
     * Was the current request aborted by the fcgi_client caller?
     */
    bool aborted;

    gcc_pure
    const char *GetStockKey() const {
        return child_stock_item_key(child);
    }
};

const char *
FcgiChildParams::GetStockKey(struct pool &pool) const
{
    const char *key = executable_path;

    for (auto i : args)
        key = p_strcat(&pool, key, " ", i, nullptr);

    for (auto i : env)
        key = p_strcat(&pool, key, "$", i, nullptr);

    char options_buffer[4096];
    *options->MakeId(options_buffer) = 0;
    if (*options_buffer != 0)
        key = p_strcat(&pool, key, options_buffer, nullptr);

    return key;
}

/*
 * libevent callback
 *
 */

static void
fcgi_connection_event_callback(int fd, gcc_unused short event, void *ctx)
{
    auto *connection = (FcgiConnection *)ctx;

    assert(fd == connection->fd);

    p_event_consumed(&connection->event, connection->base.pool);

    if ((event & EV_TIMEOUT) == 0) {
        char buffer;
        ssize_t nbytes = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
        if (nbytes < 0)
            daemon_log(2, "error on idle FastCGI connection '%s': %s\n",
                       connection->GetStockKey(), strerror(errno));
        else if (nbytes > 0)
            daemon_log(2, "unexpected data from idle FastCGI connection '%s'\n",
                       connection->GetStockKey());
    }

    stock_del(connection->base);
    pool_commit();
}

/*
 * child_stock class
 *
 */

static int
fcgi_child_stock_clone_flags(gcc_unused const char *key, void *info, int flags,
                             gcc_unused void *ctx)
{
    const FcgiChildParams *params =
        (const FcgiChildParams *)info;
    const ChildOptions *const options = params->options;

    return options->ns.GetCloneFlags(flags);
}

static int
fcgi_child_stock_run(gcc_unused struct pool *pool, gcc_unused const char *key,
                     void *info, gcc_unused void *ctx)
{
    const FcgiChildParams *params =
        (const FcgiChildParams *)info;
    const ChildOptions *const options = params->options;

    options->Apply(true);

    fcgi_run(&options->jail, params->executable_path,
             params->args, params->env);
}

static const struct child_stock_class fcgi_child_stock_class = {
    .shutdown_signal = SIGUSR1,
    .clone_flags = fcgi_child_stock_clone_flags,
    .run = fcgi_child_stock_run,
};

/*
 * stock class
 *
 */

static constexpr FcgiConnection &
ToFcgiConnection(StockItem &item)
{
    return ContainerCast2(item, &FcgiConnection::base);
}

static constexpr const FcgiConnection &
ToFcgiConnection(const StockItem &item)
{
    return ContainerCast2(item, &FcgiConnection::base);
}

static struct pool *
fcgi_stock_pool(void *ctx gcc_unused, struct pool &parent,
               const char *uri gcc_unused)
{
    return pool_new_linear(&parent, "fcgi_connection", 2048);
}

static void
fcgi_stock_create(void *ctx, StockItem &item,
                  const char *key, void *info,
                  gcc_unused struct pool &caller_pool,
                  gcc_unused struct async_operation_ref &async_ref)
{
    FcgiStock *fcgi_stock = (FcgiStock *)ctx;
    struct pool *pool = item.pool;
    FcgiChildParams *params = (FcgiChildParams *)info;
    auto *connection = &ToFcgiConnection(item);

    assert(key != nullptr);
    assert(params != nullptr);
    assert(params->executable_path != nullptr);

    const ChildOptions *const options = params->options;
    if (options->jail.enabled) {
        connection->jail_params.CopyFrom(*pool, options->jail);

        if (!jail_config_load(&connection->jail_config,
                              "/etc/cm4all/jailcgi/jail.conf", pool)) {
            GError *error = g_error_new(fcgi_quark(), 0,
                                        "Failed to load /etc/cm4all/jailcgi/jail.conf");
            stock_item_failed(item, error);
            return;
        }
    } else
        connection->jail_params.enabled = false;

    GError *error = nullptr;
    connection->child = hstock_get_now(*fcgi_stock->child_stock, *pool,
                                       key, params, &error);
    if (connection->child == nullptr) {
        g_prefix_error(&error, "failed to start to FastCGI server '%s': ",
                       key);

        stock_item_failed(item, error);
        return;
    }

    connection->fd = child_stock_item_connect(connection->child, &error);
    if (connection->fd < 0) {
        g_prefix_error(&error, "failed to connect to FastCGI server '%s': ",
                       key);

        child_stock_put(fcgi_stock->child_stock, connection->child, true);
        stock_item_failed(item, error);
        return;
    }

    connection->fresh = true;
    connection->kill = false;

    event_set(&connection->event, connection->fd, EV_READ|EV_TIMEOUT,
              fcgi_connection_event_callback, connection);

    stock_item_available(connection->base);
}

static bool
fcgi_stock_borrow(void *ctx gcc_unused, StockItem &item)
{
    auto *connection = &ToFcgiConnection(item);

    /* check the connection status before using it, just in case the
       FastCGI server has decided to close the connection before
       fcgi_connection_event_callback() got invoked */
    char buffer;
    ssize_t nbytes = recv(connection->fd, &buffer, sizeof(buffer),
                          MSG_DONTWAIT);
    if (nbytes > 0) {
        daemon_log(2, "unexpected data from idle FastCGI connection '%s'\n",
                   connection->GetStockKey());
        return false;
    } else if (nbytes == 0) {
        /* connection closed (not worth a log message) */
        return false;
    } else if (errno != EAGAIN) {
        daemon_log(2, "error on idle FastCGI connection '%s': %s\n",
                   connection->GetStockKey(), strerror(errno));
        return false;
    }

    p_event_del(&connection->event, connection->base.pool);
    connection->aborted = false;
    return true;
}

static void
fcgi_stock_release(void *ctx gcc_unused, StockItem &item)
{
    auto *connection = &ToFcgiConnection(item);
    static const struct timeval tv = {
        .tv_sec = 300,
        .tv_usec = 0,
    };

    connection->fresh = false;

    p_event_add(&connection->event, &tv, connection->base.pool,
                "fcgi_connection_event");
}

static void
fcgi_stock_destroy(void *ctx, StockItem &item)
{
    FcgiStock *fcgi_stock = (FcgiStock *)ctx;
    auto *connection = &ToFcgiConnection(item);

    p_event_del(&connection->event, connection->base.pool);
    close(connection->fd);

    child_stock_put(fcgi_stock->child_stock, connection->child,
                    connection->kill);
}

static constexpr StockClass fcgi_stock_class = {
    .item_size = sizeof(FcgiConnection),
    .pool = fcgi_stock_pool,
    .create = fcgi_stock_create,
    .borrow = fcgi_stock_borrow,
    .release = fcgi_stock_release,
    .destroy = fcgi_stock_destroy,
};

/*
 * interface
 *
 */

FcgiStock *
fcgi_stock_new(struct pool *pool, unsigned limit, unsigned max_idle)
{
    auto fcgi_stock = NewFromPool<FcgiStock>(*pool);
    fcgi_stock->child_stock = child_stock_new(pool, limit, max_idle,
                                              &fcgi_child_stock_class);
    fcgi_stock->hstock = hstock_new(*pool, fcgi_stock_class, fcgi_stock,
                                    limit, max_idle);

    return fcgi_stock;
}

void
fcgi_stock_free(FcgiStock *fcgi_stock)
{
    hstock_free(fcgi_stock->hstock);
    hstock_free(fcgi_stock->child_stock);
}

void
fcgi_stock_fade_all(FcgiStock &fs)
{
    fs.FadeAll();
}

StockItem *
fcgi_stock_get(FcgiStock *fcgi_stock, struct pool *pool,
               const ChildOptions &options,
               const char *executable_path,
               ConstBuffer<const char *> args,
               ConstBuffer<const char *> env,
               GError **error_r)
{
    auto params = NewFromPool<FcgiChildParams>(*pool);
    params->executable_path = executable_path;
    params->args = args;
    params->env = env;
    params->options = &options;

    return hstock_get_now(*fcgi_stock->hstock, *pool,
                          params->GetStockKey(*pool), params,
                          error_r);
}

int
fcgi_stock_item_get_domain(gcc_unused const StockItem &item)
{
    return AF_UNIX;
}

int
fcgi_stock_item_get(const StockItem &item)
{
    const auto *connection = &ToFcgiConnection(item);

    assert(connection->fd >= 0);

    return connection->fd;
}

const char *
fcgi_stock_translate_path(const StockItem &item,
                          const char *path, struct pool *pool)
{
    const auto *connection = &ToFcgiConnection(item);

    if (!connection->jail_params.enabled)
        /* no JailCGI - application's namespace is the same as ours,
           no translation needed */
        return path;

    const char *jailed = jail_translate_path(&connection->jail_config, path,
                                             connection->jail_params.home_directory,
                                             pool);
    return jailed != nullptr ? jailed : path;
}

void
fcgi_stock_put(FcgiStock *fcgi_stock, StockItem &item,
               bool destroy)
{
    auto *connection = &ToFcgiConnection(item);

    if (connection->fresh && connection->aborted && destroy)
        /* the fcgi_client caller has aborted the request before the
           first response on a fresh connection was received: better
           kill the child process, it may be failing on us
           completely */
        connection->kill = true;

    hstock_put(*fcgi_stock->hstock, connection->GetStockKey(),
               item, destroy);
}

void
fcgi_stock_aborted(StockItem &item)
{
    auto *connection = &ToFcgiConnection(item);

    connection->aborted = true;
}
<|endoftext|>


<commit_before>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                               libparaver-api                              *
 *                      API Library for libparaver-kernel                    *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in hope that it will be      *
 *   \  \__         useful but WITHOUT ANY WARRANTY; without even the        *
 *    \___          implied warranty of MERCHANTABILITY or FITNESS FOR A     *
 *                  PARTICULAR PURPOSE. See the GNU LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *\
 | @file: $HeadURL$
 | @last_commit: $Date$
 | @version:     $Revision$
\* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

#include <limits>
#include <time.h>
#include <stdlib.h>
#include <map>
#include "drawmode.h"

using std::vector;
using std::map;

// LAST is the default method
template <int method>
inline TSemanticValue selectMethod( vector<TSemanticValue>& v )
{
  return v[ v.size() -1 ];
}

template <>
inline TSemanticValue selectMethod<DRAW_MAXIMUM>( vector<TSemanticValue>& v )
{
#if __cplusplus >= 201103L
  TSemanticValue max = std::numeric_limits<TSemanticValue>::lowest();
#else
  TSemanticValue max = -std::numeric_limits<TSemanticValue>::min();
#endif

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    if( *it > max ) max = *it;
  }

  return max;
}

template <>
inline TSemanticValue selectMethod<DRAW_MINNOTZERO>( vector<TSemanticValue>& v )
{
  TSemanticValue min = std::numeric_limits<TSemanticValue>::max();

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    if( *it > 0 && *it < min ) min = *it;
  }

  if( min == std::numeric_limits<TSemanticValue>::max() )
    return 0;

  return min;
}

template <>
inline TSemanticValue selectMethod<DRAW_RANDOM>( vector<TSemanticValue>& v )
{
  int pos;

  pos = v.size() * rand() / RAND_MAX;
  if( pos >= (int) v.size() )
    pos = v.size() - 1;

  return v[ pos ];
}

template <>
inline TSemanticValue selectMethod<DRAW_RANDNOTZERO>( vector<TSemanticValue>& v )
{
  int pos;

  pos = v.size() * rand() / RAND_MAX;
  if( pos >= (int) v.size() )
    pos = v.size() - 1;

  PRV_UINT32 i = 0;
  while( v[ pos ] == 0 )
  {
    ++pos;
    pos = pos % v.size();
    i++;
    if( i == v.size() ) return 0;
  }

  return v[ pos ];
}

template <>
inline TSemanticValue selectMethod<DRAW_AVERAGE>( vector<TSemanticValue>& v )
{
  TSemanticValue avg = 0.0;

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    avg += *it;
  }

  return avg / v.size();
}

template <>
inline TSemanticValue selectMethod<DRAW_AVERAGENOTZERO>( vector<TSemanticValue>& v )
{
  TSemanticValue avg = 0.0;
  TSemanticValue times = 0.0;

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    if( *it != 0.0 )
    {
      avg += *it;
      ++times;
    }
  }

  if( times == 0.0 )
    return 0.0;
  return avg / times;
}

template<>
inline TSemanticValue selectMethod<DRAW_MODE>( vector<TSemanticValue>& v )
{
  map<TSemanticValue, int> modes;
  TSemanticValue currentMode = 0;
  int currentModeCount = 0;

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    modes[ *it ]++;
    if( modes[ *it ] > currentModeCount )
    {
      currentMode = *it;
      currentModeCount = modes[ *it ];
    }
  }

  return currentMode;
}

TSemanticValue DrawMode::selectValue( vector<TSemanticValue>& v,
                                      DrawModeMethod method )
{
  switch ( method )
  {
    case DRAW_MAXIMUM:
      return selectMethod<DRAW_MAXIMUM>( v );
      break;

    case DRAW_MINNOTZERO:
      return selectMethod<DRAW_MINNOTZERO>( v );
      break;

    case DRAW_RANDOM:
      return selectMethod<DRAW_RANDOM>( v );
      break;

    case DRAW_RANDNOTZERO:
      return selectMethod<DRAW_RANDNOTZERO>( v );
      break;

    case DRAW_AVERAGE:
      return selectMethod<DRAW_AVERAGE>( v );
      break;

    case DRAW_AVERAGENOTZERO:
      return selectMethod<DRAW_AVERAGENOTZERO>( v );
      break;

    case DRAW_MODE:
      return selectMethod<DRAW_MODE>( v );
      break;

    default:
      break;
  }

  return selectMethod<DRAW_LAST>( v );
}
<commit_msg>BUG FIXED: draw mode minimum not zero was filtering values < 0<commit_after>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                               libparaver-api                              *
 *                      API Library for libparaver-kernel                    *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in hope that it will be      *
 *   \  \__         useful but WITHOUT ANY WARRANTY; without even the        *
 *    \___          implied warranty of MERCHANTABILITY or FITNESS FOR A     *
 *                  PARTICULAR PURPOSE. See the GNU LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *\
 | @file: $HeadURL$
 | @last_commit: $Date$
 | @version:     $Revision$
\* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

#include <limits>
#include <time.h>
#include <stdlib.h>
#include <map>
#include "drawmode.h"

using std::vector;
using std::map;

// LAST is the default method
template <int method>
inline TSemanticValue selectMethod( vector<TSemanticValue>& v )
{
  return v[ v.size() -1 ];
}

template <>
inline TSemanticValue selectMethod<DRAW_MAXIMUM>( vector<TSemanticValue>& v )
{
#if __cplusplus >= 201103L
  TSemanticValue max = std::numeric_limits<TSemanticValue>::lowest();
#else
  TSemanticValue max = -std::numeric_limits<TSemanticValue>::max();
#endif

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    if( *it > max ) max = *it;
  }

  return max;
}

template <>
inline TSemanticValue selectMethod<DRAW_MINNOTZERO>( vector<TSemanticValue>& v )
{
  TSemanticValue min = std::numeric_limits<TSemanticValue>::max();

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    if( *it < min ) min = *it;
  }

  if( min == std::numeric_limits<TSemanticValue>::max() )
    return 0;

  return min;
}

template <>
inline TSemanticValue selectMethod<DRAW_RANDOM>( vector<TSemanticValue>& v )
{
  int pos;

  pos = v.size() * rand() / RAND_MAX;
  if( pos >= (int) v.size() )
    pos = v.size() - 1;

  return v[ pos ];
}

template <>
inline TSemanticValue selectMethod<DRAW_RANDNOTZERO>( vector<TSemanticValue>& v )
{
  int pos;

  pos = v.size() * rand() / RAND_MAX;
  if( pos >= (int) v.size() )
    pos = v.size() - 1;

  PRV_UINT32 i = 0;
  while( v[ pos ] == 0 )
  {
    ++pos;
    pos = pos % v.size();
    i++;
    if( i == v.size() ) return 0;
  }

  return v[ pos ];
}

template <>
inline TSemanticValue selectMethod<DRAW_AVERAGE>( vector<TSemanticValue>& v )
{
  TSemanticValue avg = 0.0;

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    avg += *it;
  }

  return avg / v.size();
}

template <>
inline TSemanticValue selectMethod<DRAW_AVERAGENOTZERO>( vector<TSemanticValue>& v )
{
  TSemanticValue avg = 0.0;
  TSemanticValue times = 0.0;

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    if( *it != 0.0 )
    {
      avg += *it;
      ++times;
    }
  }

  if( times == 0.0 )
    return 0.0;
  return avg / times;
}

template<>
inline TSemanticValue selectMethod<DRAW_MODE>( vector<TSemanticValue>& v )
{
  map<TSemanticValue, int> modes;
  TSemanticValue currentMode = 0;
  int currentModeCount = 0;

  for( vector<TSemanticValue>::iterator it = v.begin(); it != v.end(); ++it )
  {
    modes[ *it ]++;
    if( modes[ *it ] > currentModeCount )
    {
      currentMode = *it;
      currentModeCount = modes[ *it ];
    }
  }

  return currentMode;
}

TSemanticValue DrawMode::selectValue( vector<TSemanticValue>& v,
                                      DrawModeMethod method )
{
  switch ( method )
  {
    case DRAW_MAXIMUM:
      return selectMethod<DRAW_MAXIMUM>( v );
      break;

    case DRAW_MINNOTZERO:
      return selectMethod<DRAW_MINNOTZERO>( v );
      break;

    case DRAW_RANDOM:
      return selectMethod<DRAW_RANDOM>( v );
      break;

    case DRAW_RANDNOTZERO:
      return selectMethod<DRAW_RANDNOTZERO>( v );
      break;

    case DRAW_AVERAGE:
      return selectMethod<DRAW_AVERAGE>( v );
      break;

    case DRAW_AVERAGENOTZERO:
      return selectMethod<DRAW_AVERAGENOTZERO>( v );
      break;

    case DRAW_MODE:
      return selectMethod<DRAW_MODE>( v );
      break;

    default:
      break;
  }

  return selectMethod<DRAW_LAST>( v );
}
<|endoftext|>


<commit_before>// Sort a linked list in O(n log n) time using constant space complexity.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
    ListNode *merge(ListNode *h1, ListNode *h2) {
        ListNode helper(0);
        ListNode *l3 = &helper;
        while (h1 && h2) {
            if (h1->val < h2->val) {
                l3->next = h1;
                h1 = h1->next;
            } else {
                l3->next = h2;
                h2 = h2->next;
            }
            l3 = l3->next;
        }
        l3->next = h1 ? h1 : h2;
        return helper.next;
    }

  public:
    ListNode *sortList(ListNode *head) {
        if (!head)
            return head;

        // break list nodes
        deque<ListNode *> working;
        while (head) {
            working.push_back(head);
            head = head->next;
            working.back()->next = NULL;
        }

        // sorting
        ListNode *l1, *l2;
        while (working.size() > 1) {
            l1 = working.front();
            working.pop_front();
            l2 = working.front();
            working.pop_front();
            working.push_back(merge(l1, l2));
        }

        return working.front();
    }
};<commit_msg>update true O(1) version<commit_after>// Sort a linked list in O(n log n) time using constant space complexity.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
// True O(1), no recursion
class Solution {
    pair<ListNode *, ListNode *> merge(ListNode *h1, ListNode *h2) {
        ListNode helper(0);
        ListNode *l3 = &helper;
        while (h1 && h2) {
            if (h1->val < h2->val) {
                l3->next = h1;
                h1 = h1->next;
            } else {
                l3->next = h2;
                h2 = h2->next;
            }
            l3 = l3->next;
        }
        l3->next = h1 ? h1 : h2;
        while (l3->next)
            l3 = l3->next;
        return make_pair(helper.next, l3); // new list head and tail
    }

    ListNode *splitk(ListNode *head, int k) {
        if (!head)
            return head;
        for (int i = 0; i < k - 1; ++i) {
            if (!head->next)
                break;
            head = head->next;
        }
        ListNode *newhead = head->next;
        head->next = NULL;
        return newhead;
    }

    // void print(ListNode *l) {
    //     while (l) {
    //         cout << l->val << ' ';
    //         l = l->next;
    //     }
    //     cout << endl;
    // }

  public:
    ListNode *sortList(ListNode *head) {
        if (!head)
            return head;

        ListNode temp(0);
        pair<ListNode *, ListNode *> dummy{&temp, &temp}, prev_sorted = dummy;

        int step = 1, cnt = 0;
        while (true) {
            while (head) {
                auto l1 = head;
                auto l2 = splitk(l1, step);
                head = splitk(l2, step);

                // now l2 length <= l1 length <= step;

                auto sorted = merge(l1, l2);
                prev_sorted.second->next = sorted.first;
                prev_sorted = sorted;

                ++cnt;
            }

            // 1 merge, must be last merge
            if (cnt == 1)
                return temp.next;

            // prepare for next round
            cnt = 0;
            step *= 2;
            head = temp.next;
            prev_sorted = dummy;
        }
        // won't go to this line
    }
};

class Solution {
    ListNode *merge(ListNode *h1, ListNode *h2) {
        ListNode helper(0);
        ListNode *l3 = &helper;
        while (h1 && h2) {
            if (h1->val < h2->val) {
                l3->next = h1;
                h1 = h1->next;
            } else {
                l3->next = h2;
                h2 = h2->next;
            }
            l3 = l3->next;
        }
        l3->next = h1 ? h1 : h2;
        return helper.next;
    }

  public:
    ListNode *sortList(ListNode *head) {
        if (!head)
            return head;

        // break list nodes
        deque<ListNode *> working;
        while (head) {
            working.push_back(head);
            head = head->next;
            working.back()->next = NULL;
        }

        // sorting
        ListNode *l1, *l2;
        while (working.size() > 1) {
            l1 = working.front();
            working.pop_front();
            l2 = working.front();
            working.pop_front();
            working.push_back(merge(l1, l2));
        }

        return working.front();
    }
};<|endoftext|>


<commit_before>//
//  cv_object.cpp
//  fibio
//
//  Created by Chen Xu on 14-3-4.
//  Copyright (c) 2014 0d0a.com. All rights reserved.
//

#include "cv_object.hpp"

namespace fibio { namespace fibers { namespace detail {
    void condition_variable_object::wait(mutex_ptr_t m, fiber_ptr_t this_fiber) {
        CHECK_CALLER(this_fiber);
        if (this_fiber->caller_) {
            std::shared_ptr<condition_variable_object> this_cv(shared_from_this());
            (*(this_fiber->caller_))([this_fiber, this_cv, m](){
                assert(this_fiber==m->owner_);
                std::lock_guard<std::mutex> lock(this_cv->m_);
                this_fiber->state_=fiber_object::BLOCKED;
                this_cv->suspended_.push_back(suspended_item({m, this_fiber, timer_ptr_t()}));
                {
                    // Release mutex
                    std::lock_guard<std::mutex> lock(m->m_);
                    if (m->owner_==this_fiber) {
                        // This fiber owns the mutex
                        if (m->suspended_.empty()) {
                            // Nobody is waiting
                            m->owner_.reset();
                        } else {
                            // There are fibers in the waiting queue, pop one to owner_ and enable its scheduling
                            std::swap(m->owner_, m->suspended_.front());
                            m->suspended_.pop_front();
                            m->owner_->schedule();
                        }
                    } else {
                        // ERROR: This fiber doesn't own the mutex
                        this_fiber->last_error_=std::make_error_code(std::errc::operation_not_permitted);
                    }
                }

            });
        }
    }

    cv_status condition_variable_object::wait_usec(mutex_ptr_t m, fiber_ptr_t this_fiber, uint64_t usec) {
        //CHECK_CALLER(this_fiber);
        cv_status ret=cv_status::no_timeout;
        if (this_fiber->caller_) {
            std::shared_ptr<condition_variable_object> this_cv(shared_from_this());
            (*(this_fiber->caller_))([this_fiber, this_cv, m, usec, &ret](){
                std::lock_guard<std::mutex> lock(this_cv->m_);
                timer_ptr_t t(std::make_shared<timer_t>(this_fiber->io_service_));
                this_cv->suspended_.push_back(suspended_item({m, this_fiber, t}));
                t->expires_from_now(std::chrono::microseconds(usec));
                t->async_wait(this_fiber->fiber_strand_.wrap([this_fiber, this_cv, t, &ret](std::error_code ec){
                    if(ec==std::errc::operation_canceled) {
                        // Timer canceled, wait successful
                        return;
                    }
                    // Timeout
                    // Timeout handler, find and remove this fiber from waiting queue
                    std::lock_guard<std::mutex> lock(this_cv->m_);
                    ret=cv_status::timeout;
                    suspended_item p;
                    // Find and remove this fiber from waiting queue
                    std::deque<suspended_item>::iterator i=std::find_if(this_cv->suspended_.begin(),
                                                                        this_cv->suspended_.end(),
                                                                        [this_fiber](const suspended_item &i)->bool{
                                                                            return i.f_==this_fiber;
                                                                        });
                    if (i!=this_cv->suspended_.end()) {
                        std::swap(p, *i);
                        this_cv->suspended_.erase(i);
                        // std::cout << "waiting queue size: " << this_cv->suspended_.size() << std::endl;
                    }
                    if (p.m_) {
                        // Acquire lock for this fiber befor re-scheduling
                        std::lock_guard<std::mutex> lock(p.m_->m_);
                        if (p.m_->owner_) {
                            if (p.m_->owner_==p.f_) {
                                // ERROR: Shouldn't happen
                            } else {
                                // p.mutex is lock by someone else, add fiber to waiting queue
                                p.m_->suspended_.push_back(p.f_);
                                p.f_->state_=fiber_object::BLOCKED;
                            }
                        } else {
                            // p.mutex is not locked, just lock it for p.fiber
                            p.m_->owner_=p.f_;
                            p.f_->schedule();
                        }
                    }
                }));
                this_fiber->state_=fiber_object::BLOCKED;
                //m->unlock(this_fiber);
                {
                    mutex_ptr_t this_mutex=m;
                    std::lock_guard<std::mutex> lock(this_mutex->m_);
                    if (this_mutex->owner_==this_fiber) {
                        // This fiber owns the mutex
                        if (this_mutex->suspended_.empty()) {
                            // Nobody is waiting
                            this_mutex->owner_.reset();
                        } else {
                            // There are fibers in the waiting queue, pop one to owner_ and enable its scheduling
                            std::swap(this_mutex->owner_, this_mutex->suspended_.front());
                            this_mutex->suspended_.pop_front();
                            this_mutex->owner_->schedule();
                        }
                    } else {
                        // ERROR: This fiber doesn't own the mutex
                        this_fiber->last_error_=std::make_error_code(std::errc::operation_not_permitted);
                    }
                }
            });
        }
        return ret;
    }

    void condition_variable_object::notify_one() {
        std::lock_guard<std::mutex> lock(m_);
        if (suspended_.empty()) {
            return;
        }
        suspended_item p(suspended_.front());
        suspended_.pop_front();
        if (p.t_) {
            // Cancel attached timer if it's set
            p.t_->cancel();
            p.t_.reset();
        }
        p.f_->fiber_strand_.post([p](){
            // Acquire lock for p.fiber
            std::lock_guard<std::mutex> lock(p.m_->m_);
            if (p.m_->owner_) {
                if (p.m_->owner_==p.f_) {
                    // ERROR: Shouldn't happen
                } else {
                    // p.mutex is lock by someone else, add fiber to waiting queue
                    p.m_->suspended_.push_back(p.f_);
                    p.f_->state_=fiber_object::BLOCKED;
                }
            } else {
                // p.mutex is not locked, just lock it for p.fiber
                p.m_->owner_=p.f_;
                p.f_->schedule();
            }
        });
        if (fiber_object::current_fiber_) {
            fiber_object::current_fiber_->yield();
        }
    }

    void condition_variable_object::notify_all() {
        std::lock_guard<std::mutex> lock(m_);
        while (!suspended_.empty()) {
            suspended_item p(suspended_.front());
            suspended_.pop_front();
            if (p.t_) {
                // Cancel attached timer if it's set
                p.t_->cancel();
            }
            p.f_->fiber_strand_.post([p](){
                // Acquire lock p.first for p->second
                std::lock_guard<std::mutex> lock(p.m_->m_);
                if (p.m_->owner_) {
                    if (p.m_->owner_==p.f_) {
                        // ERROR: Shouldn't happen
                    } else {
                        // p.first is lock by someone else
                        p.m_->suspended_.push_back(p.f_);
                        p.f_->state_=fiber_object::BLOCKED;
                    }
                } else {
                    // p.first is not locked, just lock it for p.second
                    p.m_->owner_=p.f_;
                    p.f_->schedule();
                }
            });
        }
        if (fiber_object::current_fiber_) {
            fiber_object::current_fiber_->yield();
        }
    }
}}} // End of namespace fibio::fibers::detail

namespace fibio { namespace fibers {
    condition_variable::condition_variable()
    : m_(std::make_shared<detail::condition_variable_object>())
    {}

    void condition_variable::wait(std::unique_lock<mutex>& lock) {
        CHECK_CURRENT_FIBER;
        if (detail::fiber_object::current_fiber_) {
            m_->wait(lock.mutex()->m_, detail::fiber_object::current_fiber_->shared_from_this());
        }
    }

    cv_status condition_variable::wait_usec(std::unique_lock<mutex>& lock, uint64_t usec) {
        CHECK_CURRENT_FIBER;
        if (detail::fiber_object::current_fiber_) {
            return m_->wait_usec(lock.mutex()->m_, detail::fiber_object::current_fiber_->shared_from_this(), usec);
        }
        return cv_status::timeout;
    }

    void condition_variable::notify_one() {
        m_->notify_one();
    }

    void condition_variable::notify_all() {
        m_->notify_all();
    }

    void notify_all_at_thread_exit(condition_variable &cond,
                                   std::unique_lock<mutex> lk)
    {
        CHECK_CURRENT_FIBER;
        struct cleanup_handler {
            condition_variable &c_;
            std::unique_lock<mutex> l_;

            cleanup_handler(condition_variable &cond,
                            std::unique_lock<mutex> lk)
            : c_(cond)
            , l_(std::move(lk))
            {}

            void operator()() {
                l_.unlock();
                c_.notify_all();
            }
        };

        if (detail::fiber_object::current_fiber_) {
            cleanup_handler *h=new cleanup_handler(cond, std::move(lk));
            detail::fiber_object::current_fiber_->add_cleanup_function([&](){
                (*h)();
                delete h;
            });
        }
    }
}}  // End of namespace fibio::fibers
<commit_msg>Error condition<commit_after>//
//  cv_object.cpp
//  fibio
//
//  Created by Chen Xu on 14-3-4.
//  Copyright (c) 2014 0d0a.com. All rights reserved.
//

#include "cv_object.hpp"

namespace fibio { namespace fibers { namespace detail {
    void condition_variable_object::wait(mutex_ptr_t m, fiber_ptr_t this_fiber) {
        CHECK_CALLER(this_fiber);
        if (this_fiber->caller_) {
            std::shared_ptr<condition_variable_object> this_cv(shared_from_this());
            (*(this_fiber->caller_))([this_fiber, this_cv, m](){
                assert(this_fiber==m->owner_);
                std::lock_guard<std::mutex> lock(this_cv->m_);
                this_fiber->state_=fiber_object::BLOCKED;
                this_cv->suspended_.push_back(suspended_item({m, this_fiber, timer_ptr_t()}));
                {
                    // Release mutex
                    std::lock_guard<std::mutex> lock(m->m_);
                    if (m->owner_==this_fiber) {
                        // This fiber owns the mutex
                        if (m->suspended_.empty()) {
                            // Nobody is waiting
                            m->owner_.reset();
                        } else {
                            // There are fibers in the waiting queue, pop one to owner_ and enable its scheduling
                            std::swap(m->owner_, m->suspended_.front());
                            m->suspended_.pop_front();
                            m->owner_->schedule();
                        }
                    } else {
                        // ERROR: This fiber doesn't own the mutex
                        this_fiber->last_error_=std::make_error_code(std::errc::operation_not_permitted);
                    }
                }

            });
        }
    }

    cv_status condition_variable_object::wait_usec(mutex_ptr_t m, fiber_ptr_t this_fiber, uint64_t usec) {
        //CHECK_CALLER(this_fiber);
        cv_status ret=cv_status::no_timeout;
        if (this_fiber->caller_) {
            std::shared_ptr<condition_variable_object> this_cv(shared_from_this());
            (*(this_fiber->caller_))([this_fiber, this_cv, m, usec, &ret](){
                std::lock_guard<std::mutex> lock(this_cv->m_);
                timer_ptr_t t(std::make_shared<timer_t>(this_fiber->io_service_));
                this_cv->suspended_.push_back(suspended_item({m, this_fiber, t}));
                t->expires_from_now(std::chrono::microseconds(usec));
                t->async_wait(this_fiber->fiber_strand_.wrap([this_fiber, this_cv, t, &ret](std::error_code ec){
                    if(ec) {
                        // Timer canceled, wait successful
                        return;
                    }
                    // Timeout
                    // Timeout handler, find and remove this fiber from waiting queue
                    std::lock_guard<std::mutex> lock(this_cv->m_);
                    ret=cv_status::timeout;
                    suspended_item p;
                    // Find and remove this fiber from waiting queue
                    std::deque<suspended_item>::iterator i=std::find_if(this_cv->suspended_.begin(),
                                                                        this_cv->suspended_.end(),
                                                                        [this_fiber](const suspended_item &i)->bool{
                                                                            return i.f_==this_fiber;
                                                                        });
                    if (i!=this_cv->suspended_.end()) {
                        std::swap(p, *i);
                        this_cv->suspended_.erase(i);
                        // std::cout << "waiting queue size: " << this_cv->suspended_.size() << std::endl;
                    }
                    if (p.m_) {
                        // Acquire lock for this fiber befor re-scheduling
                        std::lock_guard<std::mutex> lock(p.m_->m_);
                        if (p.m_->owner_) {
                            if (p.m_->owner_==p.f_) {
                                // ERROR: Shouldn't happen
                            } else {
                                // p.mutex is lock by someone else, add fiber to waiting queue
                                p.m_->suspended_.push_back(p.f_);
                                p.f_->state_=fiber_object::BLOCKED;
                            }
                        } else {
                            // p.mutex is not locked, just lock it for p.fiber
                            p.m_->owner_=p.f_;
                            p.f_->schedule();
                        }
                    }
                }));
                this_fiber->state_=fiber_object::BLOCKED;
                //m->unlock(this_fiber);
                {
                    mutex_ptr_t this_mutex=m;
                    std::lock_guard<std::mutex> lock(this_mutex->m_);
                    if (this_mutex->owner_==this_fiber) {
                        // This fiber owns the mutex
                        if (this_mutex->suspended_.empty()) {
                            // Nobody is waiting
                            this_mutex->owner_.reset();
                        } else {
                            // There are fibers in the waiting queue, pop one to owner_ and enable its scheduling
                            std::swap(this_mutex->owner_, this_mutex->suspended_.front());
                            this_mutex->suspended_.pop_front();
                            this_mutex->owner_->schedule();
                        }
                    } else {
                        // ERROR: This fiber doesn't own the mutex
                        this_fiber->last_error_=std::make_error_code(std::errc::operation_not_permitted);
                    }
                }
            });
        }
        return ret;
    }

    void condition_variable_object::notify_one() {
        std::lock_guard<std::mutex> lock(m_);
        if (suspended_.empty()) {
            return;
        }
        suspended_item p(suspended_.front());
        suspended_.pop_front();
        if (p.t_) {
            // Cancel attached timer if it's set
            p.t_->cancel();
            p.t_.reset();
        }
        p.f_->fiber_strand_.post([p](){
            // Acquire lock for p.fiber
            std::lock_guard<std::mutex> lock(p.m_->m_);
            if (p.m_->owner_) {
                if (p.m_->owner_==p.f_) {
                    // ERROR: Shouldn't happen
                } else {
                    // p.mutex is lock by someone else, add fiber to waiting queue
                    p.m_->suspended_.push_back(p.f_);
                    p.f_->state_=fiber_object::BLOCKED;
                }
            } else {
                // p.mutex is not locked, just lock it for p.fiber
                p.m_->owner_=p.f_;
                p.f_->schedule();
            }
        });
        if (fiber_object::current_fiber_) {
            fiber_object::current_fiber_->yield();
        }
    }

    void condition_variable_object::notify_all() {
        std::lock_guard<std::mutex> lock(m_);
        while (!suspended_.empty()) {
            suspended_item p(suspended_.front());
            suspended_.pop_front();
            if (p.t_) {
                // Cancel attached timer if it's set
                p.t_->cancel();
            }
            p.f_->fiber_strand_.post([p](){
                // Acquire lock p.first for p->second
                std::lock_guard<std::mutex> lock(p.m_->m_);
                if (p.m_->owner_) {
                    if (p.m_->owner_==p.f_) {
                        // ERROR: Shouldn't happen
                    } else {
                        // p.first is lock by someone else
                        p.m_->suspended_.push_back(p.f_);
                        p.f_->state_=fiber_object::BLOCKED;
                    }
                } else {
                    // p.first is not locked, just lock it for p.second
                    p.m_->owner_=p.f_;
                    p.f_->schedule();
                }
            });
        }
        if (fiber_object::current_fiber_) {
            fiber_object::current_fiber_->yield();
        }
    }
}}} // End of namespace fibio::fibers::detail

namespace fibio { namespace fibers {
    condition_variable::condition_variable()
    : m_(std::make_shared<detail::condition_variable_object>())
    {}

    void condition_variable::wait(std::unique_lock<mutex>& lock) {
        CHECK_CURRENT_FIBER;
        if (detail::fiber_object::current_fiber_) {
            m_->wait(lock.mutex()->m_, detail::fiber_object::current_fiber_->shared_from_this());
        }
    }

    cv_status condition_variable::wait_usec(std::unique_lock<mutex>& lock, uint64_t usec) {
        CHECK_CURRENT_FIBER;
        if (detail::fiber_object::current_fiber_) {
            return m_->wait_usec(lock.mutex()->m_, detail::fiber_object::current_fiber_->shared_from_this(), usec);
        }
        return cv_status::timeout;
    }

    void condition_variable::notify_one() {
        m_->notify_one();
    }

    void condition_variable::notify_all() {
        m_->notify_all();
    }

    void notify_all_at_thread_exit(condition_variable &cond,
                                   std::unique_lock<mutex> lk)
    {
        CHECK_CURRENT_FIBER;
        struct cleanup_handler {
            condition_variable &c_;
            std::unique_lock<mutex> l_;

            cleanup_handler(condition_variable &cond,
                            std::unique_lock<mutex> lk)
            : c_(cond)
            , l_(std::move(lk))
            {}

            void operator()() {
                l_.unlock();
                c_.notify_all();
            }
        };

        if (detail::fiber_object::current_fiber_) {
            cleanup_handler *h=new cleanup_handler(cond, std::move(lk));
            detail::fiber_object::current_fiber_->add_cleanup_function([&](){
                (*h)();
                delete h;
            });
        }
    }
}}  // End of namespace fibio::fibers
<|endoftext|>


<commit_before>
#include "tests/gtest/gtest.h"
#include <cmath>

#include "pbrt.h"
#include "rng.h"
#include "ext/tinyexr.h"

union FP32 {
    uint u;
    float f;
    struct {
        uint Mantissa : 23;
        uint Exponent : 8;
        uint Sign : 1;
    };
};

union FP16 {
    unsigned short u;
    struct {
        uint Mantissa : 10;
        uint Exponent : 5;
        uint Sign : 1;
    };
};

static FP32 half_to_float_full(FP16 h)
{
  FP32 o = { 0 };

  // From ISPC ref code
  if (h.Exponent == 0 && h.Mantissa == 0) // (Signed) zero
    o.Sign = h.Sign;
  else {
    if (h.Exponent == 0) { // Denormal (will convert to normalized)
      // Adjust mantissa so it's normalized (and keep track of exp adjust)
      int e = -1;
      uint m = h.Mantissa;
      do {
        e++;
        m <<= 1;
      } while ((m & 0x400) == 0);

      o.Mantissa = (m & 0x3ff) << 13;
      o.Exponent = 127 - 15 - e;
      o.Sign = h.Sign;
    }
    else if (h.Exponent == 0x1f) { // Inf/NaN
      // NOTE: It's safe to treat both with the same code path by just
      // truncating lower Mantissa bits in NaNs (this is valid).
      o.Mantissa = h.Mantissa << 13;
      o.Exponent = 255;
      o.Sign = h.Sign;
    }
    else { // Normalized number
      o.Mantissa = h.Mantissa << 13;
      o.Exponent = 127 - 15 + h.Exponent;
      o.Sign = h.Sign;
    }
  }

  return o;
}

int float_to_half(float f) {
  unsigned int sign_mask = 0x80000000u;
  int o;

  int fint = FloatToBits(f);
  int sign = fint & sign_mask;
  fint ^= sign;

  // NOTE all the integer compares in this function can be safely
  // compiled into signed compares since all operands are below
  // 0x80000000. Important if you want fast straight SSE2 code (since
  // there's no unsigned PCMPGTD).

  // Inf or NaN (all exponent bits set)
  // NaN->qNaN and Inf->Inf
  // unconditional assignment here, will override with right value for
  // the regular case below.
  int f32infty = 255ul << 23;
  o = (fint > f32infty) ? 0x7e00u : 0x7c00u;

  // (De)normalized number or zero
  // update fint unconditionally to save the blending; we don't need it
  // anymore for the Inf/NaN case anyway.
  const unsigned int round_mask = ~0xfffu;
  const uint32_t magic = 15ul << 23;
  const int f16infty = 31ul << 23;

  int fint2 = FloatToBits(BitsToFloat(fint & round_mask) * BitsToFloat(magic)) -
      round_mask;
  // Clamp to signed infinity if overflowed
  fint2 = (fint2 > f16infty) ? f16infty : fint2;

  if (fint < f32infty)
    o = fint2 >> 13; // Take the bits!

  return (o | (sign >> 16));
}

static void CompareImages(const EXRImage &a, const EXRImage &b,
                          bool halfQuantize) {
   EXPECT_EQ(a.num_channels, b.num_channels);
   EXPECT_EQ(a.width, b.width);
   EXPECT_EQ(a.height, b.height);
   for (int i = 0; i < a.num_channels; ++i) {
     EXPECT_EQ(a.pixel_types[i], b.pixel_types[i]);
     EXPECT_EQ(std::string(a.channel_names[i]),
               std::string(b.channel_names[i]));
   }
   for (int i = 0; i < a.width * a.height; ++i) {
     for (int c = 0; c < a.num_channels; ++c) {
       float ap = ((float *)a.images[c])[i];
       float bp = ((float *)b.images[c])[i];
       if (std::isnan(ap) && std::isnan(bp))
         continue;
       if (halfQuantize) {
         int ha = float_to_half(ap);
         int hb = float_to_half(bp);
         EXPECT_EQ(ha, hb) <<  "offset " << i << ", channel " << c <<
             ", fa " << ap << ", fb " << bp;
       }
       else {
         EXPECT_EQ(ap, bp) << "offset " << i << ", channel " << c;
       }
     }
   }
}

TEST(EXR, BasicRoundTrip) {
   int width = 289;
   int height = 1 + 65536 / width;

   float *buf = new float[width * height];
   for (int i = 0; i < 65536; ++i) {
     FP16 half;
     half.u = i;
     buf[i] = half_to_float_full(half).f;
   }
   for (int i = 65536; i < width * height; ++i)
     buf[i] = 0;

   EXRImage image;
   image.num_channels = 1;
   const char *channels[] = { "R" };
   image.channel_names = channels;
   unsigned char *images[] = { (unsigned char *)buf };
   image.images = images;
   int pixel_types[] = { TINYEXR_PIXELTYPE_HALF };
   image.pixel_types = pixel_types;
   image.width = width;
   image.height = height;

   const char *err = nullptr;
   EXPECT_EQ(0, SaveMultiChannelEXRToFile(&image, "test.exr", &err)) << err;

   EXRImage readImage;
   EXPECT_EQ(0, LoadMultiChannelEXRFromFile(&readImage, "test.exr", &err))
       << err;

   CompareImages(image, readImage, false);
}

TEST(EXR, Randoms) {
   int width = 1024;
   int height = 1024;

   RNG rng;
   float *buf = new float[4 * width * height];
   for (int i = 0; i < 4 * width * height; ++i) {
     buf[i] = -20 + 20. * rng.UniformFloat();
   }

   EXRImage image;
   image.num_channels = 4;
   const char *channels[] = { "B", "G", "R", "A" };
   image.channel_names = channels;
   unsigned char *images[] = { (unsigned char *)buf,
                               (unsigned char *)(buf + width * height),
                               (unsigned char *)(buf + 2 * width * height),
                               (unsigned char *)(buf + 3 * width * height) };
   image.images = images;
   int pixel_types[] = { TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF,
                         TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF };
   image.pixel_types = pixel_types;
   image.width = width;
   image.height = height;

   const char *err = nullptr;
   EXPECT_EQ(0, SaveMultiChannelEXRToFile(&image, "test.exr", &err)) << err;

   EXRImage readImage;
   EXPECT_EQ(0, LoadMultiChannelEXRFromFile(&readImage, "test.exr", &err))
       << err;

   CompareImages(image, readImage, true);
}
<commit_msg>Replaced non-standard uint with proper sized typedefs<commit_after>
#include "tests/gtest/gtest.h"
#include <stdint.h>
#include <cmath>

#include "pbrt.h"
#include "rng.h"
#include "ext/tinyexr.h"

union FP32 {
    uint32_t u;
    float f;
    struct {
        uint32_t Mantissa : 23;
        uint32_t Exponent : 8;
        uint32_t Sign : 1;
    };
};

union FP16 {
    uint16_t u;
    struct {
        uint32_t Mantissa : 10;
        uint32_t Exponent : 5;
        uint32_t Sign : 1;
    };
};

static FP32 half_to_float_full(FP16 h)
{
  FP32 o = { 0 };

  // From ISPC ref code
  if (h.Exponent == 0 && h.Mantissa == 0) // (Signed) zero
    o.Sign = h.Sign;
  else {
    if (h.Exponent == 0) { // Denormal (will convert to normalized)
      // Adjust mantissa so it's normalized (and keep track of exp adjust)
      int e = -1;
      uint32_t m = h.Mantissa;
      do {
        e++;
        m <<= 1;
      } while ((m & 0x400) == 0);

      o.Mantissa = (m & 0x3ff) << 13;
      o.Exponent = 127 - 15 - e;
      o.Sign = h.Sign;
    }
    else if (h.Exponent == 0x1f) { // Inf/NaN
      // NOTE: It's safe to treat both with the same code path by just
      // truncating lower Mantissa bits in NaNs (this is valid).
      o.Mantissa = h.Mantissa << 13;
      o.Exponent = 255;
      o.Sign = h.Sign;
    }
    else { // Normalized number
      o.Mantissa = h.Mantissa << 13;
      o.Exponent = 127 - 15 + h.Exponent;
      o.Sign = h.Sign;
    }
  }

  return o;
}

int float_to_half(float f) {
  unsigned int sign_mask = 0x80000000u;
  int o;

  int fint = FloatToBits(f);
  int sign = fint & sign_mask;
  fint ^= sign;

  // NOTE all the integer compares in this function can be safely
  // compiled into signed compares since all operands are below
  // 0x80000000. Important if you want fast straight SSE2 code (since
  // there's no unsigned PCMPGTD).

  // Inf or NaN (all exponent bits set)
  // NaN->qNaN and Inf->Inf
  // unconditional assignment here, will override with right value for
  // the regular case below.
  int f32infty = 255ul << 23;
  o = (fint > f32infty) ? 0x7e00u : 0x7c00u;

  // (De)normalized number or zero
  // update fint unconditionally to save the blending; we don't need it
  // anymore for the Inf/NaN case anyway.
  const unsigned int round_mask = ~0xfffu;
  const uint32_t magic = 15ul << 23;
  const int f16infty = 31ul << 23;

  int fint2 = FloatToBits(BitsToFloat(fint & round_mask) * BitsToFloat(magic)) -
      round_mask;
  // Clamp to signed infinity if overflowed
  fint2 = (fint2 > f16infty) ? f16infty : fint2;

  if (fint < f32infty)
    o = fint2 >> 13; // Take the bits!

  return (o | (sign >> 16));
}

static void CompareImages(const EXRImage &a, const EXRImage &b,
                          bool halfQuantize) {
   EXPECT_EQ(a.num_channels, b.num_channels);
   EXPECT_EQ(a.width, b.width);
   EXPECT_EQ(a.height, b.height);
   for (int i = 0; i < a.num_channels; ++i) {
     EXPECT_EQ(a.pixel_types[i], b.pixel_types[i]);
     EXPECT_EQ(std::string(a.channel_names[i]),
               std::string(b.channel_names[i]));
   }
   for (int i = 0; i < a.width * a.height; ++i) {
     for (int c = 0; c < a.num_channels; ++c) {
       float ap = ((float *)a.images[c])[i];
       float bp = ((float *)b.images[c])[i];
       if (std::isnan(ap) && std::isnan(bp))
         continue;
       if (halfQuantize) {
         int ha = float_to_half(ap);
         int hb = float_to_half(bp);
         EXPECT_EQ(ha, hb) <<  "offset " << i << ", channel " << c <<
             ", fa " << ap << ", fb " << bp;
       }
       else {
         EXPECT_EQ(ap, bp) << "offset " << i << ", channel " << c;
       }
     }
   }
}

TEST(EXR, BasicRoundTrip) {
   int width = 289;
   int height = 1 + 65536 / width;

   float *buf = new float[width * height];
   for (int i = 0; i < 65536; ++i) {
     FP16 half;
     half.u = i;
     buf[i] = half_to_float_full(half).f;
   }
   for (int i = 65536; i < width * height; ++i)
     buf[i] = 0;

   EXRImage image;
   image.num_channels = 1;
   const char *channels[] = { "R" };
   image.channel_names = channels;
   unsigned char *images[] = { (unsigned char *)buf };
   image.images = images;
   int pixel_types[] = { TINYEXR_PIXELTYPE_HALF };
   image.pixel_types = pixel_types;
   image.width = width;
   image.height = height;

   const char *err = nullptr;
   EXPECT_EQ(0, SaveMultiChannelEXRToFile(&image, "test.exr", &err)) << err;

   EXRImage readImage;
   EXPECT_EQ(0, LoadMultiChannelEXRFromFile(&readImage, "test.exr", &err))
       << err;

   CompareImages(image, readImage, false);
}

TEST(EXR, Randoms) {
   int width = 1024;
   int height = 1024;

   RNG rng;
   float *buf = new float[4 * width * height];
   for (int i = 0; i < 4 * width * height; ++i) {
     buf[i] = -20 + 20. * rng.UniformFloat();
   }

   EXRImage image;
   image.num_channels = 4;
   const char *channels[] = { "B", "G", "R", "A" };
   image.channel_names = channels;
   unsigned char *images[] = { (unsigned char *)buf,
                               (unsigned char *)(buf + width * height),
                               (unsigned char *)(buf + 2 * width * height),
                               (unsigned char *)(buf + 3 * width * height) };
   image.images = images;
   int pixel_types[] = { TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF,
                         TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF };
   image.pixel_types = pixel_types;
   image.width = width;
   image.height = height;

   const char *err = nullptr;
   EXPECT_EQ(0, SaveMultiChannelEXRToFile(&image, "test.exr", &err)) << err;

   EXRImage readImage;
   EXPECT_EQ(0, LoadMultiChannelEXRFromFile(&readImage, "test.exr", &err))
       << err;

   CompareImages(image, readImage, true);
}
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrStencil.h"

////////////////////////////////////////////////////////////////////////////////
// Stencil Rules for Merging user stencil space into clip

// We can't include the clip bit in the ref or mask values because the division
// between user and clip bits in the stencil depends on the number of stencil
// bits in the runtime. Comments below indicate what the code should do to
// incorporate the clip bit into these settings.

///////
// Replace

// set the ref to be the clip bit, but mask it out for the test
GR_STATIC_CONST_SAME_STENCIL(gUserToClipReplace,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,           // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipReplace,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,           // set clip bit
    0xffff);

///////
// Intersect
GR_STATIC_CONST_SAME_STENCIL(gUserToClipIsect,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipIsect,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

///////
// Difference
GR_STATIC_CONST_SAME_STENCIL(gUserToClipDiff,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipDiff,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

///////
// Union

// first pass makes all the passing cases >= just clip bit set.
GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass0,
    kReplace_StencilOp,
    kKeep_StencilOp,
    kLEqual_StencilFunc,
    0xffff,
    0x0001,           // set clip bit
    0xffff);

// second pass allows anything greater than just clip bit set to pass
GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

// first pass finds zeros in the user bits and if found sets
// the clip bit to 1
GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass0,
    kReplace_StencilOp,
    kKeep_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0x0000            // set clip bit
);

// second pass zeros the user bits
GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass1,
    kZero_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,
    0xffff            // unset clip bit
);

///////
// Xor
GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass0,
    kInvert_StencilOp,
    kKeep_StencilOp,
    kEqual_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kGreater_StencilFunc,
    0xffff,
    0x0000,          // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass0,
    kInvert_StencilOp,
    kKeep_StencilOp,
    kEqual_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,          // set clip bit
    0xffff);

///////
// Reverse Diff
GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass0,
    kInvert_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,         // unset clip bit
    0x0000,         // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0x0000,          // set clip bit
    0x0000,          // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiff,
    kInvert_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,
    0x0000           // set clip bit
);
///////
// Direct to Stencil

// We can render a clip element directly without first writing to the client
// portion of the clip when the fill is not inverse and the set operation will
// only modify the in/out status of samples covered by the clip element.

// this one only works if used right after stencil clip was cleared.
// Our GrClip doesn't allow midstream replace ops.
GR_STATIC_CONST_SAME_STENCIL(gReplaceClip,
    kReplace_StencilOp,
    kReplace_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0x0000            // set clipBit
);

GR_STATIC_CONST_SAME_STENCIL(gUnionClip,
    kReplace_StencilOp,
    kReplace_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0x0000            // set clip bit
);

GR_STATIC_CONST_SAME_STENCIL(gXorClip,
    kInvert_StencilOp,
    kInvert_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,
    0x0000            // set clip bit
);

GR_STATIC_CONST_SAME_STENCIL(gDiffClip,
    kZero_StencilOp,
    kZero_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,
    0x0000            // set clip bit
);

bool GrStencilSettings::GetClipPasses(
                            SkRegion::Op op,
                            bool canBeDirect,
                            unsigned int stencilClipMask,
                            bool invertedFill,
                            int* numPasses,
                            GrStencilSettings settings[kMaxStencilClipPasses]) {
    if (canBeDirect && !invertedFill) {
        *numPasses = 0;
        switch (op) {
            case SkRegion::kReplace_Op:
                *numPasses = 1;
                settings[0] = gReplaceClip;
                break;
            case SkRegion::kUnion_Op:
                *numPasses = 1;
                settings[0] = gUnionClip;
                break;
            case SkRegion::kXOR_Op:
                *numPasses = 1;
                settings[0] = gXorClip;
                break;
            case SkRegion::kDifference_Op:
                *numPasses = 1;
                settings[0] = gDiffClip;
                break;
            default: // suppress warning
                break;
        }
        if (1 == *numPasses) {
            settings[0].fFuncRefs[kFront_Face]   |= stencilClipMask;
            settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            settings[0].fWriteMasks[kBack_Face] =
                settings[0].fWriteMasks[kFront_Face];
            return true;
        }
    }
    switch (op) {
        // if we make the path renderer go to stencil we always give it a
        // non-inverted fill and we use the stencil rules on the client->clipbit
        // pass to select either the zeros or nonzeros.
        case SkRegion::kReplace_Op:
            *numPasses= 1;
            settings[0] = invertedFill ? gInvUserToClipReplace :
                                         gUserToClipReplace;
            settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
            settings[0].fFuncMasks[kBack_Face] =
                settings[0].fFuncMasks[kFront_Face];
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            break;
        case SkRegion::kIntersect_Op:
            *numPasses = 1;
            settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect;
            settings[0].fFuncRefs[kFront_Face] = stencilClipMask;
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            break;
        case SkRegion::kUnion_Op:
            *numPasses = 2;
            if (invertedFill) {
                settings[0] = gInvUserToClipUnionPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];
                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[0].fFuncRefs[kBack_Face] =
                    settings[0].fFuncRefs[kFront_Face];
                settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
                settings[0].fWriteMasks[kBack_Face] =
                    settings[0].fWriteMasks[kFront_Face];

                settings[1] = gInvUserToClipUnionPass1;
                settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask;
                settings[1].fWriteMasks[kBack_Face] &=
                    settings[1].fWriteMasks[kFront_Face];

            } else {
                settings[0] = gUserToClipUnionPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];
                settings[0].fFuncRefs[kBack_Face] =
                    settings[0].fFuncRefs[kFront_Face];

                settings[1] = gUserToClipUnionPass1;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            }
            break;
        case SkRegion::kXOR_Op:
            *numPasses = 2;
            if (invertedFill) {
                settings[0] = gInvUserToClipXorPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];

                settings[1] = gInvUserToClipXorPass1;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            } else {
                settings[0] = gUserToClipXorPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];

                settings[1] = gUserToClipXorPass1;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            }
            break;
        case SkRegion::kDifference_Op:
            *numPasses = 1;
            settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff;
            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            break;
        case SkRegion::kReverseDifference_Op:
            if (invertedFill) {
                *numPasses = 1;
                settings[0] = gInvUserToClipRDiff;
                settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
                settings[0].fWriteMasks[kBack_Face] =
                    settings[0].fWriteMasks[kFront_Face];
            } else {
                *numPasses = 2;
                settings[0] = gUserToClipRDiffPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];
                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[0].fFuncRefs[kBack_Face] =
                    settings[0].fFuncRefs[kFront_Face];

                settings[1] = gUserToClipRDiffPass1;
                settings[1].fFuncMasks[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncMasks[kBack_Face] =
                    settings[1].fFuncMasks[kFront_Face];
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            }
            break;
        default:
            GrCrash("Unknown set op");
    }
    return false;
}
<commit_msg>Fix the stencil rules to perfom an rdiff with an inverse path<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrStencil.h"

////////////////////////////////////////////////////////////////////////////////
// Stencil Rules for Merging user stencil space into clip

// We can't include the clip bit in the ref or mask values because the division
// between user and clip bits in the stencil depends on the number of stencil
// bits in the runtime. Comments below indicate what the code should do to
// incorporate the clip bit into these settings.

///////
// Replace

// set the ref to be the clip bit, but mask it out for the test
GR_STATIC_CONST_SAME_STENCIL(gUserToClipReplace,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,           // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipReplace,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,           // set clip bit
    0xffff);

///////
// Intersect
GR_STATIC_CONST_SAME_STENCIL(gUserToClipIsect,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipIsect,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

///////
// Difference
GR_STATIC_CONST_SAME_STENCIL(gUserToClipDiff,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipDiff,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

///////
// Union

// first pass makes all the passing cases >= just clip bit set.
GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass0,
    kReplace_StencilOp,
    kKeep_StencilOp,
    kLEqual_StencilFunc,
    0xffff,
    0x0001,           // set clip bit
    0xffff);

// second pass allows anything greater than just clip bit set to pass
GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0xffff);

// first pass finds zeros in the user bits and if found sets
// the clip bit to 1
GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass0,
    kReplace_StencilOp,
    kKeep_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0x0000            // set clip bit
);

// second pass zeros the user bits
GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass1,
    kZero_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,
    0xffff            // unset clip bit
);

///////
// Xor
GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass0,
    kInvert_StencilOp,
    kKeep_StencilOp,
    kEqual_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kGreater_StencilFunc,
    0xffff,
    0x0000,          // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass0,
    kInvert_StencilOp,
    kKeep_StencilOp,
    kEqual_StencilFunc,
    0xffff,           // unset clip bit
    0x0000,
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,
    0x0000,          // set clip bit
    0xffff);

///////
// Reverse Diff
GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass0,
    kInvert_StencilOp,
    kZero_StencilOp,
    kLess_StencilFunc,
    0xffff,         // unset clip bit
    0x0000,         // set clip bit
    0xffff);

GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass1,
    kReplace_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0x0000,          // set clip bit
    0x0000,          // set clip bit
    0xffff);

// We are looking for stencil values that are all zero. The first pass sets the
// clip bit if the stencil is all zeros. The second pass clears the user bits.
GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiffPass0,
    kInvert_StencilOp,
    kZero_StencilOp,
    kEqual_StencilFunc,
    0xffff,
    0x0000,
    0x0000           // set clip bit
);

GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiffPass1,
    kZero_StencilOp,
    kZero_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,
    0xffff           // unset clip bit
);

///////
// Direct to Stencil

// We can render a clip element directly without first writing to the client
// portion of the clip when the fill is not inverse and the set operation will
// only modify the in/out status of samples covered by the clip element.

// this one only works if used right after stencil clip was cleared.
// Our GrClip doesn't allow midstream replace ops.
GR_STATIC_CONST_SAME_STENCIL(gReplaceClip,
    kReplace_StencilOp,
    kReplace_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0x0000            // set clipBit
);

GR_STATIC_CONST_SAME_STENCIL(gUnionClip,
    kReplace_StencilOp,
    kReplace_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,           // set clip bit
    0x0000            // set clip bit
);

GR_STATIC_CONST_SAME_STENCIL(gXorClip,
    kInvert_StencilOp,
    kInvert_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,
    0x0000            // set clip bit
);

GR_STATIC_CONST_SAME_STENCIL(gDiffClip,
    kZero_StencilOp,
    kZero_StencilOp,
    kAlways_StencilFunc,
    0xffff,
    0x0000,
    0x0000            // set clip bit
);

bool GrStencilSettings::GetClipPasses(
                            SkRegion::Op op,
                            bool canBeDirect,
                            unsigned int stencilClipMask,
                            bool invertedFill,
                            int* numPasses,
                            GrStencilSettings settings[kMaxStencilClipPasses]) {
    if (canBeDirect && !invertedFill) {
        *numPasses = 0;
        switch (op) {
            case SkRegion::kReplace_Op:
                *numPasses = 1;
                settings[0] = gReplaceClip;
                break;
            case SkRegion::kUnion_Op:
                *numPasses = 1;
                settings[0] = gUnionClip;
                break;
            case SkRegion::kXOR_Op:
                *numPasses = 1;
                settings[0] = gXorClip;
                break;
            case SkRegion::kDifference_Op:
                *numPasses = 1;
                settings[0] = gDiffClip;
                break;
            default: // suppress warning
                break;
        }
        if (1 == *numPasses) {
            settings[0].fFuncRefs[kFront_Face]   |= stencilClipMask;
            settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            settings[0].fWriteMasks[kBack_Face] =
                settings[0].fWriteMasks[kFront_Face];
            return true;
        }
    }
    switch (op) {
        // if we make the path renderer go to stencil we always give it a
        // non-inverted fill and we use the stencil rules on the client->clipbit
        // pass to select either the zeros or nonzeros.
        case SkRegion::kReplace_Op:
            *numPasses= 1;
            settings[0] = invertedFill ? gInvUserToClipReplace :
                                         gUserToClipReplace;
            settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
            settings[0].fFuncMasks[kBack_Face] =
                settings[0].fFuncMasks[kFront_Face];
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            break;
        case SkRegion::kIntersect_Op:
            *numPasses = 1;
            settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect;
            settings[0].fFuncRefs[kFront_Face] = stencilClipMask;
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            break;
        case SkRegion::kUnion_Op:
            *numPasses = 2;
            if (invertedFill) {
                settings[0] = gInvUserToClipUnionPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];
                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[0].fFuncRefs[kBack_Face] =
                    settings[0].fFuncRefs[kFront_Face];
                settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
                settings[0].fWriteMasks[kBack_Face] =
                    settings[0].fWriteMasks[kFront_Face];

                settings[1] = gInvUserToClipUnionPass1;
                settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask;
                settings[1].fWriteMasks[kBack_Face] &=
                    settings[1].fWriteMasks[kFront_Face];

            } else {
                settings[0] = gUserToClipUnionPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];
                settings[0].fFuncRefs[kBack_Face] =
                    settings[0].fFuncRefs[kFront_Face];

                settings[1] = gUserToClipUnionPass1;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            }
            break;
        case SkRegion::kXOR_Op:
            *numPasses = 2;
            if (invertedFill) {
                settings[0] = gInvUserToClipXorPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];

                settings[1] = gInvUserToClipXorPass1;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            } else {
                settings[0] = gUserToClipXorPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];

                settings[1] = gUserToClipXorPass1;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            }
            break;
        case SkRegion::kDifference_Op:
            *numPasses = 1;
            settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff;
            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
            settings[0].fFuncRefs[kBack_Face] =
                settings[0].fFuncRefs[kFront_Face];
            break;
        case SkRegion::kReverseDifference_Op:
            if (invertedFill) {
                *numPasses = 2;
                settings[0] = gInvUserToClipRDiffPass0;
                settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
                settings[0].fWriteMasks[kBack_Face] =
                    settings[0].fWriteMasks[kFront_Face];
                settings[1] = gInvUserToClipRDiffPass1;
                settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask;
                settings[1].fWriteMasks[kBack_Face] =
                    settings[1].fWriteMasks[kFront_Face];
            } else {
                *numPasses = 2;
                settings[0] = gUserToClipRDiffPass0;
                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
                settings[0].fFuncMasks[kBack_Face] =
                    settings[0].fFuncMasks[kFront_Face];
                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[0].fFuncRefs[kBack_Face] =
                    settings[0].fFuncRefs[kFront_Face];

                settings[1] = gUserToClipRDiffPass1;
                settings[1].fFuncMasks[kFront_Face] |= stencilClipMask;
                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
                settings[1].fFuncMasks[kBack_Face] =
                    settings[1].fFuncMasks[kFront_Face];
                settings[1].fFuncRefs[kBack_Face] =
                    settings[1].fFuncRefs[kFront_Face];
            }
            break;
        default:
            GrCrash("Unknown set op");
    }
    return false;
}
<|endoftext|>