<commit_before>/*
 * Copyright (C) 2017 ScyllaDB
 *
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <seastar/util/gcc6-concepts.hh>
#include <seastar/core/apply.hh>
#include "cql3/statements/modification_statement.hh"
#include "cql3/statements/prepared_statement.hh"
#include "cql3/query_processor.hh"
#include "service/migration_manager.hh"
#include "database.hh"

/**
 * \class table_helper
 * \brief A helper class that unites the operations on a single table under the same roof.
 */
class table_helper {
private:
    const sstring _keyspace; /** a keyspace name */
    const sstring _name; /** a table name */
    const sstring _create_cql; /** a CQL CREATE TABLE statement for the table */
    const sstring _insert_cql; /** a CQL INSERT statement */

    cql3::statements::prepared_statement::checked_weak_ptr _prepared_stmt; /** a raw prepared statement object (containing the INSERT statement) */
    shared_ptr<cql3::statements::modification_statement> _insert_stmt; /** INSERT prepared statement */

public:
    table_helper(sstring keyspace, sstring name, sstring create_cql, sstring insert_cql)
        : _keyspace(std::move(keyspace))
        , _name(std::move(name))
        , _create_cql(std::move(create_cql))
        , _insert_cql(std::move(insert_cql)) {}

    /**
     * Tries to create a table using create_cql command.
     *
     * @return A future that resolves when the operation is complete. Any
     *         possible errors are ignored.
     */
    future<> setup_table() const;

    /**
     * @return a future that resolves when the given t_helper is ready to be used for
     * data insertion.
     */
    future<> cache_table_info(service::query_state&);

    /**
     * @return The table name
     */
    const sstring& name() const {
        return _name;
    }

    /**
     * @return A pointer to the INSERT prepared statement
     */
    shared_ptr<cql3::statements::modification_statement> insert_stmt() const {
        return _insert_stmt;
    }

    /**
     * Execute a single insertion into the table.
     *
     * @tparam OptMaker cql_options maker functor type
     * @tparam Args OptMaker arguments' types
     * @param opt_maker cql_options maker functor
     * @param opt_maker_args opt_maker arguments
     */
    template <typename OptMaker, typename... Args>
    GCC6_CONCEPT( requires seastar::CanApply<OptMaker, Args...> )
    future<> insert(service::query_state& qs, OptMaker opt_maker, Args&&... opt_maker_args) {
        return cache_table_info(qs).then([this, &qs, opt_maker = std::move(opt_maker), args = std::forward_as_tuple(std::forward<Args>(opt_maker_args)...)] () mutable {
            return do_with(apply(opt_maker, std::move(args)), [this, &qs] (auto& opts) {
                return _insert_stmt->execute(service::get_storage_proxy(), qs, opts);
            });
        }).discard_result();
    }

    template <typename... Args>
    static inline future<> setup_keyspace(const sstring& keyspace_name, sstring replication_factor, service::query_state& qs, const Args&... args) {
        if (engine().cpu_id() == 0) {
            return seastar::async([&keyspace_name, replication_factor, &qs, &args...] {
                auto& db = cql3::get_local_query_processor().db().local();

                // Create a keyspace
                if (!db.has_keyspace(keyspace_name)) {
                    std::map<sstring, sstring> opts;
                    opts["replication_factor"] = replication_factor;
                    auto ksm = keyspace_metadata::new_keyspace(keyspace_name, "org.apache.cassandra.locator.SimpleStrategy", std::move(opts), true);
                    // We use min_timestamp so that default keyspace metadata will loose with any manual adjustments. See issue #2129.
                    service::get_local_migration_manager().announce_new_keyspace(ksm, api::min_timestamp, false).get();
                }

                qs.get_client_state().set_keyspace(cql3::get_local_query_processor().db(), keyspace_name);

                // Create tables
                size_t n = sizeof...(args);
                const table_helper* tables[sizeof...(args)] = {&args...};
                for (size_t i = 0; i < n; ++i) {
                    tables[i]->setup_table().get();
                }
            });
        } else {
            return make_ready_future<>();
        }
    }

    /**
     * Makes a monotonically increasing value in 100ns ("nanos") based on the given time
     * stamp and the "nanos" value of the previous event.
     *
     * If the amount of 100s of ns evaluated from the @param tp is equal to the
     * given @param last_event_nanos increment @param last_event_nanos by one
     * and return a time point based its new value.
     *
     * @param last_event_nanos a reference to the last nanos to align the given time point to.
     * @param tp the amount of time passed since the Epoch that will be used for the calculation.
     *
     * @return the monotonically increasing vlaue in 100s of ns based on the
     * given time stamp and on the "nanos" value of the previous event.
     */
    static std::chrono::system_clock::time_point make_monotonic_UUID_tp(int64_t& last_event_nanos, std::chrono::system_clock::time_point tp) {
        using namespace std::chrono;

        auto tp_nanos = duration_cast<nanoseconds>(tp.time_since_epoch()).count() / 100;
        if (tp_nanos > last_event_nanos) {
            last_event_nanos = tp_nanos;
            return tp;
        } else {
            return std::chrono::system_clock::time_point(nanoseconds((++last_event_nanos) * 100));
        }
    }
};

struct bad_column_family : public std::exception {
private:
    sstring _keyspace;
    sstring _cf;
    sstring _what;
public:
    bad_column_family(const sstring& keyspace, const sstring& cf)
        : _keyspace(keyspace)
        , _cf(cf)
        , _what(sprint("%s.%s doesn't meet expected schema.", _keyspace, _cf))
    { }
    bad_column_family(const sstring& keyspace, const sstring& cf, const std::exception& e)
        : _keyspace(keyspace)
        , _cf(cf)
        , _what(sprint("%s.%s doesn't meet expected schema: %s", _keyspace, _cf, e.what()))
    { }
    const char* what() const noexcept override {
        return _what.c_str();
    }
};
<commit_msg>Check arguments of table_helper::setup_keyspace<commit_after>/*
 * Copyright (C) 2017 ScyllaDB
 *
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <seastar/util/gcc6-concepts.hh>
#include <seastar/core/apply.hh>
#include "cql3/statements/modification_statement.hh"
#include "cql3/statements/prepared_statement.hh"
#include "cql3/query_processor.hh"
#include "service/migration_manager.hh"
#include "database.hh"

/**
 * \class table_helper
 * \brief A helper class that unites the operations on a single table under the same roof.
 */
class table_helper {
private:
    const sstring _keyspace; /** a keyspace name */
    const sstring _name; /** a table name */
    const sstring _create_cql; /** a CQL CREATE TABLE statement for the table */
    const sstring _insert_cql; /** a CQL INSERT statement */

    cql3::statements::prepared_statement::checked_weak_ptr _prepared_stmt; /** a raw prepared statement object (containing the INSERT statement) */
    shared_ptr<cql3::statements::modification_statement> _insert_stmt; /** INSERT prepared statement */

public:
    table_helper(sstring keyspace, sstring name, sstring create_cql, sstring insert_cql)
        : _keyspace(std::move(keyspace))
        , _name(std::move(name))
        , _create_cql(std::move(create_cql))
        , _insert_cql(std::move(insert_cql)) {}

    /**
     * Tries to create a table using create_cql command.
     *
     * @return A future that resolves when the operation is complete. Any
     *         possible errors are ignored.
     */
    future<> setup_table() const;

    /**
     * @return a future that resolves when the given t_helper is ready to be used for
     * data insertion.
     */
    future<> cache_table_info(service::query_state&);

    /**
     * @return The table name
     */
    const sstring& name() const {
        return _name;
    }

    /**
     * @return A pointer to the INSERT prepared statement
     */
    shared_ptr<cql3::statements::modification_statement> insert_stmt() const {
        return _insert_stmt;
    }

    /**
     * Execute a single insertion into the table.
     *
     * @tparam OptMaker cql_options maker functor type
     * @tparam Args OptMaker arguments' types
     * @param opt_maker cql_options maker functor
     * @param opt_maker_args opt_maker arguments
     */
    template <typename OptMaker, typename... Args>
    GCC6_CONCEPT( requires seastar::CanApply<OptMaker, Args...> )
    future<> insert(service::query_state& qs, OptMaker opt_maker, Args&&... opt_maker_args) {
        return cache_table_info(qs).then([this, &qs, opt_maker = std::move(opt_maker), args = std::forward_as_tuple(std::forward<Args>(opt_maker_args)...)] () mutable {
            return do_with(apply(opt_maker, std::move(args)), [this, &qs] (auto& opts) {
                return _insert_stmt->execute(service::get_storage_proxy(), qs, opts);
            });
        }).discard_result();
    }

    template <typename... Args>
    static inline future<> setup_keyspace(const sstring& keyspace_name, sstring replication_factor, service::query_state& qs, const Args&... args) {
        if (engine().cpu_id() == 0) {
            size_t n = sizeof...(args);
            const table_helper* tables[sizeof...(args)] = {&args...};
            for (size_t i = 0; i < n; ++i) {
                if (tables[i]->_keyspace != keyspace_name) {
                    throw std::invalid_argument("setup_keyspace called with table_helper for different keyspace");
                }
            }
            return seastar::async([&keyspace_name, replication_factor, &qs, &args...] {
                auto& db = cql3::get_local_query_processor().db().local();

                // Create a keyspace
                if (!db.has_keyspace(keyspace_name)) {
                    std::map<sstring, sstring> opts;
                    opts["replication_factor"] = replication_factor;
                    auto ksm = keyspace_metadata::new_keyspace(keyspace_name, "org.apache.cassandra.locator.SimpleStrategy", std::move(opts), true);
                    // We use min_timestamp so that default keyspace metadata will loose with any manual adjustments. See issue #2129.
                    service::get_local_migration_manager().announce_new_keyspace(ksm, api::min_timestamp, false).get();
                }

                qs.get_client_state().set_keyspace(cql3::get_local_query_processor().db(), keyspace_name);

                // Create tables
                size_t n = sizeof...(args);
                const table_helper* tables[sizeof...(args)] = {&args...};
                for (size_t i = 0; i < n; ++i) {
                    tables[i]->setup_table().get();
                }
            });
        } else {
            return make_ready_future<>();
        }
    }

