<commit_before>#include "config.h"

#include "sensor.hpp"

#include "env.hpp"
#include "gpio_handle.hpp"
#include "hwmon.hpp"
#include "sensorset.hpp"
#include "sysfs.hpp"

#include <cmath>
#include <cstring>
#include <filesystem>
#include <phosphor-logging/elog-errors.hpp>
#include <thread>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Sensor/Device/error.hpp>

namespace sensor
{

using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;

// todo: this can be deleted once we move to double
// helper class to set the scale on the value iface only when it's available
template <typename T>
void setScale(T& iface, int64_t value, double)
{
}
template <typename T>
void setScale(T& iface, int64_t value, int64_t)
{
    iface->scale(value);
}

// todo: this can be simplified once we move to the double interface
Sensor::Sensor(const SensorSet::key_type& sensor,
               const hwmonio::HwmonIOInterface* ioAccess,
               const std::string& devPath) :
    _sensor(sensor),
    _ioAccess(ioAccess), _devPath(devPath), _scale(0)
{
    auto chip = env::getEnv("GPIOCHIP", sensor);
    auto access = env::getEnv("GPIO", sensor);
    if (!access.empty() && !chip.empty())
    {
        _handle = gpio::BuildGpioHandle(chip, access);

        if (!_handle)
        {
            log<level::ERR>("Unable to set up gpio locking");
            elog<InternalFailure>();
        }
    }

    auto gain = env::getEnv("GAIN", sensor);
    if (!gain.empty())
    {
        _sensorAdjusts.gain = std::stod(gain);
    }

    auto offset = env::getEnv("OFFSET", sensor);
    if (!offset.empty())
    {
        _sensorAdjusts.offset = std::stoi(offset);
    }
    auto senRmRCs = env::getEnv("REMOVERCS", sensor);
    // Add sensor removal return codes defined per sensor
    addRemoveRCs(senRmRCs);
}

void Sensor::addRemoveRCs(const std::string& rcList)
{
    if (rcList.empty())
    {
        return;
    }

    // Convert to a char* for strtok
    std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
    auto rmRC = std::strtok(&rmRCs[0], ", ");
    while (rmRC != nullptr)
    {
        try
        {
            _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
        }
        catch (const std::logic_error& le)
        {
            // Unable to convert to int, continue to next token
            std::string name = _sensor.first + "_" + _sensor.second;
            log<level::INFO>("Unable to convert sensor removal return code",
                             entry("SENSOR=%s", name.c_str()),
                             entry("RC=%s", rmRC),
                             entry("EXCEPTION=%s", le.what()));
        }
        rmRC = std::strtok(nullptr, ", ");
    }
}

SensorValueType Sensor::adjustValue(SensorValueType value)
{
// Because read doesn't have an out pointer to store errors.
// let's assume negative values are errors if they have this
// set.
#ifdef NEGATIVE_ERRNO_ON_FAIL
    if (value < 0)
    {
        return value;
    }
#endif

    // Adjust based on gain and offset
    value = static_cast<decltype(value)>(static_cast<double>(value) *
                                             _sensorAdjusts.gain +
                                         _sensorAdjusts.offset);

    if constexpr (std::is_same<SensorValueType, double>::value)
    {
        value *= std::pow(10, _scale);
    }

    return value;
}

std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
                                              ObjectInfo& info)
{
    static constexpr bool deferSignals = true;

    // Get the initial value for the value interface.
    auto& bus = *std::get<sdbusplus::bus::bus*>(info);
    auto& obj = std::get<InterfaceMap>(info);
    auto& objPath = std::get<std::string>(info);

    SensorValueType val = 0;
    std::shared_ptr<StatusObject> statusIface = nullptr;
    auto it = obj.find(InterfaceType::STATUS);
    if (it != obj.end())
    {
        statusIface = std::any_cast<std::shared_ptr<StatusObject>>(it->second);
    }

    // If there's no fault file or the sensor has a fault file and
    // its status is functional, read the input value.
    if (!statusIface || (statusIface && statusIface->functional()))
    {
        // RAII object for GPIO unlock / lock
        GpioLock gpioLock(getGpio());

        // Retry for up to a second if device is busy
        // or has a transient error.
        val = _ioAccess->read(_sensor.first, _sensor.second,
                              hwmon::entry::cinput, std::get<size_t>(retryIO),
                              std::get<std::chrono::milliseconds>(retryIO));

        val = adjustValue(val);
    }

    auto iface =
        std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
    iface->value(val);

    hwmon::Attributes attrs;
    if (hwmon::getAttributes(_sensor.first, attrs))
    {
        iface->unit(hwmon::getUnit(attrs));

        setScale(iface, hwmon::getScale(attrs), val);

        _scale = hwmon::getScale(attrs);
    }

    auto maxValue = env::getEnv("MAXVALUE", _sensor);
    if (!maxValue.empty())
    {
        iface->maxValue(std::stoll(maxValue));
    }
    auto minValue = env::getEnv("MINVALUE", _sensor);
    if (!minValue.empty())
    {
        iface->minValue(std::stoll(minValue));
    }

    obj[InterfaceType::VALUE] = iface;
    return iface;
}

std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
{
    namespace fs = std::filesystem;

    std::shared_ptr<StatusObject> iface = nullptr;
    auto& objPath = std::get<std::string>(info);
    auto& obj = std::get<InterfaceMap>(info);

    // Check if fault sysfs file exists
    std::string faultName = _sensor.first;
    std::string faultID = _sensor.second;
    std::string entry = hwmon::entry::fault;

    auto sysfsFullPath =
        sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry);
    if (fs::exists(sysfsFullPath))
    {
        bool functional = true;
        try
        {
            uint32_t fault = _ioAccess->read(faultName, faultID, entry,
                                             hwmonio::retries, hwmonio::delay);
            if (fault != 0)
            {
                functional = false;
            }
        }
        catch (const std::system_error& e)
        {
            using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
                Error;
            using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;

            report<ReadFailure>(
                metadata::CALLOUT_ERRNO(e.code().value()),
                metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));

            log<level::INFO>(
                "Logging failing sysfs file",
                phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
        }

        static constexpr bool deferSignals = true;
        auto& bus = *std::get<sdbusplus::bus::bus*>(info);

        iface =
            std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
        // Set functional property
        iface->functional(functional);

        obj[InterfaceType::STATUS] = iface;
    }

    return iface;
}

GpioLock::GpioLock(const gpioplus::HandleInterface* handle) : _handle(handle)
{
    unlockGpio();
}

GpioLock::~GpioLock()
{
    lockGpio();
}

void GpioLock::unlockGpio()
{
    if (_handle)
    {
        _handle->setValues({1});
        std::this_thread::sleep_for(_pause);
    }
}

void GpioLock::lockGpio()
{
    if (_handle)
    {
        _handle->setValues({0});
    }
}

} // namespace sensor
<commit_msg>sensor: remove redundant condition<commit_after>#include "config.h"

#include "sensor.hpp"

#include "env.hpp"
#include "gpio_handle.hpp"
#include "hwmon.hpp"
#include "sensorset.hpp"
#include "sysfs.hpp"

#include <cmath>
#include <cstring>
#include <filesystem>
#include <phosphor-logging/elog-errors.hpp>
#include <thread>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Sensor/Device/error.hpp>

namespace sensor
{

using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;

// todo: this can be deleted once we move to double
// helper class to set the scale on the value iface only when it's available
template <typename T>
void setScale(T& iface, int64_t value, double)
{
}
template <typename T>
void setScale(T& iface, int64_t value, int64_t)
{
    iface->scale(value);
}

// todo: this can be simplified once we move to the double interface
Sensor::Sensor(const SensorSet::key_type& sensor,
               const hwmonio::HwmonIOInterface* ioAccess,
               const std::string& devPath) :
    _sensor(sensor),
    _ioAccess(ioAccess), _devPath(devPath), _scale(0)
{
    auto chip = env::getEnv("GPIOCHIP", sensor);
    auto access = env::getEnv("GPIO", sensor);
    if (!access.empty() && !chip.empty())
    {
        _handle = gpio::BuildGpioHandle(chip, access);

        if (!_handle)
        {
            log<level::ERR>("Unable to set up gpio locking");
            elog<InternalFailure>();
        }
    }

    auto gain = env::getEnv("GAIN", sensor);
    if (!gain.empty())
    {
        _sensorAdjusts.gain = std::stod(gain);
    }

    auto offset = env::getEnv("OFFSET", sensor);
    if (!offset.empty())
    {
        _sensorAdjusts.offset = std::stoi(offset);
    }
    auto senRmRCs = env::getEnv("REMOVERCS", sensor);
    // Add sensor removal return codes defined per sensor
    addRemoveRCs(senRmRCs);
}

void Sensor::addRemoveRCs(const std::string& rcList)
{
    if (rcList.empty())
    {
        return;
    }

    // Convert to a char* for strtok
    std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
    auto rmRC = std::strtok(&rmRCs[0], ", ");
    while (rmRC != nullptr)
    {
        try
        {
            _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
        }
        catch (const std::logic_error& le)
        {
            // Unable to convert to int, continue to next token
            std::string name = _sensor.first + "_" + _sensor.second;
            log<level::INFO>("Unable to convert sensor removal return code",
                             entry("SENSOR=%s", name.c_str()),
                             entry("RC=%s", rmRC),
                             entry("EXCEPTION=%s", le.what()));
        }
        rmRC = std::strtok(nullptr, ", ");
    }
}

SensorValueType Sensor::adjustValue(SensorValueType value)
{
// Because read doesn't have an out pointer to store errors.
// let's assume negative values are errors if they have this
// set.
#ifdef NEGATIVE_ERRNO_ON_FAIL
    if (value < 0)
    {
        return value;
    }
#endif

    // Adjust based on gain and offset
    value = static_cast<decltype(value)>(static_cast<double>(value) *
                                             _sensorAdjusts.gain +
                                         _sensorAdjusts.offset);

    if constexpr (std::is_same<SensorValueType, double>::value)
    {
        value *= std::pow(10, _scale);
    }

    return value;
}

std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
                                              ObjectInfo& info)
{
    static constexpr bool deferSignals = true;

    // Get the initial value for the value interface.
    auto& bus = *std::get<sdbusplus::bus::bus*>(info);
    auto& obj = std::get<InterfaceMap>(info);
    auto& objPath = std::get<std::string>(info);

    SensorValueType val = 0;
    std::shared_ptr<StatusObject> statusIface = nullptr;
    auto it = obj.find(InterfaceType::STATUS);
    if (it != obj.end())
    {
        statusIface = std::any_cast<std::shared_ptr<StatusObject>>(it->second);
    }

    // If there's no fault file or the sensor has a fault file and
    // its status is functional, read the input value.
    if (!statusIface || statusIface->functional())
    {
        // RAII object for GPIO unlock / lock
        GpioLock gpioLock(getGpio());

        // Retry for up to a second if device is busy
        // or has a transient error.
        val = _ioAccess->read(_sensor.first, _sensor.second,
                              hwmon::entry::cinput, std::get<size_t>(retryIO),
                              std::get<std::chrono::milliseconds>(retryIO));

        val = adjustValue(val);
    }

    auto iface =
        std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
    iface->value(val);

    hwmon::Attributes attrs;
    if (hwmon::getAttributes(_sensor.first, attrs))
    {
        iface->unit(hwmon::getUnit(attrs));

        setScale(iface, hwmon::getScale(attrs), val);

        _scale = hwmon::getScale(attrs);
    }

    auto maxValue = env::getEnv("MAXVALUE", _sensor);
    if (!maxValue.empty())
    {
        iface->maxValue(std::stoll(maxValue));
    }
    auto minValue = env::getEnv("MINVALUE", _sensor);
    if (!minValue.empty())
    {
        iface->minValue(std::stoll(minValue));
    }

    obj[InterfaceType::VALUE] = iface;
    return iface;
}

