<commit_before>/*
 * Website:
 *      https://github.com/wo3kie/dojo
 *
 * Author:
 *      Lukasz Czerwinski
 *
 * Compilation:
 *      g++ --std=c++11 pca.cpp -o pca -lgsl -lgslcblas -lm
 *
 * Usage:
 *      $ ./pca
 */

/*
 * https://en.wikipedia.org/wiki/Principal_component_analysis
 *  #Computing_PCA_using_the_covariance_method
 */

 /*
  * Inspired with, https://gist.github.com/microo8/4065693
  */

#include <cassert>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_statistics.h>

gsl_matrix * pca(
    gsl_matrix const * const matrix /* (points, dim) */,
    unsigned newDimension
){
    /* `points' is a number of points
     * `dim' is a number of dimensions,
     *
     * [a0, a1, a2, ..., aN]
     * [b0, b1, b2, ..., bN]
     * ...
     * [z0, z1, z2, ..., zN]
     */

    unsigned const points = matrix->size1;
    unsigned const dim = matrix->size2;

    /* For each dimension calculate mean
     */

    gsl_vector * const mean = gsl_vector_alloc(dim);

    for(unsigned i = 0; i < dim; ++i){
        gsl_vector_set(
            mean,
            i,
            gsl_stats_mean(matrix->data + i, dim, points)
        );
    }

    /* Subtract mean
     */

    gsl_matrix * const mean_subtracted_matrix
        = gsl_matrix_alloc(points, dim);

    gsl_matrix_memcpy(mean_subtracted_matrix, matrix);

    for(unsigned i = 0; i < points; ++i){
        gsl_vector_view mean_subtracted_view
            = gsl_matrix_row(mean_subtracted_matrix, i);

        gsl_vector_sub(&mean_subtracted_view.vector, mean);
    }

    /* Calculate covariance matrix
     */

    gsl_matrix * const covariance_matrix = gsl_matrix_alloc(dim, dim);

    /*
     * Compute matrix-matrix product and sum
     * gsl_blas_dgemm(op1, op2, a, A, B, b, C )
     * C = a * op1(A) dot op2(B) + b * C
     * where op1, op2 can be CblasNoTrans, CblasTrans, CblasConjTrans
     *
     * covariance_matrix = mean_subtracted_matrix dot
     *     mean_subtracted_matrix.T + 0 * covariance_matrix
     *
     * (points, dim).T dot (points, dim) =>
     * (dim, points) dot (points, dim) =>
     * (dim, dim)
     */

    gsl_blas_dgemm(
        CblasConjTrans,
        CblasNoTrans,
        1.0 / (dim - 1),
        mean_subtracted_matrix,
        mean_subtracted_matrix,
        0.0,
        covariance_matrix
    );

    gsl_vector * const eigenvalues = gsl_vector_alloc(dim);
    gsl_matrix * const eigenvectors = gsl_matrix_alloc(dim, dim);

    /* Allocate a workspace for computing eigenvalues and eigenvectors
     * with the size O(4n)
     */

    gsl_eigen_symmv_workspace * const workspace
        = gsl_eigen_symmv_alloc(dim);

    /* Compute the eigenvalues and eigenvectors of the real
     * symmetric matrix A.
     * The corresponding eigenvectors are stored in the dim
     * of the matrix `eigenvectors'
     */

    gsl_eigen_symmv(
        covariance_matrix,
        eigenvalues,
        eigenvectors,
        workspace
    );

    /* Simultaneously sorts the eigenvalues stored in the vector
     * and the corresponding eigenvectors
     *     GSL_EIGEN_SORT_VAL_ASC
     *     GSL_EIGEN_SORT_VAL_DESC
     *     GSL_EIGEN_SORT_ABS_ASC
     *     GSL_EIGEN_SORT_ABS_DESC
     */

    gsl_eigen_symmv_sort(
        eigenvalues,
        eigenvectors,
        GSL_EIGEN_SORT_ABS_DESC
    );

    /* Project original matrix to a new space with reduced dimensions
     */

    gsl_matrix * const result = gsl_matrix_alloc(points, newDimension);

    /* Make a matrix view of a submatrix of the matrix m.
     * The upper-left element of the submatrix is the element (k1,k2)
     * of the original matrix. The submatrix has n1 rows and n2 columns.
     *
     * gsl_matrix_submatrix(matrix, k1, k2, n1, n2)
     */

    gsl_matrix_view const reduced_eigenvectors = gsl_matrix_submatrix(
        eigenvectors,
        0,
        0,
        dim,
        newDimension
    );

    /* result = 1 * mean_subtracted_matrix dot reduced_eigenvectors + 0 * result
     * (points, dim) dot (dim, new_dim) =>
     * (points, new_dim)
     */

    gsl_blas_dgemm(
        CblasNoTrans,
        CblasNoTrans,
        1.0,
        matrix,
        &reduced_eigenvectors.matrix,
        0.0,
        result
    );

    gsl_eigen_symmv_free(workspace);
    gsl_matrix_free(eigenvectors);
    gsl_vector_free(eigenvalues);
    gsl_matrix_free(covariance_matrix);
    gsl_matrix_free(mean_subtracted_matrix);
    gsl_vector_free(mean);

    return result;
}

#include <iostream>

int main(){
    double points[] = {
     // x  y
        1, 0,
        2, 1,
        3, 0,
        4, 1,
        5, 0,
        6, 1,
        7, 0,
        8, 1,
        9, 0
    };

    gsl_matrix_view const view = gsl_matrix_view_array(points, 9, 2);

    gsl_matrix * const result = pca(&view.matrix, 1);

    assert(gsl_matrix_get(result, 0, 0) == 1);
    assert(gsl_matrix_get(result, 1, 0) == 2);
    assert(gsl_matrix_get(result, 2, 0) == 3);
    assert(gsl_matrix_get(result, 3, 0) == 4);
    assert(gsl_matrix_get(result, 4, 0) == 5);
    assert(gsl_matrix_get(result, 5, 0) == 6);
    assert(gsl_matrix_get(result, 6, 0) == 7);
    assert(gsl_matrix_get(result, 7, 0) == 8);
    assert(gsl_matrix_get(result, 8, 0) == 9);

    gsl_matrix_free(result);
}
<commit_msg>pca.cpp<commit_after>/*
 * Website:
 *      https://github.com/wo3kie/dojo
 *
 * Author:
 *      Lukasz Czerwinski
 *
 * Compilation:
 *      g++ --std=c++11 pca.cpp -o pca -lgsl -lgslcblas -lm
 *
 * Usage:
 *      $ ./pca
 */

/*
 * https://en.wikipedia.org/wiki/Principal_component_analysis
 *  #Computing_PCA_using_the_covariance_method
 */

 /*
  * Inspired with, https://gist.github.com/microo8/4065693
  */

#include <cassert>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_statistics.h>

gsl_matrix * pca(
    gsl_matrix const * const matrix /* (points, dim) */,
    unsigned newDimension
){
    /* `points' is a number of points
     * `dim' is a number of dimensions,
     *
     * [a0, a1, a2, ..., aN]
     * [b0, b1, b2, ..., bN]
     * ...
     * [z0, z1, z2, ..., zN]
     */

    unsigned const points = matrix->size1;
    unsigned const dim = matrix->size2;

    /* For each dimension calculate mean
     */

    gsl_vector * const mean = gsl_vector_alloc(dim);

    for(unsigned i = 0; i < dim; ++i){
        gsl_vector_set(
            mean,
            i,
            gsl_stats_mean(matrix->data + i, dim, points)
        );
    }

    /* Subtract mean
     */

    gsl_matrix * const mean_subtracted_matrix
        = gsl_matrix_alloc(points, dim);

    gsl_matrix_memcpy(mean_subtracted_matrix, matrix);

    for(unsigned i = 0; i < points; ++i){
        gsl_vector_view mean_subtracted_view
            = gsl_matrix_row(mean_subtracted_matrix, i);

        gsl_vector_sub(&mean_subtracted_view.vector, mean);
    }

    /* Calculate covariance matrix
     */

    gsl_matrix * const covariance_matrix = gsl_matrix_alloc(dim, dim);

    /*
     * Compute matrix-matrix product and sum
     * gsl_blas_dgemm(op1, op2, a, A, B, b, C )
     * C = a * op1(A) dot op2(B) + b * C
     * where op1, op2 can be CblasNoTrans, CblasTrans, CblasConjTrans
     *
     * covariance_matrix = mean_subtracted_matrix dot
     *     mean_subtracted_matrix.T + 0 * covariance_matrix
     *
     * (points, dim).T dot (points, dim) =>
     * (dim, points) dot (points, dim) =>
     * (dim, dim)
     */

    gsl_blas_dgemm(
        CblasConjTrans,
        CblasNoTrans,
        1.0 / (dim - 1),
        mean_subtracted_matrix,
        mean_subtracted_matrix,
        0.0,
        covariance_matrix
    );

    gsl_vector * const eigenvalues = gsl_vector_alloc(dim);
    gsl_matrix * const eigenvectors = gsl_matrix_alloc(dim, dim);

    /* Allocate a workspace for computing eigenvalues and eigenvectors
     * with the size O(4n)
     */

    gsl_eigen_symmv_workspace * const workspace
        = gsl_eigen_symmv_alloc(dim);

    /* Compute the eigenvalues and eigenvectors of the real
     * symmetric matrix A.
     * The corresponding eigenvectors are stored in the dim
     * of the matrix `eigenvectors'
     */

    gsl_eigen_symmv(
        covariance_matrix,
        eigenvalues,
        eigenvectors,
        workspace
    );

    /* Simultaneously sorts the eigenvalues stored in the vector
     * and the corresponding eigenvectors
     *     GSL_EIGEN_SORT_VAL_ASC
     *     GSL_EIGEN_SORT_VAL_DESC
     *     GSL_EIGEN_SORT_ABS_ASC
     *     GSL_EIGEN_SORT_ABS_DESC
     */

    gsl_eigen_symmv_sort(
        eigenvalues,
        eigenvectors,
        GSL_EIGEN_SORT_ABS_DESC
    );

    /* Project original matrix to a new space with reduced dimensions
     */

    gsl_matrix * const result = gsl_matrix_alloc(points, newDimension);

    /* Make a matrix view of a submatrix of the matrix m.
     * The upper-left element of the submatrix is the element (k1,k2)
     * of the original matrix. The submatrix has n1 rows and n2 columns.
     *
     * gsl_matrix_submatrix(matrix, k1, k2, n1, n2)
     */

    gsl_matrix_view const reduced_eigenvectors = gsl_matrix_submatrix(
        eigenvectors,
        0,
        0,
        dim,
        newDimension
    );

    /* result = 1 * mean_subtracted_matrix dot reduced_eigenvectors + 0 * result
     * (points, dim) dot (dim, new_dim) =>
     * (points, new_dim)
     */

    gsl_blas_dgemm(
        CblasNoTrans,
        CblasNoTrans,
        1.0,
        matrix,
        &reduced_eigenvectors.matrix,
        0.0,
        result
    );

    gsl_eigen_symmv_free(workspace);
    gsl_matrix_free(eigenvectors);
    gsl_vector_free(eigenvalues);
    gsl_matrix_free(covariance_matrix);
    gsl_matrix_free(mean_subtracted_matrix);
    gsl_vector_free(mean);

    return result;
}

#include <iostream>

int main(){
    double points[] = {
     // x  y
        1, 0,
        2, 1,
        3, 0,
        4, 1,
        5, 0,
        6, 1,
        7, 0,
        8, 1,
        9, 0
    };

    gsl_matrix_view const view = gsl_matrix_view_array(points, 9, 2);

    gsl_matrix * const result = pca(&view.matrix, 1);

    assert(gsl_matrix_get(result, 0, 0) == 1);
    assert(gsl_matrix_get(result, 1, 0) == 2);
    assert(gsl_matrix_get(result, 2, 0) == 3);
    assert(gsl_matrix_get(result, 3, 0) == 4);
    assert(gsl_matrix_get(result, 4, 0) == 5);
    assert(gsl_matrix_get(result, 5, 0) == 6);
    assert(gsl_matrix_get(result, 6, 0) == 7);
    assert(gsl_matrix_get(result, 7, 0) == 8);
    assert(gsl_matrix_get(result, 8, 0) == 9);

    gsl_matrix_free(result);
}

<|endoftext|>


<commit_before>/* Copyright (c) 2007-2014, Eddie Kohler
 * Copyright (c) 2007, Regents of the University of California
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Tamer LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Tamer LICENSE file; the license in that file is
 * legally binding.
 */
#include "config.h"
#include <tamer/tamer.hh>
#include "dinternal.hh"
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

namespace tamer {
namespace {
using tamerpriv::make_fd_callback;
using tamerpriv::fd_callback_driver;
using tamerpriv::fd_callback_fd;

class driver_tamer : public driver {
  public:
    driver_tamer();
    ~driver_tamer();

    virtual void at_fd(int fd, int action, event<int> e);
    virtual void at_time(const timeval &expiry, event<> e, bool bg);
    virtual void at_asap(event<> e);
    virtual void at_preblock(event<> e);
    virtual void kill_fd(int fd);

    virtual void loop(loop_flags flags);
    virtual void break_loop();

  private:

    struct fdp {
	inline fdp(driver_tamer*, int) {
	}
    };

    union xfd_set {
	fd_set fds;
	char s[1];
        uint32_t u[1];
    };

    enum {
        fdreadnow = fdread + 2, fdwritenow = fdwrite + 2
    };