    /**
     * Makes a monotonically increasing value in 100ns ("nanos") based on the given time
     * stamp and the "nanos" value of the previous event.
     *
     * If the amount of 100s of ns evaluated from the @param tp is equal to the
     * given @param last_event_nanos increment @param last_event_nanos by one
     * and return a time point based its new value.
     *
     * @param last_event_nanos a reference to the last nanos to align the given time point to.
     * @param tp the amount of time passed since the Epoch that will be used for the calculation.
     *
     * @return the monotonically increasing vlaue in 100s of ns based on the
     * given time stamp and on the "nanos" value of the previous event.
     */
    static std::chrono::system_clock::time_point make_monotonic_UUID_tp(int64_t& last_event_nanos, std::chrono::system_clock::time_point tp) {
        using namespace std::chrono;

        auto tp_nanos = duration_cast<nanoseconds>(tp.time_since_epoch()).count() / 100;
        if (tp_nanos > last_event_nanos) {
            last_event_nanos = tp_nanos;
            return tp;
        } else {
            return std::chrono::system_clock::time_point(nanoseconds((++last_event_nanos) * 100));
        }
    }
};

struct bad_column_family : public std::exception {
private:
    sstring _keyspace;
    sstring _cf;
    sstring _what;
public:
    bad_column_family(const sstring& keyspace, const sstring& cf)
        : _keyspace(keyspace)
        , _cf(cf)
        , _what(sprint("%s.%s doesn't meet expected schema.", _keyspace, _cf))
    { }
    bad_column_family(const sstring& keyspace, const sstring& cf, const std::exception& e)
        : _keyspace(keyspace)
        , _cf(cf)
        , _what(sprint("%s.%s doesn't meet expected schema: %s", _keyspace, _cf, e.what()))
    { }
    const char* what() const noexcept override {
        return _what.c_str();
    }
};
<|endoftext|>


<commit_before>#include "reGlobalsExtern.hpp"
#include "dataObjChksum.h"
#include "objMetaOpr.hpp"
#include "resource.hpp"
#include "specColl.hpp"
#include "dataObjOpr.hpp"
#include "physPath.hpp"
#include "rsApiHandler.hpp"
#include "modDataObjMeta.h"
#include "getRemoteZoneResc.h"

// =-=-=-=-=-=-=-
#include "irods_resource_backport.hpp"
#include "irods_resource_redirect.hpp"

int
rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjChksumInp,
                 char **outChksum ) {
    int status;
    dataObjInfo_t *dataObjInfoHead;
    int remoteFlag;
    rodsServerHost_t *rodsServerHost;
    specCollCache_t *specCollCache = NULL;

    resolveLinkedPath( rsComm, dataObjChksumInp->objPath, &specCollCache, &dataObjChksumInp->condInput );

    remoteFlag = getAndConnRemoteZone( rsComm, dataObjChksumInp, &rodsServerHost, REMOTE_OPEN );

    if ( remoteFlag < 0 ) {
        return remoteFlag;
    }
    else if ( remoteFlag == REMOTE_HOST ) {
        status = rcDataObjChksum( rodsServerHost->conn, dataObjChksumInp,
                                  outChksum );
        return status;
    }
    else {
        // =-=-=-=-=-=-=-
        // determine the resource hierarchy if one is not provided
        if ( getValByKey( &dataObjChksumInp->condInput, RESC_HIER_STR_KW ) == NULL ) {
            std::string       hier;
            irods::error ret = irods::resolve_resource_hierarchy( irods::OPEN_OPERATION,
                               rsComm, dataObjChksumInp, hier );
            if ( !ret.ok() ) {
                std::stringstream msg;
                msg << "failed in irods::resolve_resource_hierarchy for [";
                msg << dataObjChksumInp->objPath << "]";
                irods::log( PASSMSG( msg.str(), ret ) );
                return ret.code();
            }

            // =-=-=-=-=-=-=-
            // we resolved the redirect and have a host, set the hier str for subsequent
            // api calls, etc.
            addKeyVal( &dataObjChksumInp->condInput, RESC_HIER_STR_KW, hier.c_str() );

        } // if keyword

        status = _rsDataObjChksum( rsComm, dataObjChksumInp, outChksum,
                                   &dataObjInfoHead );
    }
    freeAllDataObjInfo( dataObjInfoHead );
    rodsLog( LOG_NOTICE, "rsDataObjChksum - returning status %d", status );
    return status;
}

int
_rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjInp,
                  char **outChksumStr, dataObjInfo_t **dataObjInfoHead ) {
    int status;
    dataObjInfo_t *tmpDataObjInfo;
    int allFlag;
    int verifyFlag;
    int forceFlag;

    char* inp_chksum = getValByKey( &dataObjInp->condInput, ORIG_CHKSUM_KW );

    if ( getValByKey( &dataObjInp->condInput, CHKSUM_ALL_KW ) != NULL ) {
        allFlag = 1;
    }
    else {
        allFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, VERIFY_CHKSUM_KW ) != NULL ) {
        verifyFlag = 1;
    }
    else {
        verifyFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, FORCE_CHKSUM_KW ) != NULL ) {
        forceFlag = 1;
    }
    else {
        forceFlag = 0;
    }

    *dataObjInfoHead = NULL;
    *outChksumStr = NULL;

    status = getDataObjInfoIncSpecColl( rsComm, dataObjInp, dataObjInfoHead );
    if ( status < 0 ) {
        return status;
    }
    else if ( allFlag == 0 ) {
        /* screen out any stale copies */
        status = sortObjInfoForOpen( dataObjInfoHead, &dataObjInp->condInput, 0 );
        if ( status < 0 ) {
            return status;
        }

        tmpDataObjInfo = *dataObjInfoHead;
        if ( tmpDataObjInfo->next == NULL ) {
            /* the only copy */
            if ( strlen( tmpDataObjInfo->chksum ) > 0 ) {
                if ( verifyFlag == 0 && forceFlag == 0 ) {
                    *outChksumStr = strdup( tmpDataObjInfo->chksum );
                    return 0;
                }
            }
        }
        else {
            while ( tmpDataObjInfo != NULL ) {
                if ( tmpDataObjInfo->replStatus > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
                    if ( verifyFlag == 0 && forceFlag == 0 ) {
                        *outChksumStr = strdup( tmpDataObjInfo->chksum );
                        return 0;
                    }
                    else {
                        break;
                    }
                }

                tmpDataObjInfo = tmpDataObjInfo->next;
            }
        }
        /* need to compute the chksum */
        if ( tmpDataObjInfo == NULL ) {
            tmpDataObjInfo = *dataObjInfoHead;
        }
        if ( verifyFlag > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         outChksumStr );
        }
        else {
            addKeyVal( &tmpDataObjInfo->condInput, ORIG_CHKSUM_KW, inp_chksum );
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo, outChksumStr );
        }

        return status;

    }

    /* allFlag == 1 */
    tmpDataObjInfo = *dataObjInfoHead;
    while ( tmpDataObjInfo != NULL ) {
        char *tmpChksumStr = 0;
        //JMC - legacy resource :: int rescClass = getRescClass (tmpDataObjInfo->rescInfo);
        std::string resc_class;
        irods::error err = irods::get_resource_property< std::string >(
                               tmpDataObjInfo->rescName,
                               irods::RESOURCE_CLASS,
                               resc_class );
        if ( !err.ok() ) {
            irods::log( PASSMSG( "failed in get_resource_property [class]", err ) );
        }

        if ( resc_class == irods::RESOURCE_CLASS_BUNDLE ) { // (rescClass == BUNDLE_CL) {
            /* don't do BUNDLE_CL. should be done on the bundle file */
            tmpDataObjInfo = tmpDataObjInfo->next;
            status = 0;
            continue;
        }
        if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
            /* need to chksum no matter what */
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else if ( verifyFlag > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         &tmpChksumStr );
        }
        else if ( forceFlag > 0 ) {
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else {
            tmpChksumStr = strdup( tmpDataObjInfo->chksum );
            status = 0;
        }

        if ( status < 0 ) {
            return status;
        }
        if ( tmpDataObjInfo->replStatus > 0 && *outChksumStr == NULL ) {
            *outChksumStr = tmpChksumStr;
        }
        else {
            /* save it */
            if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
                rstrcpy( tmpDataObjInfo->chksum, tmpChksumStr, NAME_LEN );
            }
            free( tmpChksumStr );
        }
        tmpDataObjInfo = tmpDataObjInfo->next;
    }
    if ( *outChksumStr == NULL ) {
        *outChksumStr = strdup( ( *dataObjInfoHead )->chksum );
    }

    return status;
}

int
dataObjChksumAndRegInfo( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                         char **outChksumStr ) {
    int              status;
    keyValPair_t     regParam;
    modDataObjMeta_t modDataObjMetaInp;

    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR, "dataObjChksumAndRegInfo: _dataObjChksum error for %s, status = %d",
                 dataObjInfo->objPath, status );
        return status;
    }

    if ( dataObjInfo->specColl != NULL ) {
        return status;
    }

    memset( &regParam, 0, sizeof( regParam ) );
    addKeyVal( &regParam, CHKSUM_KW, *outChksumStr );
    // set pdmo flag so that chksum doesn't trigger file operations
    addKeyVal( &regParam, IN_PDMO_KW, "" );
    modDataObjMetaInp.dataObjInfo = dataObjInfo;
    modDataObjMetaInp.regParam = &regParam;
    status = rsModDataObjMeta( rsComm, &modDataObjMetaInp );
    rodsLog( LOG_NOTICE, "dataObjChksumAndRegInfo - rsModDataObjMeta status %d", status );
    clearKeyVal( &regParam );

    return status;
}

int
verifyDatObjChksum( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                    char **outChksumStr ) {
    int status;

    addKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW, dataObjInfo->chksum );
    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );
    rmKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum:_dataObjChksum error for %s, stat=%d",
                 dataObjInfo->objPath, status );
        return status;
    }
    if ( *outChksumStr == NULL ) {
        rodsLog( LOG_ERROR, "verifyDatObjChksum: outChkSumStr is null." );
        return SYS_INTERNAL_NULL_INPUT_ERR;
    }

    if ( strcmp( *outChksumStr, dataObjInfo->chksum ) != 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum: computed chksum %s != icat value %s for %s",
                 *outChksumStr, dataObjInfo->chksum, dataObjInfo->objPath );
        return USER_CHKSUM_MISMATCH;
    }
    else {
        return status;
    }
}