std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
{
    namespace fs = std::filesystem;

    std::shared_ptr<StatusObject> iface = nullptr;
    auto& objPath = std::get<std::string>(info);
    auto& obj = std::get<InterfaceMap>(info);

    // Check if fault sysfs file exists
    std::string faultName = _sensor.first;
    std::string faultID = _sensor.second;
    std::string entry = hwmon::entry::fault;

    auto sysfsFullPath =
        sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry);
    if (fs::exists(sysfsFullPath))
    {
        bool functional = true;
        try
        {
            uint32_t fault = _ioAccess->read(faultName, faultID, entry,
                                             hwmonio::retries, hwmonio::delay);
            if (fault != 0)
            {
                functional = false;
            }
        }
        catch (const std::system_error& e)
        {
            using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
                Error;
            using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;

            report<ReadFailure>(
                metadata::CALLOUT_ERRNO(e.code().value()),
                metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));

            log<level::INFO>(
                "Logging failing sysfs file",
                phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
        }

        static constexpr bool deferSignals = true;
        auto& bus = *std::get<sdbusplus::bus::bus*>(info);

        iface =
            std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
        // Set functional property
        iface->functional(functional);

        obj[InterfaceType::STATUS] = iface;
    }

    return iface;
}

GpioLock::GpioLock(const gpioplus::HandleInterface* handle) : _handle(handle)
{
    unlockGpio();
}

GpioLock::~GpioLock()
{
    lockGpio();
}

void GpioLock::unlockGpio()
{
    if (_handle)
    {
        _handle->setValues({1});
        std::this_thread::sleep_for(_pause);
    }
}

void GpioLock::lockGpio()
{
    if (_handle)
    {
        _handle->setValues({0});
    }
}

} // namespace sensor
<|endoftext|>


<commit_before>//Evan John Tausif
<commit_msg>added main and last names<commit_after>//Names: Evan Thompson, John Cusick, Tausif Ahmed

#include <cstdlib>
#include <iostream>

int main(){
  std::cout<<"Hello World!"<<std::endl;
  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * DISTRHO Plugin Framework (DPF)
 * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any purpose with
 * or without fee is hereby granted, provided that the above copyright notice and this
 * permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef DISTRHO_MUTEX_HPP_INCLUDED
#define DISTRHO_MUTEX_HPP_INCLUDED

#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# include <winsock2.h>
# include <windows.h>
#endif

#include <pthread.h>

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// Mutex class

class Mutex
{
public:
    /*
     * Constructor.
     */
    Mutex(bool inheritPriority = true) noexcept
        : fMutex()
    {
        pthread_mutexattr_t atts;
        pthread_mutexattr_init(&atts);
        pthread_mutexattr_setprotocol(&atts, inheritPriority ? PTHREAD_PRIO_INHERIT : PTHREAD_PRIO_NONE);
        pthread_mutexattr_settype(&atts, PTHREAD_MUTEX_NORMAL);
        pthread_mutex_init(&fMutex, &atts);
        pthread_mutexattr_destroy(&atts);
    }

    /*
     * Destructor.
     */
    ~Mutex() noexcept
    {
        pthread_mutex_destroy(&fMutex);
    }

    /*
     * Lock the mutex.
     */
    void lock() const noexcept
    {
        pthread_mutex_lock(&fMutex);
    }

    /*
     * Try to lock the mutex.
     * Returns true if successful.
     */
    bool tryLock() const noexcept
    {
        return (pthread_mutex_trylock(&fMutex) == 0);
    }

    /*
     * Unlock the mutex.
     */
    void unlock() const noexcept
    {
        pthread_mutex_unlock(&fMutex);
    }

private:
    mutable pthread_mutex_t fMutex;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(Mutex)
};

// -----------------------------------------------------------------------
// RecursiveMutex class

class RecursiveMutex
{
public:
    /*
     * Constructor.
     */
    RecursiveMutex() noexcept
#ifdef DISTRHO_OS_WINDOWS
        : fSection()
#else
        : fMutex()
#endif
    {
#ifdef DISTRHO_OS_WINDOWS
        InitializeCriticalSection(&fSection);
#else
        pthread_mutexattr_t atts;
        pthread_mutexattr_init(&atts);
        pthread_mutexattr_setprotocol(&atts, PTHREAD_PRIO_INHERIT);
        pthread_mutexattr_settype(&atts, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&fMutex, &atts);
        pthread_mutexattr_destroy(&atts);
#endif
    }

    /*
     * Destructor.
     */
    ~RecursiveMutex() noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        DeleteCriticalSection(&fSection);
#else
        pthread_mutex_destroy(&fMutex);
#endif
    }

    /*
     * Lock the mutex.
     */
    void lock() const noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        EnterCriticalSection(&fSection);
#else
        pthread_mutex_lock(&fMutex);
#endif
    }

    /*
     * Try to lock the mutex.
     * Returns true if successful.
     */
    bool tryLock() const noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        return (TryEnterCriticalSection(&fSection) != FALSE);
#else
        return (pthread_mutex_trylock(&fMutex) == 0);
#endif
    }

    /*
     * Unlock the mutex.
     */
    void unlock() const noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        LeaveCriticalSection(&fSection);
#else
        pthread_mutex_unlock(&fMutex);
#endif
    }

private:
#ifdef DISTRHO_OS_WINDOWS
    mutable CRITICAL_SECTION fSection;
#else
    mutable pthread_mutex_t fMutex;
#endif

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex)
};

// -----------------------------------------------------------------------
// Helper class to lock&unlock a mutex during a function scope.

template <class Mutex>
class ScopeLocker
{
public:
    ScopeLocker(const Mutex& mutex) noexcept
        : fMutex(mutex)
    {
        fMutex.lock();
    }

    ~ScopeLocker() noexcept
    {
        fMutex.unlock();
    }

private:
    const Mutex& fMutex;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker)
};

// -----------------------------------------------------------------------
// Helper class to try-lock&unlock a mutex during a function scope.

template <class Mutex>
class ScopeTryLocker
{
public:
    ScopeTryLocker(const Mutex& mutex) noexcept
        : fMutex(mutex),
          fLocked(mutex.tryLock()) {}

    ~ScopeTryLocker() noexcept
    {
        if (fLocked)
            fMutex.unlock();
    }

    bool wasLocked() const noexcept
    {
        return fLocked;
    }

    bool wasNotLocked() const noexcept
    {
        return !fLocked;
    }

private:
    const Mutex& fMutex;
    const bool   fLocked;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker)
};

// -----------------------------------------------------------------------
// Helper class to unlock&lock a mutex during a function scope.

template <class Mutex>
class ScopeUnlocker
{
public:
    ScopeUnlocker(const Mutex& mutex) noexcept
        : fMutex(mutex)
    {
        fMutex.unlock();
    }

    ~ScopeUnlocker() noexcept
    {
        fMutex.lock();
    }

private:
    const Mutex& fMutex;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker)
};

// -----------------------------------------------------------------------
// Define types

typedef ScopeLocker<Mutex>          MutexLocker;
typedef ScopeLocker<RecursiveMutex> RecursiveMutexLocker;

typedef ScopeTryLocker<Mutex>          MutexTryLocker;
typedef ScopeTryLocker<RecursiveMutex> RecursiveMutexTryLocker;

typedef ScopeUnlocker<Mutex>          MutexUnlocker;
typedef ScopeUnlocker<RecursiveMutex> RecursiveMutexUnlocker;

// -----------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_MUTEX_HPP_INCLUDED
<commit_msg>Add Signal class alongside Mutex<commit_after>/*
 * DISTRHO Plugin Framework (DPF)
 * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any purpose with
 * or without fee is hereby granted, provided that the above copyright notice and this
 * permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef DISTRHO_MUTEX_HPP_INCLUDED
#define DISTRHO_MUTEX_HPP_INCLUDED

#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# include <winsock2.h>
# include <windows.h>
#endif

#include <pthread.h>

START_NAMESPACE_DISTRHO

class Signal;

// -----------------------------------------------------------------------
// Mutex class

class Mutex
{
public:
    /*
     * Constructor.
     */
    Mutex(bool inheritPriority = true) noexcept
        : fMutex()
    {
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setprotocol(&attr, inheritPriority ? PTHREAD_PRIO_INHERIT : PTHREAD_PRIO_NONE);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
        pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
        pthread_mutex_init(&fMutex, &attr);
        pthread_mutexattr_destroy(&attr);
    }

    /*
     * Destructor.
     */
    ~Mutex() noexcept
    {
        pthread_mutex_destroy(&fMutex);
    }

    /*
     * Lock the mutex.
     */
    void lock() const noexcept
    {
        pthread_mutex_lock(&fMutex);
    }

    /*
     * Try to lock the mutex.
     * Returns true if successful.
     */
    bool tryLock() const noexcept
    {
        return (pthread_mutex_trylock(&fMutex) == 0);
    }

    /*
     * Unlock the mutex.
     */
    void unlock() const noexcept
    {
        pthread_mutex_unlock(&fMutex);
    }

private:
    mutable pthread_mutex_t fMutex;

    friend class Signal;
    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(Mutex)
};

// -----------------------------------------------------------------------
// RecursiveMutex class

class RecursiveMutex
{
public:
    /*
     * Constructor.
     */
    RecursiveMutex() noexcept
#ifdef DISTRHO_OS_WINDOWS
        : fSection()
#else
        : fMutex()
#endif
    {
#ifdef DISTRHO_OS_WINDOWS
        InitializeCriticalSection(&fSection);
#else
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&fMutex, &attr);
        pthread_mutexattr_destroy(&attr);
#endif
    }

    /*
     * Destructor.
     */
    ~RecursiveMutex() noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        DeleteCriticalSection(&fSection);
#else
        pthread_mutex_destroy(&fMutex);
#endif
    }

    /*
     * Lock the mutex.
     */
    void lock() const noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        EnterCriticalSection(&fSection);
#else
        pthread_mutex_lock(&fMutex);
#endif
    }

    /*
     * Try to lock the mutex.
     * Returns true if successful.
     */
    bool tryLock() const noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        return (TryEnterCriticalSection(&fSection) != FALSE);
#else
        return (pthread_mutex_trylock(&fMutex) == 0);
#endif
    }

    /*
     * Unlock the mutex.
     */
    void unlock() const noexcept
    {
#ifdef DISTRHO_OS_WINDOWS
        LeaveCriticalSection(&fSection);
#else
        pthread_mutex_unlock(&fMutex);
#endif
    }

private:
#ifdef DISTRHO_OS_WINDOWS
    mutable CRITICAL_SECTION fSection;
#else
    mutable pthread_mutex_t fMutex;
#endif

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex)
};

// -----------------------------------------------------------------------
// Signal class