    tamerpriv::driver_fdset<fdp> fds_;
    int fdbound_;
    xfd_set *fdset_[4];
    int fdset_fdcap_;

    tamerpriv::driver_timerset timers_;

    tamerpriv::driver_asapset asap_;
    tamerpriv::driver_asapset preblock_;

    bool loop_state_;

    static void fd_disinterest(void* arg);
    void update_fds();
    int find_bad_fds();
};

driver_tamer::driver_tamer()
    : fdbound_(0), fdset_fdcap_(sizeof(xfd_set) * 8), loop_state_(false) {
    fdset_fdcap_ = (fdset_fdcap_ + 31) & ~31; // make multiple of 4 bytes
    assert(FD_SETSIZE <= fdset_fdcap_);
    assert((fdset_fdcap_ % 32) == 0);
    for (int i = 0; i < 4; ++i) {
	fdset_[i] = reinterpret_cast<xfd_set *>(new char[fdset_fdcap_ / 8]);
	if (i < fdreadnow)
	    memset(fdset_[i], 0, fdset_fdcap_ / 8);
    }
}

driver_tamer::~driver_tamer() {
    // free fd_sets
    for (int i = 0; i < 4; ++i)
	delete[] reinterpret_cast<char *>(fdset_[i]);
}

void driver_tamer::fd_disinterest(void* arg) {
    driver_tamer* d = static_cast<driver_tamer*>(fd_callback_driver(arg));
    d->fds_.push_change(fd_callback_fd(arg));
}

void driver_tamer::at_fd(int fd, int action, event<int> e) {
    assert(fd >= 0);
    if (e && (action == 0 || action == 1)) {
	fds_.expand(this, fd);
	tamerpriv::driver_fd<fdp> &x = fds_[fd];
        x.e[action] += TAMER_MOVE(e);
	tamerpriv::simple_event::at_trigger(x.e[action].__get_simple(),
                                            fd_disinterest,
                                            make_fd_callback(this, fd));
	fds_.push_change(fd);
    }
}

void driver_tamer::kill_fd(int fd) {
    if (fd >= 0 && fd < fds_.size()) {
	tamerpriv::driver_fd<fdp> &x = fds_[fd];
	for (int action = 0; action < 2; ++action)
	    x.e[action].trigger(-ECANCELED);
	fds_.push_change(fd);
    }
}

void driver_tamer::update_fds() {
    int fd;
    while ((fd = fds_.pop_change()) >= 0) {
	tamerpriv::driver_fd<fdp> &x = fds_[fd];
	if (fd >= fdset_fdcap_) {
	    int ncap = fdset_fdcap_ * 2;
	    while (ncap < fd)
		ncap *= 2;
	    for (int acti = 0; acti < 4; ++acti) {
		xfd_set *x = reinterpret_cast<xfd_set *>(new char[ncap / 8]);
		if (acti < 2) {
		    memcpy(x, fdset_[acti], fdset_fdcap_ / 8);
		    memset(&x->s[fdset_fdcap_ / 8], 0, (ncap - fdset_fdcap_) / 8);
		}
		delete[] reinterpret_cast<char *>(fdset_[acti]);
		fdset_[acti] = x;
	    }
	    fdset_fdcap_ = ncap;
	}
	for (int action = 0; action < 2; ++action)
	    if (x.e[action])
		FD_SET(fd, &fdset_[action]->fds);
	    else
		FD_CLR(fd, &fdset_[action]->fds);
	if (x.e[0] || x.e[1]) {
	    if (fd >= fdbound_)
		fdbound_ = fd + 1;
	} else if (fd + 1 == fdbound_)
	    do {
		--fdbound_;
	    } while (fdbound_ > 0
		     && !fds_[fdbound_ - 1].e[0]
		     && !fds_[fdbound_ - 1].e[1]);
    }
}

void driver_tamer::at_time(const timeval &expiry, event<> e, bool bg) {
    if (e)
	timers_.push(expiry, e.__release_simple(), bg);
}

void driver_tamer::at_asap(event<> e) {
    if (e)
	asap_.push(e.__release_simple());
}

void driver_tamer::at_preblock(event<> e) {
    if (e)
	preblock_.push(e.__release_simple());
}

void driver_tamer::loop(loop_flags flags)
{
    if (flags == loop_forever)
        loop_state_ = true;

 again:
    // process asap events
    while (!preblock_.empty())
	preblock_.pop_trigger();
    run_unblocked();

    // fix file descriptors
    if (fds_.has_change())
	update_fds();

    // determine timeout
    struct timeval to, *toptr;
    timers_.cull();
    if (!asap_.empty()
	|| (!timers_.empty() && !timercmp(&timers_.expiry(), &recent(), >))
        || (!timers_.empty() && tamerpriv::time_type == time_virtual)
	|| sig_any_active
	|| has_unblocked()) {
	timerclear(&to);
	toptr = &to;
    } else if (!timers_.has_foreground()
               && fdbound_ == 0
               && sig_nforeground == 0)
        // no foreground events!
        return;
    else if (!timers_.empty()) {
	timersub(&timers_.expiry(), &recent(), &to);
	toptr = &to;
    } else
	toptr = 0;

    // select!
    int nfds = fdbound_;
    if (sig_pipe[0] > nfds)
	nfds = sig_pipe[0] + 1;
    if (nfds > 0) {
	memcpy(fdset_[fdreadnow], fdset_[fdread], ((nfds + 63) & ~63) >> 3);
	memcpy(fdset_[fdwritenow], fdset_[fdwrite], ((nfds + 63) & ~63) >> 3);
	if (sig_pipe[0] >= 0)
	    FD_SET(sig_pipe[0], &fdset_[fdreadnow]->fds);
    }
    if (nfds > 0 || !toptr || to.tv_sec != 0 || to.tv_usec != 0) {
	nfds = select(nfds, &fdset_[fdreadnow]->fds,
		      &fdset_[fdwritenow]->fds, 0, toptr);
         if (nfds == -1 && errno == EBADF)
             nfds = find_bad_fds();
    }

    // process signals
    set_recent();
    if (sig_any_active)
	dispatch_signals();

    // process fd events
    if (nfds > 0) {
	for (int fd = 0; fd < fdbound_; ++fd) {
	    tamerpriv::driver_fd<fdp> &x = fds_[fd];
	    for (int action = 0; action < 2; ++action)
		if (FD_ISSET(fd, &fdset_[action + 2]->fds) && x.e[action])
		    x.e[action].trigger(0);
	}
        run_unblocked();
    } else if (!timers_.empty())
        tamerpriv::recent = timers_.expiry();

    // process timer events
    while (!timers_.empty() && !timercmp(&timers_.expiry(), &recent(), >))
	timers_.pop_trigger();
    run_unblocked();

    // process asap events
    while (!asap_.empty())
	asap_.pop_trigger();
    run_unblocked();

    // check flags
    if (loop_state_)
	goto again;
}

int driver_tamer::find_bad_fds() {
    // first, combine all file descriptors from read & write
    memcpy(fdset_[fdreadnow], fdset_[fdread], (fdbound_ + 31) & ~31);
    for (int i = 0; i < fdbound_ / 32; ++i)
        fdset_[fdreadnow]->u[i] |= fdset_[fdwrite]->u[i];
    // use binary search to find a bad file descriptor
    int l = 0, r = (fdbound_ + 7) & ~7;
    while (r - l > 1) {
        int m = l + ((r - l) >> 1);
        memset(fdset_[fdwritenow], 0, l);
        memcpy(&fdset_[fdwritenow]->s[l], &fdset_[fdreadnow]->s[l], m - l);
        struct timeval tv = {0, 0};
        int nfds = select(m << 3, &fdset_[fdwritenow]->fds, 0, 0, &tv);
        if (nfds == -1 && errno == EBADF)
            r = m;
        else if (nfds != -1)
            l = m;
    }
    // down to <= 8 file descriptors; test them one by one
    // clear result sets
    memset(fdset_[fdreadnow], 0, ((fdbound_ + 63) & ~63) >> 3);
    memset(fdset_[fdwritenow], 0, ((fdbound_ + 63) & ~63) >> 3);
    // set up result sets
    int nfds = 0;
    for (int f = l * 8; f != r * 8; ++f) {
        int fr = FD_ISSET(f, &fdset_[fdread]->fds);
        int fw = FD_ISSET(f, &fdset_[fdwrite]->fds);
        if ((fr || fw) && fcntl(f, F_GETFL) == -1 && errno == EBADF) {
            if (fr)
                FD_SET(f, &fdset_[fdreadnow]->fds);
            if (fw)
                FD_SET(f, &fdset_[fdwritenow]->fds);
            ++nfds;
        }
    }
    return nfds;
}

void driver_tamer::break_loop() {
    loop_state_ = false;
}

} // namespace

driver *driver::make_tamer() {
    return new driver_tamer;
}

} // namespace tamer
<commit_msg>Tamer driver: 8-byte multiples, some unsignedness fixes.<commit_after>/* Copyright (c) 2007-2014, Eddie Kohler
 * Copyright (c) 2007, Regents of the University of California
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Tamer LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Tamer LICENSE file; the license in that file is
 * legally binding.
 */
#include "config.h"
#include <tamer/tamer.hh>
#include "dinternal.hh"
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

namespace tamer {
namespace {
using tamerpriv::make_fd_callback;
using tamerpriv::fd_callback_driver;
using tamerpriv::fd_callback_fd;

class driver_tamer : public driver {
  public:
    driver_tamer();
    ~driver_tamer();

    virtual void at_fd(int fd, int action, event<int> e);
    virtual void at_time(const timeval &expiry, event<> e, bool bg);
    virtual void at_asap(event<> e);
    virtual void at_preblock(event<> e);
    virtual void kill_fd(int fd);

    virtual void loop(loop_flags flags);
    virtual void break_loop();

  private:

    struct fdp {
	inline fdp(driver_tamer*, int) {
	}
    };

    union xfd_set {
	fd_set fds;
	char s[1];
        uint64_t q[1];
    };

    enum {
        fdreadnow = fdread + 2, fdwritenow = fdwrite + 2
    };

    tamerpriv::driver_fdset<fdp> fds_;
    unsigned fdbound_;
    xfd_set* fdset_[4];
    unsigned fdset_fdcap_;

    tamerpriv::driver_timerset timers_;

    tamerpriv::driver_asapset asap_;
    tamerpriv::driver_asapset preblock_;

    bool loop_state_;