<commit_msg>[#858] turn down logging for successful checksums<commit_after>#include "reGlobalsExtern.hpp"
#include "dataObjChksum.h"
#include "objMetaOpr.hpp"
#include "resource.hpp"
#include "specColl.hpp"
#include "dataObjOpr.hpp"
#include "physPath.hpp"
#include "rsApiHandler.hpp"
#include "modDataObjMeta.h"
#include "getRemoteZoneResc.h"

// =-=-=-=-=-=-=-
#include "irods_resource_backport.hpp"
#include "irods_resource_redirect.hpp"

int
rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjChksumInp,
                 char **outChksum ) {
    int status;
    dataObjInfo_t *dataObjInfoHead;
    int remoteFlag;
    rodsServerHost_t *rodsServerHost;
    specCollCache_t *specCollCache = NULL;

    resolveLinkedPath( rsComm, dataObjChksumInp->objPath, &specCollCache, &dataObjChksumInp->condInput );

    remoteFlag = getAndConnRemoteZone( rsComm, dataObjChksumInp, &rodsServerHost, REMOTE_OPEN );

    if ( remoteFlag < 0 ) {
        return remoteFlag;
    }
    else if ( remoteFlag == REMOTE_HOST ) {
        status = rcDataObjChksum( rodsServerHost->conn, dataObjChksumInp,
                                  outChksum );
        return status;
    }
    else {
        // =-=-=-=-=-=-=-
        // determine the resource hierarchy if one is not provided
        if ( getValByKey( &dataObjChksumInp->condInput, RESC_HIER_STR_KW ) == NULL ) {
            std::string       hier;
            irods::error ret = irods::resolve_resource_hierarchy( irods::OPEN_OPERATION,
                               rsComm, dataObjChksumInp, hier );
            if ( !ret.ok() ) {
                std::stringstream msg;
                msg << "failed in irods::resolve_resource_hierarchy for [";
                msg << dataObjChksumInp->objPath << "]";
                irods::log( PASSMSG( msg.str(), ret ) );
                return ret.code();
            }

            // =-=-=-=-=-=-=-
            // we resolved the redirect and have a host, set the hier str for subsequent
            // api calls, etc.
            addKeyVal( &dataObjChksumInp->condInput, RESC_HIER_STR_KW, hier.c_str() );

        } // if keyword

        status = _rsDataObjChksum( rsComm, dataObjChksumInp, outChksum,
                                   &dataObjInfoHead );
    }
    freeAllDataObjInfo( dataObjInfoHead );
    rodsLog( LOG_DEBUG, "rsDataObjChksum - returning status %d", status );
    return status;
}

int
_rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjInp,
                  char **outChksumStr, dataObjInfo_t **dataObjInfoHead ) {
    int status;
    dataObjInfo_t *tmpDataObjInfo;
    int allFlag;
    int verifyFlag;
    int forceFlag;

    char* inp_chksum = getValByKey( &dataObjInp->condInput, ORIG_CHKSUM_KW );

    if ( getValByKey( &dataObjInp->condInput, CHKSUM_ALL_KW ) != NULL ) {
        allFlag = 1;
    }
    else {
        allFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, VERIFY_CHKSUM_KW ) != NULL ) {
        verifyFlag = 1;
    }
    else {
        verifyFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, FORCE_CHKSUM_KW ) != NULL ) {
        forceFlag = 1;
    }
    else {
        forceFlag = 0;
    }

    *dataObjInfoHead = NULL;
    *outChksumStr = NULL;

    status = getDataObjInfoIncSpecColl( rsComm, dataObjInp, dataObjInfoHead );
    if ( status < 0 ) {
        return status;
    }
    else if ( allFlag == 0 ) {
        /* screen out any stale copies */
        status = sortObjInfoForOpen( dataObjInfoHead, &dataObjInp->condInput, 0 );
        if ( status < 0 ) {
            return status;
        }

        tmpDataObjInfo = *dataObjInfoHead;
        if ( tmpDataObjInfo->next == NULL ) {
            /* the only copy */
            if ( strlen( tmpDataObjInfo->chksum ) > 0 ) {
                if ( verifyFlag == 0 && forceFlag == 0 ) {
                    *outChksumStr = strdup( tmpDataObjInfo->chksum );
                    return 0;
                }
            }
        }
        else {
            while ( tmpDataObjInfo != NULL ) {
                if ( tmpDataObjInfo->replStatus > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
                    if ( verifyFlag == 0 && forceFlag == 0 ) {
                        *outChksumStr = strdup( tmpDataObjInfo->chksum );
                        return 0;
                    }
                    else {
                        break;
                    }
                }

                tmpDataObjInfo = tmpDataObjInfo->next;
            }
        }
        /* need to compute the chksum */
        if ( tmpDataObjInfo == NULL ) {
            tmpDataObjInfo = *dataObjInfoHead;
        }
        if ( verifyFlag > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         outChksumStr );
        }
        else {
            addKeyVal( &tmpDataObjInfo->condInput, ORIG_CHKSUM_KW, inp_chksum );
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo, outChksumStr );
        }

        return status;

    }

    /* allFlag == 1 */
    tmpDataObjInfo = *dataObjInfoHead;
    while ( tmpDataObjInfo != NULL ) {
        char *tmpChksumStr = 0;
        //JMC - legacy resource :: int rescClass = getRescClass (tmpDataObjInfo->rescInfo);
        std::string resc_class;
        irods::error err = irods::get_resource_property< std::string >(
                               tmpDataObjInfo->rescName,
                               irods::RESOURCE_CLASS,
                               resc_class );
        if ( !err.ok() ) {
            irods::log( PASSMSG( "failed in get_resource_property [class]", err ) );
        }

        if ( resc_class == irods::RESOURCE_CLASS_BUNDLE ) { // (rescClass == BUNDLE_CL) {
            /* don't do BUNDLE_CL. should be done on the bundle file */
            tmpDataObjInfo = tmpDataObjInfo->next;
            status = 0;
            continue;
        }
        if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
            /* need to chksum no matter what */
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else if ( verifyFlag > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         &tmpChksumStr );
        }
        else if ( forceFlag > 0 ) {
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else {
            tmpChksumStr = strdup( tmpDataObjInfo->chksum );
            status = 0;
        }

        if ( status < 0 ) {
            return status;
        }
        if ( tmpDataObjInfo->replStatus > 0 && *outChksumStr == NULL ) {
            *outChksumStr = tmpChksumStr;
        }
        else {
            /* save it */
            if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
                rstrcpy( tmpDataObjInfo->chksum, tmpChksumStr, NAME_LEN );
            }
            free( tmpChksumStr );
        }
        tmpDataObjInfo = tmpDataObjInfo->next;
    }
    if ( *outChksumStr == NULL ) {
        *outChksumStr = strdup( ( *dataObjInfoHead )->chksum );
    }

    return status;
}

int
dataObjChksumAndRegInfo( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                         char **outChksumStr ) {
    int              status;
    keyValPair_t     regParam;
    modDataObjMeta_t modDataObjMetaInp;

    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR, "dataObjChksumAndRegInfo: _dataObjChksum error for %s, status = %d",
                 dataObjInfo->objPath, status );
        return status;
    }

    if ( dataObjInfo->specColl != NULL ) {
        return status;
    }

    memset( &regParam, 0, sizeof( regParam ) );
    addKeyVal( &regParam, CHKSUM_KW, *outChksumStr );
    // set pdmo flag so that chksum doesn't trigger file operations
    addKeyVal( &regParam, IN_PDMO_KW, "" );
    modDataObjMetaInp.dataObjInfo = dataObjInfo;
    modDataObjMetaInp.regParam = &regParam;
    status = rsModDataObjMeta( rsComm, &modDataObjMetaInp );
    rodsLog( LOG_DEBUG, "dataObjChksumAndRegInfo - rsModDataObjMeta status %d", status );
    clearKeyVal( &regParam );

    return status;
}

int
verifyDatObjChksum( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                    char **outChksumStr ) {
    int status;

    addKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW, dataObjInfo->chksum );
    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );
    rmKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum:_dataObjChksum error for %s, stat=%d",
                 dataObjInfo->objPath, status );
        return status;
    }
    if ( *outChksumStr == NULL ) {
        rodsLog( LOG_ERROR, "verifyDatObjChksum: outChkSumStr is null." );
        return SYS_INTERNAL_NULL_INPUT_ERR;
    }

    if ( strcmp( *outChksumStr, dataObjInfo->chksum ) != 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum: computed chksum %s != icat value %s for %s",
                 *outChksumStr, dataObjInfo->chksum, dataObjInfo->objPath );
        return USER_CHKSUM_MISMATCH;
    }
    else {
        return status;
    }
}

<|endoftext|>


<commit_before>#include "reGlobalsExtern.hpp"
#include "dataObjChksum.h"
#include "objMetaOpr.hpp"
#include "resource.hpp"
#include "specColl.hpp"
#include "dataObjOpr.hpp"
#include "physPath.hpp"
#include "rsApiHandler.hpp"
#include "modDataObjMeta.h"
#include "getRemoteZoneResc.h"

// =-=-=-=-=-=-=-
#include "irods_resource_backport.hpp"
#include "irods_resource_redirect.hpp"

int
rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjChksumInp,
                 char **outChksum ) {
    int status;
    dataObjInfo_t *dataObjInfoHead;
    int remoteFlag;
    rodsServerHost_t *rodsServerHost;
    specCollCache_t *specCollCache = NULL;

    resolveLinkedPath( rsComm, dataObjChksumInp->objPath, &specCollCache, &dataObjChksumInp->condInput );

    remoteFlag = getAndConnRemoteZone( rsComm, dataObjChksumInp, &rodsServerHost, REMOTE_OPEN );

    if ( remoteFlag < 0 ) {
        return remoteFlag;
    }
    else if ( remoteFlag == REMOTE_HOST ) {
        status = rcDataObjChksum( rodsServerHost->conn, dataObjChksumInp,
                                  outChksum );
        return status;
    }
    else {
        // =-=-=-=-=-=-=-
        // determine the resource hierarchy if one is not provided
        if ( getValByKey( &dataObjChksumInp->condInput, RESC_HIER_STR_KW ) == NULL ) {
            std::string       hier;
            irods::error ret = irods::resolve_resource_hierarchy( irods::OPEN_OPERATION,
                               rsComm, dataObjChksumInp, hier );
            if ( !ret.ok() ) {
                std::stringstream msg;
                msg << "failed in irods::resolve_resource_hierarchy for [";
                msg << dataObjChksumInp->objPath << "]";
                irods::log( PASSMSG( msg.str(), ret ) );
                return ret.code();
            }

            // =-=-=-=-=-=-=-
            // we resolved the redirect and have a host, set the hier str for subsequent
            // api calls, etc.
            addKeyVal( &dataObjChksumInp->condInput, RESC_HIER_STR_KW, hier.c_str() );

        } // if keyword

        status = _rsDataObjChksum( rsComm, dataObjChksumInp, outChksum,
                                   &dataObjInfoHead );
    }
    freeAllDataObjInfo( dataObjInfoHead );
    rodsLog( LOG_NOTICE, "rsDataObjChksum - returning status %d", status );
    return status;
}