class Signal
{
public:
    /*
     * Constructor.
     */
    Signal(Mutex& mutex) noexcept
        : fCondition(),
          fMutex(mutex.fMutex)
    {
        pthread_condattr_t attr;
        pthread_condattr_init(&attr);
        pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
        pthread_cond_init(&fCondition, &attr);
        pthread_condattr_destroy(&attr);
    }

    /*
     * Destructor.
     */
    ~Signal() noexcept
    {
        pthread_cond_destroy(&fCondition);
    }

    /*
     * Wait for a broadcast.
     */
    bool wait() noexcept
    {
        try {
            return (pthread_cond_wait(&fCondition, &fMutex) == 0);
        } DISTRHO_SAFE_EXCEPTION_RETURN("pthread_cond_wait", false);
    }

    /*
     * Wake up one waiting thread.
     */
    void signal() noexcept
    {
        pthread_cond_signal(&fCondition);
    }

    /*
     * Wake up all waiting threads.
     */
    void broadcast() noexcept
    {
        pthread_cond_broadcast(&fCondition);
    }

private:
    pthread_cond_t   fCondition;
    pthread_mutex_t& fMutex;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(Signal)
};

// -----------------------------------------------------------------------
// Helper class to lock&unlock a mutex during a function scope.

template <class Mutex>
class ScopeLocker
{
public:
    ScopeLocker(const Mutex& mutex) noexcept
        : fMutex(mutex)
    {
        fMutex.lock();
    }

    ~ScopeLocker() noexcept
    {
        fMutex.unlock();
    }

private:
    const Mutex& fMutex;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker)
};

// -----------------------------------------------------------------------
// Helper class to try-lock&unlock a mutex during a function scope.

template <class Mutex>
class ScopeTryLocker
{
public:
    ScopeTryLocker(const Mutex& mutex) noexcept
        : fMutex(mutex),
          fLocked(mutex.tryLock()) {}

    ~ScopeTryLocker() noexcept
    {
        if (fLocked)
            fMutex.unlock();
    }

    bool wasLocked() const noexcept
    {
        return fLocked;
    }

    bool wasNotLocked() const noexcept
    {
        return !fLocked;
    }

private:
    const Mutex& fMutex;
    const bool   fLocked;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker)
};

// -----------------------------------------------------------------------
// Helper class to unlock&lock a mutex during a function scope.

template <class Mutex>
class ScopeUnlocker
{
public:
    ScopeUnlocker(const Mutex& mutex) noexcept
        : fMutex(mutex)
    {
        fMutex.unlock();
    }

    ~ScopeUnlocker() noexcept
    {
        fMutex.lock();
    }

private:
    const Mutex& fMutex;

    DISTRHO_PREVENT_HEAP_ALLOCATION
    DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker)
};

// -----------------------------------------------------------------------
// Define types

typedef ScopeLocker<Mutex>          MutexLocker;
typedef ScopeLocker<RecursiveMutex> RecursiveMutexLocker;

typedef ScopeTryLocker<Mutex>          MutexTryLocker;
typedef ScopeTryLocker<RecursiveMutex> RecursiveMutexTryLocker;

typedef ScopeUnlocker<Mutex>          MutexUnlocker;
typedef ScopeUnlocker<RecursiveMutex> RecursiveMutexUnlocker;

// -----------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_MUTEX_HPP_INCLUDED
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling -Xclang -verify
// This test verifies that we get nice warning if a method on null ptr object is
// called.
// XFAIL:*
extern "C" int printf(const char* fmt, ...);
class MyClass {
private:
  int a;
public:
  MyClass() : a(1){}
  int getA(){return a;}
};
MyClass* my = 0;
my->getA() // expected-warning {{you are about to dereference null ptr, which probably will lead to seg violation. Do you want to proceed?[y/n]}}

struct AggregatedNull {
  MyClass* m;
  AggregatedNull() : m(0) {}
}

AggregatedNull agrNull;
agrNull.m->getA(); // expected-warning {{you are about to dereference null ptr, which probably will lead to seg violation. Do you want to proceed?[y/n]}}

.q
<commit_msg>Enable the NullDeref Cling test for MethodCalls.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling -Xclang -verify
// This test verifies that we get nice warning if a method on null ptr object is
// called.

extern "C" int printf(const char* fmt, ...);
class MyClass {
private:
  int a;
public:
  MyClass() : a(1){}
  int getA(){return a;}
};
MyClass* my = 0;
my->getA() // expected-warning {{null passed to a callee that requires a non-null argument}}

struct AggregatedNull {
  MyClass* m;
  AggregatedNull() : m(0) {}
}

AggregatedNull agrNull;
agrNull.m->getA(); // expected-warning {{null passed to a callee that requires a non-null argument}}

.q
<|endoftext|>


<commit_before>//
// Copyright (C) Microsoft. All rights reserved.
//

#include "stdafx.h"
#include "Helpers.h"
#include <atlsafe.h>
#include <DocObj.h>
#include <Mshtmhst.h>
#include <ExDisp.h>
#include <webapplication.h>
#include <sstream>

#define IDM_STARTDIAGNOSTICSMODE 3802
#define CP_AUTO 50001
#define VERSION_SIGNATURE 0xFEEF04BD

namespace Helpers
{
    BOOL EnumWindowsHelper(_In_ const function<BOOL(HWND)>& callbackFunc)
    {
        return ::EnumWindows([](HWND hwnd, LPARAM lparam) -> BOOL {
            return (*(function<BOOL(HWND)>*)lparam)(hwnd);
        }, (LPARAM)&callbackFunc);
    }