    static void fd_disinterest(void* arg);
    void initialize_fdsets();
    void update_fds();
    int find_bad_fds();
};

driver_tamer::driver_tamer()
    : fdbound_(0), fdset_fdcap_(0), loop_state_(false) {
    fdset_[0] = fdset_[1] = fdset_[2] = fdset_[3] = (xfd_set*) 0;
    initialize_fdsets();
}

driver_tamer::~driver_tamer() {
    for (int i = 0; i < 4; ++i)
	delete[] reinterpret_cast<char*>(fdset_[i]);
}

void driver_tamer::initialize_fdsets() {
    assert(!fdset_fdcap_);
    fdset_fdcap_ = sizeof(xfd_set) * 8; // make multiple of 8B
    assert(FD_SETSIZE <= fdset_fdcap_);
    assert((fdset_fdcap_ % 64) == 0);
    for (int i = 0; i < 4; ++i) {
	fdset_[i] = reinterpret_cast<xfd_set*>(new char[fdset_fdcap_ / 8]);
	if (i < fdreadnow)
	    memset(fdset_[i], 0, fdset_fdcap_ / 8);
    }
}

void driver_tamer::fd_disinterest(void* arg) {
    driver_tamer* d = static_cast<driver_tamer*>(fd_callback_driver(arg));
    d->fds_.push_change(fd_callback_fd(arg));
}

void driver_tamer::at_fd(int fd, int action, event<int> e) {
    assert(fd >= 0);
    if (e && (action == 0 || action == 1)) {
	fds_.expand(this, fd);
	tamerpriv::driver_fd<fdp>& x = fds_[fd];
        x.e[action] += TAMER_MOVE(e);
	tamerpriv::simple_event::at_trigger(x.e[action].__get_simple(),
                                            fd_disinterest,
                                            make_fd_callback(this, fd));
	fds_.push_change(fd);
    }
}

void driver_tamer::kill_fd(int fd) {
    if (fd >= 0 && fd < fds_.size()) {
	tamerpriv::driver_fd<fdp> &x = fds_[fd];
        x.e[0].trigger(-ECANCELED);
        x.e[1].trigger(-ECANCELED);
	fds_.push_change(fd);
    }
}

void driver_tamer::update_fds() {
    int fd;
    while ((fd = fds_.pop_change()) >= 0) {
	tamerpriv::driver_fd<fdp>& x = fds_[fd];
	if ((unsigned) fd >= fdset_fdcap_) {
	    unsigned ncap = fdset_fdcap_ * 2;
	    while (ncap < (unsigned) fd)
		ncap *= 2;
	    for (int acti = 0; acti < 4; ++acti) {
		xfd_set *x = reinterpret_cast<xfd_set*>(new char[ncap / 8]);
		if (acti < 2) {
		    memcpy(x, fdset_[acti], fdset_fdcap_ / 8);
		    memset(&x->s[fdset_fdcap_ / 8], 0, (ncap - fdset_fdcap_) / 8);
		}
		delete[] reinterpret_cast<char*>(fdset_[acti]);
		fdset_[acti] = x;
	    }
	    fdset_fdcap_ = ncap;
	}
	for (int action = 0; action < 2; ++action)
	    if (x.e[action])
		FD_SET(fd, &fdset_[action]->fds);
	    else
		FD_CLR(fd, &fdset_[action]->fds);
	if (x.e[0] || x.e[1]) {
	    if ((unsigned) fd >= fdbound_)
		fdbound_ = fd + 1;
	} else if ((unsigned) fd + 1 == fdbound_)
	    do {
		--fdbound_;
	    } while (fdbound_ > 0
		     && !fds_[fdbound_ - 1].e[0]
		     && !fds_[fdbound_ - 1].e[1]);
    }
}

void driver_tamer::at_time(const timeval &expiry, event<> e, bool bg) {
    if (e)
	timers_.push(expiry, e.__release_simple(), bg);
}

void driver_tamer::at_asap(event<> e) {
    if (e)
	asap_.push(e.__release_simple());
}

void driver_tamer::at_preblock(event<> e) {
    if (e)
	preblock_.push(e.__release_simple());
}

void driver_tamer::loop(loop_flags flags)
{
    if (flags == loop_forever)
        loop_state_ = true;

 again:
    // process asap events
    while (!preblock_.empty())
	preblock_.pop_trigger();
    run_unblocked();

    // fix file descriptors
    if (fds_.has_change())
	update_fds();

    // determine timeout
    struct timeval to, *toptr;
    timers_.cull();
    if (!asap_.empty()
	|| (!timers_.empty() && !timercmp(&timers_.expiry(), &recent(), >))
        || (!timers_.empty() && tamerpriv::time_type == time_virtual)
	|| sig_any_active
	|| has_unblocked()) {
	timerclear(&to);
	toptr = &to;
    } else if (!timers_.has_foreground()
               && fdbound_ == 0
               && sig_nforeground == 0)
        // no foreground events!
        return;
    else if (!timers_.empty()) {
	timersub(&timers_.expiry(), &recent(), &to);
	toptr = &to;
    } else
	toptr = 0;

    // select!
    int nfds = fdbound_;
    if (sig_pipe[0] > nfds)
	nfds = sig_pipe[0] + 1;
    if (nfds > 0) {
	memcpy(fdset_[fdreadnow], fdset_[fdread], ((nfds + 63) & ~63) >> 3);
	memcpy(fdset_[fdwritenow], fdset_[fdwrite], ((nfds + 63) & ~63) >> 3);
	if (sig_pipe[0] >= 0)
	    FD_SET(sig_pipe[0], &fdset_[fdreadnow]->fds);
    }
    if (nfds > 0 || !toptr || to.tv_sec != 0 || to.tv_usec != 0) {
	nfds = select(nfds, &fdset_[fdreadnow]->fds,
		      &fdset_[fdwritenow]->fds, 0, toptr);
        if (nfds == -1 && errno == EBADF)
            nfds = find_bad_fds();
    }

    // process signals
    set_recent();
    if (sig_any_active)
	dispatch_signals();

    // process fd events
    if (nfds > 0) {
	for (unsigned fd = 0; fd < fdbound_; ++fd) {
	    tamerpriv::driver_fd<fdp> &x = fds_[fd];
	    for (int action = 0; action < 2; ++action)
		if (FD_ISSET(fd, &fdset_[action + 2]->fds) && x.e[action])
		    x.e[action].trigger(0);
	}
        run_unblocked();
    } else if (!timers_.empty())
        tamerpriv::recent = timers_.expiry();

    // process timer events
    while (!timers_.empty() && !timercmp(&timers_.expiry(), &recent(), >))
	timers_.pop_trigger();
    run_unblocked();

    // process asap events
    while (!asap_.empty())
	asap_.pop_trigger();
    run_unblocked();

    // check flags
    if (loop_state_)
	goto again;
}

int driver_tamer::find_bad_fds() {
    // first, combine all file descriptors from read & write
    unsigned nbytes = ((fdbound_ + 63) & ~63) >> 3;
    memcpy(fdset_[fdreadnow], fdset_[fdread], nbytes);
    for (unsigned i = 0; i < nbytes / 8; ++i)
        fdset_[fdreadnow]->q[i] |= fdset_[fdwrite]->q[i];
    // use binary search to find a bad file descriptor
    int l = 0, r = (fdbound_ + 7) & ~7;
    while (r - l > 1) {
        int m = l + ((r - l) >> 1);
        memset(fdset_[fdwritenow], 0, l);
        memcpy(&fdset_[fdwritenow]->s[l], &fdset_[fdreadnow]->s[l], m - l);
        struct timeval tv = {0, 0};
        int nfds = select(m << 3, &fdset_[fdwritenow]->fds, 0, 0, &tv);
        if (nfds == -1 && errno == EBADF)
            r = m;
        else if (nfds != -1)
            l = m;
    }
    // down to <= 8 file descriptors; test them one by one
    // clear result sets
    memset(fdset_[fdreadnow], 0, nbytes);
    memset(fdset_[fdwritenow], 0, nbytes);
    // set up result sets
    int nfds = 0;
    for (int f = l * 8; f != r * 8; ++f) {
        int fr = FD_ISSET(f, &fdset_[fdread]->fds);
        int fw = FD_ISSET(f, &fdset_[fdwrite]->fds);
        if ((fr || fw) && fcntl(f, F_GETFL) == -1 && errno == EBADF) {
            if (fr)
                FD_SET(f, &fdset_[fdreadnow]->fds);
            if (fw)
                FD_SET(f, &fdset_[fdwritenow]->fds);
            ++nfds;
        }
    }
    return nfds;
}

void driver_tamer::break_loop() {
    loop_state_ = false;
}

} // namespace

driver *driver::make_tamer() {
    return new driver_tamer;
}

} // namespace tamer
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Rafael Rivera
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

// An Anonymous fellow and Peter Bright (yes, that guy) helped me refactor.
// Appreciate it, sirs.

#include "stdafx.h"

// {7B2D8DFB-D190-344E-BF60-6EAC09922BBF}
DEFINE_PROPERTYKEY(PKEY_WINX_HASH, 0xFB8D2D7B, 0x90D1, 0x4E34, 0xBF, 0x60, 0x6E, 0xAC, 0x09, 0x92, 0x2B, 0xBF, 0x02);

#define CHECKHR(HR, MESSAGE) \
    if(FAILED(HR)) \
    { \
        if(MESSAGE) wprintf(L"%s (hr: %X)\r\n", MESSAGE, hr); \
        return HR; \
    }

#define GUID_MAX_LEN 40

using namespace std;

HRESULT GeneralizePath(const wchar_t*, wchar_t*, size_t);

int wmain(int argc, wchar_t* argv[])
{
    wprintf(
        L"\nHashLnk v0.2.0.0\n"
        L"Copyright(c) 2012 Rafael Rivera\n"
        L"Within Windows - http://withinwindows.com\n\n");

    if(argc != 2)
    {
        wprintf(L"usage: hashlnk <.lnk file>\n\n");
        return ERROR_BAD_ARGUMENTS;
    }

    DWORD bufferSize = GetFullPathNameW(argv[1], 0, nullptr, nullptr);
    if(bufferSize == 0)
    {
        wprintf(L"Specified path is invalid.");
        return ERROR_INVALID_PARAMETER;
    }

    unique_ptr<wchar_t[]> resolvedTarget(new wchar_t[bufferSize]);
    GetFullPathNameW(argv[1], bufferSize, resolvedTarget.get(), nullptr);

    if(!PathFileExistsW(resolvedTarget.get()))
    {
        wprintf(L"Specified file does not exist.");
        return ERROR_FILE_NOT_FOUND;
    }

    HRESULT hr = CoInitialize(nullptr);
    CHECKHR(hr, L"Failed to initialize COM.");

    //
    // Let's pull out the shortcut's target path/args
    //
    CComPtr<IShellItem2> lnk;
    hr = SHCreateItemFromParsingName(resolvedTarget.get(), nullptr, IID_PPV_ARGS(&lnk));
    CHECKHR(hr, L"Failed to create shell item.");

    CComHeapPtr<wchar_t> targetPath;
    hr = lnk->GetString(PKEY_Link_TargetParsingPath, &targetPath);
    CHECKHR(hr, L"Failed to retrieve target path.");

    unique_ptr<wchar_t[]> generalizedPath(new wchar_t[MAX_PATH]);
    hr = GeneralizePath(targetPath, generalizedPath.get(), MAX_PATH);
    if(FAILED(hr))
    {
        return hr;
    }

    CComHeapPtr<wchar_t> targetArgs;
    hr = lnk->GetString(PKEY_Link_Arguments, &targetArgs);
    if(FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
    {
        wprintf(L"Failed to retrieve target arguments.");
        return hr;
    }

    //
    // Glue everything together and lowercase it, so we can hash it.
    //
    const wchar_t salt[] = L"do not prehash links.  this should only be done by the user.";

    wstring blob = generalizedPath.get();
    if(targetArgs)
    {
        blob += targetArgs;
    }
    blob += salt;

    int lowerCaseLength = LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_LOWERCASE, blob.data(), blob.size(), nullptr, 0, nullptr, nullptr, 0);
    if(!lowerCaseLength)
    {
        wprintf(L"Failed to lowercase blob string.");
        return GetLastError();
    }

    unique_ptr<wchar_t[]> hashableBlob(new wchar_t[lowerCaseLength]);
    LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_LOWERCASE, blob.data(), blob.size(), hashableBlob.get(), lowerCaseLength, nullptr, nullptr, 0);

    ULONG hash = 0;
    hr = HashData(reinterpret_cast<BYTE*>(hashableBlob.get()), lowerCaseLength * sizeof(wchar_t), reinterpret_cast<BYTE*>(&hash), sizeof(hash));
    CHECKHR(hr, L"Failed to hash data.");

    //
    // We have a hash, let's stamp it onto the .lnk now.
    //
    CComPtr<IPropertyStore> store;
    hr = lnk->GetPropertyStore(GPS_READWRITE, IID_PPV_ARGS(&store));
    CHECKHR(hr, L"Failed to get property store.");

    PROPVARIANT propValue = {0};
    propValue.ulVal = hash;
    propValue.vt = VT_UI4;

    hr = store->SetValue(PKEY_WINX_HASH, propValue);
    CHECKHR(hr, L"Failed to set property store value.");

    hr = store->Commit();
    CHECKHR(hr, L"Failed to write changes to .lnk.");

    wprintf(L"Hash generated and applied (0x%X)\n", hash);

    CoUninitialize();

    return 0;
}