int
_rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjInp,
                  char **outChksumStr, dataObjInfo_t **dataObjInfoHead ) {
    int status;
    dataObjInfo_t *tmpDataObjInfo;
    int allFlag;
    int verifyFlag;
    int forceFlag;

    if ( getValByKey( &dataObjInp->condInput, CHKSUM_ALL_KW ) != NULL ) {
        allFlag = 1;
    }
    else {
        allFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, VERIFY_CHKSUM_KW ) != NULL ) {
        verifyFlag = 1;
    }
    else {
        verifyFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, FORCE_CHKSUM_KW ) != NULL ) {
        forceFlag = 1;
    }
    else {
        forceFlag = 0;
    }

    *dataObjInfoHead = NULL;
    *outChksumStr = NULL;

    status = getDataObjInfoIncSpecColl( rsComm, dataObjInp, dataObjInfoHead );
    if ( status < 0 ) {
        return status;
    }
    else if ( allFlag == 0 ) {
        /* screen out any stale copies */
        status = sortObjInfoForOpen( dataObjInfoHead, &dataObjInp->condInput, 0 );
        if ( status < 0 ) {
            return status;
        }

        tmpDataObjInfo = *dataObjInfoHead;
        if ( tmpDataObjInfo->next == NULL ) {
            /* the only copy */
            if ( strlen( tmpDataObjInfo->chksum ) > 0 ) {
                if ( verifyFlag == 0 && forceFlag == 0 ) {
                    *outChksumStr = strdup( tmpDataObjInfo->chksum );
                    return 0;
                }
            }
        }
        else {
            while ( tmpDataObjInfo != NULL ) {
                if ( tmpDataObjInfo->replStatus > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
                    if ( verifyFlag == 0 && forceFlag == 0 ) {
                        *outChksumStr = strdup( tmpDataObjInfo->chksum );
                        return 0;
                    }
                    else {
                        break;
                    }
                }

                tmpDataObjInfo = tmpDataObjInfo->next;
            }
        }
        /* need to compute the chksum */
        if ( tmpDataObjInfo == NULL ) {
            tmpDataObjInfo = *dataObjInfoHead;
        }
        if ( verifyFlag > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         outChksumStr );
        }
        else {
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo, outChksumStr );
        }

        return status;

    }

    /* allFlag == 1 */
    tmpDataObjInfo = *dataObjInfoHead;
    while ( tmpDataObjInfo != NULL ) {
        char *tmpChksumStr;
        //JMC - legacy resource :: int rescClass = getRescClass (tmpDataObjInfo->rescInfo);
        std::string resc_class;
        irods::error err = irods::get_resource_property< std::string >(
                               tmpDataObjInfo->rescName,
                               irods::RESOURCE_CLASS,
                               resc_class );
        if ( !err.ok() ) {
            irods::log( PASSMSG( "failed in get_resource_property [class]", err ) );
        }

        if ( resc_class == irods::RESOURCE_CLASS_BUNDLE ) { // (rescClass == BUNDLE_CL) {
            /* don't do BUNDLE_CL. should be done on the bundle file */
            tmpDataObjInfo = tmpDataObjInfo->next;
            status = 0;
            continue;
        }
        if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
            /* need to chksum no matter what */
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else if ( verifyFlag > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         &tmpChksumStr );
        }
        else if ( forceFlag > 0 ) {
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else {
            tmpChksumStr = strdup( tmpDataObjInfo->chksum );
            status = 0;
        }

        if ( status < 0 ) {
            return status;
        }
        if ( tmpDataObjInfo->replStatus > 0 && *outChksumStr == NULL ) {
            *outChksumStr = tmpChksumStr;
        }
        else {
            /* save it */
            if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
                rstrcpy( tmpDataObjInfo->chksum, tmpChksumStr, NAME_LEN );
            }
            free( tmpChksumStr );
        }
        tmpDataObjInfo = tmpDataObjInfo->next;
    }
    if ( *outChksumStr == NULL ) {
        *outChksumStr = strdup( ( *dataObjInfoHead )->chksum );
    }

    return status;
}

int
dataObjChksumAndRegInfo( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                         char **outChksumStr ) {
    int              status;
    keyValPair_t     regParam;
    modDataObjMeta_t modDataObjMetaInp;

    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR, "dataObjChksumAndRegInfo: _dataObjChksum error for %s, status = %d",
                 dataObjInfo->objPath, status );
        return status;
    }

    if ( dataObjInfo->specColl != NULL ) {
        return status;
    }

    memset( &regParam, 0, sizeof( regParam ) );
    addKeyVal( &regParam, CHKSUM_KW, *outChksumStr );
    // set pdmo flag so that chksum doesn't trigger file operations
    addKeyVal( &regParam, IN_PDMO_KW, "" );
    modDataObjMetaInp.dataObjInfo = dataObjInfo;
    modDataObjMetaInp.regParam = &regParam;
    status = rsModDataObjMeta( rsComm, &modDataObjMetaInp );
    rodsLog( LOG_NOTICE, "dataObjChksumAndRegInfo - rsModDataObjMeta status %d", status );
    clearKeyVal( &regParam );

    return status;
}

int
verifyDatObjChksum( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                    char **outChksumStr ) {
    int status;

    addKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW, dataObjInfo->chksum );
    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );
    rmKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum:_dataObjChksum error for %s, stat=%d",
                 dataObjInfo->objPath, status );
        return status;
    }
    if ( *outChksumStr == NULL ) {
        rodsLog( LOG_ERROR, "verifyDatObjChksum: outChkSumStr is null." );
        return SYS_INTERNAL_NULL_INPUT_ERR;
    }

    if ( strcmp( *outChksumStr, dataObjInfo->chksum ) != 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum: computed chksum %s != icat value %s for %s",
                 *outChksumStr, dataObjInfo->chksum, dataObjInfo->objPath );
        return USER_CHKSUM_MISMATCH;
    }
    else {
        return status;
    }
}

<commit_msg>[#2692] initlize the pointer<commit_after>#include "reGlobalsExtern.hpp"
#include "dataObjChksum.h"
#include "objMetaOpr.hpp"
#include "resource.hpp"
#include "specColl.hpp"
#include "dataObjOpr.hpp"
#include "physPath.hpp"
#include "rsApiHandler.hpp"
#include "modDataObjMeta.h"
#include "getRemoteZoneResc.h"

// =-=-=-=-=-=-=-
#include "irods_resource_backport.hpp"
#include "irods_resource_redirect.hpp"

int
rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjChksumInp,
                 char **outChksum ) {
    int status;
    dataObjInfo_t *dataObjInfoHead;
    int remoteFlag;
    rodsServerHost_t *rodsServerHost;
    specCollCache_t *specCollCache = NULL;

    resolveLinkedPath( rsComm, dataObjChksumInp->objPath, &specCollCache, &dataObjChksumInp->condInput );

    remoteFlag = getAndConnRemoteZone( rsComm, dataObjChksumInp, &rodsServerHost, REMOTE_OPEN );

    if ( remoteFlag < 0 ) {
        return remoteFlag;
    }
    else if ( remoteFlag == REMOTE_HOST ) {
        status = rcDataObjChksum( rodsServerHost->conn, dataObjChksumInp,
                                  outChksum );
        return status;
    }
    else {
        // =-=-=-=-=-=-=-
        // determine the resource hierarchy if one is not provided
        if ( getValByKey( &dataObjChksumInp->condInput, RESC_HIER_STR_KW ) == NULL ) {
            std::string       hier;
            irods::error ret = irods::resolve_resource_hierarchy( irods::OPEN_OPERATION,
                               rsComm, dataObjChksumInp, hier );
            if ( !ret.ok() ) {
                std::stringstream msg;
                msg << "failed in irods::resolve_resource_hierarchy for [";
                msg << dataObjChksumInp->objPath << "]";
                irods::log( PASSMSG( msg.str(), ret ) );
                return ret.code();
            }

            // =-=-=-=-=-=-=-
            // we resolved the redirect and have a host, set the hier str for subsequent
            // api calls, etc.
            addKeyVal( &dataObjChksumInp->condInput, RESC_HIER_STR_KW, hier.c_str() );

        } // if keyword

        status = _rsDataObjChksum( rsComm, dataObjChksumInp, outChksum,
                                   &dataObjInfoHead );
    }
    freeAllDataObjInfo( dataObjInfoHead );
    rodsLog( LOG_NOTICE, "rsDataObjChksum - returning status %d", status );
    return status;
}