    BOOL EnumChildWindowsHelper(HWND hwndParent, _In_ const function<BOOL(HWND)>& callbackFunc)
    {
        return EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lparam) -> BOOL {
            return (*(function<BOOL(HWND)>*)lparam)(hwnd);
        }, (LPARAM)&callbackFunc);
    }

    bool IsWindowClass(_In_ const HWND hwnd, _In_ LPCWSTR pszWindowClass)
    {
        if (hwnd && pszWindowClass && *pszWindowClass)
        {
            const int BUFFER_SIZE = 100;
            WCHAR szClassname[BUFFER_SIZE];

            int result = ::GetClassName(hwnd, (LPWSTR)&szClassname, BUFFER_SIZE);
            if (result && _wcsicmp(szClassname, pszWindowClass) == 0)
            {
                return true;
            }
        }

        return false;
    }

    HRESULT GetCurrentModuleWithoutRef(_Out_ HMODULE& moduleOut)
    {
        BOOL result = ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)Helpers::GetCurrentModuleWithoutRef, &moduleOut);
        ATLENSURE_RETURN_HR(result != FALSE, AtlHresultFromLastError());
        return S_OK;
    }

    HRESULT ReadFileFromModule(_In_ LPCWSTR resourceName, _Out_ CString& sFileData)
    {
        HMODULE module;
        Helpers::GetCurrentModuleWithoutRef(module);
        ATLENSURE_RETURN_HR(module, E_FAIL);

        HRSRC hresInfo = ::FindResource(module, resourceName, RT_HTML);
        if (hresInfo != nullptr)
        {
            // Load the resource
            HGLOBAL hGlobal = ::LoadResource(module, hresInfo);
            ATLENSURE_RETURN_HR(hGlobal != nullptr, AtlHresultFromLastError());

            // Get the size of resource
            DWORD size = ::SizeofResource(module, hresInfo);
            ATLENSURE_RETURN_HR(size > 0, AtlHresultFromLastError());

            // Get the raw data
            const BYTE* pbData = (const BYTE*) ::LockResource(hGlobal);
            ATLENSURE_RETURN_HR(pbData != nullptr, E_FAIL);

            // Convert the resource into unicode text
            int nDstBufferSize = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (const CHAR*)pbData, (int)size, nullptr, 0);
            ATLENSURE_RETURN_HR(nDstBufferSize > 0, AtlHresultFromLastError());

            try
            {
                nDstBufferSize = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (const CHAR*)pbData, (int)size, sFileData.GetBuffer(nDstBufferSize), nDstBufferSize);
                sFileData.ReleaseBuffer(nDstBufferSize);
            }
            catch (CAtlException& ex) // GetBuffer out of memory
            {
                return ex.m_hr;
            }
        }

        // Ensure that we actually got some text
        ATLENSURE_RETURN_HR(sFileData.GetLength() > 0, E_UNEXPECTED);

        return S_OK;
    }

    HRESULT ExpandEnvironmentString(_In_z_ LPCWSTR path, _Out_ CString& expandedString)
    {
        ATLENSURE_RETURN_HR(path != nullptr, E_INVALIDARG);

        expandedString.Empty();
        CString correctedFilePath(path);

        DWORD charsNeeded = ::ExpandEnvironmentStrings(correctedFilePath, expandedString.GetBuffer(MAX_PATH), MAX_PATH);
        ATLENSURE_RETURN_HR(charsNeeded > 0, AtlHresultFromLastError());

        if (charsNeeded > MAX_PATH)
        {
            charsNeeded = ::ExpandEnvironmentStrings(path, expandedString.GetBuffer(charsNeeded), charsNeeded);
            ATLENSURE_RETURN_HR(charsNeeded > 0, AtlHresultFromLastError());
        }
        expandedString.ReleaseBufferSetLength(charsNeeded - 1);

        return S_OK;
    }

    bool Is64OS()
    {
#ifdef _WIN64
        return true;  // 64-bit programs run only on Win64
#else
        BOOL is64 = FALSE;

        typedef BOOL(APIENTRY *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);

        LPFN_ISWOW64PROCESS fnIsWow64Process;

        HMODULE mod = ::GetModuleHandle(L"kernel32");
        fnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(mod, "IsWow64Process");

        if (fnIsWow64Process != nullptr)
        {
            fnIsWow64Process(::GetCurrentProcess(), &is64) && is64;
        }

        return !!is64;
#endif
    }

    HRESULT GetDocumentFromSite(_In_ IUnknown* spSite, _Out_ CComPtr<IDispatch>& spDocumentOut)
    {
        ATLENSURE_RETURN_HR(spSite != nullptr, E_INVALIDARG);

        CComQIPtr<IHTMLDocument2> spDocument(spSite);
        if (spDocument.p != nullptr)
        {
            CComQIPtr<IDispatch> spDocDisp(spDocument);
            ATLENSURE_RETURN_HR(spDocDisp.p != nullptr, E_NOINTERFACE);

            spDocumentOut = spDocDisp;
            return S_OK;
        }
        else
        {
            CComQIPtr<IWebBrowser2> spBrowser2(spSite);
            if (spBrowser2 != nullptr)
            {
                CComPtr<IDispatch> spDocDisp;
                HRESULT hr = spBrowser2->get_Document(&spDocDisp);
                FAIL_IF_NOT_S_OK(hr);
                ATLENSURE_RETURN_HR(spDocDisp.p != nullptr, E_FAIL);

                spDocumentOut = spDocDisp;
                return hr;
            }
            else
            {
                CComQIPtr<IWebApplicationHost> webAppHost(spSite);
                if (webAppHost.p != nullptr)
                {
                    CComPtr<IHTMLDocument2> spDoc;
                    HRESULT hr = webAppHost->get_Document(&spDoc);
                    FAIL_IF_NOT_S_OK(hr);
                    CComQIPtr<IDispatch> spDocDisp(spDoc);
                    ATLENSURE_RETURN_HR(spDocDisp.p != nullptr, E_NOINTERFACE);

                    spDocumentOut = spDocDisp;
                    return S_OK;
                }
            }
        }

        return E_NOINTERFACE;
    }

    HRESULT GetDocumentFromHwnd(_In_ const HWND browserHwnd, _Out_ CComPtr<IHTMLDocument2>& spDocument)
    {
        DWORD_PTR messageResult = 0;
        LRESULT sendMessageResult = ::SendMessageTimeoutW(browserHwnd, Helpers::GetHtmlDocumentMessage(), 0L, 0L, SMTO_ABORTIFHUNG, 2000, &messageResult);

        if (sendMessageResult != 0 && messageResult != 0)
        {
            HMODULE* hModule = new HMODULE(::LoadLibraryEx(L"oleacc.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
            shared_ptr<HMODULE> spOleAcc(hModule, [](HMODULE* hModule) { ::FreeLibrary(*hModule); delete hModule; });
            if (spOleAcc)
            {
                auto pfObjectFromLresult = reinterpret_cast<LPFNOBJECTFROMLRESULT>(::GetProcAddress(*spOleAcc.get(), "ObjectFromLresult"));
                if (pfObjectFromLresult != nullptr)
                {
                    return pfObjectFromLresult(messageResult, IID_IHTMLDocument2, 0, reinterpret_cast<void**>(&spDocument.p));
                }
            }
        }

        return E_FAIL;
    }

    UINT GetHtmlDocumentMessage()
    {
        if (s_getHtmlDocumentMessage == 0)
        {
            s_getHtmlDocumentMessage = ::RegisterWindowMessageW(L"WM_HTML_GETOBJECT");
        }

        return s_getHtmlDocumentMessage;
    }

    HRESULT StartDiagnosticsMode(_In_ IHTMLDocument2* pDocument, REFCLSID clsid, _In_ LPCWSTR path, REFIID iid, _COM_Outptr_opt_ void** ppOut)
    {
        ATLENSURE_RETURN_HR(pDocument != nullptr, E_INVALIDARG);

        if (ppOut)
        {
            (*ppOut) = nullptr;
        }

        // Get the target from the document
        CComQIPtr<IOleCommandTarget> spCommandTarget(pDocument);
        ATLENSURE_RETURN_HR(spCommandTarget.p != nullptr, E_INVALIDARG);

        // Setup the diagnostics mode parameters
        HRESULT hr = S_OK;
        CComBSTR guid(clsid);
        CComSafeArray<BSTR> spSafeArray(4);
        hr = spSafeArray.SetAt(0, ::SysAllocString(guid), FALSE);
        FAIL_IF_NOT_S_OK(hr);
        hr = spSafeArray.SetAt(1, ::SysAllocString(path), FALSE);
        FAIL_IF_NOT_S_OK(hr);
        hr = spSafeArray.SetAt(2, ::SysAllocString(L""), FALSE);
        FAIL_IF_NOT_S_OK(hr);
        hr = spSafeArray.SetAt(3, ::SysAllocString(L""), FALSE);
        FAIL_IF_NOT_S_OK(hr);

        // Start diagnostics mode
        CComVariant varParams(spSafeArray);
        CComVariant varSite;
        hr = spCommandTarget->Exec(&CGID_MSHTML, IDM_STARTDIAGNOSTICSMODE, OLECMDEXECOPT_DODEFAULT, &varParams, &varSite);
        FAIL_IF_NOT_S_OK(hr);
        ATLENSURE_RETURN_VAL(varSite.vt == VT_UNKNOWN, E_UNEXPECTED);
        ATLENSURE_RETURN_VAL(varSite.punkVal != nullptr, E_UNEXPECTED);

        // Get the requested interface
        if (ppOut)
        {
            hr = varSite.punkVal->QueryInterface(iid, ppOut);
            FAIL_IF_NOT_S_OK(hr);
        }
        return hr;
    }

    CStringA EscapeJsonString(const CString& value)
    {
        CStringA escapedValue;

        for (auto i = 0; i < value.GetLength(); i++)
        {
            auto c = value[i];
            switch (c)
            {
            case '\\': escapedValue.Append("\\\\"); break;
            case '\"': escapedValue.Append("\\\""); break;
            case '\b': escapedValue.Append("\\b"); break;
            case '\f': escapedValue.Append("\\f"); break;
            case '\n': escapedValue.Append("\\n"); break;
            case '\r': escapedValue.Append("\\r"); break;
            case '\t': escapedValue.Append("\\t"); break;
            default:
                if (c < 0x20 || c > 0x7e)
                {
                    CStringA charLiteral;
                    charLiteral.Format("\\u%04x", c);
                    escapedValue.Append(charLiteral);
                }
                else
                {
                    escapedValue.AppendChar((char)c);
                }
                break;
            }
        }

        return escapedValue;
    }

    CStringA GetFileVersion(_In_ LPCSTR filePath)
    {
        ::SetLastError(0);
        DWORD  verSize = ::GetFileVersionInfoSizeA(filePath, NULL);
        if (::GetLastError() != 0 && verSize == NULL && verSize == 0)
        {
            return NULL;
        }
        std::vector<char> verData(verSize);

        if (!::GetFileVersionInfoA(filePath, NULL, verSize, &verData[0]))
        {
            return NULL;
        }

        VS_FIXEDFILEINFO* verInfo = nullptr;
        UINT sizeOfVersionNumber;
        if (!::VerQueryValueA(&verData[0], "\\", (LPVOID*)&verInfo, &sizeOfVersionNumber))
        {
            return NULL;
        }

        if (sizeOfVersionNumber <= 0)
        {
            return NULL;
        }

        // The signature value should always be 0xFEEF04BD according to MSDN
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx
        // Though checking in case it's not as the bitwise operators below won't work if not
        if (verInfo->dwSignature != VERSION_SIGNATURE)
        {
            return NULL;
        }

        std::stringstream  ss;
        ss << ((verInfo->dwFileVersionMS >> 16) & 0xffff);
        ss << ".";
        ss << ((verInfo->dwFileVersionMS >> 0) & 0xffff);
        ss << ".";
        ss << ((verInfo->dwFileVersionLS >> 16) & 0xffff);
        ss << ".";
        ss << ((verInfo->dwFileVersionLS >> 0) & 0xffff);

        return ss.str().c_str();
    }
}<commit_msg>Update helpers<commit_after>//
// Copyright (C) Microsoft. All rights reserved.
//

#include "stdafx.h"
#include "Helpers.h"
#include <atlsafe.h>
#include <DocObj.h>
#include <Mshtmhst.h>
#include <ExDisp.h>
#include <webapplication.h>
#include <sstream>

#define IDM_STARTDIAGNOSTICSMODE 3802
#define CP_AUTO 50001
#define VERSION_SIGNATURE 0xFEEF04BD

namespace Helpers
{
    BOOL EnumWindowsHelper(_In_ const function<BOOL(HWND)>& callbackFunc)
    {
        return ::EnumWindows([](HWND hwnd, LPARAM lparam) -> BOOL {
            return (*(function<BOOL(HWND)>*)lparam)(hwnd);
        }, (LPARAM)&callbackFunc);
    }

    BOOL EnumChildWindowsHelper(HWND hwndParent, _In_ const function<BOOL(HWND)>& callbackFunc)
    {
        return EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lparam) -> BOOL {
            return (*(function<BOOL(HWND)>*)lparam)(hwnd);
        }, (LPARAM)&callbackFunc);
    }

    bool IsWindowClass(_In_ const HWND hwnd, _In_ LPCWSTR pszWindowClass)
    {
        if (hwnd && pszWindowClass && *pszWindowClass)
        {
            const int BUFFER_SIZE = 100;
            WCHAR szClassname[BUFFER_SIZE];

            int result = ::GetClassName(hwnd, (LPWSTR)&szClassname, BUFFER_SIZE);
            if (result && _wcsicmp(szClassname, pszWindowClass) == 0)
            {
                return true;
            }
        }

        return false;
    }

    HRESULT GetCurrentModuleWithoutRef(_Out_ HMODULE& moduleOut)
    {
        BOOL result = ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)Helpers::GetCurrentModuleWithoutRef, &moduleOut);
        ATLENSURE_RETURN_HR(result != FALSE, AtlHresultFromLastError());
        return S_OK;
    }

    HRESULT ReadFileFromModule(_In_ LPCWSTR resourceName, _Out_ CString& sFileData)
    {
        HMODULE module;
        Helpers::GetCurrentModuleWithoutRef(module);
        ATLENSURE_RETURN_HR(module, E_FAIL);

        HRSRC hresInfo = ::FindResource(module, resourceName, RT_HTML);
        if (hresInfo != nullptr)
        {
            // Load the resource
            HGLOBAL hGlobal = ::LoadResource(module, hresInfo);
            ATLENSURE_RETURN_HR(hGlobal != nullptr, AtlHresultFromLastError());

            // Get the size of resource
            DWORD size = ::SizeofResource(module, hresInfo);
            ATLENSURE_RETURN_HR(size > 0, AtlHresultFromLastError());

            // Get the raw data
            const BYTE* pbData = (const BYTE*) ::LockResource(hGlobal);
            ATLENSURE_RETURN_HR(pbData != nullptr, E_FAIL);

            // Convert the resource into unicode text
            int nDstBufferSize = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (const CHAR*)pbData, (int)size, nullptr, 0);
            ATLENSURE_RETURN_HR(nDstBufferSize > 0, AtlHresultFromLastError());

            try
            {
                nDstBufferSize = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (const CHAR*)pbData, (int)size, sFileData.GetBuffer(nDstBufferSize), nDstBufferSize);
                sFileData.ReleaseBuffer(nDstBufferSize);
            }
            catch (CAtlException& ex) // GetBuffer out of memory
            {
                return ex.m_hr;
            }
        }

        // Ensure that we actually got some text
        ATLENSURE_RETURN_HR(sFileData.GetLength() > 0, E_UNEXPECTED);

        return S_OK;
    }

    HRESULT ExpandEnvironmentString(_In_z_ LPCWSTR path, _Out_ CString& expandedString)
    {
        ATLENSURE_RETURN_HR(path != nullptr, E_INVALIDARG);

        expandedString.Empty();
        CString correctedFilePath(path);

        DWORD charsNeeded = ::ExpandEnvironmentStrings(correctedFilePath, expandedString.GetBuffer(MAX_PATH), MAX_PATH);
        ATLENSURE_RETURN_HR(charsNeeded > 0, AtlHresultFromLastError());

        if (charsNeeded > MAX_PATH)
        {
            charsNeeded = ::ExpandEnvironmentStrings(path, expandedString.GetBuffer(charsNeeded), charsNeeded);
            ATLENSURE_RETURN_HR(charsNeeded > 0, AtlHresultFromLastError());
        }
        expandedString.ReleaseBufferSetLength(charsNeeded - 1);

        return S_OK;
    }

    bool Is64OS()
    {
#ifdef _WIN64
        return true;  // 64-bit programs run only on Win64
#else
        BOOL is64 = FALSE;

        typedef BOOL(APIENTRY *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);

        LPFN_ISWOW64PROCESS fnIsWow64Process;

        HMODULE mod = ::GetModuleHandle(L"kernel32");
        fnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(mod, "IsWow64Process");

        if (fnIsWow64Process != nullptr)
        {
            fnIsWow64Process(::GetCurrentProcess(), &is64) && is64;
        }

        return !!is64;
#endif
    }

    HRESULT GetDocumentFromSite(_In_ IUnknown* spSite, _Out_ CComPtr<IDispatch>& spDocumentOut)
    {
        ATLENSURE_RETURN_HR(spSite != nullptr, E_INVALIDARG);

        CComQIPtr<IHTMLDocument2> spDocument(spSite);
        if (spDocument.p != nullptr)
        {
            CComQIPtr<IDispatch> spDocDisp(spDocument);
            ATLENSURE_RETURN_HR(spDocDisp.p != nullptr, E_NOINTERFACE);

            spDocumentOut = spDocDisp;
            return S_OK;
        }
        else
        {
            CComQIPtr<IWebBrowser2> spBrowser2(spSite);
            if (spBrowser2 != nullptr)
            {
                CComPtr<IDispatch> spDocDisp;
                HRESULT hr = spBrowser2->get_Document(&spDocDisp);
                FAIL_IF_NOT_S_OK(hr);
                ATLENSURE_RETURN_HR(spDocDisp.p != nullptr, E_FAIL);

                spDocumentOut = spDocDisp;
                return hr;
            }
            else
            {
                CComQIPtr<IWebApplicationHost> webAppHost(spSite);
                if (webAppHost.p != nullptr)
                {
                    CComPtr<IHTMLDocument2> spDoc;
                    HRESULT hr = webAppHost->get_Document(&spDoc);
                    FAIL_IF_NOT_S_OK(hr);
                    CComQIPtr<IDispatch> spDocDisp(spDoc);
                    ATLENSURE_RETURN_HR(spDocDisp.p != nullptr, E_NOINTERFACE);

                    spDocumentOut = spDocDisp;
                    return S_OK;
                }
            }
        }

        return E_NOINTERFACE;
    }

    HRESULT GetDocumentFromHwnd(_In_ const HWND browserHwnd, _Out_ CComPtr<IHTMLDocument2>& spDocument)
    {
        DWORD_PTR messageResult = 0;
        LRESULT sendMessageResult = ::SendMessageTimeoutW(browserHwnd, Helpers::GetHtmlDocumentMessage(), 0L, 0L, SMTO_ABORTIFHUNG, 2000, &messageResult);

        if (sendMessageResult != 0 && messageResult != 0)
        {
            HMODULE* hModule = new HMODULE(::LoadLibraryEx(L"oleacc.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
            shared_ptr<HMODULE> spOleAcc(hModule, [](HMODULE* hModule) { ::FreeLibrary(*hModule); delete hModule; });
            if (spOleAcc)
            {
                auto pfObjectFromLresult = reinterpret_cast<LPFNOBJECTFROMLRESULT>(::GetProcAddress(*spOleAcc.get(), "ObjectFromLresult"));
                if (pfObjectFromLresult != nullptr)
                {
                    return pfObjectFromLresult(messageResult, IID_IHTMLDocument2, 0, reinterpret_cast<void**>(&spDocument.p));
                }
            }
        }

        return E_FAIL;
    }

    UINT GetHtmlDocumentMessage()
    {
        if (s_getHtmlDocumentMessage == 0)
        {
            s_getHtmlDocumentMessage = ::RegisterWindowMessageW(L"WM_HTML_GETOBJECT");
        }

        return s_getHtmlDocumentMessage;
    }

    HRESULT StartDiagnosticsMode(_In_ IHTMLDocument2* pDocument, REFCLSID clsid, _In_ LPCWSTR path, REFIID iid, _COM_Outptr_opt_ void** ppOut)
    {
        ATLENSURE_RETURN_HR(pDocument != nullptr, E_INVALIDARG);

        if (ppOut)
        {
            (*ppOut) = nullptr;
        }

        // Get the target from the document
        CComQIPtr<IOleCommandTarget> spCommandTarget(pDocument);
        ATLENSURE_RETURN_HR(spCommandTarget.p != nullptr, E_INVALIDARG);

        // Setup the diagnostics mode parameters
        HRESULT hr = S_OK;
        CComBSTR guid(clsid);
        CComSafeArray<BSTR> spSafeArray(4);
        hr = spSafeArray.SetAt(0, ::SysAllocString(guid), FALSE);
        FAIL_IF_NOT_S_OK(hr);
        hr = spSafeArray.SetAt(1, ::SysAllocString(path), FALSE);
        FAIL_IF_NOT_S_OK(hr);
        hr = spSafeArray.SetAt(2, ::SysAllocString(L""), FALSE);
        FAIL_IF_NOT_S_OK(hr);
        hr = spSafeArray.SetAt(3, ::SysAllocString(L""), FALSE);
        FAIL_IF_NOT_S_OK(hr);

        // Start diagnostics mode
        CComVariant varParams(spSafeArray);
        CComVariant varSite;
        hr = spCommandTarget->Exec(&CGID_MSHTML, IDM_STARTDIAGNOSTICSMODE, OLECMDEXECOPT_DODEFAULT, &varParams, &varSite);
        FAIL_IF_NOT_S_OK(hr);
        ATLENSURE_RETURN_VAL(varSite.vt == VT_UNKNOWN, E_UNEXPECTED);
        ATLENSURE_RETURN_VAL(varSite.punkVal != nullptr, E_UNEXPECTED);

        // Get the requested interface
        if (ppOut)
        {
            hr = varSite.punkVal->QueryInterface(iid, ppOut);
            FAIL_IF_NOT_S_OK(hr);
        }
        return hr;
    }

    CStringA EscapeJsonString(const CString& value)
    {
        CStringA escapedValue;

        for (auto i = 0; i < value.GetLength(); i++)
        {
            auto c = value[i];
            switch (c)
            {
            case '\\': escapedValue.Append("\\\\"); break;
            case '\"': escapedValue.Append("\\\""); break;
            case '\b': escapedValue.Append("\\b"); break;
            case '\f': escapedValue.Append("\\f"); break;
            case '\n': escapedValue.Append("\\n"); break;
            case '\r': escapedValue.Append("\\r"); break;
            case '\t': escapedValue.Append("\\t"); break;
            default:
                if (c < 0x20 || c > 0x7e)
                {
                    CStringA charLiteral;
                    charLiteral.Format("\\u%04x", c);
                    escapedValue.Append(charLiteral);
                }
                else
                {
                    escapedValue.AppendChar((char)c);
                }
                break;
            }
        }

        return escapedValue;
    }

    CStringA GetFileVersion(_In_ LPCSTR filePath)
    {
        ::SetLastError(0);
        DWORD  verSize = ::GetFileVersionInfoSizeA(filePath, NULL);
        if (::GetLastError() != 0 || verSize == NULL || verSize == 0)
        {
            return "";
        }

        std::vector<char> verData(verSize);

        if (!::GetFileVersionInfoA(filePath, NULL, verSize, &verData[0]))
        {
            return "";
        }

        VS_FIXEDFILEINFO* verInfo = 0;
        UINT sizeOfVersionNumber;
        if (!::VerQueryValueA(&verData[0], "\\", (LPVOID*)&verInfo, &sizeOfVersionNumber))
        {
            return "";
        }

        if (sizeOfVersionNumber <= 0)
        {
            return "";
        }

        // The signature value should always be 0xFEEF04BD according to MSDN
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx
        // Though checking in case it's not as the bitwise operators below won't work if not
        if (verInfo->dwSignature != VERSION_SIGNATURE)
        {
            return "";
        }

        std::stringstream ss;
        ss << ((verInfo->dwFileVersionMS >> 16) & 0xffff);
        ss << ".";
        ss << ((verInfo->dwFileVersionMS >> 0) & 0xffff);
        ss << ".";
        ss << ((verInfo->dwFileVersionLS >> 16) & 0xffff);
        ss << ".";
        ss << ((verInfo->dwFileVersionLS >> 0) & 0xffff);

        return ss.str().c_str();
    }
}<|endoftext|>


<commit_before>#include "SoftXMT.hpp"
#include "Addressing.hpp"
#include <iostream>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>

#define SLOG(verboselevel) VLOG(verboselevel) << "<" << SoftXMT_mynode() << "> "

#define min(A,B) ( (A) < (B) ? (A) : (B))

DEFINE_int64(max_forkjoin_threads_per_node, 256, "maximum number of threads to spawn for a fork-join region");

struct range_t { int64_t start, end; };

static range_t blockDist(int64_t start, int64_t end, int64_t rank, int64_t numBlocks) {
	int64_t numElems = end-start;
	int64_t each   = numElems / numBlocks,
  remain = numElems % numBlocks;
	int64_t mynum = (rank < remain) ? each+1 : each;
	int64_t mystart = (rank < remain) ? (each+1)*rank : (each+1)*remain + (rank-remain)*each;
	range_t r = { mystart, mystart+mynum };
  return r;
}

class Semaphore {
protected:
  thread * sleeper;
  int count;
  int total;
public:
  Semaphore(int total, int starting): total(total), count(starting) {}
  void acquire_all(thread * me) {
    sleeper = me;
    while (count < total) {
      SLOG(2) << "Semaphore.count = " << count << " of " << total << ", suspending...";
      SoftXMT_suspend();
    }
  }
  void release(int n=1) {
    count += n;
    if (count >= total) {
      SoftXMT_wake(sleeper);
    }
  }
  static void am_release(GlobalAddress<Semaphore>* gaddr, size_t sz, void* payload, size_t psz) {
    SLOG(2) << "in am_release()";
    assert(gaddr->node() == SoftXMT_mynode());
    int n = (int)reinterpret_cast<int64_t>(payload);
    gaddr->pointer()->release(n);
  }
  static void release(GlobalAddress<Semaphore>* gaddr, int n) {
    int64_t nn = n;
    SLOG(2) << "about to call on " << gaddr->node();
    SoftXMT_call_on(gaddr->node(), &Semaphore::am_release, gaddr, sizeof(GlobalAddress<Semaphore>), reinterpret_cast<void*>(nn), 0);
  }
};

struct ForkJoinIteration {
  void operator()(thread * me, int64_t index);
};

template<typename T>
struct NodeForkJoinArgs {
  BOOST_STATIC_ASSERT((boost::is_base_of<ForkJoinIteration, T>::value));
  size_t start;
  size_t end;
  T func;
  GlobalAddress<Semaphore> sem;
};

template<typename T>
struct forkjoin_data_t {
  T* func;
  size_t nthreads;
  size_t finished;
  thread * node_th;
  size_t local_start;
  size_t local_end;

  forkjoin_data_t(thread * me, T* f, int64_t start, int64_t end) {
    size_t each_n = (end-start); // / SoftXMT_nodes();
    local_start = start; //start + SoftXMT_mynode() * each_n;
    local_end = end; //local_start + each_n;
    func = f;
    nthreads = min(FLAGS_max_forkjoin_threads_per_node, each_n);
    finished = 0;
    node_th = me;
  }
};

struct iters_args {
  size_t rank;
  void* fjdata;
};

template<typename T>
static void th_iters(thread * me, iters_args* arg) {
  forkjoin_data_t<T> * fj = static_cast<forkjoin_data_t<T>*>(arg->fjdata);
  range_t myblock = blockDist(fj->local_start, fj->local_end, arg->rank, fj->nthreads);

  for (int64_t i=myblock.start; i < myblock.end; i++) {
    (*fj->func)(me, i);
  }
  fj->finished++;
  if (fj->finished == fj->nthreads) {
    SoftXMT_wake(fj->node_th);
  }
}

template<typename T>
static void fork_join_onenode(thread * spawner, T* func, int64_t start, int64_t end) {
  forkjoin_data_t<T> fj(spawner, func, start, end);
  iters_args args[fj.nthreads];

  for (int i=0; i<fj.nthreads; i++) {
    args[i].fjdata = &fj;
    args[i].rank = i;

    SoftXMT_template_spawn(&th_iters<T>, &args[i]);
  }
  while (fj.finished < fj.nthreads) SoftXMT_suspend();
}

template<typename T>
static void th_node_fork_join(thread * me, NodeForkJoinArgs<T>* a) {
  range_t myblock = blockDist(a->start, a->end, SoftXMT_mynode(), SoftXMT_nodes());
  fork_join_onenode(me, &a->func, myblock.start, myblock.end);

  SLOG(2) << "about to update sem on " << a->sem.node();
  Semaphore::release(&a->sem, 1);
}

template<typename T>
static void fork_join(thread * me, T* func, int64_t start, int64_t end) {
  Semaphore sem(SoftXMT_nodes(), 0);

  NodeForkJoinArgs<T> fj;
  fj.start = start;
  fj.end = end;
  fj.func = *func;
  fj.sem = make_global(&sem);

  for (int i=0; i < SoftXMT_nodes(); i++) {
    SoftXMT_remote_spawn(&th_node_fork_join, &fj, i);
    SoftXMT_flush(i); // TODO: remove this?
  }
  SLOG(2) << "waiting to acquire all";
  sem.acquire_all(me);

  SLOG(2) << "fork_join done";
}

<commit_msg>Fixed forkjoin to have correct indexes, and send release value correctly.<commit_after>#include "SoftXMT.hpp"
#include "Addressing.hpp"
#include <iostream>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>

#define SLOG(verboselevel) VLOG(verboselevel) << "<" << SoftXMT_mynode() << "> "

#define min(A,B) ( (A) < (B) ? (A) : (B))

DEFINE_int64(max_forkjoin_threads_per_node, 256, "maximum number of threads to spawn for a fork-join region");

struct range_t { int64_t start, end; };

static range_t blockDist(int64_t start, int64_t end, int64_t rank, int64_t numBlocks) {
	int64_t numElems = end-start;
	int64_t each   = numElems / numBlocks,
  remain = numElems % numBlocks;
	int64_t mynum = (rank < remain) ? each+1 : each;
	int64_t mystart = start + ((rank < remain) ? (each+1)*rank : (each+1)*remain + (rank-remain)*each);
	range_t r = { mystart, mystart+mynum };
  return r;
}

class Semaphore {
protected:
  thread * sleeper;
  int count;
  int total;
public:
  Semaphore(int total, int starting): total(total), count(starting) {}
  void acquire_all(thread * me) {
    sleeper = me;
    while (count < total) {
      SLOG(2) << "Semaphore.count = " << count << " of " << total << ", suspending...";
      SoftXMT_suspend();
    }
  }
  void release(int n=1) {
    count += n;
    if (count >= total) {
      SoftXMT_wake(sleeper);
    }
  }
  static void am_release(GlobalAddress<Semaphore>* gaddr, size_t sz, void* payload, size_t psz) {
    SLOG(2) << "in am_release()";
    assert(gaddr->node() == SoftXMT_mynode());
//    int64_t n = reinterpret_cast<int64_t>(payload);
    int n = (int)*((int64_t*)payload);
    SLOG(2) << "am_release n=" << n;
    gaddr->pointer()->release((int)n);
  }
  static void release(GlobalAddress<Semaphore>* gaddr, int n) {
//    int64_t nn = n;
    SLOG(2) << "about to call on " << gaddr->node();
//    SoftXMT_call_on(gaddr->node(), &Semaphore::am_release, gaddr, sizeof(GlobalAddress<Semaphore>), reinterpret_cast<void*>(nn), 0);
    SoftXMT_call_on(gaddr->node(), &Semaphore::am_release, gaddr, sizeof(GlobalAddress<Semaphore>), &n, sizeof(int64_t));
  }
};

struct ForkJoinIteration {
  void operator()(thread * me, int64_t index);
};

template<typename T>
struct NodeForkJoinArgs {
  BOOST_STATIC_ASSERT((boost::is_base_of<ForkJoinIteration, T>::value));
  size_t start;
  size_t end;
  T func;
  GlobalAddress<Semaphore> sem;
};

template<typename T>
struct forkjoin_data_t {
  T* func;
  size_t nthreads;
  size_t finished;
  thread * node_th;
  size_t local_start;
  size_t local_end;

  forkjoin_data_t(thread * me, T* f, int64_t start, int64_t end) {
    size_t each_n = (end-start); // / SoftXMT_nodes();
    local_start = start; //start + SoftXMT_mynode() * each_n;
    local_end = end; //local_start + each_n;
    func = f;
    nthreads = min(FLAGS_max_forkjoin_threads_per_node, each_n);
    finished = 0;
    node_th = me;
  }
};

struct iters_args {
  size_t rank;
  void* fjdata;
};

template<typename T>
static void th_iters(thread * me, iters_args* arg) {
  forkjoin_data_t<T> * fj = static_cast<forkjoin_data_t<T>*>(arg->fjdata);
  range_t myblock = blockDist(fj->local_start, fj->local_end, arg->rank, fj->nthreads);
  SLOG(2) << "iters_block: " << myblock.start << " - " << myblock.end;

  for (int64_t i=myblock.start; i < myblock.end; i++) {
    (*fj->func)(me, i);
  }
  fj->finished++;
  if (fj->finished == fj->nthreads) {
    SoftXMT_wake(fj->node_th);
  }
}

template<typename T>
static void fork_join_onenode(thread * spawner, T* func, int64_t start, int64_t end) {
  forkjoin_data_t<T> fj(spawner, func, start, end);
  iters_args args[fj.nthreads];

  for (int i=0; i<fj.nthreads; i++) {
    args[i].fjdata = &fj;
    args[i].rank = i;

    SoftXMT_template_spawn(&th_iters<T>, &args[i]);
  }
  while (fj.finished < fj.nthreads) SoftXMT_suspend();
}

template<typename T>
static void th_node_fork_join(thread * me, NodeForkJoinArgs<T>* a) {
  range_t myblock = blockDist(a->start, a->end, SoftXMT_mynode(), SoftXMT_nodes());
  SLOG(2) << "myblock: " << myblock.start << " - " << myblock.end;
  fork_join_onenode(me, &a->func, myblock.start, myblock.end);

  SLOG(2) << "about to update sem on " << a->sem.node();
  Semaphore::release(&a->sem, 1);
}

template<typename T>
static void fork_join(thread * me, T* func, int64_t start, int64_t end) {
  Semaphore sem(SoftXMT_nodes(), 0);

  NodeForkJoinArgs<T> fj;
  fj.start = start;
  fj.end = end;
  fj.func = *func;
  fj.sem = make_global(&sem);

  for (int i=0; i < SoftXMT_nodes(); i++) {
    SoftXMT_remote_spawn(&th_node_fork_join, &fj, i);
    SoftXMT_flush(i); // TODO: remove this?
  }
  SLOG(2) << "waiting to acquire all";
  sem.acquire_all(me);

  SLOG(2) << "fork_join done";
}

<|endoftext|>


<commit_before>/* ************************************************************************
 * Copyright 2016 Advanced Micro Devices, Inc.
 *
 * ************************************************************************ */

#include <gtest/gtest.h>
#include <math.h>
#include <stdexcept>
#include <vector>
#include "testing_gemm.hpp"
#include "utility.h"

using ::testing::TestWithParam;
using ::testing::Values;
using ::testing::ValuesIn;
using ::testing::Combine;
using namespace std;

//only GCC/VS 2010 comes with std::tr1::tuple, but it is unnecessary,  std::tuple is good enough;

typedef std::tuple<vector<int>, vector<double>, vector<char>> gemm_tuple;

/* =====================================================================
README: This file contains testers to verify the correctness of
        BLAS routines with google test

        It is supposed to be played/used by advance / expert users
        Normal users only need to get the library routines without testers
     =================================================================== */

/* =====================================================================
Advance users only: BrainStorm the parameters but do not make artificial one which invalidates the matrix.
like lda pairs with M, and "lda must >= M". case "lda < M" will be guarded by argument-checkers inside API of course.
Yet, the goal of this file is to verify result correctness not argument-checkers.

Representative sampling is sufficient, endless brute-force sampling is not necessary
=================================================================== */

//vector of vector, each vector is a {M, N, K, lda, ldb, ldc};
//add/delete as a group
const
vector<vector<int>> matrix_size_range = {
                                        {-1, -1, -1, -1, 1, 1},
                                        { 3, 33,  3,  3, 3, 3},
                                        { 5,  5,  5,  5, 5, 5},
                                        {10, 10, 20, 100, 10, 10},
                                        {600,500, 500, 500, 600, 500},
                                        {1024, 1024, 1024, 1024, 1024, 1024}
                                       };

const
vector<vector<int>> full_matrix_size_range = {
                                        {192, 192, 192, 192, 192, 192},
                                        {640, 640, 640, 640, 640, 640},
                                        {1000, 1000, 1000, 1000, 1000, 1000},
                                        {4011, 4011, 4011, 4011, 4011, 4011},
                                       };

//vector of vector, each pair is a {alpha, beta};
//add/delete this list in pairs, like {2.0, 4.0}
const
vector<vector<double>> alpha_beta_range = { {1.0, 0.0},
                                          };

const
vector<vector<double>> full_alpha_beta_range = {
                                            {1.0, 0.0},
                                            {-1.0, -1.0},
                                            {2.0, 1.0},
                                            {0.0, 1.0}
                                          };

//vector of vector, each pair is a {transA, transB};
//add/delete this list in pairs, like {'N', 'T'}
//for single/double precision, 'C'(conjTranspose) will downgraded to 'T' (transpose) internally in sgemm/dgemm,
const
vector<vector<char>> transA_transB_range = {
                                        {'N', 'N'},
                                        {'N', 'T'},
                                        {'C', 'N'},
                                        {'T', 'C'}
                                       };

/* ===============Google Unit Test==================================================== */

/* =====================================================================
     BLAS-3 GEMM:
=================================================================== */

/* ============================Setup Arguments======================================= */

//Please use "class Arguments" (see utility.hpp) to pass parameters to templated testers;
//Some routines may not touch/use certain "members" of objects "argus".
//like BLAS-1 Scal does not have lda, BLAS-2 GEMV does not have ldb, ldc;
//That is fine. These testers & routines will leave untouched members alone.
//Do not use std::tuple to directly pass parameters to testers
//by std:tuple, you have unpack it with extreme care for each one by like "std::get<0>" which is not intuitive and error-prone

Arguments setup_gemm_arguments(gemm_tuple tup)
{

    vector<int> matrix_size = std::get<0>(tup);
    vector<double> alpha_beta = std::get<1>(tup);
    vector<char> transA_transB = std::get<2>(tup);

    Arguments arg;

    // see the comments about matrix_size_range above
    arg.M = matrix_size[0];
    arg.N = matrix_size[1];
    arg.K = matrix_size[2];
    arg.lda = matrix_size[3];
    arg.ldb = matrix_size[4];
    arg.ldc = matrix_size[5];

    //the first element of alpha_beta_range is always alpha, and the second is always beta
    arg.alpha = alpha_beta[0];
    arg.beta = alpha_beta[1];

    arg.transA_option = transA_transB[0];
    arg.transB_option = transA_transB[1];

    arg.timing = 0;

    return arg;
}

class gemm_gtest: public :: TestWithParam <gemm_tuple>
{
    protected:
        gemm_gtest(){}
        virtual ~gemm_gtest(){}
        virtual void SetUp(){}
        virtual void TearDown(){}
};

TEST_P(gemm_gtest, gemm_gtest_float)
{
    // GetParam return a tuple. Tee setup routine unpack the tuple
    // and initializes arg(Arguments) which will be passed to testing routine
    // The Arguments data struture have physical meaning associated.
    // while the tuple is non-intuitive.

    Arguments arg = setup_gemm_arguments( GetParam() );

    rocblas_status status = testing_gemm<float>( arg );

    // if not success, then the input argument is problematic, so detect the error message
    if(status != rocblas_status_success){

        if( arg.M < 0 || arg.N < 0 || arg.K < 0 ){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transA_option == 'N' ? arg.lda < arg.M : arg.lda < arg.K){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transB_option == 'N' ? arg.ldb < arg.K : arg.ldb < arg.N){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.ldc < arg.M){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
    }

}

TEST_P(gemm_gtest, gemm_gtest_double)
{
    // GetParam return a tuple. Tee setup routine unpack the tuple
    // and initializes arg(Arguments) which will be passed to testing routine
    // The Arguments data struture have physical meaning associated.
    // while the tuple is non-intuitive.

    Arguments arg = setup_gemm_arguments( GetParam() );

    rocblas_status status = testing_gemm<double>( arg );

    // if not success, then the input argument is problematic, so detect the error message
    if(status != rocblas_status_success){

        if( arg.M < 0 || arg.N < 0 || arg.K < 0 ){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transA_option == 'N' ? arg.lda < arg.M : arg.lda < arg.K){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transB_option == 'N' ? arg.ldb < arg.K : arg.ldb < arg.N){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.ldc < arg.M){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
    }

}

//notice we are using vector of vector
//so each elment in xxx_range is a avector,
//ValuesIn take each element (a vector) and combine them and feed them to test_p
// The combinations are  { {M, N, K, lda, ldb, ldc}, {alpha, beta}, {transA, transB} }

//THis function mainly test the scope of matrix_size. the scope of alpha_beta, transA_transB is small
INSTANTIATE_TEST_CASE_P(rocblas_gemm_matrix_size,
                        gemm_gtest,
                        Combine(
                                  ValuesIn(full_matrix_size_range), ValuesIn(alpha_beta_range), ValuesIn(transA_transB_range)
                               )
                        );

//THis function mainly test the scope of alpha_beta, transA_transB,.the scope of matrix_size_range is small

INSTANTIATE_TEST_CASE_P(rocblas_gemm_scalar_transpose,
                        gemm_gtest,
                        Combine(
                                  ValuesIn(matrix_size_range), ValuesIn(full_alpha_beta_range), ValuesIn(transA_transB_range)
                               )
                        );

<commit_msg>remove advanced users only comment<commit_after>/* ************************************************************************
 * Copyright 2016 Advanced Micro Devices, Inc.
 *
 * ************************************************************************ */

#include <gtest/gtest.h>
#include <math.h>
#include <stdexcept>
#include <vector>
#include "testing_gemm.hpp"
#include "utility.h"

using ::testing::TestWithParam;
using ::testing::Values;
using ::testing::ValuesIn;
using ::testing::Combine;
using namespace std;

//only GCC/VS 2010 comes with std::tr1::tuple, but it is unnecessary,  std::tuple is good enough;

typedef std::tuple<vector<int>, vector<double>, vector<char>> gemm_tuple;

/* =====================================================================
README: This file contains testers to verify the correctness of
        BLAS routines with google test

        It is supposed to be played/used by advance / expert users
        Normal users only need to get the library routines without testers
     =================================================================== */

//vector of vector, each vector is a {M, N, K, lda, ldb, ldc};
//add/delete as a group
const
vector<vector<int>> matrix_size_range = {
                                        {-1, -1, -1, -1, 1, 1},
                                        { 3, 33,  3,  3, 3, 3},
                                        { 5,  5,  5,  5, 5, 5},
                                        {10, 10, 20, 100, 10, 10},
                                        {600,500, 500, 500, 600, 500},
                                        {1024, 1024, 1024, 1024, 1024, 1024}
                                       };

const
vector<vector<int>> full_matrix_size_range = {
                                        {192, 192, 192, 192, 192, 192},
                                        {640, 640, 640, 640, 640, 640},
                                        {1000, 1000, 1000, 1000, 1000, 1000},
                                        {4011, 4011, 4011, 4011, 4011, 4011},
                                       };

//vector of vector, each pair is a {alpha, beta};
//add/delete this list in pairs, like {2.0, 4.0}
const
vector<vector<double>> alpha_beta_range = { {1.0, 0.0},
                                          };

const
vector<vector<double>> full_alpha_beta_range = {
                                            {1.0, 0.0},
                                            {-1.0, -1.0},
                                            {2.0, 1.0},
                                            {0.0, 1.0}
                                          };

//vector of vector, each pair is a {transA, transB};
//add/delete this list in pairs, like {'N', 'T'}
//for single/double precision, 'C'(conjTranspose) will downgraded to 'T' (transpose) internally in sgemm/dgemm,
const
vector<vector<char>> transA_transB_range = {
                                        {'N', 'N'},
                                        {'N', 'T'},
                                        {'C', 'N'},
                                        {'T', 'C'}
                                       };

/* ===============Google Unit Test==================================================== */

/* =====================================================================
     BLAS-3 GEMM:
=================================================================== */

/* ============================Setup Arguments======================================= */

//Please use "class Arguments" (see utility.hpp) to pass parameters to templated testers;
//Some routines may not touch/use certain "members" of objects "argus".
//like BLAS-1 Scal does not have lda, BLAS-2 GEMV does not have ldb, ldc;
//That is fine. These testers & routines will leave untouched members alone.
//Do not use std::tuple to directly pass parameters to testers
//by std:tuple, you have unpack it with extreme care for each one by like "std::get<0>" which is not intuitive and error-prone

Arguments setup_gemm_arguments(gemm_tuple tup)
{

    vector<int> matrix_size = std::get<0>(tup);
    vector<double> alpha_beta = std::get<1>(tup);
    vector<char> transA_transB = std::get<2>(tup);

    Arguments arg;

    // see the comments about matrix_size_range above
    arg.M = matrix_size[0];
    arg.N = matrix_size[1];
    arg.K = matrix_size[2];
    arg.lda = matrix_size[3];
    arg.ldb = matrix_size[4];
    arg.ldc = matrix_size[5];

    //the first element of alpha_beta_range is always alpha, and the second is always beta
    arg.alpha = alpha_beta[0];
    arg.beta = alpha_beta[1];

    arg.transA_option = transA_transB[0];
    arg.transB_option = transA_transB[1];

    arg.timing = 0;

    return arg;
}

class gemm_gtest: public :: TestWithParam <gemm_tuple>
{
    protected:
        gemm_gtest(){}
        virtual ~gemm_gtest(){}
        virtual void SetUp(){}
        virtual void TearDown(){}
};

TEST_P(gemm_gtest, gemm_gtest_float)
{
    // GetParam return a tuple. Tee setup routine unpack the tuple
    // and initializes arg(Arguments) which will be passed to testing routine
    // The Arguments data struture have physical meaning associated.
    // while the tuple is non-intuitive.

    Arguments arg = setup_gemm_arguments( GetParam() );

    rocblas_status status = testing_gemm<float>( arg );

    // if not success, then the input argument is problematic, so detect the error message
    if(status != rocblas_status_success){

        if( arg.M < 0 || arg.N < 0 || arg.K < 0 ){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transA_option == 'N' ? arg.lda < arg.M : arg.lda < arg.K){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transB_option == 'N' ? arg.ldb < arg.K : arg.ldb < arg.N){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.ldc < arg.M){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
    }

}

TEST_P(gemm_gtest, gemm_gtest_double)
{
    // GetParam return a tuple. Tee setup routine unpack the tuple
    // and initializes arg(Arguments) which will be passed to testing routine
    // The Arguments data struture have physical meaning associated.
    // while the tuple is non-intuitive.

    Arguments arg = setup_gemm_arguments( GetParam() );

    rocblas_status status = testing_gemm<double>( arg );

    // if not success, then the input argument is problematic, so detect the error message
    if(status != rocblas_status_success){

        if( arg.M < 0 || arg.N < 0 || arg.K < 0 ){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transA_option == 'N' ? arg.lda < arg.M : arg.lda < arg.K){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.transB_option == 'N' ? arg.ldb < arg.K : arg.ldb < arg.N){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
        else if(arg.ldc < arg.M){
            EXPECT_EQ(rocblas_status_invalid_size, status);
        }
    }

}

//notice we are using vector of vector
//so each elment in xxx_range is a avector,
//ValuesIn take each element (a vector) and combine them and feed them to test_p
// The combinations are  { {M, N, K, lda, ldb, ldc}, {alpha, beta}, {transA, transB} }

//THis function mainly test the scope of matrix_size. the scope of alpha_beta, transA_transB is small
INSTANTIATE_TEST_CASE_P(rocblas_gemm_matrix_size,
                        gemm_gtest,
                        Combine(
                                  ValuesIn(full_matrix_size_range), ValuesIn(alpha_beta_range), ValuesIn(transA_transB_range)
                               )
                        );

//THis function mainly test the scope of alpha_beta, transA_transB,.the scope of matrix_size_range is small

INSTANTIATE_TEST_CASE_P(rocblas_gemm_scalar_transpose,
                        gemm_gtest,
                        Combine(
                                  ValuesIn(matrix_size_range), ValuesIn(full_alpha_beta_range), ValuesIn(transA_transB_range)
                               )
                        );

<|endoftext|>


<commit_before>//Includes/namespaces
#include <Rcpp.h>
using namespace Rcpp;

//Generalised NumericVector expander
// [[Rcpp::export]]
NumericVector numeric_vector_expander(NumericVector x, int to_insert, bool start = false) {

  //Calculate existing vector's size, create dummy output
  int existing_size = x.size();
  NumericVector holding_out(1);

  //If the existing vector's first value is zero, sod it, just return to_insert as a NumericVector
  if( (existing_size == 0) | ((existing_size == 1) & (x[0] == 0))){

    holding_out[0] = to_insert;

  } else {//Otherwise...

    //Create new vector
    NumericVector holding_out((existing_size+1));

    //If we need to append to_insert to the start, cool! Otherwise, the end.
    if(start){

      holding_out[0] = to_insert;
      holding_out[seq(1,(existing_size))] = x;

    } else {

      holding_out[seq(0,(existing_size-1))] = x;
      holding_out[existing_size] = to_insert;
    }

    //Return
    return holding_out;
  }

}
//' @title
//' session_count
//' @description
//' Session counting function
//'
//' @param x a vector of intertime values
//'
//' @param local_minimum the threshold (in seconds) to split out a new session on. Set to 3600
//' by default.
//'
//' @details
//' \code{session_count} takes a vector of intertime values (generated via \code{\link{intertimes}},
//' or in any other way you see fit) and returns the total number of sessions within that dataset.
//' It's implimented in C++, providing a (small) increase in speed over the R equivalent.
//'
//' @seealso
//' \code{\link{intertimes}}, for generating inter-time values,or \code{\link{session_length}} for
//' session length, in seconds.
//'
//' @export
// [[Rcpp::export]]
int session_count(NumericVector x, int local_minimum = 3600) {

  //Create output object
  int count = 1;

  //Loop.
  for(int i = 0; i < x.size(); i++) {

    //If the value is greater than the local minimum, increment the count
    if(x[i] > local_minimum){
      count ++;
    }

  }

  //Return
  return count;
}

//'@title
//'session_length
//'
//'@description
//'Session counting function
//'
//'@param intertimes a vector of inter-time values.
//'
//'@param local_minimum the threshold (in seconds) to split out a new session on. Set to 3600
//' by default.
//'
//'@details
//'\code{session_length} takes a vector of intertime values (generated via \code{\link{intertimes}},
//'or in any other way you see fit), splits them into sessions, and calculates the approximate
//'length (in seconds) of each session. It's implemented in C++ since, even with the hinkiness
//'of my C++, it's still several OoM faster than the equivalent R (recursion in R is dumb).
//'
//'@section Warning:
//'Unlike \code{\link{intertime}} or \code{\link{session_count}}, the methodology here has not been
//'settled on; the internal workings of this function can and probably will change.
//'
//'@return a vector of session length counts, in seconds.
//'
//'@seealso
//'\code{\link{intertimes}}, for generating inter-time values, and \code{\link{session_count}} for
//'simply counting the number of sessions.
//'
//'@export
// [[Rcpp::export]]
NumericVector session_length(NumericVector intertimes, int local_minimum = 3600) {

  //Declare output object
  NumericVector output(1);

  //If there's only one element, this is easy
  if(intertimes.size() == 1){

    output[0] = 2 * intertimes[0];
    return output;

  }

  //Alternately, if we have >1 element, create holding objects
  int sum = 0;
  int vals_in_sum = 0;
  bool recurse_init = false;
  NumericVector recursed_output = 0;

  //Loop
  for(int i = 0; i < intertimes.size(); i++) {

    //If the value is less than the local minimum, include in the sum
    if(intertimes[i] < local_minimum){

      sum += intertimes[i];
      vals_in_sum ++;

    } else {

      //If the value is *greater* than the local minimum, recurse.
      recurse_init = true;
      recursed_output = session_length(intertimes = intertimes[seq(i, (intertimes.size()-1))],
                                       local_minimum = local_minimum);

      //When the recursed call completes, break
      break;
    }

  }

  //Whether we end up with one object or two objects, work out the time if appropriate
  if(vals_in_sum != 0){

    output[0] = (sum + (sum/vals_in_sum));

  }

  //If the recursed_output exists, pop on to that
  if(recurse_init){

    int recursed_size = recursed_output.size();
    NumericVector hold_vector(recursed_size+1);
    hold_vector[seq(0,(recursed_size - 1))] = recursed_output;
    hold_vector[recursed_size] = output[0];
    output = hold_vector;

  }

  //Return
  return output;
}

// [[Rcpp::export]]
NumericVector session_pages(NumericVector intertimes, int local_minimum = 3600) {

  //Declare output object
  NumericVector output(1);

  //If there's only one intertime, this is easy - one session, which is two pages.
  if(intertimes.size() == 1){

    //Unless the intertime is >= the local_minimum, in which case we're looking at two one-page sessions.
    if(intertimes[0] >= local_minimum){

      //Create holding object, assign values to it, replace output
      NumericVector hold(2);
      hold[0] = 1;
      hold[1] = 1;
      output = hold;

    } else {

      //Otherwise just rely on the existing "output"
      output[0] = 2;

    }

  //If there are multiple intertimes we actually need to loop through.
  } else {

    //Create the actual page counter. This starts at 1, because intertimes are 2 events, only 1 captured for intertimes[0]
    int pages = 1;

    //Loop
    for(int i = 0; i < intertimes.size(); i++) {

      //If the value is below the local minimum, increment
      if(intertimes[i] < local_minimum){

        pages ++;

      //Otherwise, pop pages on output and create a new entry
      } else {

        output = numeric_vector_expander(output, pages);
        pages = 1;

      }

    }

    //If there has been a value > intertime_period, we need to pop things on again right at the end.
    if((output.size() > 1) | (output[0] > 1)){

      output = numeric_vector_expander(output, pages, true);

    } else {

      //Otherwise just add the pagecount for the "first" session.
      output[0] = pages;

    }
  }

  return output;

}

// [[Rcpp::export]]
NumericVector cpp_intertimes(NumericVector timestamps) {

  //Identify size of input object
  int input_size = timestamps.size();

  //Instantiate output object
  NumericVector output(input_size-1);

  //Sort input
  std::sort(timestamps.begin(),timestamps.end());

  //Loop over the data
  for(int i = 1; i < input_size;++i){

    //For each entry, the output value is [entry] minus [previous entry]
    output[i-1] = (timestamps[i] - timestamps[i-1]);

  }

  //Return
  return output;
}<commit_msg>that is THE STUPIDEST GODDAMN BUG.<commit_after>//Includes/namespaces
#include <Rcpp.h>
using namespace Rcpp;

//Generalised NumericVector expander
NumericVector numeric_vector_expander(NumericVector x, int to_insert) {

  //Calculate existing vector's size, create dummy output
  int existing_size = x.size();

  //If the existing vector's first value is zero, sod it, just return to_insert as a NumericVector
  if( (existing_size == 0) | ((existing_size == 1) & (x[0] == 0))){

    NumericVector holding_out(1);
    holding_out[0] = to_insert;
    return holding_out;

  } else {//Otherwise...

    //Create new vector, which is existing_size+1 in length
    NumericVector holding_out(existing_size+1);

    //Insert the existing values, then pop the new one on the end
    holding_out[seq(0,(existing_size-1))] = x;
    holding_out[existing_size] = to_insert;

    //Return
    return holding_out;
  }

}

//' @title
//' session_count
//' @description
//' Session counting function
//'
//' @param x a vector of intertime values
//'
//' @param local_minimum the threshold (in seconds) to split out a new session on. Set to 3600
//' by default.
//'
//' @details
//' \code{session_count} takes a vector of intertime values (generated via \code{\link{intertimes}},
//' or in any other way you see fit) and returns the total number of sessions within that dataset.
//' It's implimented in C++, providing a (small) increase in speed over the R equivalent.
//'
//' @seealso
//' \code{\link{intertimes}}, for generating inter-time values,or \code{\link{session_length}} for
//' session length, in seconds.
//'
//' @export
// [[Rcpp::export]]
int session_count(NumericVector x, int local_minimum = 3600) {

  //Create output object
  int count = 1;

  //Loop.
  for(int i = 0; i < x.size(); i++) {

    //If the value is greater than the local minimum, increment the count
    if(x[i] > local_minimum){
      count ++;
    }

  }

  //Return
  return count;
}

//'@title
//'session_length
//'
//'@description
//'Session counting function
//'
//'@param intertimes a vector of inter-time values.
//'
//'@param local_minimum the threshold (in seconds) to split out a new session on. Set to 3600
//' by default.
//'
//'@details
//'\code{session_length} takes a vector of intertime values (generated via \code{\link{intertimes}},
//'or in any other way you see fit), splits them into sessions, and calculates the approximate
//'length (in seconds) of each session. It's implemented in C++ since, even with the hinkiness
//'of my C++, it's still several OoM faster than the equivalent R (recursion in R is dumb).
//'
//'@section Warning:
//'Unlike \code{\link{intertime}} or \code{\link{session_count}}, the methodology here has not been
//'settled on; the internal workings of this function can and probably will change.
//'
//'@return a vector of session length counts, in seconds.
//'
//'@seealso
//'\code{\link{intertimes}}, for generating inter-time values, and \code{\link{session_count}} for
//'simply counting the number of sessions.
//'
//'@export
// [[Rcpp::export]]
NumericVector session_length(NumericVector intertimes, int local_minimum = 3600) {

  //Declare output object
  NumericVector output(1);

  //If there's only one element, this is easy
  if(intertimes.size() == 1){

    output[0] = 2 * intertimes[0];
    return output;

  }

  //Alternately, if we have >1 element, create holding objects
  int sum = 0;
  int vals_in_sum = 0;
  bool recurse_init = false;
  NumericVector recursed_output = 0;

  //Loop
  for(int i = 0; i < intertimes.size(); i++) {

    //If the value is less than the local minimum, include in the sum
    if(intertimes[i] < local_minimum){

      sum += intertimes[i];
      vals_in_sum ++;

    } else {

      //If the value is *greater* than the local minimum, recurse.
      recurse_init = true;
      recursed_output = session_length(intertimes = intertimes[seq(i, (intertimes.size()-1))],
                                       local_minimum = local_minimum);

      //When the recursed call completes, break
      break;
    }

  }

  //Whether we end up with one object or two objects, work out the time if appropriate
  if(vals_in_sum != 0){

    output[0] = (sum + (sum/vals_in_sum));

  }

  //If the recursed_output exists, pop on to that
  if(recurse_init){

    int recursed_size = recursed_output.size();
    NumericVector hold_vector(recursed_size+1);
    hold_vector[seq(0,(recursed_size - 1))] = recursed_output;
    hold_vector[recursed_size] = output[0];
    output = hold_vector;

  }

  //Return
  return output;
}

// [[Rcpp::export]]
NumericVector session_pages(NumericVector intertimes, int local_minimum = 3600) {

  //Declare output object
  NumericVector output(1);

  //If there's only one intertime, this is easy - one session, which is two pages.
  if(intertimes.size() == 1){

    //Unless the intertime is >= the local_minimum, in which case we're looking at two one-page sessions.
    if(intertimes[0] >= local_minimum){

      //Create holding object, assign values to it, replace output
      NumericVector hold(2);
      hold[0] = 1;
      hold[1] = 1;
      output = hold;

    } else {

      //Otherwise just rely on the existing "output"
      output[0] = 2;

    }

  //If there are multiple intertimes we actually need to loop through.
  } else {

    //Create the actual page counter. This starts at 1, because intertimes are 2 events, only 1 captured for intertimes[0]
    int pages = 1;

    //Loop
    for(int i = 0; i < intertimes.size(); i++) {

      //If the value is below the local minimum, increment
      if(intertimes[i] < local_minimum){

        pages ++;

      //Otherwise, pop pages on output and create a new entry
      } else {

        output = numeric_vector_expander(output, pages);
        pages = 1;

      }

    }

    //If there has been a value > intertime_period, we need to pop things on again right at the end.
    if((output.size() > 1) | (output[0] > 1)){

      output = numeric_vector_expander(output, pages);

    } else {

      //Otherwise just add the pagecount for the "first" session.
      output[0] = pages;

    }
  }

  return output;

}

// [[Rcpp::export]]
NumericVector cpp_intertimes(NumericVector timestamps) {

  //Identify size of input object
  int input_size = timestamps.size();

  //Instantiate output object
  NumericVector output(input_size-1);

  //Sort input
  std::sort(timestamps.begin(),timestamps.end());

  //Loop over the data
  for(int i = 1; i < input_size;++i){

    //For each entry, the output value is [entry] minus [previous entry]
    output[i-1] = (timestamps[i] - timestamps[i-1]);

  }

  //Return
  return output;
}<|endoftext|>