HRESULT GeneralizePath(const wchar_t* originalPath, wchar_t* generalizedPath, size_t capacity)
{
    HRESULT hr = ERROR_SUCCESS;

    //
    // Do we need to do some trickery to get the Program Files
    // known folder?
    //
    BOOL isRunningUnderEmulation = FALSE;
    IsWow64Process(GetCurrentProcess(), &isRunningUnderEmulation);

    //
    // Because SHGetKnownFolderPath doesn't properly retrieve
    // FOLDERID_ProgramFilesX64 from 32-bit processes, I
    // abandoned its use. We'll use good ol' environment
    // variables instead. >_>. The FOLDERIDs are for
    // generalization purposes only.
    //
    // It sucks but not as much as distributing two
    // flavors of hashlnk.
    //

    KNOWNFOLDERID guids[3];
    wchar_t* tokens[3];

    if(isRunningUnderEmulation)
    {
        tokens[0] = L"%ProgramW6432%";
        guids[0] = FOLDERID_ProgramFilesX64;
    }
    else
    {
        tokens[0] = L"%ProgramFiles%";
        guids[0] = FOLDERID_ProgramFilesX86;
    }

    tokens[1] = L"%SystemRoot%\\System32";
    guids[1] = FOLDERID_System;

    tokens[2] = L"%SystemRoot%";
    guids[2] = FOLDERID_Windows;

    for(int i = 0; i < sizeof(guids) / sizeof(KNOWNFOLDERID); ++i)
    {
        unique_ptr<wchar_t[]> folderPath(new wchar_t[MAX_PATH]);

        int numCharsInPath = ExpandEnvironmentStringsW(tokens[i], folderPath.get(), MAX_PATH);
        if(numCharsInPath == 0)
        {
            CHECKHR(hr, L"Failed to resolve known folder location.");
        }

        --numCharsInPath; // Remove NULL terminator from count.

        int numCommonChars = PathCommonPrefixW(folderPath.get(), originalPath, NULL);
        if(numCommonChars < numCharsInPath)
        {
            continue;
        }

        unique_ptr<wchar_t[]> guid(new wchar_t[GUID_MAX_LEN]);
        if(!StringFromGUID2(guids[i], guid.get(), GUID_MAX_LEN))
        {
            hr = E_OUTOFMEMORY;
            CHECKHR(hr, L"Failed to derive string from known folder GUID.");
        }

        ZeroMemory(generalizedPath, capacity);

        hr = StringCchCatW(generalizedPath, MAX_PATH, guid.get());
        CHECKHR(hr, L"Failed to build generalized path.");

        hr = StringCchCatW(generalizedPath, MAX_PATH, originalPath + numCommonChars);
        CHECKHR(hr, L"Failed to build generalized path.");

        break;
    }

    return hr;
}
<commit_msg>Corrected use of FOLDERID_ProgramFiles<commit_after>/*
 * Copyright (c) 2012 Rafael Rivera
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

// An Anonymous fellow and Peter Bright (yes, that guy) helped me refactor.
// Appreciate it, sirs.

#include "stdafx.h"

// {7B2D8DFB-D190-344E-BF60-6EAC09922BBF}
DEFINE_PROPERTYKEY(PKEY_WINX_HASH, 0xFB8D2D7B, 0x90D1, 0x4E34, 0xBF, 0x60, 0x6E, 0xAC, 0x09, 0x92, 0x2B, 0xBF, 0x02);

#define CHECKHR(HR, MESSAGE) \
    if(FAILED(HR)) \
    { \
        if(MESSAGE) wprintf(L"%s (hr: %X)\r\n", MESSAGE, hr); \
        return HR; \
    }

#define GUID_MAX_LEN 40

using namespace std;

HRESULT GeneralizePath(const wchar_t*, wchar_t*, size_t);

int wmain(int argc, wchar_t* argv[])
{
    wprintf(
        L"\nHashLnk v0.2.0.0\n"
        L"Copyright(c) 2012 Rafael Rivera\n"
        L"Within Windows - http://withinwindows.com\n\n");

    if(argc != 2)
    {
        wprintf(L"usage: hashlnk <.lnk file>\n\n");
        return ERROR_BAD_ARGUMENTS;
    }

    DWORD bufferSize = GetFullPathNameW(argv[1], 0, nullptr, nullptr);
    if(bufferSize == 0)
    {
        wprintf(L"Specified path is invalid.");
        return ERROR_INVALID_PARAMETER;
    }

    unique_ptr<wchar_t[]> resolvedTarget(new wchar_t[bufferSize]);
    GetFullPathNameW(argv[1], bufferSize, resolvedTarget.get(), nullptr);

    if(!PathFileExistsW(resolvedTarget.get()))
    {
        wprintf(L"Specified file does not exist.");
        return ERROR_FILE_NOT_FOUND;
    }

    HRESULT hr = CoInitialize(nullptr);
    CHECKHR(hr, L"Failed to initialize COM.");

    //
    // Let's pull out the shortcut's target path/args
    //
    CComPtr<IShellItem2> lnk;
    hr = SHCreateItemFromParsingName(resolvedTarget.get(), nullptr, IID_PPV_ARGS(&lnk));
    CHECKHR(hr, L"Failed to create shell item.");

    CComHeapPtr<wchar_t> targetPath;
    hr = lnk->GetString(PKEY_Link_TargetParsingPath, &targetPath);
    CHECKHR(hr, L"Failed to retrieve target path.");

    unique_ptr<wchar_t[]> generalizedPath(new wchar_t[MAX_PATH]);
    hr = GeneralizePath(targetPath, generalizedPath.get(), MAX_PATH);
    if(FAILED(hr))
    {
        return hr;
    }

    CComHeapPtr<wchar_t> targetArgs;
    hr = lnk->GetString(PKEY_Link_Arguments, &targetArgs);
    if(FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
    {
        wprintf(L"Failed to retrieve target arguments.");
        return hr;
    }

    //
    // Glue everything together and lowercase it, so we can hash it.
    //
    const wchar_t salt[] = L"do not prehash links.  this should only be done by the user.";

    wstring blob = generalizedPath.get();
    if(targetArgs)
    {
        blob += targetArgs;
    }
    blob += salt;

    int lowerCaseLength = LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_LOWERCASE, blob.data(), blob.size(), nullptr, 0, nullptr, nullptr, 0);
    if(!lowerCaseLength)
    {
        wprintf(L"Failed to lowercase blob string.");
        return GetLastError();
    }

    unique_ptr<wchar_t[]> hashableBlob(new wchar_t[lowerCaseLength]);
    int test = LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_LOWERCASE, blob.data(), blob.size(), hashableBlob.get(), lowerCaseLength, nullptr, nullptr, 0);

    ULONG hash = 0;
    hr = HashData(reinterpret_cast<BYTE*>(hashableBlob.get()), lowerCaseLength * sizeof(wchar_t), reinterpret_cast<BYTE*>(&hash), sizeof(hash));
    CHECKHR(hr, L"Failed to hash data.");

    //
    // We have a hash, let's stamp it onto the .lnk now.
    //
    CComPtr<IPropertyStore> store;
    hr = lnk->GetPropertyStore(GPS_READWRITE, IID_PPV_ARGS(&store));
    CHECKHR(hr, L"Failed to get property store.");

    PROPVARIANT propValue = {0};
    propValue.ulVal = hash;
    propValue.vt = VT_UI4;

    hr = store->SetValue(PKEY_WINX_HASH, propValue);
    CHECKHR(hr, L"Failed to set property store value.");

    hr = store->Commit();
    CHECKHR(hr, L"Failed to write changes to .lnk.");

    wprintf(L"Hash generated and applied (0x%X)\n", hash);

    CoUninitialize();

    return 0;
}

HRESULT GeneralizePath(const wchar_t* originalPath, wchar_t* generalizedPath, size_t capacity)
{
    HRESULT hr = ERROR_SUCCESS;

    //
    // Do we need to do some trickery to get the Program Files
    // known folder?
    //
    BOOL isRunningUnderEmulation = FALSE;
    IsWow64Process(GetCurrentProcess(), &isRunningUnderEmulation);

    //
    // Because SHGetKnownFolderPath doesn't properly retrieve
    // FOLDERID_ProgramFilesX64 from 32-bit processes, I
    // abandoned its use. We'll use good ol' environment
    // variables instead. >_>. The FOLDERIDs are for
    // generalization purposes only.
    //
    // It sucks but not as much as distributing two
    // flavors of hashlnk.
    //

    //
    // Also, you're probably wondering about
    // FOLDERID_ProgramFiles dupes here. twinui!_CheckLinkHash
    // uses FOLDERID_ProgramFiles to retrieve the "native"
    // default Program Files location. To emulate this
    // behavior, without cutting two archs of the program,
    // I use the variables below. That's fine, but I must
    // use the FOLDERID_ProgramFiles GUID in the resulting
    // generalized path as Windows' expects a hash based
    // on that. Grr.
    //

    KNOWNFOLDERID guids[3];
    wchar_t* tokens[3];

    if(isRunningUnderEmulation)
    {
        tokens[0] = L"%ProgramW6432%";
        guids[0] = FOLDERID_ProgramFiles;
    }
    else
    {
        tokens[0] = L"%ProgramFiles%";
        guids[0] = FOLDERID_ProgramFiles;
    }

    tokens[1] = L"%SystemRoot%\\System32";
    guids[1] = FOLDERID_System;

    tokens[2] = L"%SystemRoot%";
    guids[2] = FOLDERID_Windows;

    for(int i = 0; i < sizeof(guids) / sizeof(KNOWNFOLDERID); ++i)
    {
        unique_ptr<wchar_t[]> folderPath(new wchar_t[MAX_PATH]);

        int numCharsInPath = ExpandEnvironmentStringsW(tokens[i], folderPath.get(), MAX_PATH);
        if(numCharsInPath == 0)
        {
            CHECKHR(hr, L"Failed to resolve known folder location.");
        }

        --numCharsInPath; // Remove NULL terminator from count.

        int numCommonChars = PathCommonPrefixW(folderPath.get(), originalPath, NULL);
        if(numCommonChars < numCharsInPath)
        {
            continue;
        }

        unique_ptr<wchar_t[]> guid(new wchar_t[GUID_MAX_LEN]);
        if(!StringFromGUID2(guids[i], guid.get(), GUID_MAX_LEN))
        {
            hr = E_OUTOFMEMORY;
            CHECKHR(hr, L"Failed to derive string from known folder GUID.");
        }

        ZeroMemory(generalizedPath, capacity);

        hr = StringCchCatW(generalizedPath, MAX_PATH, guid.get());
        CHECKHR(hr, L"Failed to build generalized path.");

        hr = StringCchCatW(generalizedPath, MAX_PATH, originalPath + numCommonChars);
        CHECKHR(hr, L"Failed to build generalized path.");

        break;
    }

    return hr;
}<|endoftext|>


<commit_before>#include <codecvt>
#include <iomanip>
#include <iostream>
#include <locale>

#include <hc.hpp>

namespace {
std::wostream& operator<<(std::wostream& out, hc::access_type type)
{
  switch (type) {
    case hc::access_type_auto:
      out << "auto";
      break;
    case hc::access_type_none:
      out << "none";
      break;
    case hc::access_type_read:
      out << "read";
      break;
    case hc::access_type_write:
      out << "write";
      break;
    case hc::access_type_read_write:
      out << "read write";
      break;
  }

  return out;
}

std::wostream& operator<<(std::wostream& out, hc::hcAgentProfile profile)
{
  switch (profile) {
    case hc::hcAgentProfileBase:
      out << "base";
      break;
    case hc::hcAgentProfileFull:
      out << "full";
      break;
    case hc::hcAgentProfileNone:
      out << "none";
      break;
  }
  return out;
}

std::wstring double_precision_str(const hc::accelerator& accelerator)
{
  if (accelerator.get_supports_double_precision()) {
    return L"yes";
  }
  if (accelerator.get_supports_limited_double_precision()) {
    return L"limited";
  }

  return L"none";
}

std::wstring bool_str(bool value)
{
  return value ? L"true": L"false";
}

std::wstring version_string(const hc::accelerator& acc)
{
  unsigned int version {acc.get_version()};
  unsigned minor { version & ((1 << 16) -1) };
  unsigned major { version >> 16 };

  return std::to_wstring(major) + L'.' + std::to_wstring(minor);
}

std::wostream& operator<<(std::wostream& out, const hc::accelerator& acc)
{
  out << std::left;
  auto out_w = [&]() -> std::wostream& { return out << std::setw(25); };

  out_w() << "device path:" << acc.get_device_path() << '\n';
  out_w() << "description:" << acc.get_description() << '\n';
  out_w() << "version:" << version_string(acc) << '\n';

  out_w() << "dedicated memory:" << acc.get_dedicated_memory() << " KB\n";
  out_w() << "CPU shared memory:"
    << bool_str(acc.get_supports_cpu_shared_memory()) << '\n';

  out_w() << "max static tile size:"
    << const_cast<hc::accelerator&>(acc).get_max_tile_static_size() << '\n';

  out_w() << "default CPU mem access:"
    << acc.get_default_cpu_access_type() << '\n';

  out_w() << "compute unit count:" << acc.get_cu_count() << '\n';

  out_w() << "double precision:" << double_precision_str(acc) << '\n';

  out_w() << "debug:" << bool_str(acc.get_is_debug()) << '\n';
  out_w() << "has display:" << bool_str(acc.get_has_display()) << '\n';
  out_w() << "emulated:" << bool_str(acc.get_is_emulated()) << '\n';

  out_w() << "HSA Profile:" << acc.get_profile() << '\n';

  out_w() << "peers:";
  const auto peers = acc.get_peers();
  std::for_each(std::begin(peers), std::end(peers),
      [&out](const hc::accelerator& p) { out << p.get_device_path() << " "; });
  if (peers.empty()) {
    out << '-';
  }
  out << '\n';

  return out;
}

std::string backend_str(int backend)
{
  switch (backend) {
    case HCC_BACKEND_AMDGPU:
      return "HCC_BACKEND_AMDGPU";
    case HCC_BACKEND_HSAIL:
      return "HCC_BACKEND_HSAIL";
    case HCC_BACKEND_CL:
      return "HCC_BACKEND_CL";

    default:
      return "Unknown HCC Backend";
  }
}

void display_usage(std::ostream& out=std::cout)
{
  out << "usage: hc-info [DEVICE-PATH | default]\n";
  out << '\n';
  out << "prints information about the available HSA devices for the HC API\n";
  out << "see: https://github.com/RadeonOpenCompute/hcc\n";
  out << '\n';
  out << "compiled for:\n";
  out << "HCC version: " << __hcc_version__ << '\n';
  out << "Backend: " << backend_str(__hcc_backend__) << '\n';
}

} // namespace

int main(int argc, char* argv[])
{
  if (argc > 2) {
    display_usage(std::cerr);
    return EXIT_FAILURE;
  }

  if (2 == argc) {
    std::string path = argv[1];
    if ("-h" == path || "--help" == path) {
      display_usage();
      return EXIT_SUCCESS;
    }

    std::wstring_convert<std::codecvt_utf8<wchar_t>> u8towide("Error");

    std::wstring wpath = u8towide.from_bytes(path);
    try {
      const hc::accelerator acc(wpath);
      if (wpath != L"default" && acc.get_device_path() != wpath) { // hc::acc does not throw on no dev
        throw std::runtime_error("no accelerator");
      }

      std::wcout << acc;
    } catch (...) {
      std::cerr << "no accelerator for path: '" << path << "'\n";
      return EXIT_FAILURE;
    }
  } else {
    const auto accelerators = hc::accelerator::get_all();
    if (accelerators.empty()) {
      std::cout << "no accelerators found.\n";
    } else {
      std::wcout << accelerators.size() << " available accelerators:\n\n";

      std::for_each(std::begin(accelerators), std::end(accelerators),
          [](const hc::accelerator& a) { std::wcout << a << '\n'; });

      const hc::accelerator default_accelerator;
      std::wcout << "default accelerator:\t"
        << default_accelerator.get_device_path() << '\n';
    }
  }

  return EXIT_SUCCESS;
}
<commit_msg>cleanup includes<commit_after>#include <codecvt>
#include <iomanip>
#include <iostream>

#include <hc.hpp>

namespace {
std::wostream& operator<<(std::wostream& out, hc::access_type type)
{
  switch (type) {
    case hc::access_type_auto:
      out << "auto";
      break;
    case hc::access_type_none:
      out << "none";
      break;
    case hc::access_type_read:
      out << "read";
      break;
    case hc::access_type_write:
      out << "write";
      break;
    case hc::access_type_read_write:
      out << "read write";
      break;
  }

  return out;
}

std::wostream& operator<<(std::wostream& out, hc::hcAgentProfile profile)
{
  switch (profile) {
    case hc::hcAgentProfileBase:
      out << "base";
      break;
    case hc::hcAgentProfileFull:
      out << "full";
      break;
    case hc::hcAgentProfileNone:
      out << "none";
      break;
  }
  return out;
}

std::wstring double_precision_str(const hc::accelerator& accelerator)
{
  if (accelerator.get_supports_double_precision()) {
    return L"yes";
  }
  if (accelerator.get_supports_limited_double_precision()) {
    return L"limited";
  }

  return L"none";
}

std::wstring bool_str(bool value)
{
  return value ? L"true": L"false";
}

std::wstring version_string(const hc::accelerator& acc)
{
  unsigned int version {acc.get_version()};
  unsigned minor { version & ((1 << 16) -1) };
  unsigned major { version >> 16 };

  return std::to_wstring(major) + L'.' + std::to_wstring(minor);
}

std::wostream& operator<<(std::wostream& out, const hc::accelerator& acc)
{
  out << std::left;
  auto out_w = [&]() -> std::wostream& { return out << std::setw(25); };

  out_w() << "device path:" << acc.get_device_path() << '\n';
  out_w() << "description:" << acc.get_description() << '\n';
  out_w() << "version:" << version_string(acc) << '\n';

  out_w() << "dedicated memory:" << acc.get_dedicated_memory() << " KB\n";
  out_w() << "CPU shared memory:"
    << bool_str(acc.get_supports_cpu_shared_memory()) << '\n';

  out_w() << "max static tile size:"
    << const_cast<hc::accelerator&>(acc).get_max_tile_static_size() << '\n';

  out_w() << "default CPU mem access:"
    << acc.get_default_cpu_access_type() << '\n';

  out_w() << "compute unit count:" << acc.get_cu_count() << '\n';

  out_w() << "double precision:" << double_precision_str(acc) << '\n';

  out_w() << "debug:" << bool_str(acc.get_is_debug()) << '\n';
  out_w() << "has display:" << bool_str(acc.get_has_display()) << '\n';
  out_w() << "emulated:" << bool_str(acc.get_is_emulated()) << '\n';

  out_w() << "HSA Profile:" << acc.get_profile() << '\n';

  out_w() << "peers:";
  const auto peers = acc.get_peers();
  std::for_each(std::begin(peers), std::end(peers),
      [&out](const hc::accelerator& p) { out << p.get_device_path() << " "; });
  if (peers.empty()) {
    out << '-';
  }
  out << '\n';

  return out;
}

std::string backend_str(int backend)
{
  switch (backend) {
    case HCC_BACKEND_AMDGPU:
      return "HCC_BACKEND_AMDGPU";
    case HCC_BACKEND_HSAIL:
      return "HCC_BACKEND_HSAIL";
    case HCC_BACKEND_CL:
      return "HCC_BACKEND_CL";

    default:
      return "Unknown HCC Backend";
  }
}

void display_usage(std::ostream& out=std::cout)
{
  out << "usage: hc-info [DEVICE-PATH | default]\n";
  out << '\n';
  out << "prints information about the available HSA devices for the HC API\n";
  out << "see: https://github.com/RadeonOpenCompute/hcc\n";
  out << '\n';
  out << "compiled for:\n";
  out << "HCC version: " << __hcc_version__ << '\n';
  out << "Backend: " << backend_str(__hcc_backend__) << '\n';
}

} // namespace

int main(int argc, char* argv[])
{
  if (argc > 2) {
    display_usage(std::cerr);
    return EXIT_FAILURE;
  }

  if (2 == argc) {
    std::string path = argv[1];
    if ("-h" == path || "--help" == path) {
      display_usage();
      return EXIT_SUCCESS;
    }

    std::wstring_convert<std::codecvt_utf8<wchar_t>> u8towide("Error");

    std::wstring wpath = u8towide.from_bytes(path);
    try {
      const hc::accelerator acc(wpath);
      if (wpath != L"default" && acc.get_device_path() != wpath) { // hc::acc does not throw on no dev
        throw std::runtime_error("no accelerator");
      }

      std::wcout << acc;
    } catch (...) {
      std::cerr << "no accelerator for path: '" << path << "'\n";
      return EXIT_FAILURE;
    }
  } else {
    const auto accelerators = hc::accelerator::get_all();
    if (accelerators.empty()) {
      std::cout << "no accelerators found.\n";
    } else {
      std::wcout << accelerators.size() << " available accelerators:\n\n";

      std::for_each(std::begin(accelerators), std::end(accelerators),
          [](const hc::accelerator& a) { std::wcout << a << '\n'; });

      const hc::accelerator default_accelerator;
      std::wcout << "default accelerator:\t"
        << default_accelerator.get_device_path() << '\n';
    }
  }

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include "keymap.h"

#include "3rd-party/catch.hpp"

#include "exceptions.h"

using namespace newsboat;

TEST_CASE("get_operation()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_operation("u", "article") == OP_SHOWURLS);
	REQUIRE(k.get_operation("X", "feedlist") == OP_NIL);
	REQUIRE(k.get_operation("", "feedlist") == OP_NIL);
	REQUIRE(k.get_operation("ENTER", "feedlist") == OP_OPEN);

	SECTION("Returns OP_NIL after unset_key()")
	{
		k.unset_key("ENTER", "all");
		REQUIRE(k.get_operation("ENTER", "all") == OP_NIL);
	}
}

TEST_CASE("unset_key() and set_key()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_operation("ENTER", "feedlist") == OP_OPEN);
	REQUIRE(k.getkey(OP_OPEN, "all") == "ENTER");

	SECTION("unset_key() removes the mapping")
	{
		k.unset_key("ENTER", "all");
		REQUIRE(k.get_operation("ENTER", "all") == OP_NIL);

		SECTION("set_key() sets the mapping")
		{
			k.set_key(OP_OPEN, "ENTER", "all");
			REQUIRE(k.get_operation("ENTER", "all") == OP_OPEN);
			REQUIRE(k.getkey(OP_OPEN, "all") == "ENTER");
		}
	}
}

TEST_CASE("unset_all_keys() clears the keymap from key bindings for a given context",
	"[KeyMap]")
{
	SECTION("keymap has most of the keys set up by default") {
		KeyMap k(KM_NEWSBOAT);

		for (int i = OP_QUIT; i < OP_NB_MAX; ++i) {
			if (i == OP_OPENALLUNREADINBROWSER ||
					i == OP_MARKALLABOVEASREAD ||
					i == OP_OPENALLUNREADINBROWSER_AND_MARK) {
				continue;
			}
			REQUIRE(k.getkey(static_cast<Operation>(i), "all") != "<none>");
		}
	}

	SECTION("\"all\" context clears the keymap from key bindings") {
		KeyMap k(KM_NEWSBOAT);
		k.unset_all_keys("all");

		for (int i = OP_NB_MIN; i < OP_NB_MAX; ++i) {
			REQUIRE(k.getkey(static_cast<Operation>(i), "all") == "<none>");
		}
	}

	SECTION("clears key bindings just for a given context") {
		KeyMap k(KM_NEWSBOAT);
		k.unset_all_keys("articlelist");

		for (int i = OP_NB_MIN; i < OP_NB_MAX; ++i) {
			REQUIRE(k.getkey(static_cast<Operation>(i), "articlelist") == "<none>");
		}

		for (int i = OP_QUIT; i < OP_NB_MAX; ++i) {
			if (i == OP_OPENALLUNREADINBROWSER ||
					i == OP_MARKALLABOVEASREAD ||
					i == OP_OPENALLUNREADINBROWSER_AND_MARK) {
				continue;
			}
			REQUIRE(k.getkey(static_cast<Operation>(i), "feedlist") != "<none>");
		}
	}
}

TEST_CASE("get_opcode()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_opcode("open") == OP_OPEN);
	REQUIRE(k.get_opcode("some-noexistent-operation") == OP_NIL);
}

TEST_CASE("getkey()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	SECTION("Retrieves general bindings")
	{
		REQUIRE(k.getkey(OP_OPEN, "all") == "ENTER");
		REQUIRE(k.getkey(OP_TOGGLEITEMREAD, "all") == "N");
		REQUIRE(k.getkey(static_cast<Operation>(30000), "all") ==
			"<none>");
	}

	SECTION("Returns context-specific bindings only in that context")
	{
		k.unset_key("q", "article");
		k.set_key(OP_QUIT, "O", "article");
		REQUIRE(k.getkey(OP_QUIT, "article") == "O");
		REQUIRE(k.getkey(OP_QUIT, "all") == "q");
	}

	SECTION("Returns context-specific binding if asked to search in all contexts")
	{
		k.unset_all_keys("all");
		REQUIRE(k.getkey(OP_QUIT, "all") == "<none>");
		k.set_key(OP_QUIT, "O", "article");
		REQUIRE(k.getkey(OP_QUIT, "all") == "O");
	}
}

TEST_CASE("get_key()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_key(" ") == ' ');
	REQUIRE(k.get_key("U") == 'U');
	REQUIRE(k.get_key("~") == '~');
	REQUIRE(k.get_key("INVALID") == 0);
	REQUIRE(k.get_key("ENTER") == '\n');
	REQUIRE(k.get_key("ESC") == '\033');
	REQUIRE(k.get_key("^A") == '\001');
}

TEST_CASE("handle_action()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);
	std::vector<std::string> params;

	SECTION("without parameters")
	{
		REQUIRE_THROWS_AS(k.handle_action("bind-key", params),
			ConfigHandlerException);
		REQUIRE_THROWS_AS(k.handle_action("unbind-key", params),
			ConfigHandlerException);
		REQUIRE_THROWS_AS(k.handle_action("macro", params),
			ConfigHandlerException);
	}

	SECTION("with one parameter")
	{
		params.push_back("r");

		REQUIRE_THROWS_AS(k.handle_action("bind-key", params),
			ConfigHandlerException);
		REQUIRE_NOTHROW(k.handle_action("unbind-key", params));
	}

	SECTION("with two parameters")
	{
		params.push_back("r");
		params.push_back("open");
		REQUIRE_NOTHROW(k.handle_action("bind-key", params));
		REQUIRE_THROWS_AS(k.handle_action("an-invalid-action", params),
			ConfigHandlerException);
	}

	SECTION("invalid-op throws exception")
	{
		params.push_back("I");
		params.push_back("invalid-op");

		REQUIRE_THROWS_AS(k.handle_action("bind-key", params),
			ConfigHandlerException);
		REQUIRE_THROWS_AS(k.handle_action("macro", params),
			ConfigHandlerException);
	}
}
<commit_msg>Correct test descriptions<commit_after>#include "keymap.h"

#include "3rd-party/catch.hpp"

#include "exceptions.h"

using namespace newsboat;

TEST_CASE("get_operation()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_operation("u", "article") == OP_SHOWURLS);
	REQUIRE(k.get_operation("X", "feedlist") == OP_NIL);
	REQUIRE(k.get_operation("", "feedlist") == OP_NIL);
	REQUIRE(k.get_operation("ENTER", "feedlist") == OP_OPEN);

	SECTION("Returns OP_NIL after unset_key()")
	{
		k.unset_key("ENTER", "all");
		REQUIRE(k.get_operation("ENTER", "all") == OP_NIL);
	}
}

TEST_CASE("unset_key() and set_key()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_operation("ENTER", "feedlist") == OP_OPEN);
	REQUIRE(k.getkey(OP_OPEN, "all") == "ENTER");

	SECTION("unset_key() removes the mapping")
	{
		k.unset_key("ENTER", "all");
		REQUIRE(k.get_operation("ENTER", "all") == OP_NIL);

		SECTION("set_key() sets the mapping")
		{
			k.set_key(OP_OPEN, "ENTER", "all");
			REQUIRE(k.get_operation("ENTER", "all") == OP_OPEN);
			REQUIRE(k.getkey(OP_OPEN, "all") == "ENTER");
		}
	}
}

TEST_CASE(
	"unset_all_keys() clears the keymap from key bindings for a given context",
	"[KeyMap]")
{
	SECTION("KeyMap has most of the keys set up by default") {
		KeyMap k(KM_NEWSBOAT);

		for (int i = OP_QUIT; i < OP_NB_MAX; ++i) {
			if (i == OP_OPENALLUNREADINBROWSER ||
					i == OP_MARKALLABOVEASREAD ||
					i == OP_OPENALLUNREADINBROWSER_AND_MARK) {
				continue;
			}
			REQUIRE(k.getkey(static_cast<Operation>(i), "all") != "<none>");
		}
	}

	SECTION("\"all\" context clears the keymap from all key bindings") {
		KeyMap k(KM_NEWSBOAT);
		k.unset_all_keys("all");

		for (int i = OP_NB_MIN; i < OP_NB_MAX; ++i) {
			REQUIRE(k.getkey(static_cast<Operation>(i), "all") == "<none>");
		}
	}

	SECTION("Clears key bindings just for a given context") {
		KeyMap k(KM_NEWSBOAT);
		k.unset_all_keys("articlelist");

		for (int i = OP_NB_MIN; i < OP_NB_MAX; ++i) {
			REQUIRE(k.getkey(static_cast<Operation>(i), "articlelist") == "<none>");
		}

		for (int i = OP_QUIT; i < OP_NB_MAX; ++i) {
			if (i == OP_OPENALLUNREADINBROWSER ||
					i == OP_MARKALLABOVEASREAD ||
					i == OP_OPENALLUNREADINBROWSER_AND_MARK) {
				continue;
			}
			REQUIRE(k.getkey(static_cast<Operation>(i), "feedlist") != "<none>");
		}
	}
}

TEST_CASE("get_opcode()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_opcode("open") == OP_OPEN);
	REQUIRE(k.get_opcode("some-noexistent-operation") == OP_NIL);
}

TEST_CASE("getkey()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	SECTION("Retrieves general bindings")
	{
		REQUIRE(k.getkey(OP_OPEN, "all") == "ENTER");
		REQUIRE(k.getkey(OP_TOGGLEITEMREAD, "all") == "N");
		REQUIRE(k.getkey(static_cast<Operation>(30000), "all") ==
			"<none>");
	}

	SECTION("Returns context-specific bindings only in that context")
	{
		k.unset_key("q", "article");
		k.set_key(OP_QUIT, "O", "article");
		REQUIRE(k.getkey(OP_QUIT, "article") == "O");
		REQUIRE(k.getkey(OP_QUIT, "all") == "q");
	}

	SECTION("Returns context-specific binding if asked to search in all contexts")
	{
		k.unset_all_keys("all");
		REQUIRE(k.getkey(OP_QUIT, "all") == "<none>");
		k.set_key(OP_QUIT, "O", "article");
		REQUIRE(k.getkey(OP_QUIT, "all") == "O");
	}
}

TEST_CASE("get_key()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);

	REQUIRE(k.get_key(" ") == ' ');
	REQUIRE(k.get_key("U") == 'U');
	REQUIRE(k.get_key("~") == '~');
	REQUIRE(k.get_key("INVALID") == 0);
	REQUIRE(k.get_key("ENTER") == '\n');
	REQUIRE(k.get_key("ESC") == '\033');
	REQUIRE(k.get_key("^A") == '\001');
}

TEST_CASE("handle_action()", "[KeyMap]")
{
	KeyMap k(KM_NEWSBOAT);
	std::vector<std::string> params;

	SECTION("without parameters")
	{
		REQUIRE_THROWS_AS(k.handle_action("bind-key", params),
			ConfigHandlerException);
		REQUIRE_THROWS_AS(k.handle_action("unbind-key", params),
			ConfigHandlerException);
		REQUIRE_THROWS_AS(k.handle_action("macro", params),
			ConfigHandlerException);
	}

	SECTION("with one parameter")
	{
		params.push_back("r");

		REQUIRE_THROWS_AS(k.handle_action("bind-key", params),
			ConfigHandlerException);
		REQUIRE_NOTHROW(k.handle_action("unbind-key", params));
	}

	SECTION("with two parameters")
	{
		params.push_back("r");
		params.push_back("open");
		REQUIRE_NOTHROW(k.handle_action("bind-key", params));
		REQUIRE_THROWS_AS(k.handle_action("an-invalid-action", params),
			ConfigHandlerException);
	}

	SECTION("invalid-op throws exception")
	{
		params.push_back("I");
		params.push_back("invalid-op");

		REQUIRE_THROWS_AS(k.handle_action("bind-key", params),
			ConfigHandlerException);
		REQUIRE_THROWS_AS(k.handle_action("macro", params),
			ConfigHandlerException);
	}
}
<|endoftext|>


<commit_before>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <gtest/gtest.h>
#include <arrayfire.h>
#include <af/dim4.hpp>
#include <af/defines.h>
#include <af/traits.hpp>
#include <vector>
#include <iostream>
#include <complex>
#include <string>
#include <testHelpers.hpp>

using std::vector;
using std::string;
using std::cout;
using std::endl;
using std::abs;
using af::cfloat;
using af::cdouble;

///////////////////////////////// CPP ////////////////////////////////////
//

template<typename T>
af::array makeSparse(af::array A, int factor)
{
    A = floor(A * 1000);
    A = A * ((A % factor) == 0) / 1000;
    return A;
}

template<>
af::array makeSparse<cfloat>(af::array A, int factor)
{
    af::array r = real(A);
    r = floor(r * 1000);
    r = r * ((r % factor) == 0) / 1000;

    af::array i = r / 2;

    A = af::complex(r, i);
    return A;
}

template<>
af::array makeSparse<cdouble>(af::array A, int factor)
{
    af::array r = real(A);
    r = floor(r * 1000);
    r = r * ((r % factor) == 0) / 1000;

    af::array i = r / 2;

    A = af::complex(r, i);
    return A;
}

double calc_norm(af::array lhs, af::array rhs)
{
    return af::max<double>(af::abs(lhs - rhs) / (af::abs(lhs) + af::abs(rhs) + 1E-5));
}

template<typename T>
void sparseTester(const int m, const int n, const int k, int factor, double eps)
{
    af::deviceGC();

    if (noDoubleTests<T>()) return;

#if 1
    af::array A = cpu_randu<T>(af::dim4(m, n));
    af::array B = cpu_randu<T>(af::dim4(n, k));
#else
    af::array A = af::randu(m, n, (af::dtype)af::dtype_traits<T>::af_type);
    af::array B = af::randu(n, k, (af::dtype)af::dtype_traits<T>::af_type);
#endif

    A = makeSparse<T>(A, factor);

    // Result of GEMM
    af::array dRes1 = matmul(A, B);

    // Create Sparse Array From Dense
    af::array sA = af::sparse(A, AF_STORAGE_CSR);

    // Sparse Matmul
    af::array sRes1 = matmul(sA, B);

    // Verify Results
    ASSERT_NEAR(0, calc_norm(real(dRes1), real(sRes1)), eps);
    ASSERT_NEAR(0, calc_norm(imag(dRes1), imag(sRes1)), eps);
}

template<typename T>
void sparseTransposeTester(const int m, const int n, const int k, int factor, double eps)
{
    af::deviceGC();

    if (noDoubleTests<T>()) return;

#if 1
    af::array A = cpu_randu<T>(af::dim4(m, n));
    af::array B = cpu_randu<T>(af::dim4(m, k));
#else
    af::array A = af::randu(m, n, (af::dtype)af::dtype_traits<T>::af_type);
    af::array B = af::randu(m, k, (af::dtype)af::dtype_traits<T>::af_type);
#endif

    A = makeSparse<T>(A, factor);

    // Result of GEMM
    af::array dRes2 = matmul(A, B, AF_MAT_TRANS, AF_MAT_NONE);
    af::array dRes3 = matmul(A, B, AF_MAT_CTRANS, AF_MAT_NONE);

    // Create Sparse Array From Dense
    af::array sA = af::sparse(A, AF_STORAGE_CSR);

    // Sparse Matmul
    af::array sRes2 = matmul(sA, B, AF_MAT_TRANS, AF_MAT_NONE);
    af::array sRes3 = matmul(sA, B, AF_MAT_CTRANS, AF_MAT_NONE);

    // Verify Results
    ASSERT_NEAR(0, calc_norm(real(dRes2), real(sRes2)), eps);
    ASSERT_NEAR(0, calc_norm(imag(dRes2), imag(sRes2)), eps);

    ASSERT_NEAR(0, calc_norm(real(dRes3), real(sRes3)), eps);
    ASSERT_NEAR(0, calc_norm(imag(dRes3), imag(sRes3)), eps);
}

template<typename T>
void convertCSR(const int M, const int N, const float ratio)
{
    if (noDoubleTests<T>()) return;
#if 1
    af::array a = cpu_randu<T>(af::dim4(M, N));
#else
    af::array a = af::randu(M, N);
#endif
    a = a * (a > ratio);

    af::array s = af::sparse(a, AF_STORAGE_CSR);
    af::array aa = af::dense(s);

    ASSERT_EQ(0, af::max<double>(af::abs(a - aa)));
}

#define SPARSE_TESTS(T, eps)                                \
    TEST(SPARSE, T##Square)                                 \
    {                                                       \
        sparseTester<T>(1000, 1000, 100, 5, eps);           \
    }                                                       \
    TEST(SPARSE, T##RectMultiple)                           \
    {                                                       \
        sparseTester<T>(2048, 1024, 512, 3, eps);           \
    }                                                       \
    TEST(SPARSE, T##RectDense)                              \
    {                                                       \
        sparseTester<T>(500, 1000, 250, 1, eps);            \
    }                                                       \
    TEST(SPARSE, T##MatVec)                                 \
    {                                                       \
        sparseTester<T>(625, 1331, 1, 2, eps);              \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##MatVec)                       \
    {                                                       \
        sparseTransposeTester<T>(625, 1331, 1, 2, eps);     \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##Square)                       \
    {                                                       \
        sparseTransposeTester<T>(1000, 1000, 100, 5, eps);  \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##RectMultiple)                 \
    {                                                       \
        sparseTransposeTester<T>(2048, 1024, 512, 3, eps);  \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##RectDense)                    \
    {                                                       \
        sparseTransposeTester<T>(453, 751, 397, 1, eps);    \
    }                                                       \
    TEST(SPARSE, T##ConvertCSR)                             \
    {                                                       \
        convertCSR<T>(2345, 5678, 0.5);                     \
    }                                                       \

SPARSE_TESTS(float, 1E-3)
SPARSE_TESTS(double, 1E-5)
SPARSE_TESTS(cfloat, 1E-3)
SPARSE_TESTS(cdouble, 1E-5)

#undef SPARSE_TESTS

// This test essentially verifies that the sparse structures have the correct
// dimensions and indices using a very basic test
template<af_storage stype>
void createFunction()
{
    af::array in = af::sparse(af::identity(3, 3), stype);

    af::array values = sparseGetValues(in);
    af::array rowIdx = sparseGetRowIdx(in);
    af::array colIdx = sparseGetColIdx(in);
    dim_t     nNZ    = sparseGetNNZ(in);

    ASSERT_EQ(nNZ, values.elements());

    ASSERT_EQ(0, af::max<double>(values - af::constant(1, nNZ)));
    ASSERT_EQ(0, af::max<int   >(rowIdx - af::range(af::dim4(rowIdx.elements()), 0, s32)));
    ASSERT_EQ(0, af::max<int   >(colIdx - af::range(af::dim4(colIdx.elements()), 0, s32)));
}

#define CREATE_TESTS(STYPE)                                         \
    TEST(SPARSE_CREATE, STYPE)                                      \
    {                                                               \
        createFunction<STYPE>();                                    \
    }

CREATE_TESTS(AF_STORAGE_CSR)
CREATE_TESTS(AF_STORAGE_COO)

#undef CREATE_TESTS

template<typename T, af_storage src, af_storage dest>
void sparseConvertTester(const int m, const int n, int factor)
{
    af::deviceGC();

    if (noDoubleTests<T>()) return;

#if 1
    af::array A = cpu_randu<T>(af::dim4(m, n));
#else
    af::array A = af::randu(m, n, (af::dtype)af::dtype_traits<T>::af_type);
#endif

    A = makeSparse<T>(A, factor);

    // Create Sparse Array of type src and dest From Dense
    af::array sA = af::sparse(A, src);
    af::array dA = af::sparse(A, dest);

    //// Convert src to dest format and dest to src
    af::array s2d = sparseConvertTo(sA, dest);
    af::array d2s = sparseConvertTo(dA, src);

    //// Get the individual arrays and verify equality
    af::array dValues = sparseGetValues(dA);
    af::array dRowIdx = sparseGetRowIdx(dA);
    af::array dColIdx = sparseGetColIdx(dA);
    dim_t     dNNZ    = sparseGetNNZ   (dA);

    af::array s2dValues = sparseGetValues(s2d);
    af::array s2dRowIdx = sparseGetRowIdx(s2d);
    af::array s2dColIdx = sparseGetColIdx(s2d);
    dim_t     s2dNNZ    = sparseGetNNZ   (s2d);

    af::array sValues = sparseGetValues(sA);
    af::array sRowIdx = sparseGetRowIdx(sA);
    af::array sColIdx = sparseGetColIdx(sA);
    dim_t     sNNZ    = sparseGetNNZ   (sA);

    af::array d2sValues = sparseGetValues(d2s);
    af::array d2sRowIdx = sparseGetRowIdx(d2s);
    af::array d2sColIdx = sparseGetColIdx(d2s);
    dim_t     d2sNNZ    = sparseGetNNZ   (d2s);

    ASSERT_EQ(dNNZ, s2dNNZ);
    ASSERT_EQ(0, af::max<double>(dValues - s2dValues));
    ASSERT_EQ(0, af::max<int   >(dRowIdx - s2dRowIdx));
    ASSERT_EQ(0, af::max<int   >(dColIdx - s2dColIdx));

    ASSERT_EQ(sNNZ, d2sNNZ);
    ASSERT_EQ(0, af::max<double>(sValues - d2sValues));
    ASSERT_EQ(0, af::max<int   >(sRowIdx - d2sRowIdx));
    ASSERT_EQ(0, af::max<int   >(sColIdx - d2sColIdx));
}

#define CONVERT_TESTS(T, STYPE, DTYPE)                                          \
    TEST(SPARSE_CONVERT, T##_##STYPE##_##DTYPE)                                 \
    {                                                                           \
        sparseConvertTester<T, STYPE, DTYPE>(10, 10, 5);                        \
    }                                                                           \

CONVERT_TESTS(float  , AF_STORAGE_CSR, AF_STORAGE_COO)
CONVERT_TESTS(double , AF_STORAGE_CSR, AF_STORAGE_COO)
CONVERT_TESTS(cfloat , AF_STORAGE_CSR, AF_STORAGE_COO)
CONVERT_TESTS(cdouble, AF_STORAGE_CSR, AF_STORAGE_COO)
<commit_msg>Add more tests for sparse conversion<commit_after>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <gtest/gtest.h>
#include <arrayfire.h>
#include <af/dim4.hpp>
#include <af/defines.h>
#include <af/traits.hpp>
#include <vector>
#include <iostream>
#include <complex>
#include <string>
#include <testHelpers.hpp>

using std::vector;
using std::string;
using std::cout;
using std::endl;
using std::abs;
using af::cfloat;
using af::cdouble;

///////////////////////////////// CPP ////////////////////////////////////
//

template<typename T>
af::array makeSparse(af::array A, int factor)
{
    A = floor(A * 1000);
    A = A * ((A % factor) == 0) / 1000;
    return A;
}

template<>
af::array makeSparse<cfloat>(af::array A, int factor)
{
    af::array r = real(A);
    r = floor(r * 1000);
    r = r * ((r % factor) == 0) / 1000;

    af::array i = r / 2;

    A = af::complex(r, i);
    return A;
}

template<>
af::array makeSparse<cdouble>(af::array A, int factor)
{
    af::array r = real(A);
    r = floor(r * 1000);
    r = r * ((r % factor) == 0) / 1000;

    af::array i = r / 2;

    A = af::complex(r, i);
    return A;
}

double calc_norm(af::array lhs, af::array rhs)
{
    return af::max<double>(af::abs(lhs - rhs) / (af::abs(lhs) + af::abs(rhs) + 1E-5));
}

template<typename T>
void sparseTester(const int m, const int n, const int k, int factor, double eps)
{
    af::deviceGC();

    if (noDoubleTests<T>()) return;

#if 1
    af::array A = cpu_randu<T>(af::dim4(m, n));
    af::array B = cpu_randu<T>(af::dim4(n, k));
#else
    af::array A = af::randu(m, n, (af::dtype)af::dtype_traits<T>::af_type);
    af::array B = af::randu(n, k, (af::dtype)af::dtype_traits<T>::af_type);
#endif

    A = makeSparse<T>(A, factor);

    // Result of GEMM
    af::array dRes1 = matmul(A, B);

    // Create Sparse Array From Dense
    af::array sA = af::sparse(A, AF_STORAGE_CSR);

    // Sparse Matmul
    af::array sRes1 = matmul(sA, B);

    // Verify Results
    ASSERT_NEAR(0, calc_norm(real(dRes1), real(sRes1)), eps);
    ASSERT_NEAR(0, calc_norm(imag(dRes1), imag(sRes1)), eps);
}

template<typename T>
void sparseTransposeTester(const int m, const int n, const int k, int factor, double eps)
{
    af::deviceGC();

    if (noDoubleTests<T>()) return;

#if 1
    af::array A = cpu_randu<T>(af::dim4(m, n));
    af::array B = cpu_randu<T>(af::dim4(m, k));
#else
    af::array A = af::randu(m, n, (af::dtype)af::dtype_traits<T>::af_type);
    af::array B = af::randu(m, k, (af::dtype)af::dtype_traits<T>::af_type);
#endif

    A = makeSparse<T>(A, factor);

    // Result of GEMM
    af::array dRes2 = matmul(A, B, AF_MAT_TRANS, AF_MAT_NONE);
    af::array dRes3 = matmul(A, B, AF_MAT_CTRANS, AF_MAT_NONE);

    // Create Sparse Array From Dense
    af::array sA = af::sparse(A, AF_STORAGE_CSR);

    // Sparse Matmul
    af::array sRes2 = matmul(sA, B, AF_MAT_TRANS, AF_MAT_NONE);
    af::array sRes3 = matmul(sA, B, AF_MAT_CTRANS, AF_MAT_NONE);

    // Verify Results
    ASSERT_NEAR(0, calc_norm(real(dRes2), real(sRes2)), eps);
    ASSERT_NEAR(0, calc_norm(imag(dRes2), imag(sRes2)), eps);

    ASSERT_NEAR(0, calc_norm(real(dRes3), real(sRes3)), eps);
    ASSERT_NEAR(0, calc_norm(imag(dRes3), imag(sRes3)), eps);
}

template<typename T>
void convertCSR(const int M, const int N, const float ratio)
{
    if (noDoubleTests<T>()) return;
#if 1
    af::array a = cpu_randu<T>(af::dim4(M, N));
#else
    af::array a = af::randu(M, N);
#endif
    a = a * (a > ratio);

    af::array s = af::sparse(a, AF_STORAGE_CSR);
    af::array aa = af::dense(s);

    ASSERT_EQ(0, af::max<double>(af::abs(a - aa)));
}

#define SPARSE_TESTS(T, eps)                                \
    TEST(SPARSE, T##Square)                                 \
    {                                                       \
        sparseTester<T>(1000, 1000, 100, 5, eps);           \
    }                                                       \
    TEST(SPARSE, T##RectMultiple)                           \
    {                                                       \
        sparseTester<T>(2048, 1024, 512, 3, eps);           \
    }                                                       \
    TEST(SPARSE, T##RectDense)                              \
    {                                                       \
        sparseTester<T>(500, 1000, 250, 1, eps);            \
    }                                                       \
    TEST(SPARSE, T##MatVec)                                 \
    {                                                       \
        sparseTester<T>(625, 1331, 1, 2, eps);              \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##MatVec)                       \
    {                                                       \
        sparseTransposeTester<T>(625, 1331, 1, 2, eps);     \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##Square)                       \
    {                                                       \
        sparseTransposeTester<T>(1000, 1000, 100, 5, eps);  \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##RectMultiple)                 \
    {                                                       \
        sparseTransposeTester<T>(2048, 1024, 512, 3, eps);  \
    }                                                       \
    TEST(SPARSE_TRANSPOSE, T##RectDense)                    \
    {                                                       \
        sparseTransposeTester<T>(453, 751, 397, 1, eps);    \
    }                                                       \
    TEST(SPARSE, T##ConvertCSR)                             \
    {                                                       \
        convertCSR<T>(2345, 5678, 0.5);                     \
    }                                                       \

SPARSE_TESTS(float, 1E-3)
SPARSE_TESTS(double, 1E-5)
SPARSE_TESTS(cfloat, 1E-3)
SPARSE_TESTS(cdouble, 1E-5)

#undef SPARSE_TESTS

// This test essentially verifies that the sparse structures have the correct
// dimensions and indices using a very basic test
template<af_storage stype>
void createFunction()
{
    af::array in = af::sparse(af::identity(3, 3), stype);

    af::array values = sparseGetValues(in);
    af::array rowIdx = sparseGetRowIdx(in);
    af::array colIdx = sparseGetColIdx(in);
    dim_t     nNZ    = sparseGetNNZ(in);

    ASSERT_EQ(nNZ, values.elements());

    ASSERT_EQ(0, af::max<double>(values - af::constant(1, nNZ)));
    ASSERT_EQ(0, af::max<int   >(rowIdx - af::range(af::dim4(rowIdx.elements()), 0, s32)));
    ASSERT_EQ(0, af::max<int   >(colIdx - af::range(af::dim4(colIdx.elements()), 0, s32)));
}

#define CREATE_TESTS(STYPE)                                         \
    TEST(SPARSE_CREATE, STYPE)                                      \
    {                                                               \
        createFunction<STYPE>();                                    \
    }

CREATE_TESTS(AF_STORAGE_CSR)
CREATE_TESTS(AF_STORAGE_COO)

#undef CREATE_TESTS

template<typename T, af_storage src, af_storage dest>
void sparseConvertTester(const int m, const int n, int factor)
{
    af::deviceGC();

    if (noDoubleTests<T>()) return;

#if 1
    af::array A = cpu_randu<T>(af::dim4(m, n));
#else
    af::array A = af::randu(m, n, (af::dtype)af::dtype_traits<T>::af_type);
#endif

    A = makeSparse<T>(A, factor);

    // Create Sparse Array of type src and dest From Dense
    af::array sA = af::sparse(A, src);
    af::array dA = af::sparse(A, dest);

    // Convert src to dest format and dest to src
    af::array s2d = sparseConvertTo(sA, dest);
    af::array d2s = sparseConvertTo(dA, src);

    // Get the individual arrays and verify equality
    af::array sValues = sparseGetValues(sA);
    af::array sRowIdx = sparseGetRowIdx(sA);
    af::array sColIdx = sparseGetColIdx(sA);
    dim_t     sNNZ    = sparseGetNNZ   (sA);

    af::array dValues = sparseGetValues(dA);
    af::array dRowIdx = sparseGetRowIdx(dA);
    af::array dColIdx = sparseGetColIdx(dA);
    dim_t     dNNZ    = sparseGetNNZ   (dA);

    af::array s2dValues = sparseGetValues(s2d);
    af::array s2dRowIdx = sparseGetRowIdx(s2d);
    af::array s2dColIdx = sparseGetColIdx(s2d);
    dim_t     s2dNNZ    = sparseGetNNZ   (s2d);

    af::array d2sValues = sparseGetValues(d2s);
    af::array d2sRowIdx = sparseGetRowIdx(d2s);
    af::array d2sColIdx = sparseGetColIdx(d2s);
    dim_t     d2sNNZ    = sparseGetNNZ   (d2s);

    ASSERT_EQ(dNNZ, s2dNNZ);
    ASSERT_EQ(0, af::max<double>(dValues - s2dValues));
    ASSERT_EQ(0, af::max<int   >(dRowIdx - s2dRowIdx));
    ASSERT_EQ(0, af::max<int   >(dColIdx - s2dColIdx));

    ASSERT_EQ(sNNZ, d2sNNZ);
    ASSERT_EQ(0, af::max<double>(sValues - d2sValues));
    ASSERT_EQ(0, af::max<int   >(sRowIdx - d2sRowIdx));
    ASSERT_EQ(0, af::max<int   >(sColIdx - d2sColIdx));
}

#define CONVERT_TESTS(T, STYPE, DTYPE)                                          \
    TEST(SPARSE_CONVERT, T##_##STYPE##_##DTYPE##_1)                             \
    {                                                                           \
        sparseConvertTester<T, STYPE, DTYPE>(1000, 1000, 5);                    \
    }                                                                           \
    TEST(SPARSE_CONVERT, T##_##STYPE##_##DTYPE##_2)                             \
    {                                                                           \
        sparseConvertTester<T, STYPE, DTYPE>(512, 512, 1);                      \
    }                                                                           \
    TEST(SPARSE_CONVERT, T##_##STYPE##_##DTYPE##_3)                             \
    {                                                                           \
        sparseConvertTester<T, STYPE, DTYPE>(512, 1024, 2);                     \
    }                                                                           \
    TEST(SPARSE_CONVERT, T##_##STYPE##_##DTYPE##_4)                             \
    {                                                                           \
        sparseConvertTester<T, STYPE, DTYPE>(2048, 1024, 10);                   \
    }                                                                           \

CONVERT_TESTS(float  , AF_STORAGE_CSR, AF_STORAGE_COO)
CONVERT_TESTS(double , AF_STORAGE_CSR, AF_STORAGE_COO)
CONVERT_TESTS(cfloat , AF_STORAGE_CSR, AF_STORAGE_COO)
CONVERT_TESTS(cdouble, AF_STORAGE_CSR, AF_STORAGE_COO)
<|endoftext|>


<commit_before>// Copyright (C) 2010-2014 Joshua Boyce.
// See the file COPYING for copying permission.

#include "render.hpp"

#include <cstdint>

#include <windows.h>
#include <winnt.h>
#include <winternl.h>

#include <d3d9.h>
#include <d3d11.h>
#include <d3d10.h>
#include <dxgi.h>

#include <hadesmem/detail/warning_disable_prefix.hpp>
#include <anttweakbar.h>
#include <hadesmem/detail/warning_disable_suffix.hpp>

#include <hadesmem/detail/warning_disable_prefix.hpp>
#include <fw1fontwrapper.h>
#include <hadesmem/detail/warning_disable_suffix.hpp>

#include <hadesmem/config.hpp>
#include <hadesmem/detail/smart_handle.hpp>

#include "callbacks.hpp"
#include "dxgi.hpp"
#include "main.hpp"

namespace
{

hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnFrameCallback>&
  GetOnFrameCallbacks()
{
  static hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnFrameCallback>
    callbacks;
  return callbacks;
}

struct RenderInfoD3D11
{
  bool first_time_{true};
  bool tw_initialized_{false};
  IDXGISwapChain* swap_chain_{};
  ID3D11Device* device_;
  ID3D11DeviceContext* context_;
  IFW1FontWrapper* font_wrapper_{};
};

RenderInfoD3D11& GetRenderInfoD3D11() HADESMEM_DETAIL_NOEXCEPT
{
  static RenderInfoD3D11 render_info;
  return render_info;
}

void HandleChangedSwapChain(IDXGISwapChain* swap_chain,
                            RenderInfoD3D11& render_info)
{
  HADESMEM_DETAIL_TRACE_FORMAT_A("Got a new swap chain. Old = %p, New = %p.",
                                 render_info.swap_chain_,
                                 swap_chain);
  render_info.swap_chain_ = swap_chain;

  if (render_info.font_wrapper_)
  {
    render_info.font_wrapper_->Release();
    render_info.font_wrapper_ = nullptr;
  }

  render_info.device_ = nullptr;
  render_info.context_ = nullptr;

  render_info.first_time_ = true;

  if (render_info.tw_initialized_)
  {
    if (!TwTerminate())
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(
        hadesmem::Error{} << hadesmem::ErrorString{"TwTerminate failed."});
    }

    render_info.tw_initialized_ = false;
  }
}

void InitializeD3D11RenderInfo(IDXGISwapChain* swap_chain,
                               RenderInfoD3D11& render_info)
{
  HADESMEM_DETAIL_TRACE_A("Initializing.");

  render_info.first_time_ = false;

  auto const get_device_hr =
    swap_chain->GetDevice(__uuidof(render_info.device_),
                          reinterpret_cast<void**>(&render_info.device_));
  if (FAILED(get_device_hr))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{
                                         "IDXGISwapChain::GetDevice failed."}
                                    << hadesmem::ErrorCodeWinHr{get_device_hr});
  }

  render_info.device_->GetImmediateContext(&render_info.context_);

  if (!TwInit(TW_DIRECT3D11, render_info.device_))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{"TwInit failed."});
  }

  render_info.tw_initialized_ = true;

  if (!TwWindowSize(800, 600))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwWindowSize failed."});
  }

  auto const bar = TwNewBar("HadesMem");
  if (!bar)
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwNewBar failed."});
  }

  HADESMEM_DETAIL_TRACE_A("Initialized successfully.");
}

void HandleOnFrameD3D11(IDXGISwapChain* swap_chain)
{
  auto& render_info = GetRenderInfoD3D11();
  if (render_info.swap_chain_ != swap_chain)
  {
    HandleChangedSwapChain(swap_chain, render_info);
  }

  if (render_info.first_time_)
  {
    InitializeD3D11RenderInfo(swap_chain, render_info);
  }
}

void OnFrameDXGI(IDXGISwapChain* swap_chain)
{
  HandleOnFrameD3D11(swap_chain);

  auto& callbacks = GetOnFrameCallbacks();
  auto& render_interface = hadesmem::cerberus::GetRenderInterface();
  callbacks.Run(&render_interface);
}

void DrawTweakBarImpl()
{
  if (!TwDraw())
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{"TwDraw failed."});
  }
}

class RenderImpl : public hadesmem::cerberus::RenderInterface
{
public:
  virtual std::size_t RegisterOnFrame(
    std::function<hadesmem::cerberus::OnFrameCallback> const& callback) final
  {
    return hadesmem::cerberus::RegisterOnFrameCallback(callback);
  }

  virtual void UnregisterOnFrame(std::size_t id) final
  {
    hadesmem::cerberus::UnregisterOnFrameCallback(id);
  }

  virtual void DrawTweakBar() final
  {
    DrawTweakBarImpl();
  }
};
}

namespace hadesmem
{

namespace cerberus
{

RenderInterface& GetRenderInterface() HADESMEM_DETAIL_NOEXCEPT
{
  static RenderImpl render_impl;
  return render_impl;
}

void InitializeRender()
{
  auto const dxgi_callback_id =
    hadesmem::cerberus::RegisterOnFrameCallbackDXGI(OnFrameDXGI);
  (void)dxgi_callback_id;

  auto const draw_tweak_bar = [](RenderInterface* render)
  { render->DrawTweakBar(); };
  RegisterOnFrameCallback(draw_tweak_bar);
}

std::size_t
  RegisterOnFrameCallback(std::function<OnFrameCallback> const& callback)
{
  auto& callbacks = GetOnFrameCallbacks();
  return callbacks.Register(callback);
}

void UnregisterOnFrameCallback(std::size_t id)
{
  auto& callbacks = GetOnFrameCallbacks();
  return callbacks.Unregister(id);
}
}
}
<commit_msg>* [Cerberus] Add test rendering for D3D10 and D3D9.<commit_after>// Copyright (C) 2010-2014 Joshua Boyce.
// See the file COPYING for copying permission.

#include "render.hpp"

#include <cstdint>

#include <windows.h>
#include <winnt.h>
#include <winternl.h>

#include <d3d9.h>
#include <d3d11.h>
#include <d3d10.h>
#include <dxgi.h>

#include <hadesmem/detail/warning_disable_prefix.hpp>
#include <anttweakbar.h>
#include <hadesmem/detail/warning_disable_suffix.hpp>

#include <hadesmem/config.hpp>
#include <hadesmem/detail/smart_handle.hpp>

#include "callbacks.hpp"
#include "d3d9.hpp"
#include "dxgi.hpp"
#include "main.hpp"

namespace
{

hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnFrameCallback>&
  GetOnFrameCallbacks()
{
  static hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnFrameCallback>
    callbacks;
  return callbacks;
}

struct RenderInfoD3D11
{
  bool first_time_{true};
  bool tw_initialized_{false};
  IDXGISwapChain* swap_chain_{};
  ID3D11Device* device_;
};

RenderInfoD3D11& GetRenderInfoD3D11() HADESMEM_DETAIL_NOEXCEPT
{
  static RenderInfoD3D11 render_info;
  return render_info;
}

struct RenderInfoD3D10
{
  bool first_time_{true};
  bool tw_initialized_{false};
  IDXGISwapChain* swap_chain_{};
  ID3D10Device* device_;
};

RenderInfoD3D10& GetRenderInfoD3D10() HADESMEM_DETAIL_NOEXCEPT
{
  static RenderInfoD3D10 render_info;
  return render_info;
}

struct RenderInfoD3D9
{
  bool first_time_{true};
  bool tw_initialized_{false};
  IDirect3DDevice9* device_{};
};

RenderInfoD3D9& GetRenderInfoD3D9() HADESMEM_DETAIL_NOEXCEPT
{
  static RenderInfoD3D9 render_info;
  return render_info;
}

void HandleChangedSwapChainD3D11(IDXGISwapChain* swap_chain,
                                 RenderInfoD3D11& render_info)
{
  HADESMEM_DETAIL_TRACE_FORMAT_A("Got a new swap chain. Old = %p, New = %p.",
                                 render_info.swap_chain_,
                                 swap_chain);
  render_info.swap_chain_ = swap_chain;

  render_info.device_ = nullptr;

  render_info.first_time_ = true;

  if (render_info.tw_initialized_)
  {
    if (!TwTerminate())
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(
        hadesmem::Error{} << hadesmem::ErrorString{"TwTerminate failed."});
    }

    render_info.tw_initialized_ = false;
  }
}

void HandleChangedSwapChainD3D10(IDXGISwapChain* swap_chain,
                                 RenderInfoD3D10& render_info)
{
  HADESMEM_DETAIL_TRACE_FORMAT_A("Got a new swap chain. Old = %p, New = %p.",
                                 render_info.swap_chain_,
                                 swap_chain);
  render_info.swap_chain_ = swap_chain;

  render_info.device_ = nullptr;

  render_info.first_time_ = true;

  if (render_info.tw_initialized_)
  {
    if (!TwTerminate())
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(
        hadesmem::Error{} << hadesmem::ErrorString{"TwTerminate failed."});
    }

    render_info.tw_initialized_ = false;
  }
}

void HandleChangedDeviceD3D9(IDirect3DDevice9* device,
                             RenderInfoD3D9& render_info)
{
  HADESMEM_DETAIL_TRACE_FORMAT_A(
    "Got a new device. Old = %p, New = %p.", render_info.device_, device);
  render_info.device_ = device;

  render_info.first_time_ = true;

  if (render_info.tw_initialized_)
  {
    if (!TwTerminate())
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(
        hadesmem::Error{} << hadesmem::ErrorString{"TwTerminate failed."});
    }

    render_info.tw_initialized_ = false;
  }
}

void InitializeD3D11RenderInfo(RenderInfoD3D11& render_info)
{
  HADESMEM_DETAIL_TRACE_A("Initializing.");

  render_info.first_time_ = false;

  auto const get_device_hr = render_info.swap_chain_->GetDevice(
    __uuidof(render_info.device_),
    reinterpret_cast<void**>(&render_info.device_));
  if (FAILED(get_device_hr))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{
                                         "IDXGISwapChain::GetDevice failed."}
                                    << hadesmem::ErrorCodeWinHr{get_device_hr});
  }

  if (!TwInit(TW_DIRECT3D11, render_info.device_))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{"TwInit failed."});
  }

  render_info.tw_initialized_ = true;

  if (!TwWindowSize(800, 600))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwWindowSize failed."});
  }

  auto const bar = TwNewBar("HadesMem");
  if (!bar)
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwNewBar failed."});
  }

  HADESMEM_DETAIL_TRACE_A("Initialized successfully.");
}

void InitializeD3D10RenderInfo(RenderInfoD3D10& render_info)
{
  HADESMEM_DETAIL_TRACE_A("Initializing.");

  render_info.first_time_ = false;

  auto const get_device_hr = render_info.swap_chain_->GetDevice(
    __uuidof(render_info.device_),
    reinterpret_cast<void**>(&render_info.device_));
  if (FAILED(get_device_hr))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{
                                         "IDXGISwapChain::GetDevice failed."}
                                    << hadesmem::ErrorCodeWinHr{get_device_hr});
  }

  if (!TwInit(TW_DIRECT3D10, render_info.device_))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{"TwInit failed."});
  }

  render_info.tw_initialized_ = true;

  if (!TwWindowSize(800, 600))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwWindowSize failed."});
  }

  auto const bar = TwNewBar("HadesMem");
  if (!bar)
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwNewBar failed."});
  }

  HADESMEM_DETAIL_TRACE_A("Initialized successfully.");
}

void InitializeD3D9RenderInfo(RenderInfoD3D9& render_info)
{
  HADESMEM_DETAIL_TRACE_A("Initializing.");

  render_info.first_time_ = false;

  if (!TwInit(TW_DIRECT3D9, render_info.device_))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{"TwInit failed."});
  }

  render_info.tw_initialized_ = true;

  if (!TwWindowSize(800, 600))
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwWindowSize failed."});
  }

  auto const bar = TwNewBar("HadesMem");
  if (!bar)
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(
      hadesmem::Error{} << hadesmem::ErrorString{"TwNewBar failed."});
  }

  HADESMEM_DETAIL_TRACE_A("Initialized successfully.");
}

void HandleOnFrameD3D11(IDXGISwapChain* swap_chain)
{
  auto& render_info = GetRenderInfoD3D11();
  if (render_info.swap_chain_ != swap_chain)
  {
    HandleChangedSwapChainD3D11(swap_chain, render_info);
  }

  if (render_info.first_time_)
  {
    InitializeD3D11RenderInfo(render_info);
  }
}

void HandleOnFrameD3D10(IDXGISwapChain* swap_chain)
{
  auto& render_info = GetRenderInfoD3D10();
  if (render_info.swap_chain_ != swap_chain)
  {
    HandleChangedSwapChainD3D10(swap_chain, render_info);
  }

  if (render_info.first_time_)
  {
    InitializeD3D10RenderInfo(render_info);
  }
}

void HandleOnFrameD3D9(IDirect3DDevice9* device)
{
  auto& render_info = GetRenderInfoD3D9();
  if (render_info.device_ != device)
  {
    HandleChangedDeviceD3D9(device, render_info);
  }

  if (render_info.first_time_)
  {
    InitializeD3D9RenderInfo(render_info);
  }
}

void OnFrameDXGI(IDXGISwapChain* swap_chain)
{
  HandleOnFrameD3D11(swap_chain);

  HandleOnFrameD3D10(swap_chain);

  auto& callbacks = GetOnFrameCallbacks();
  auto& render_interface = hadesmem::cerberus::GetRenderInterface();
  callbacks.Run(&render_interface);
}

void OnFrameD3D9(IDirect3DDevice9* device)
{
  HandleOnFrameD3D9(device);

  auto& callbacks = GetOnFrameCallbacks();
  auto& render_interface = hadesmem::cerberus::GetRenderInterface();
  callbacks.Run(&render_interface);
}

void DrawTweakBarImpl()
{
  if (!TwDraw())
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{}
                                    << hadesmem::ErrorString{"TwDraw failed."});
  }
}

class RenderImpl : public hadesmem::cerberus::RenderInterface
{
public:
  virtual std::size_t RegisterOnFrame(
    std::function<hadesmem::cerberus::OnFrameCallback> const& callback) final
  {
    return hadesmem::cerberus::RegisterOnFrameCallback(callback);
  }

  virtual void UnregisterOnFrame(std::size_t id) final
  {
    hadesmem::cerberus::UnregisterOnFrameCallback(id);
  }

  virtual void DrawTweakBar() final
  {
    DrawTweakBarImpl();
  }
};
}

namespace hadesmem
{

namespace cerberus
{

RenderInterface& GetRenderInterface() HADESMEM_DETAIL_NOEXCEPT
{
  static RenderImpl render_impl;
  return render_impl;
}

void InitializeRender()
{
  auto const dxgi_callback_id =
    hadesmem::cerberus::RegisterOnFrameCallbackDXGI(OnFrameDXGI);
  (void)dxgi_callback_id;

  auto const d3d9_callback_id =
    hadesmem::cerberus::RegisterOnFrameCallbackD3D9(OnFrameD3D9);
  (void)d3d9_callback_id;

  auto const draw_tweak_bar = [](RenderInterface* render)
  { render->DrawTweakBar(); };
  RegisterOnFrameCallback(draw_tweak_bar);
}

std::size_t
  RegisterOnFrameCallback(std::function<OnFrameCallback> const& callback)
{
  auto& callbacks = GetOnFrameCallbacks();
  return callbacks.Register(callback);
}

void UnregisterOnFrameCallback(std::size_t id)
{
  auto& callbacks = GetOnFrameCallbacks();
  return callbacks.Unregister(id);
}
}
}
<|endoftext|>