int
_rsDataObjChksum( rsComm_t *rsComm, dataObjInp_t *dataObjInp,
                  char **outChksumStr, dataObjInfo_t **dataObjInfoHead ) {
    int status;
    dataObjInfo_t *tmpDataObjInfo;
    int allFlag;
    int verifyFlag;
    int forceFlag;

    if ( getValByKey( &dataObjInp->condInput, CHKSUM_ALL_KW ) != NULL ) {
        allFlag = 1;
    }
    else {
        allFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, VERIFY_CHKSUM_KW ) != NULL ) {
        verifyFlag = 1;
    }
    else {
        verifyFlag = 0;
    }

    if ( getValByKey( &dataObjInp->condInput, FORCE_CHKSUM_KW ) != NULL ) {
        forceFlag = 1;
    }
    else {
        forceFlag = 0;
    }

    *dataObjInfoHead = NULL;
    *outChksumStr = NULL;

    status = getDataObjInfoIncSpecColl( rsComm, dataObjInp, dataObjInfoHead );
    if ( status < 0 ) {
        return status;
    }
    else if ( allFlag == 0 ) {
        /* screen out any stale copies */
        status = sortObjInfoForOpen( dataObjInfoHead, &dataObjInp->condInput, 0 );
        if ( status < 0 ) {
            return status;
        }

        tmpDataObjInfo = *dataObjInfoHead;
        if ( tmpDataObjInfo->next == NULL ) {
            /* the only copy */
            if ( strlen( tmpDataObjInfo->chksum ) > 0 ) {
                if ( verifyFlag == 0 && forceFlag == 0 ) {
                    *outChksumStr = strdup( tmpDataObjInfo->chksum );
                    return 0;
                }
            }
        }
        else {
            while ( tmpDataObjInfo != NULL ) {
                if ( tmpDataObjInfo->replStatus > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
                    if ( verifyFlag == 0 && forceFlag == 0 ) {
                        *outChksumStr = strdup( tmpDataObjInfo->chksum );
                        return 0;
                    }
                    else {
                        break;
                    }
                }

                tmpDataObjInfo = tmpDataObjInfo->next;
            }
        }
        /* need to compute the chksum */
        if ( tmpDataObjInfo == NULL ) {
            tmpDataObjInfo = *dataObjInfoHead;
        }
        if ( verifyFlag > 0 && strlen( tmpDataObjInfo->chksum ) > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         outChksumStr );
        }
        else {
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo, outChksumStr );
        }

        return status;

    }

    /* allFlag == 1 */
    tmpDataObjInfo = *dataObjInfoHead;
    while ( tmpDataObjInfo != NULL ) {
        char *tmpChksumStr = 0;
        //JMC - legacy resource :: int rescClass = getRescClass (tmpDataObjInfo->rescInfo);
        std::string resc_class;
        irods::error err = irods::get_resource_property< std::string >(
                               tmpDataObjInfo->rescName,
                               irods::RESOURCE_CLASS,
                               resc_class );
        if ( !err.ok() ) {
            irods::log( PASSMSG( "failed in get_resource_property [class]", err ) );
        }

        if ( resc_class == irods::RESOURCE_CLASS_BUNDLE ) { // (rescClass == BUNDLE_CL) {
            /* don't do BUNDLE_CL. should be done on the bundle file */
            tmpDataObjInfo = tmpDataObjInfo->next;
            status = 0;
            continue;
        }
        if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
            /* need to chksum no matter what */
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else if ( verifyFlag > 0 ) {
            status = verifyDatObjChksum( rsComm, tmpDataObjInfo,
                                         &tmpChksumStr );
        }
        else if ( forceFlag > 0 ) {
            status = dataObjChksumAndRegInfo( rsComm, tmpDataObjInfo,
                                              &tmpChksumStr );
        }
        else {
            tmpChksumStr = strdup( tmpDataObjInfo->chksum );
            status = 0;
        }

        if ( status < 0 ) {
            return status;
        }
        if ( tmpDataObjInfo->replStatus > 0 && *outChksumStr == NULL ) {
            *outChksumStr = tmpChksumStr;
        }
        else {
            /* save it */
            if ( strlen( tmpDataObjInfo->chksum ) == 0 ) {
                rstrcpy( tmpDataObjInfo->chksum, tmpChksumStr, NAME_LEN );
            }
            free( tmpChksumStr );
        }
        tmpDataObjInfo = tmpDataObjInfo->next;
    }
    if ( *outChksumStr == NULL ) {
        *outChksumStr = strdup( ( *dataObjInfoHead )->chksum );
    }

    return status;
}

int
dataObjChksumAndRegInfo( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                         char **outChksumStr ) {
    int              status;
    keyValPair_t     regParam;
    modDataObjMeta_t modDataObjMetaInp;

    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR, "dataObjChksumAndRegInfo: _dataObjChksum error for %s, status = %d",
                 dataObjInfo->objPath, status );
        return status;
    }

    if ( dataObjInfo->specColl != NULL ) {
        return status;
    }

    memset( &regParam, 0, sizeof( regParam ) );
    addKeyVal( &regParam, CHKSUM_KW, *outChksumStr );
    // set pdmo flag so that chksum doesn't trigger file operations
    addKeyVal( &regParam, IN_PDMO_KW, "" );
    modDataObjMetaInp.dataObjInfo = dataObjInfo;
    modDataObjMetaInp.regParam = &regParam;
    status = rsModDataObjMeta( rsComm, &modDataObjMetaInp );
    rodsLog( LOG_NOTICE, "dataObjChksumAndRegInfo - rsModDataObjMeta status %d", status );
    clearKeyVal( &regParam );

    return status;
}

int
verifyDatObjChksum( rsComm_t *rsComm, dataObjInfo_t *dataObjInfo,
                    char **outChksumStr ) {
    int status;

    addKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW, dataObjInfo->chksum );
    status = _dataObjChksum( rsComm, dataObjInfo, outChksumStr );
    rmKeyVal( &dataObjInfo->condInput, ORIG_CHKSUM_KW );

    if ( status < 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum:_dataObjChksum error for %s, stat=%d",
                 dataObjInfo->objPath, status );
        return status;
    }
    if ( *outChksumStr == NULL ) {
        rodsLog( LOG_ERROR, "verifyDatObjChksum: outChkSumStr is null." );
        return SYS_INTERNAL_NULL_INPUT_ERR;
    }

    if ( strcmp( *outChksumStr, dataObjInfo->chksum ) != 0 ) {
        rodsLog( LOG_ERROR,
                 "verifyDatObjChksum: computed chksum %s != icat value %s for %s",
                 *outChksumStr, dataObjInfo->chksum, dataObjInfo->objPath );
        return USER_CHKSUM_MISMATCH;
    }
    else {
        return status;
    }
}

<|endoftext|>


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

#include "base/string_util.h"
#include "base/test/test_timeouts.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/automation_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"
#include "net/base/escape.h"
#include "net/test/test_server.h"

struct IsSearchProviderTestData;

class SearchProviderTest : public UITest {
 protected:
  SearchProviderTest();

  IsSearchProviderTestData StartIsSearchProviderInstalledTest(
      BrowserProxy* browser_proxy,
      const char* host,
      const char* expected_result);

  void FinishIsSearchProviderInstalledTest(
      const IsSearchProviderTestData& data);

  net::TestServer test_server_;
  GURL search_provider_test_url_;
  bool test_server_started_;

 private:
  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
};

SearchProviderTest::SearchProviderTest()
    : test_server_(net::TestServer::TYPE_HTTP,
                   FilePath(FILE_PATH_LITERAL("chrome/test/data"))),
      test_server_started_(false) {
  // The test_server is started in the constructor (rather than the test body)
  // so the mapping rules below can include the ephemeral port number.
  test_server_started_ = test_server_.Start();
  if (!test_server_started_)
    return;

  // Enable the search provider additions.
  launch_arguments_.AppendSwitch(switches::kEnableSearchProviderApiV2);

  // Map all hosts to our local server.
  std::string host_rule("MAP * " + test_server_.host_port_pair().ToString());
  launch_arguments_.AppendSwitchASCII(switches::kHostRules, host_rule);

  // Get the url for the test page.
  search_provider_test_url_ =
      test_server_.GetURL("files/is_search_provider_installed.html");
}

struct IsSearchProviderTestData {
  IsSearchProviderTestData() {
  }

  IsSearchProviderTestData(TabProxy* t,
                           std::string h,
                           GURL url)
      : tab(t),
        host(h),
        test_url(url) {
  }

  scoped_refptr<TabProxy> tab;
  std::string host;
  GURL test_url;
};

IsSearchProviderTestData SearchProviderTest::StartIsSearchProviderInstalledTest(
    BrowserProxy* browser_proxy,
    const char* host,
    const char* expected_result) {
  // Set-up a new tab for the navigation.
  int num_tabs = 0;
  if (!browser_proxy->GetTabCount(&num_tabs)) {
    ADD_FAILURE() << "BrowserProxy::GetTabCount failed.";
    return IsSearchProviderTestData();
  }

  GURL blank(chrome::kAboutBlankURL);
  if (!browser_proxy->AppendTab(blank)) {
    ADD_FAILURE() << "BrowserProxy::AppendTab failed.";
    return IsSearchProviderTestData();
  }

  scoped_refptr<TabProxy> tab(browser_proxy->GetTab(num_tabs));
  if (!tab.get()) {
    ADD_FAILURE() << "BrowserProxy::GetTab for the new tab failed.";
    return IsSearchProviderTestData();
  }

  // Go to the test page.
  GURL test_url(std::string("http://") + host +
                search_provider_test_url_.path() + "#" + expected_result);
  EXPECT_TRUE(tab->NavigateToURLAsync(test_url));

  // Bundle up information needed to verify the result.
  return IsSearchProviderTestData(tab, host, test_url);
}

void SearchProviderTest::FinishIsSearchProviderInstalledTest(
    const IsSearchProviderTestData& data) {
  ASSERT_TRUE(data.tab.get());

  std::string cookie_name = data.host + "testResult";
  std::string escaped_value =
      WaitUntilCookieNonEmpty(data.tab,
                              data.test_url,
                              cookie_name.c_str(),
                              TestTimeouts::action_max_timeout_ms());

  // Unescapes and normalizes the actual result.
  std::string value = UnescapeURLComponent(
      escaped_value,
      UnescapeRule::NORMAL | UnescapeRule::SPACES |
      UnescapeRule::URL_SPECIAL_CHARS | UnescapeRule::CONTROL_CHARS);
  value += "\n";
  ReplaceSubstringsAfterOffset(&value, 0, "\r", "");
  EXPECT_STREQ("1\n", value.c_str());
  EXPECT_TRUE(data.tab->Close(true));
}

TEST_F(SearchProviderTest, TestIsSearchProviderInstalled) {
  ASSERT_TRUE(test_server_started_);

  // Use the default search provider, other installed search provider, and
  // one not installed as well. (Note that yahoo isn't tested because the
  // its host name varies a lot for different locales unlike Google and Bing,
  // which would make the test fail depending on the machine's locale.)
  const char* test_hosts[] = { "www.google.com",
                               "www.bing.com",
                               "localhost" };
  const char* expected_results[] = { "2",
                                     "1",
                                     "0" };
  COMPILE_ASSERT(arraysize(test_hosts) == arraysize(expected_results),
                 there_should_be_a_result_for_each_host);
  IsSearchProviderTestData test_data[2 * arraysize(test_hosts)];

  // Start results for the normal mode.
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i] = StartIsSearchProviderInstalledTest(
        browser, test_hosts[i], expected_results[i]);
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }

  // Start tests for incognito mode (and verify the result is 0).
  ASSERT_TRUE(browser->RunCommand(IDC_NEW_INCOGNITO_WINDOW));
  scoped_refptr<BrowserProxy> incognito(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(incognito.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i + arraysize(test_hosts)] = StartIsSearchProviderInstalledTest(
        incognito, test_hosts[i], "0");
    FinishIsSearchProviderInstalledTest(test_data[i + arraysize(test_hosts)]);
  }

  // The following should be re-enabled. At the moment, there are problems with
  // doing all of these queries in parallel -- see http://crbug.com/60043.
#if 0
  // Remove the calls to FinishIsSearchProviderInstalledTest above when
  // re-enabling this code.

  // Do the verification.
  for (size_t i = 0; i < arraysize(test_data); ++i) {
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }
#endif
}

TEST_F(SearchProviderTest, TestIsSearchProviderInstalledWithException) {
  // Change the url for the test page to one that throws an exception when
  // toString is called on the argument given to isSearchProviderInstalled.
  search_provider_test_url_ = test_server_.GetURL(
      "files/is_search_provider_installed_with_exception.html");

  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  FinishIsSearchProviderInstalledTest(StartIsSearchProviderInstalledTest(
      browser, "www.google.com", ""));
}
<commit_msg>Mark SearchProviderTest.TestIsSearchProviderInstalled as flaky on Win.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/string_util.h"
#include "base/test/test_timeouts.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/automation_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"
#include "net/base/escape.h"
#include "net/test/test_server.h"

struct IsSearchProviderTestData;

class SearchProviderTest : public UITest {
 protected:
  SearchProviderTest();

  IsSearchProviderTestData StartIsSearchProviderInstalledTest(
      BrowserProxy* browser_proxy,
      const char* host,
      const char* expected_result);

  void FinishIsSearchProviderInstalledTest(
      const IsSearchProviderTestData& data);

  net::TestServer test_server_;
  GURL search_provider_test_url_;
  bool test_server_started_;

 private:
  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
};

SearchProviderTest::SearchProviderTest()
    : test_server_(net::TestServer::TYPE_HTTP,
                   FilePath(FILE_PATH_LITERAL("chrome/test/data"))),
      test_server_started_(false) {
  // The test_server is started in the constructor (rather than the test body)
  // so the mapping rules below can include the ephemeral port number.
  test_server_started_ = test_server_.Start();
  if (!test_server_started_)
    return;

  // Enable the search provider additions.
  launch_arguments_.AppendSwitch(switches::kEnableSearchProviderApiV2);

  // Map all hosts to our local server.
  std::string host_rule("MAP * " + test_server_.host_port_pair().ToString());
  launch_arguments_.AppendSwitchASCII(switches::kHostRules, host_rule);

  // Get the url for the test page.
  search_provider_test_url_ =
      test_server_.GetURL("files/is_search_provider_installed.html");
}

struct IsSearchProviderTestData {
  IsSearchProviderTestData() {
  }

  IsSearchProviderTestData(TabProxy* t,
                           std::string h,
                           GURL url)
      : tab(t),
        host(h),
        test_url(url) {
  }

  scoped_refptr<TabProxy> tab;
  std::string host;
  GURL test_url;
};

IsSearchProviderTestData SearchProviderTest::StartIsSearchProviderInstalledTest(
    BrowserProxy* browser_proxy,
    const char* host,
    const char* expected_result) {
  // Set-up a new tab for the navigation.
  int num_tabs = 0;
  if (!browser_proxy->GetTabCount(&num_tabs)) {
    ADD_FAILURE() << "BrowserProxy::GetTabCount failed.";
    return IsSearchProviderTestData();
  }

  GURL blank(chrome::kAboutBlankURL);
  if (!browser_proxy->AppendTab(blank)) {
    ADD_FAILURE() << "BrowserProxy::AppendTab failed.";
    return IsSearchProviderTestData();
  }

  scoped_refptr<TabProxy> tab(browser_proxy->GetTab(num_tabs));
  if (!tab.get()) {
    ADD_FAILURE() << "BrowserProxy::GetTab for the new tab failed.";
    return IsSearchProviderTestData();
  }

  // Go to the test page.
  GURL test_url(std::string("http://") + host +
                search_provider_test_url_.path() + "#" + expected_result);
  EXPECT_TRUE(tab->NavigateToURLAsync(test_url));

  // Bundle up information needed to verify the result.
  return IsSearchProviderTestData(tab, host, test_url);
}

void SearchProviderTest::FinishIsSearchProviderInstalledTest(
    const IsSearchProviderTestData& data) {
  ASSERT_TRUE(data.tab.get());

  std::string cookie_name = data.host + "testResult";
  std::string escaped_value =
      WaitUntilCookieNonEmpty(data.tab,
                              data.test_url,
                              cookie_name.c_str(),
                              TestTimeouts::action_max_timeout_ms());

  // Unescapes and normalizes the actual result.
  std::string value = UnescapeURLComponent(
      escaped_value,
      UnescapeRule::NORMAL | UnescapeRule::SPACES |
      UnescapeRule::URL_SPECIAL_CHARS | UnescapeRule::CONTROL_CHARS);
  value += "\n";
  ReplaceSubstringsAfterOffset(&value, 0, "\r", "");
  EXPECT_STREQ("1\n", value.c_str());
  EXPECT_TRUE(data.tab->Close(true));
}

// Flaky on XP debug. http://crbug.com/62777
#if defined(OS_WIN)
#define MAYBE_TestIsSearchProviderInstalled FLAKY_TestIsSearchProviderInstalled
#else
#define MAYBE_TestIsSearchProviderInstalled TestIsSearchProviderInstalled
#endif
TEST_F(SearchProviderTest, MAYBE_TestIsSearchProviderInstalled) {
  ASSERT_TRUE(test_server_started_);

  // Use the default search provider, other installed search provider, and
  // one not installed as well. (Note that yahoo isn't tested because the
  // its host name varies a lot for different locales unlike Google and Bing,
  // which would make the test fail depending on the machine's locale.)
  const char* test_hosts[] = { "www.google.com",
                               "www.bing.com",
                               "localhost" };
  const char* expected_results[] = { "2",
                                     "1",
                                     "0" };
  COMPILE_ASSERT(arraysize(test_hosts) == arraysize(expected_results),
                 there_should_be_a_result_for_each_host);
  IsSearchProviderTestData test_data[2 * arraysize(test_hosts)];

  // Start results for the normal mode.
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i] = StartIsSearchProviderInstalledTest(
        browser, test_hosts[i], expected_results[i]);
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }

  // Start tests for incognito mode (and verify the result is 0).
  ASSERT_TRUE(browser->RunCommand(IDC_NEW_INCOGNITO_WINDOW));
  scoped_refptr<BrowserProxy> incognito(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(incognito.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i + arraysize(test_hosts)] = StartIsSearchProviderInstalledTest(
        incognito, test_hosts[i], "0");
    FinishIsSearchProviderInstalledTest(test_data[i + arraysize(test_hosts)]);
  }

  // The following should be re-enabled. At the moment, there are problems with
  // doing all of these queries in parallel -- see http://crbug.com/60043.
#if 0
  // Remove the calls to FinishIsSearchProviderInstalledTest above when
  // re-enabling this code.

  // Do the verification.
  for (size_t i = 0; i < arraysize(test_data); ++i) {
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }
#endif
}

TEST_F(SearchProviderTest, TestIsSearchProviderInstalledWithException) {
  // Change the url for the test page to one that throws an exception when
  // toString is called on the argument given to isSearchProviderInstalled.
  search_provider_test_url_ = test_server_.GetURL(
      "files/is_search_provider_installed_with_exception.html");

  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  FinishIsSearchProviderInstalledTest(StartIsSearchProviderInstalledTest(
      browser, "www.google.com", ""));
}
<|endoftext|>


<commit_before>#pragma once

template <int n> struct Vec {
    float v[n];

    float& operator[]( const int i ) {
        return this->v[i];
    }
};

template <int n>
Vec<n> operator+ ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> sum;
    for ( int i = 0; i < n; i++ ) {
        sum[i] = lhs[i] + rhs[i];
    }
    return sum;
}

template <int n>
Vec<n> operator- ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> difference;
    for ( int i = 0; i < n; i++ ) {
        difference[i] = lhs[i] - rhs[i];
    }
    return difference;
}

template <int n>
Vec<n> operator* ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> product;
    for ( int i = 0; i < n; i++ ) {
        product[i] = lhs[i] * rhs[i];
    }
    return product;
}

template <int n>
Vec<n> operator* ( Vec<n> lhs, float rhs ) {
    Vec<n> scaled;
    for ( int i = 0; i < n; i++ ) {
        scaled[i] = lhs[i] * rhs;
    }
    return scaled;
}

template <int n>
Vec<n> operator* ( float lhs, Vec<n> rhs ) {
    return operator*( rhs, lhs );
}

template <int n>
Vec<n> operator/ ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> quotient;
    for ( int i = 0; i < n; i++ ) {
        quotient[i] = lhs[i] / rhs[i];
    }
    return quotient;
}

template <int n>
Vec<n> operator/ ( Vec<n> lhs, float rhs ) {
    Vec<n> scaled;
    for ( int i = 0; i < n; i++ ) {
        scaled[i] = lhs[i] / rhs;
    }
    return scaled;
}

template<> struct Vec<2> {
    union {
        float v[2];
        struct { float x, y; };
        struct { float r, g; };
    };

    Vec( float x, float y ) {
        this->x = x;
        this->y = y;
    }

    Vec() {
        this->x = 0;
        this->y = 0;
    }

    float& operator[]( const int i ) {
        return this->v[i];
    }
};

template<> struct Vec<3> {
    union {
        float v[3];
        struct { float x, y, z; };
        struct { float r, g, b; };
        Vec<2> xy;
    };

    Vec( float x, float y, float z ) {
        this->x = x;
        this->y = y;
        this->z = z;
    }

    Vec() {
        this->x = 0;
        this->y = 0;
        this->z = 0;
    }

    float& operator[]( const int i ) {
        return this->v[i];
    }
};

template<> struct Vec<4> {
    union {
        float v[4];
        struct { float x, y, z, w; };
        struct { float r, g, b, a; };
        Vec<2> xy;
        Vec<3> xyz;
        Vec<3> rgb;
    };

    Vec( float x, float y, float z, float w ) {
        this->x = x;
        this->y = y;
        this->z = z;
        this->w = w;
    }

    Vec() {
        this->x = 0;
        this->y = 0;
        this->z = 0;
        this->w = 0;
    }

    float& operator[]( const int i ) {
        return this->v[i];
    }
};

typedef Vec<2> Vec2;
typedef Vec<3> Vec3;
typedef Vec<4> Vec4;
typedef Vec<4> Color;
<commit_msg>add len and lensq methods<commit_after>#pragma once

#include <math.h>

template <int n> struct Vec {
    float v[n];

    float& operator[]( const int i ) {
        return this->v[i];
    }

    float lensq() {
        float sum = 0.f;
        for ( int i = 0; i < n; i++ ) {
            sum += this->v[i] * this->v[i];
        }
        return sum;
    }

    float len() {
        return sqrt( this->lensq() );
    }
};

template <int n>
Vec<n> operator+ ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> sum;
    for ( int i = 0; i < n; i++ ) {
        sum[i] = lhs[i] + rhs[i];
    }
    return sum;
}

template <int n>
Vec<n> operator- ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> difference;
    for ( int i = 0; i < n; i++ ) {
        difference[i] = lhs[i] - rhs[i];
    }
    return difference;
}

template <int n>
Vec<n> operator* ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> product;
    for ( int i = 0; i < n; i++ ) {
        product[i] = lhs[i] * rhs[i];
    }
    return product;
}

template <int n>
Vec<n> operator* ( Vec<n> lhs, float rhs ) {
    Vec<n> scaled;
    for ( int i = 0; i < n; i++ ) {
        scaled[i] = lhs[i] * rhs;
    }
    return scaled;
}

template <int n>
Vec<n> operator* ( float lhs, Vec<n> rhs ) {
    return operator*( rhs, lhs );
}

template <int n>
Vec<n> operator/ ( Vec<n> lhs, Vec<n> rhs ) {
    Vec<n> quotient;
    for ( int i = 0; i < n; i++ ) {
        quotient[i] = lhs[i] / rhs[i];
    }
    return quotient;
}

template <int n>
Vec<n> operator/ ( Vec<n> lhs, float rhs ) {
    Vec<n> scaled;
    for ( int i = 0; i < n; i++ ) {
        scaled[i] = lhs[i] / rhs;
    }
    return scaled;
}

template<> struct Vec<2> {
    union {
        float v[2];
        struct { float x, y; };
        struct { float r, g; };
    };

    Vec( float x, float y ) {
        this->x = x;
        this->y = y;
    }

    Vec() {
        this->x = 0;
        this->y = 0;
    }

    float& operator[]( const int i ) {
        return this->v[i];
    }

    float lensq() {
        return this->x * this->x + this->y * this->y;
    }

    float len() {
        return sqrt( this->lensq() );
    }
};

template<> struct Vec<3> {
    union {
        float v[3];
        struct { float x, y, z; };
        struct { float r, g, b; };
        Vec<2> xy;
    };

    Vec( float x, float y, float z ) {
        this->x = x;
        this->y = y;
        this->z = z;
    }

    Vec() {
        this->x = 0;
        this->y = 0;
        this->z = 0;
    }

    float& operator[]( const int i ) {
        return this->v[i];
    }

    float lensq() {
        return this->x * this->x + this->y * this->y + this->z * this->z;
    }

    float len() {
        return sqrt( this->lensq() );
    }
};

template<> struct Vec<4> {
    union {
        float v[4];
        struct { float x, y, z, w; };
        struct { float r, g, b, a; };
        Vec<2> xy;
        Vec<3> xyz;
        Vec<3> rgb;
    };

    Vec( float x, float y, float z, float w ) {
        this->x = x;
        this->y = y;
        this->z = z;
        this->w = w;
    }

    Vec() {
        this->x = 0;
        this->y = 0;
        this->z = 0;
        this->w = 0;
    }

    float& operator[]( const int i ) {
        return this->v[i];
    }

    float lensq() {
        return this->x * this->x + this->y * this->y + this->z * this->z + this->w * this->w;
    }

    float len() {
        return sqrt( this->lensq() );
    }
};

typedef Vec<2> Vec2;
typedef Vec<3> Vec3;
typedef Vec<4> Vec4;
typedef Vec<4> Color;
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
///
/// \file generic.hpp
/// -----------------
///
/// (c) Copyright Domagoj Saric 2016.
///
///  Use, modification and distribution are subject to the
///  Boost Software License, Version 1.0. (See accompanying file
///  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
///  See http://www.boost.org for most recent version.
///
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
#ifndef generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#define generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#pragma once
//------------------------------------------------------------------------------
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/functionoid/functionoid.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <boost/range/iterator_range_core.hpp>

#include <atomic>
#include <cstdint>
#include <future>
#include <memory>
#include <mutex>
#include <thread>
//------------------------------------------------------------------------------
namespace boost
{
//------------------------------------------------------------------------------
namespace sweater
{
//------------------------------------------------------------------------------

#ifndef BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
#	define BOOST_SWEATER_MAX_HARDWARE_CONCURENCY 0
#endif // BOOST_SWEATER_MAX_HARDWARE_CONCURENCY

inline auto hardware_concurency() noexcept { return static_cast<std::uint8_t>( std::thread::hardware_concurrency() ); }

struct impl
{
	impl()
#if BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
    : pool_( BOOST_SWEATER_MAX_HARDWARE_CONCURENCY - 1 )
#endif
	{
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
        auto const number_of_worker_threads( hardware_concurency() - 1 );
        auto p_workers( std::make_unique<worker[]>( number_of_worker_threads ) );
        pool_ = make_iterator_range_n( p_workers.get(), number_of_worker_threads );
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
		for ( auto & worker : pool_ )
		{
			auto const worker_loop
			(
				[&worker, this]() noexcept
				{
                    auto & __restrict my_work_available( worker.have_work );
                    auto & __restrict my_work          ( worker.work  );
                    auto & __restrict my_event         ( worker.event );

                    for ( ; ; )
                    {
                        if ( BOOST_UNLIKELY( brexit_.load( std::memory_order_relaxed ) ) )
							return;

                    #ifdef __ANROID__
                        // https://petewarden.com/2015/10/11/one-weird-trick-for-faster-android-multithreading
                        auto constexpr spin_count( 30 * 1000 * 1000 );
                    #else
                        auto constexpr spin_count(                1 );
                    #endif // __ANROID__

                        for ( auto try_count( 0 ); try_count < spin_count; ++try_count )
                        {
                            if ( BOOST_LIKELY( my_work_available.load( std::memory_order_acquire ) ) )
                            {
                                my_work();
                                my_work.clear();
                                /// \note The
                                /// std::condition_variable::notify_one() call
                                /// below should imply a release so we can get
                                /// away with a relaxed store here.
                                ///           (14.10.2016.) (Domagoj Saric)
                                my_work_available.store( false, std::memory_order_relaxed );
                                my_event.notify_one();
                            }
                        }
                        worker.wait( mutex_ );
                    }
				}
			); // worker_loop
            worker.thread = std::thread( worker_loop );
		}
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
        p_workers.release();
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
	}

	~impl() noexcept
	{
		brexit_.store( true, std::memory_order_relaxed );
		for ( auto & worker : pool_ )
        {
            worker.event .notify_one();
			worker.thread.join      ();
        }
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
        delete[] pool_.begin();
    #endif // BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
	}

	auto number_of_workers() const { return static_cast<std::uint16_t>( pool_.size() + 1 ); }

    /// For GCD dispatch_apply/OMP-like parallel loops
	template <typename F>
	void spread_the_sweat( std::uint16_t const iterations, F && __restrict work ) noexcept
	{
		static_assert( noexcept( work( iterations, iterations ) ), "F must be noexcept" );

		auto const number_of_workers           ( this->number_of_workers() );
		auto const iterations_per_worker       ( iterations / number_of_workers );
		auto const threads_with_extra_iteration( iterations % number_of_workers );

		std::uint16_t iteration( 0 );
		iteration = spread_iterations
		(
			0, threads_with_extra_iteration,
			iteration, iterations_per_worker + 1,
			work
		);
		iteration = spread_iterations
		(
			threads_with_extra_iteration, pool_.size(),
			iteration, iterations_per_worker,
			work
		);

		auto const caller_thread_start_iteration( iteration );
		BOOST_ASSERT( caller_thread_start_iteration <= iterations );
		work( caller_thread_start_iteration, iterations );

		join();
	}

	template <typename F>
	static void fire_and_forget( F && work )
	{
		std::thread( std::forward<F>( work ) ).detach();
	}

    template <typename F>
    static auto dispatch( F && work )
    {
        // http://scottmeyers.blogspot.hr/2013/03/stdfutures-from-stdasync-arent-special.html
        return std::async( std::launch::async | std::launch::deferred, std::forward<F>( work ) );
    }

private:
	void join() noexcept
	{
        for ( auto & worker : pool_ )
        {
            while ( worker.have_work.load( std::memory_order_acquire ) )
                worker.wait( mutex_ );
        }
	}

    template <typename F>
	std::uint16_t spread_iterations
	(
		std::uint8_t const begin_thread,
		std::uint8_t const end_thread,
		std::uint16_t const begin_iteration,
		std::uint16_t const iterations_per_worker,
		F && work
	) noexcept
	{
		auto iteration( begin_iteration );
		for ( auto thread_index( begin_thread ); thread_index < end_thread; ++thread_index )
		{
			auto & delegate( pool_[ thread_index ].work );
            auto const end_iteration( iteration + iterations_per_worker );
            delegate = [&work, start_iteration = iteration, end_iteration]() noexcept
            {
				work( start_iteration, end_iteration );
            };
            pool_[ thread_index ].event.notify_one();
			iteration = end_iteration;
		}
		return iteration;
	}

private:
    std::atomic<bool> brexit_ = ATOMIC_FLAG_INIT;
	std::mutex        mutex_;

    struct worker_traits : functionoid::std_traits
    {
        static constexpr auto copyable    = functionoid::support_level::na    ;
        static constexpr auto moveable    = functionoid::support_level::nofail;
        static constexpr auto destructor  = functionoid::support_level::nofail;
        static constexpr auto is_noexcept = true;
        static constexpr auto rtti        = false;

        using empty_handler = functionoid::assert_on_empty;
    }; // struct worker_traits

    struct worker
    {
        std::atomic<bool>                            have_work = ATOMIC_FLAG_INIT;
        functionoid::callable<void(), worker_traits> work  ;
        std::condition_variable                      event ;
        std::thread                                  thread;

        BOOST_NOINLINE
        void wait( std::mutex & mutex ) noexcept { event.wait( std::unique_lock<std::mutex>( mutex ) ); }
	};
#if BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
	using pool_threads_t = container::static_vector<worker, BOOST_SWEATER_MAX_HARDWARE_CONCURENCY - 1>; // also sweat on the calling thread
#else
    using pool_threads_t = iterator_range<worker *>;
#endif
	pool_threads_t pool_;

    /// \todo Implement a work queue.
    /// https://en.wikipedia.org/wiki/Work_stealing
    /// http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448
    /// https://github.com/cameron314/readerwriterqueue
    /// http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
    /// http://stackoverflow.com/questions/1164023/is-there-a-production-ready-lock-free-queue-or-hash-implementation-in-c#14936831
    /// https://github.com/facebook/folly/blob/master/folly/docs/ProducerConsumerQueue.md
    /// https://github.com/facebook/folly/blob/master/folly/MPMCQueue.h
    /// http://landenlabs.com/code/ring/ring.html
    /// https://github.com/Qarterd/Honeycomb/blob/master/src/common/Honey/Thread/Pool.cpp
    ///                                       (12.10.2016.) (Domagoj Saric)
}; // struct impl

//------------------------------------------------------------------------------
} // namespace sweater
//------------------------------------------------------------------------------
} // namespace boost
//------------------------------------------------------------------------------
#endif // generic_hpp<commit_msg>Fixed a compilation error on non-MSVC compilers.<commit_after>////////////////////////////////////////////////////////////////////////////////
///
/// \file generic.hpp
/// -----------------
///
/// (c) Copyright Domagoj Saric 2016.
///
///  Use, modification and distribution are subject to the
///  Boost Software License, Version 1.0. (See accompanying file
///  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
///  See http://www.boost.org for most recent version.
///
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
#ifndef generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#define generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#pragma once
//------------------------------------------------------------------------------
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/functionoid/functionoid.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <boost/range/iterator_range_core.hpp>

#include <atomic>
#include <cstdint>
#include <future>
#include <memory>
#include <mutex>
#include <thread>
//------------------------------------------------------------------------------
namespace boost
{
//------------------------------------------------------------------------------
namespace sweater
{
//------------------------------------------------------------------------------

#ifndef BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
#	define BOOST_SWEATER_MAX_HARDWARE_CONCURENCY 0
#endif // BOOST_SWEATER_MAX_HARDWARE_CONCURENCY

inline auto hardware_concurency() noexcept { return static_cast<std::uint8_t>( std::thread::hardware_concurrency() ); }

struct impl
{
	impl()
#if BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
    : pool_( BOOST_SWEATER_MAX_HARDWARE_CONCURENCY - 1 )
#endif
	{
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
        auto const number_of_worker_threads( hardware_concurency() - 1 );
        auto p_workers( std::make_unique<worker[]>( number_of_worker_threads ) );
        pool_ = make_iterator_range_n( p_workers.get(), number_of_worker_threads );
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
		for ( auto & worker : pool_ )
		{
			auto const worker_loop
			(
				[&worker, this]() noexcept
				{
                    auto & __restrict my_work_available( worker.have_work );
                    auto & __restrict my_work          ( worker.work  );
                    auto & __restrict my_event         ( worker.event );

                    for ( ; ; )
                    {
                        if ( BOOST_UNLIKELY( brexit_.load( std::memory_order_relaxed ) ) )
							return;

                    #ifdef __ANROID__
                        // https://petewarden.com/2015/10/11/one-weird-trick-for-faster-android-multithreading
                        auto constexpr spin_count( 30 * 1000 * 1000 );
                    #else
                        auto constexpr spin_count(                1 );
                    #endif // __ANROID__

                        for ( auto try_count( 0 ); try_count < spin_count; ++try_count )
                        {
                            if ( BOOST_LIKELY( my_work_available.load( std::memory_order_acquire ) ) )
                            {
                                my_work();
                                my_work.clear();
                                /// \note The
                                /// std::condition_variable::notify_one() call
                                /// below should imply a release so we can get
                                /// away with a relaxed store here.
                                ///           (14.10.2016.) (Domagoj Saric)
                                my_work_available.store( false, std::memory_order_relaxed );
                                my_event.notify_one();
                            }
                        }
                        worker.wait( mutex_ );
                    }
				}
			); // worker_loop
            worker.thread = std::thread( worker_loop );
		}
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
        p_workers.release();
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
	}

	~impl() noexcept
	{
		brexit_.store( true, std::memory_order_relaxed );
		for ( auto & worker : pool_ )
        {
            worker.event .notify_one();
			worker.thread.join      ();
        }
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
        delete[] pool_.begin();
    #endif // BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
	}

	auto number_of_workers() const { return static_cast<std::uint16_t>( pool_.size() + 1 ); }

    /// For GCD dispatch_apply/OMP-like parallel loops
	template <typename F>
	void spread_the_sweat( std::uint16_t const iterations, F && __restrict work ) noexcept
	{
		static_assert( noexcept( work( iterations, iterations ) ), "F must be noexcept" );

		auto const number_of_workers           ( this->number_of_workers() );
		auto const iterations_per_worker       ( iterations / number_of_workers );
		auto const threads_with_extra_iteration( iterations % number_of_workers );

		std::uint16_t iteration( 0 );
		iteration = spread_iterations
		(
			0, threads_with_extra_iteration,
			iteration, iterations_per_worker + 1,
			work
		);
		iteration = spread_iterations
		(
			threads_with_extra_iteration, pool_.size(),
			iteration, iterations_per_worker,
			work
		);

		auto const caller_thread_start_iteration( iteration );
		BOOST_ASSERT( caller_thread_start_iteration <= iterations );
		work( caller_thread_start_iteration, iterations );

		join();
	}

	template <typename F>
	static void fire_and_forget( F && work )
	{
		std::thread( std::forward<F>( work ) ).detach();
	}

    template <typename F>
    static auto dispatch( F && work )
    {
        // http://scottmeyers.blogspot.hr/2013/03/stdfutures-from-stdasync-arent-special.html
        return std::async( std::launch::async | std::launch::deferred, std::forward<F>( work ) );
    }

private:
	void join() noexcept
	{
        for ( auto & worker : pool_ )
        {
            while ( worker.have_work.load( std::memory_order_acquire ) )
                worker.wait( mutex_ );
        }
	}

    template <typename F>
	std::uint16_t spread_iterations
	(
		std::uint8_t const begin_thread,
		std::uint8_t const end_thread,
		std::uint16_t const begin_iteration,
		std::uint16_t const iterations_per_worker,
		F && work
	) noexcept
	{
		auto iteration( begin_iteration );
		for ( auto thread_index( begin_thread ); thread_index < end_thread; ++thread_index )
		{
			auto & delegate( pool_[ thread_index ].work );
            auto const end_iteration( iteration + iterations_per_worker );
            delegate = [&work, start_iteration = iteration, end_iteration]() noexcept
            {
				work( start_iteration, end_iteration );
            };
            pool_[ thread_index ].event.notify_one();
			iteration = end_iteration;
		}
		return iteration;
	}

private:
    std::atomic<bool> brexit_ = ATOMIC_FLAG_INIT;
	std::mutex        mutex_;

    struct worker_traits : functionoid::std_traits
    {
        static constexpr auto copyable    = functionoid::support_level::na    ;
        static constexpr auto moveable    = functionoid::support_level::nofail;
        static constexpr auto destructor  = functionoid::support_level::nofail;
        static constexpr auto is_noexcept = true;
        static constexpr auto rtti        = false;

        using empty_handler = functionoid::assert_on_empty;
    }; // struct worker_traits

    struct worker
    {
        std::atomic<bool>                            have_work = ATOMIC_FLAG_INIT;
        functionoid::callable<void(), worker_traits> work  ;
        std::condition_variable                      event ;
        std::thread                                  thread;

        BOOST_NOINLINE
        void wait( std::mutex & mutex ) noexcept
        {
            std::unique_lock<std::mutex> lock( mutex );
            event.wait( lock );
        }
	};
#if BOOST_SWEATER_MAX_HARDWARE_CONCURENCY
	using pool_threads_t = container::static_vector<worker, BOOST_SWEATER_MAX_HARDWARE_CONCURENCY - 1>; // also sweat on the calling thread
#else
    using pool_threads_t = iterator_range<worker *>;
#endif
	pool_threads_t pool_;

    /// \todo Implement a work queue.
    /// https://en.wikipedia.org/wiki/Work_stealing
    /// http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448
    /// https://github.com/cameron314/readerwriterqueue
    /// http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
    /// http://stackoverflow.com/questions/1164023/is-there-a-production-ready-lock-free-queue-or-hash-implementation-in-c#14936831
    /// https://github.com/facebook/folly/blob/master/folly/docs/ProducerConsumerQueue.md
    /// https://github.com/facebook/folly/blob/master/folly/MPMCQueue.h
    /// http://landenlabs.com/code/ring/ring.html
    /// https://github.com/Qarterd/Honeycomb/blob/master/src/common/Honey/Thread/Pool.cpp
    ///                                       (12.10.2016.) (Domagoj Saric)
}; // struct impl

//------------------------------------------------------------------------------
} // namespace sweater
//------------------------------------------------------------------------------
} // namespace boost
//------------------------------------------------------------------------------
#endif // generic_hpp<|endoftext|>