<commit_before>/*
 MPL3115A2 Barometric Pressure Sensor Library
 By: Nathan Seidle
 SparkFun Electronics
 Date: September 24th, 2013
 License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).

 Get pressure, altitude and temperature from the MPL3115A2 sensor.

 September 6, 2016: Modified for use in OpenROV's Software

*/

#include <Arduino.h>
#include <CI2C.h>

#include "MPL3115A2.h"

using namespace mpl3115a2;

MPL3115A2::MPL3115A2( CI2C *i2cInterfaceIn )
    : m_i2cAddress( mpl3115a2::MPL3115A2_ADDRESS )
    , m_pI2C( i2cInterfaceIn )
    , m_sensorId( 420 )
{

}

ERetCode MPL3115A2::Initialize()
{
    m_isInitialized = false;

    delay( 500 );

    //Verify that the sensor is up and running
    if( VerifyChipId() != ERetCode::SUCCESS )
    {
        //We were unable to verify this sensor
        return ERetCode::FAILED;
    }

    m_isInitialized = true;
    return ERetCode::SUCCESS;
}

ERetCode MPL3115A2::SetMode( EMode modeIn )
{
    switch (modeIn)
    {
        case EMode::BAROMETER:
        {
            return SetModeBarometer();
        }
        case EMode::ALTIMETER:
        {
            return SetModeAltimeter();
        }
        case EMode::STANDBY:
        {
            return SetModeStandby();
        }
        case EMode::ACTIVE:
        {
            return SetModeActive();
        }
        default:
        {
            return ERetCode::FAILED;
        }
    }
}

//Call with a rate from 0 to 7. See page 33 for table of ratios.
//Sets the over sample rate. Datasheet calls for 128 but you can set it
//from 1 to 128 samples. The higher the oversample rate the greater
//the time between data samples.
ERetCode MPL3115A2::SetOversampleRatio( EOversampleRatio osrIn )
{
    int32_t returnCode;
    auto sampleRate = static_cast<int>( osrIn );

    //Align it for the control register
    sampleRate <<= 3;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Clear out old Oversample bits
    tempSetting &= B11000111;

    //Mask new sample rate
    tempSetting |= sampleRate;

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Enables the pressure and temp measurement event flags so that we can
//test against them. This is recommended in datasheet during setup.
ERetCode MPL3115A2::EnableEventFlags()
{
    // Enable all three pressure and temp event flags
    auto ret = WriteByte( MPL3115A2_REGISTER::PT_DATA_CFG, 0x07);
    if( ret != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Reads the current pressure in Pa
//Unit must be set in barometric pressure mode
ERetCode MPL3115A2::ReadPressure( float& pressureOut )
{
    Serial.println( "In read pressure loop" );
    ToggleOneShot();

    int32_t returnCode;
    uint8_t pdr;

    //Wait for PDR bit, indicates we have new pressure data
    auto counter = 0;
    while( ( pdr & (1<<2) ) == 0 )
    {
        Serial.println( counter );
        returnCode = ReadByte( MPL3115A2_REGISTER::STATUS, pdr );
        if( returnCode != I2C::ERetCode::SUCCESS )
        {
            return ERetCode::FAILED_PRESSURE_READ;
        }

        if( ++counter > 100 )
        {
            return ERetCode::TIMED_OUT;
        }
        delay(1);
    }

    //Read pressure registers
    uint8_t buffer[3];
    memset( buffer, 0, 3);

    returnCode = ReadNBytes( MPL3115A2_REGISTER::PRESSURE_OUT_MSB, buffer, 3 );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_PRESSURE_READ;
    }

    //Toggle the OST bit causing the sensor to immediately take another reading
    ToggleOneShot();

    auto msb = buffer[0];
    auto csb = buffer[1];
    auto lsb = buffer[2];

    // Pressure comes back as a left shifted 20 bit number
    uint64_t pressure = (uint64_t)msb<<16 | (uint64_t)csb<<8 | (uint64_t)lsb;

    //Pressure is an 18 bit number with 2 bits of decimal. Get rid of decimal portion.
    pressure >>= 6;

    //Bits 5/4 represent the fractional component
    lsb &= B00110000;

    //Get it right aligned
    lsb >>= 4;

    //Turn it into fraction
    float pressure_decimal = (float)lsb/4.0;

	pressureOut = (float)pressure + pressure_decimal;

    return ERetCode::SUCCESS;
}

/***************************************************************************
    PRIVATE FUNCTIONS
 ***************************************************************************/

ERetCode MPL3115A2::VerifyChipId()
{
    //Read the chip id
    uint8_t id;

    auto ret = ReadByte( MPL3115A2_REGISTER::WHO_AM_I, id );

    if( ret != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Check to see if it matches the proper ID (0xC4)
    if( id != 0xC4 )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

ERetCode MPL3115A2::SetModeBarometer()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Clear the altimeter bit
    tempSetting &= ~(1<<7);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    m_mode = tempSetting;

    return ERetCode::SUCCESS;
}

ERetCode MPL3115A2::SetModeAltimeter()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Set the altimeter bit
    tempSetting |= (1<<7);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Puts the sensor in standby mode
//This is needed so that we can modify the major control registers
ERetCode MPL3115A2::SetModeStandby()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Clear SBYB bit for Standby mode
    tempSetting &= ~(1<<0);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Puts the sensor in active mode
//This is needed so that we can modify the major control registers
ERetCode MPL3115A2::SetModeActive()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Set SBYB bit for Active mode
    tempSetting |= (1<<0);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Clears and then sets the OST bit which causes the sensor to take another readings
//Needed to sample faster than 1Hz
ERetCode MPL3115A2::ToggleOneShot()
{
    Serial.println( "Toggling one shot." );
    Serial.print( "Mode: " );
    Serial.println( m_mode );
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    //Clear the one shot bit
    tempSetting &= ~(1<<1);
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    //Reat the current settings, just to be safe :)
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    //Set the overshot bit
    tempSetting |= (1<<1);
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    return ERetCode::SUCCESS;
}

int32_t MPL3115A2::WriteByte( MPL3115A2_REGISTER addressIn, uint8_t dataIn )
{
	return (int32_t)m_pI2C->WriteByte( m_i2cAddress, (uint8_t)addressIn, dataIn );
}

int32_t MPL3115A2::ReadByte( MPL3115A2_REGISTER addressIn, uint8_t &dataOut )
{
	return (int32_t)m_pI2C->ReadByte( m_i2cAddress, (uint8_t)addressIn, &dataOut );
}
int32_t MPL3115A2::ReadNBytes( MPL3115A2_REGISTER addressIn, uint8_t* dataOut, uint8_t byteCountIn )
{
    return (int32_t)m_pI2C->ReadBytes( m_i2cAddress, (uint8_t)addressIn, dataOut, byteCountIn );
}<commit_msg>Debug<commit_after>/*
 MPL3115A2 Barometric Pressure Sensor Library
 By: Nathan Seidle
 SparkFun Electronics
 Date: September 24th, 2013
 License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).

 Get pressure, altitude and temperature from the MPL3115A2 sensor.

 September 6, 2016: Modified for use in OpenROV's Software

*/

#include <Arduino.h>
#include <CI2C.h>

#include "MPL3115A2.h"

using namespace mpl3115a2;

MPL3115A2::MPL3115A2( CI2C *i2cInterfaceIn )
    : m_i2cAddress( mpl3115a2::MPL3115A2_ADDRESS )
    , m_pI2C( i2cInterfaceIn )
    , m_sensorId( 420 )
{

}

ERetCode MPL3115A2::Initialize()
{
    m_isInitialized = false;

    delay( 500 );

    //Verify that the sensor is up and running
    if( VerifyChipId() != ERetCode::SUCCESS )
    {
        //We were unable to verify this sensor
        return ERetCode::FAILED;
    }

    m_isInitialized = true;
    return ERetCode::SUCCESS;
}

ERetCode MPL3115A2::SetMode( EMode modeIn )
{
    switch (modeIn)
    {
        case EMode::BAROMETER:
        {
            return SetModeBarometer();
        }
        case EMode::ALTIMETER:
        {
            return SetModeAltimeter();
        }
        case EMode::STANDBY:
        {
            return SetModeStandby();
        }
        case EMode::ACTIVE:
        {
            return SetModeActive();
        }
        default:
        {
            return ERetCode::FAILED;
        }
    }
}

//Call with a rate from 0 to 7. See page 33 for table of ratios.
//Sets the over sample rate. Datasheet calls for 128 but you can set it
//from 1 to 128 samples. The higher the oversample rate the greater
//the time between data samples.
ERetCode MPL3115A2::SetOversampleRatio( EOversampleRatio osrIn )
{
    int32_t returnCode;
    auto sampleRate = static_cast<int>( osrIn );

    //Align it for the control register
    sampleRate <<= 3;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Clear out old Oversample bits
    tempSetting &= B11000111;

    //Mask new sample rate
    tempSetting |= sampleRate;

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Enables the pressure and temp measurement event flags so that we can
//test against them. This is recommended in datasheet during setup.
ERetCode MPL3115A2::EnableEventFlags()
{
    // Enable all three pressure and temp event flags
    auto ret = WriteByte( MPL3115A2_REGISTER::PT_DATA_CFG, 0x07);
    if( ret != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Reads the current pressure in Pa
//Unit must be set in barometric pressure mode
ERetCode MPL3115A2::ReadPressure( float& pressureOut )
{
    Serial.println( "In read pressure loop" );
    ToggleOneShot();

    int32_t returnCode;
    uint8_t pdr;

    //Wait for PDR bit, indicates we have new pressure data
    auto counter = 0;

    returnCode = ReadByte( MPL3115A2_REGISTER::STATUS, pdr );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_PRESSURE_READ;
    }
    Serial.println(pdr, HEX);
    // while( ( pdr & (1<<2) ) == 0 )
    // {
    //     Serial.println( counter );

    //     if( ++counter > 100 )
    //     {
    //         return ERetCode::TIMED_OUT;
    //     }
    //     delay(1);
    // }

    //Read pressure registers
    uint8_t buffer[3];
    memset( buffer, 0, 3);

    returnCode = ReadNBytes( MPL3115A2_REGISTER::PRESSURE_OUT_MSB, buffer, 3 );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_PRESSURE_READ;
    }

    //Toggle the OST bit causing the sensor to immediately take another reading
    ToggleOneShot();

    auto msb = buffer[0];
    auto csb = buffer[1];
    auto lsb = buffer[2];

    // Pressure comes back as a left shifted 20 bit number
    uint64_t pressure = (uint64_t)msb<<16 | (uint64_t)csb<<8 | (uint64_t)lsb;

    //Pressure is an 18 bit number with 2 bits of decimal. Get rid of decimal portion.
    pressure >>= 6;

    //Bits 5/4 represent the fractional component
    lsb &= B00110000;

    //Get it right aligned
    lsb >>= 4;

    //Turn it into fraction
    float pressure_decimal = (float)lsb/4.0;

	pressureOut = (float)pressure + pressure_decimal;

    return ERetCode::SUCCESS;
}

/***************************************************************************
    PRIVATE FUNCTIONS
 ***************************************************************************/

ERetCode MPL3115A2::VerifyChipId()
{
    //Read the chip id
    uint8_t id;

    auto ret = ReadByte( MPL3115A2_REGISTER::WHO_AM_I, id );

    if( ret != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Check to see if it matches the proper ID (0xC4)
    if( id != 0xC4 )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

ERetCode MPL3115A2::SetModeBarometer()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Clear the altimeter bit
    tempSetting &= ~(1<<7);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    m_mode = tempSetting;

    return ERetCode::SUCCESS;
}

ERetCode MPL3115A2::SetModeAltimeter()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Set the altimeter bit
    tempSetting |= (1<<7);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Puts the sensor in standby mode
//This is needed so that we can modify the major control registers
ERetCode MPL3115A2::SetModeStandby()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Clear SBYB bit for Standby mode
    tempSetting &= ~(1<<0);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Puts the sensor in active mode
//This is needed so that we can modify the major control registers
ERetCode MPL3115A2::SetModeActive()
{
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    //Set SBYB bit for Active mode
    tempSetting |= (1<<0);

    //And write it to the register
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED;
    }

    return ERetCode::SUCCESS;
}

//Clears and then sets the OST bit which causes the sensor to take another readings
//Needed to sample faster than 1Hz
ERetCode MPL3115A2::ToggleOneShot()
{
    Serial.println( "Toggling one shot." );
    Serial.print( "Mode: " );
    Serial.println( m_mode );
    int32_t returnCode;

    //Read the current settings
    uint8_t tempSetting;
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    //Clear the one shot bit
    tempSetting &= ~(1<<1);
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    //Reat the current settings, just to be safe :)
    returnCode = ReadByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    //Set the overshot bit
    tempSetting |= (1<<1);
    returnCode = WriteByte( MPL3115A2_REGISTER::CONTROL_REGISTER_1, tempSetting );
    if( returnCode != I2C::ERetCode::SUCCESS )
    {
        return ERetCode::FAILED_ONESHOT;
    }

    return ERetCode::SUCCESS;
}

int32_t MPL3115A2::WriteByte( MPL3115A2_REGISTER addressIn, uint8_t dataIn )
{
	return (int32_t)m_pI2C->WriteByte( m_i2cAddress, (uint8_t)addressIn, dataIn );
}

int32_t MPL3115A2::ReadByte( MPL3115A2_REGISTER addressIn, uint8_t &dataOut )
{
	return (int32_t)m_pI2C->ReadByte( m_i2cAddress, (uint8_t)addressIn, &dataOut );
}
int32_t MPL3115A2::ReadNBytes( MPL3115A2_REGISTER addressIn, uint8_t* dataOut, uint8_t byteCountIn )
{
    return (int32_t)m_pI2C->ReadBytes( m_i2cAddress, (uint8_t)addressIn, dataOut, byteCountIn );
}<|endoftext|>


<commit_before><commit_msg>Planning: adjust log level for easier debug<commit_after><|endoftext|>


<commit_before>#include "p0compile/parser.hpp"
#include "p0compile/expression_tree.hpp"
#include "p0compile/scanner.hpp"
#include "p0compile/compiler_error.hpp"
#include <boost/test/unit_test.hpp>

namespace
{
	bool handle_unexpected_error(p0::compiler_error const &error)
	{
		(void)error;
		BOOST_CHECK(nullptr == "No error expected");
		return true;
	}
}

BOOST_AUTO_TEST_CASE(parse_empty_file_test)
{
	std::string const source = "";
	p0::source_range const source_range(
				source.data(),
				source.data() + source.size());
	p0::scanner scanner(source_range);
	p0::parser parser(scanner, handle_unexpected_error);
	auto const ast = parser.parse_unit();
	BOOST_REQUIRE(ast);
}
<commit_msg>generalize parser test<commit_after>#include "p0compile/parser.hpp"
#include "p0compile/expression_tree.hpp"
#include "p0compile/scanner.hpp"
#include "p0compile/compiler_error.hpp"
#include <boost/test/unit_test.hpp>

namespace
{
	bool handle_unexpected_error(p0::compiler_error const &error)
	{
		(void)error;
		BOOST_CHECK(nullptr == "No error expected");
		return true;
	}

	template <class ASTHandler>
	void test_parser(std::string const &source, ASTHandler const &handle_ast)
	{
		p0::source_range const source_range(
					source.data(),
					source.data() + source.size());
		p0::scanner scanner(source_range);
		p0::parser parser(scanner, handle_unexpected_error);
		auto const ast = parser.parse_unit();
		BOOST_REQUIRE(ast);
		handle_ast(*ast);
	}
}

BOOST_AUTO_TEST_CASE(parse_empty_file_test)
{
	test_parser("", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		//TODO check empty body
	});
}
<|endoftext|>


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

#include "History.hpp"

#include <QSettings>

#include <ndb/query.hpp>
#include <ndb/function.hpp>

#include "Web/WebView.hpp"

#include "History/HistoryModel.hpp"

namespace Sn
{
QString History::titleCaseLocalizedMonth(int month)
{
	switch (month) {
	case 1:
		return tr("January");
	case 2:
		return tr("February");
	case 3:
		return tr("March");
	case 4:
		return tr("April");
	case 5:
		return tr("May");
	case 6:
		return tr("June");
	case 7:
		return tr("July");
	case 8:
		return tr("August");
	case 9:
		return tr("September");
	case 10:
		return tr("October");
	case 11:
		return tr("November");
	case 12:
		return tr("December");
	default:
		qWarning("Month number out of range!");
		return QString();
	}
}

History::History(QObject* parent) :
	QObject(parent)
{
	loadSettings();
}

History::~History()
{
	// Empty
}

HistoryModel *History::model()
{
	if (!m_model)
		m_model = new HistoryModel(this);

	return m_model;
}

void History::addHistoryEntry(WebView* view)
{
	if (!m_isSaving)
		return;

	const QUrl url{view->url()};
	const QString title{view->title()};

	addHistoryEntry(url, title);
}

void History::addHistoryEntry(const QUrl& url, QString title)
{
	if (!m_isSaving)
		return;

	const QStringList ignoredSchemes = {
		QStringLiteral("sielo"),
		QStringLiteral("view-source"),
		QStringLiteral("file"),
		QStringLiteral("data")
	};

	if (url.isEmpty() || ignoredSchemes.contains(url.scheme()))
		return;

	if (title.isEmpty())
		title = tr("Empty page");

	auto oquery = ndb::oquery<dbs::navigation>() << (history.url == url.toString());

	if (!oquery.has_result()) {
		ndb::query<dbs::navigation>() + (
			history.title = title,
			history.url = url.toString(),
			history.date = QDateTime::currentMSecsSinceEpoch(),
			history.count = 1
		);

		qint64 id = ndb::last_id<dbs::navigation>();

		HistoryEntry entry{};
		entry.id = id;
		entry.count = 1;
		entry.date = QDateTime::currentDateTime();
		entry.url = url;
		entry.urlString = url.toEncoded();
		entry.title = title;

		emit historyEntryAdded(entry);
	}
	else {
		qint64 id{oquery[0].id};
		qint64 count{oquery[0].count};
		QDateTime date{QDateTime::fromMSecsSinceEpoch(oquery[0].date)};
		QString oldTitle{oquery[0].title};

		ndb::query<dbs::navigation>() >> (
			(
				history.count = count + 1,
				history.date = QDateTime::currentMSecsSinceEpoch(),
				history.title = title
			)
			<< (history.url == url.toString())
		);

		HistoryEntry before{};
		before.id = id;
		before.count = count;
		before.date = date;
		before.url = url;
		before.urlString = url.toEncoded();
		before.title = oldTitle;

		HistoryEntry after = before;
		after.count = count + 1;
		after.date = QDateTime::currentDateTime();
		after.title = title;

		emit historyEntryEdited(before, after);
	}
}

void History::deleteHistoryEntry(int index)
{
	QList<int> list{};
	list.append(index);

	deleteHistoryEntry(list);
}

void History::deleteHistoryEntry(const QList<int>& list)
{
	for (int index : list) {
		auto query = ndb::oquery<dbs::navigation>() << (history.id == index);

		if (!query.has_result())
			return;

		HistoryEntry entry{query[0]};

		ndb::query<dbs::navigation>() - (history.id == index);
		ndb::query<dbs::navigation>() - (history.url == QString::fromUtf8(entry.url.toEncoded(QUrl::RemoveFragment)));

		emit historyEntryDeleted(entry);
	}
}

void History::deleteHistoryEntry(const QString& url, const QString& title)
{
	auto query = ndb::query<dbs::navigation>() << ((history.id) << (history.url == url && history.title ==
		title));

	if (query.has_result()) {
		int id = query[0][history.id];
		deleteHistoryEntry(id);
	}
}

QList<int> History::indexesFromTimeRange(qint64 start, qint64 end)
{
	QList<int> list{};

	if (start < 0 || end < 0)
		return list;

	for (auto& data : ndb::query<dbs::navigation>() << ((history.id) << ndb::range(
		     history.date, end, start)))
		list.append(data[history.id]);

	return list;
}

bool History::urlIsStored(const QString& url)
{
	auto query = ndb::query<dbs::navigation>() << (history.url == url);

	return query.has_result();
}

QVector<History::HistoryEntry> History::mostVisited(int count)
{
	QVector<HistoryEntry> list{};

	//auto oquery = ndb::oquery<dbs::navigation>() << ((ndb::sort(ndb::desc(history.count)) << ndb::limit(count)));

	//for (auto& data : oquery) {
	//	HistoryEntry entry{data};

	//	list.append(entry);
	//}

	for (auto& data : ndb::query<dbs::navigation>() << ((history.id, history.url, history.title, history.count, history.date) << (ndb::sort(ndb::desc(history.count)) << ndb::limit(count)))) {
		HistoryEntry entry{};

		entry.id = data[history.id];
		entry.date = QDateTime::fromMSecsSinceEpoch(data[history.date]);
		entry.url = QUrl(data[history.url]);
		entry.urlString = QUrl(data[history.url]).toEncoded();
		entry.title = data[history.title];

		list.append(entry);
	}

	return list;
}

void History::clearHistory()
{
	ndb::clear<dbs::navigation>(history);

	Application::instance()->webProfile()->clearAllVisitedLinks();

	emit resetHistory();
}

void History::setSaving(bool state)
{
	m_isSaving = state;
}

void History::loadSettings()
{
	QSettings settings{};

	settings.beginGroup("Web-Settings");

	m_isSaving = settings.value("allowHistory", true).toBool();

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

#include "History.hpp"

#include <QSettings>

#include <QSqlQuery>

#include "Web/WebView.hpp"

#include "History/HistoryModel.hpp"

namespace Sn
{
QString History::titleCaseLocalizedMonth(int month)
{
	switch (month) {
	case 1:
		return tr("January");
	case 2:
		return tr("February");
	case 3:
		return tr("March");
	case 4:
		return tr("April");
	case 5:
		return tr("May");
	case 6:
		return tr("June");
	case 7:
		return tr("July");
	case 8:
		return tr("August");
	case 9:
		return tr("September");
	case 10:
		return tr("October");
	case 11:
		return tr("November");
	case 12:
		return tr("December");
	default:
		qWarning("Month number out of range!");
		return QString();
	}
}

History::History(QObject* parent) :
	QObject(parent)
{
	loadSettings();
}

History::~History()
{
	// Empty
}

HistoryModel *History::model()
{
	if (!m_model)
		m_model = new HistoryModel(this);

	return m_model;
}

void History::addHistoryEntry(WebView* view)
{
	if (!m_isSaving)
		return;

	const QUrl url{view->url()};
	const QString title{view->title()};

	addHistoryEntry(url, title);
}

void History::addHistoryEntry(const QUrl& url, QString title)
{
	if (!m_isSaving)
		return;

	const QStringList ignoredSchemes = {
		QStringLiteral("sielo"),
		QStringLiteral("view-source"),
		QStringLiteral("file"),
		QStringLiteral("data")
	};

	if (url.isEmpty() || ignoredSchemes.contains(url.scheme()))
		return;

	if (title.isEmpty())
		title = tr("Empty page");

	QSqlQuery query(SqlDatabase::instance()->database());
	query.prepare("SELECT id, count, date, title FROM history WHERE url=?");
	query.bindValue(0, url);
	query.exec();

	if (!query.next()) {
		query.prepare("INSERT INTO history (count, date, url, title) VALUES (1,?,?,?)");
		query.bindValue(0, QDateTime::currentMSecsSinceEpoch());
		query.bindValue(1, url);
		query.bindValue(2, title);
		query.exec();

		int id = query.lastInsertId().toInt();
		HistoryEntry entry;
		entry.id = id;
		entry.count = 1;
		entry.date = QDateTime::currentDateTime();
		entry.url = url;
		entry.urlString = url.toEncoded();
		entry.title = title;
		emit historyEntryAdded(entry);
	}
	else {
		int id = query.value(0).toInt();
		int count = query.value(1).toInt();
		QDateTime date = QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong());
		QString oldTitle = query.value(3).toString();

		query.prepare("UPDATE history SET count = count + 1, date=?, title=? WHERE url=?");
		query.bindValue(0, QDateTime::currentMSecsSinceEpoch());
		query.bindValue(1, title);
		query.bindValue(2, url);
		query.exec();

		HistoryEntry before;
		before.id = id;
		before.count = count;
		before.date = date;
		before.url = url;
		before.urlString = url.toEncoded();
		before.title = oldTitle;

		HistoryEntry after = before;
		after.count = count + 1;
		after.date = QDateTime::currentDateTime();
		after.title = title;

		emit historyEntryEdited(before, after);
	}
}

void History::deleteHistoryEntry(int index)
{
	QList<int> list{};
	list.append(index);

	deleteHistoryEntry(list);
}

void History::deleteHistoryEntry(const QList<int>& list)
{
	QSqlDatabase db = SqlDatabase::instance()->database();
	db.transaction();

	QList<QUrl> urls;

	for (int index : list) {
		QSqlQuery query{SqlDatabase::instance()->database()};
		query.prepare("SELECT count, date, url, title FROM history WHERE id=?");
		query.addBindValue(index);
		query.exec();

		if (!query.isActive() || !query.next())
			continue;

		HistoryEntry entry{};
		entry.id = index;
		entry.count = query.value(0).toInt();
		entry.date = QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong());
		entry.url = query.value(2).toUrl();
		entry.urlString = entry.url.toEncoded();
		entry.title = query.value(3).toString();

		query.prepare("DELETE FROM history WHERE id=?");
		query.addBindValue(index);
		query.exec();

		query.prepare("DELETE FROM icons WHERE url=?");
		query.addBindValue(entry.url.toEncoded(QUrl::RemoveFragment));
		query.exec();

		urls.append(entry.url);
		emit historyEntryDeleted(entry);
	}

	Application::instance()->webProfile()->clearVisitedLinks(urls);

	db.commit();
}

void History::deleteHistoryEntry(const QString& url, const QString& title)
{
	QSqlQuery query{SqlDatabase::instance()->database())};
	query.prepare("SELECT id FROM history WHERE url=? AND title=?");
	query.bindValue(0, url);
	query.bindValue(1, title);
	query.exec();

	if (query.next()) {
		const int id{query.value(0).toInt()};

		deleteHistoryEntry(id);
	}
}

QList<int> History::indexesFromTimeRange(qint64 start, qint64 end)
{
	QList<int> list{};

	if (start < 0 || end < 0)
		return list;

	QSqlQuery query{SqlDatabase::instance()->database()};
	query.prepare("SELECT id FROM history WHERE date BETWEEN ? AND ?");
	query.addBindValue(end);
	query.addBindValue(start);
	query.exec();

	while (query.next())
		list.append(query.value(0).toInt());

	return list;
}

bool History::urlIsStored(const QString& url)
{
	QSqlQuery query{SqlDatabase::instance()->database()};
	query.prepare("SELECT id FROM history WHERE url=?");
	query.bindValue(0, url);
	query.exec();

	return query.next();
}

QVector<History::HistoryEntry> History::mostVisited(int count)
{
	QVector<HistoryEntry> list;

	QSqlQuery query{SqlDatabase::instance()->database()};
	query.prepare(QString("SELECT count, date, id, title, url FROM history ORDER BY count DESC LIMIT %1").arg(count));
	query.exec();

	while (query.next()) {
		HistoryEntry entry{};
		entry.count = query.value(0).toInt();
		entry.date = query.value(1).toDateTime();
		entry.id = query.value(2).toInt();
		entry.title = query.value(3).toString();
		entry.url = query.value(4).toUrl();
		list.append(entry);
	}

	return list;
}

void History::clearHistory()
{
	QSqlQuery query{SqlDatabase::instance()->database()};
	query.exec("DELETE FROM history");
	query.exec("VACUUM");

	Application::instance()->webProfile()->clearAllVisitedLinks();

	emit resetHistory();
}

void History::setSaving(bool state)
{
	m_isSaving = state;
}

void History::loadSettings()
{
	QSettings settings{};

	settings.beginGroup("Web-Settings");

	m_isSaving = settings.value("allowHistory", true).toBool();

	settings.endGroup();
}
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <map>
#include <memory>

#include "common.h"

// Size of the buffer to store result of ReadDirectoryChangesW.
static const unsigned int kDirectoryWatcherBufferSize = 4096;

// Object template to create representation of WatcherHandle.
static Nan::Persistent<ObjectTemplate> g_object_template;

// Mutex for the HandleWrapper map.
static uv_mutex_t g_handle_wrap_map_mutex;

// The events to be waited on.
static std::vector<HANDLE> g_events;

// The dummy event to wakeup the thread.
static HANDLE g_wake_up_event;

// The dummy event to ensure we are not waiting on a file handle when destroying it.
static HANDLE g_file_handles_free_event;

struct ScopedLocker {
  explicit ScopedLocker(uv_mutex_t& mutex) : mutex_(&mutex) { uv_mutex_lock(mutex_); }
  ~ScopedLocker() { Unlock(); }

  void Unlock() { uv_mutex_unlock(mutex_); }

  uv_mutex_t* mutex_;
};

struct HandleWrapper {
  HandleWrapper(WatcherHandle handle, const char* path_str)
      : dir_handle(handle),
        path(strlen(path_str)),
        canceled(false) {
    memset(&overlapped, 0, sizeof(overlapped));
    overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_events.push_back(overlapped.hEvent);

    std::copy(path_str, path_str + path.size(), path.data());
    map_[overlapped.hEvent] = this;
  }

  ~HandleWrapper() {
    if (!canceled) {
      Cancel();
    }

    CloseHandle(dir_handle);
    CloseHandle(overlapped.hEvent);
  }

  void Cancel() {
    canceled = true;
    CancelIoEx(dir_handle, &overlapped);
    g_events.erase(std::remove(g_events.begin(), g_events.end(), overlapped.hEvent), g_events.end());
    map_.erase(overlapped.hEvent);
  }

  WatcherHandle dir_handle;
  std::vector<char> path;
  bool canceled;
  OVERLAPPED overlapped;
  char buffer[kDirectoryWatcherBufferSize];

  static HandleWrapper* Get(HANDLE key) { return map_[key]; }

  static std::map<WatcherHandle, HandleWrapper*> map_;
};

std::map<WatcherHandle, HandleWrapper*> HandleWrapper::map_;

struct WatcherEvent {
  EVENT_TYPE type;
  WatcherHandle handle;
  std::vector<char> new_path;
  std::vector<char> old_path;
};

static bool QueueReaddirchanges(HandleWrapper* handle) {
  return ReadDirectoryChangesW(handle->dir_handle,
                               handle->buffer,
                               kDirectoryWatcherBufferSize,
                               FALSE,
                               FILE_NOTIFY_CHANGE_FILE_NAME      |
                                 FILE_NOTIFY_CHANGE_DIR_NAME     |
                                 FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                                 FILE_NOTIFY_CHANGE_SIZE         |
                                 FILE_NOTIFY_CHANGE_LAST_WRITE   |
                                 FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                                 FILE_NOTIFY_CHANGE_CREATION     |
                                 FILE_NOTIFY_CHANGE_SECURITY,
                               NULL,
                               &handle->overlapped,
                               NULL) == TRUE;
}

Local<Value> WatcherHandleToV8Value(WatcherHandle handle) {
  Local<Value> value = Nan::New(g_object_template)->NewInstance();
  Nan::SetInternalFieldPointer(value->ToObject(), 0, handle);
  return value;
}

WatcherHandle V8ValueToWatcherHandle(Local<Value> value) {
  return reinterpret_cast<WatcherHandle>(Nan::GetInternalFieldPointer(
      value->ToObject(), 0));
}

bool IsV8ValueWatcherHandle(Local<Value> value) {
  return value->IsObject() && value->ToObject()->InternalFieldCount() == 1;
}

void PlatformInit() {
  uv_mutex_init(&g_handle_wrap_map_mutex);

  g_file_handles_free_event = CreateEvent(NULL, TRUE, TRUE, NULL);
  g_wake_up_event = CreateEvent(NULL, FALSE, FALSE, NULL);
  g_events.push_back(g_wake_up_event);

  g_object_template.Reset(Nan::New<ObjectTemplate>());
  Nan::New(g_object_template)->SetInternalFieldCount(1);

  WakeupNewThread();
}

void PlatformThread() {
  while (true) {
    // Do not use g_events directly, since reallocation could happen when there
    // are new watchers adding to g_events when WaitForMultipleObjects is still
    // polling.
    ScopedLocker locker(g_handle_wrap_map_mutex);
    std::vector<HANDLE> copied_events(g_events);
    locker.Unlock();

    ResetEvent(g_file_handles_free_event);
    DWORD r = WaitForMultipleObjects(copied_events.size(),
                                     copied_events.data(),
                                     FALSE,
                                     INFINITE);
    SetEvent(g_file_handles_free_event);
    int i = r - WAIT_OBJECT_0;
    if (i >= 0 && i < copied_events.size()) {
      // It's a wake up event, there is no fs events.
      if (copied_events[i] == g_wake_up_event)
        continue;

      ScopedLocker locker(g_handle_wrap_map_mutex);

      HandleWrapper* handle = HandleWrapper::Get(copied_events[i]);
      if (!handle || handle->canceled)
        continue;

      DWORD bytes_transferred;
      if (!GetOverlappedResult(handle->dir_handle, &handle->overlapped, &bytes_transferred, FALSE))
        continue;
      if (bytes_transferred == 0)
        continue;

      std::vector<char> old_path;
      std::vector<WatcherEvent> events;

      DWORD offset = 0;
      while (true) {
        FILE_NOTIFY_INFORMATION* file_info =
            reinterpret_cast<FILE_NOTIFY_INFORMATION*>(handle->buffer + offset);

        // Emit events for children.
        EVENT_TYPE event = EVENT_NONE;
        switch (file_info->Action) {
          case FILE_ACTION_ADDED:
            event = EVENT_CHILD_CREATE;
            break;
          case FILE_ACTION_REMOVED:
            event = EVENT_CHILD_DELETE;
            break;
          case FILE_ACTION_RENAMED_OLD_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_RENAMED_NEW_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_MODIFIED:
            event = EVENT_CHILD_CHANGE;
            break;
        }

        if (event != EVENT_NONE) {
          // The FileNameLength is in "bytes", but the WideCharToMultiByte
          // requires the length to be in "characters"!
          int file_name_length_in_characters =
              file_info->FileNameLength / sizeof(wchar_t);

          char filename[MAX_PATH] = { 0 };
          int size = WideCharToMultiByte(CP_UTF8,
                                         0,
                                         file_info->FileName,
                                         file_name_length_in_characters,
                                         filename,
                                         MAX_PATH,
                                         NULL,
                                         NULL);

          // Convert file name to file path, same with:
          // path = handle->path + '\\' + filename
          std::vector<char> path(handle->path.size() + 1 + size);
          std::vector<char>::iterator iter = path.begin();
          iter = std::copy(handle->path.begin(), handle->path.end(), iter);
          *(iter++) = '\\';
          std::copy(filename, filename + size, iter);

          if (file_info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            // Do not send rename event until the NEW_NAME event, but still keep
            // a record of old name.
            old_path.swap(path);
          } else if (file_info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            e.old_path.swap(old_path);
            events.push_back(e);
          } else {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            events.push_back(e);
          }
        }

        if (file_info->NextEntryOffset == 0) break;
        offset += file_info->NextEntryOffset;
      }

      // Restart the monitor, it was reset after each call.
      QueueReaddirchanges(handle);

      locker.Unlock();

      for (size_t i = 0; i < events.size(); ++i)
        PostEventAndWait(events[i].type,
                         events[i].handle,
                         events[i].new_path,
                         events[i].old_path);
    }
  }
}

WatcherHandle PlatformWatch(const char* path) {
  wchar_t wpath[MAX_PATH] = { 0 };
  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);

  // Requires a directory, file watching is emulated in js.
  DWORD attr = GetFileAttributesW(wpath);
  if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
    return INVALID_HANDLE_VALUE;
  }

  WatcherHandle dir_handle = CreateFileW(wpath,
                                         FILE_LIST_DIRECTORY,
                                         FILE_SHARE_READ | FILE_SHARE_DELETE |
                                           FILE_SHARE_WRITE,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_FLAG_BACKUP_SEMANTICS |
                                           FILE_FLAG_OVERLAPPED,
                                         NULL);
  if (!PlatformIsHandleValid(dir_handle)) {
    return INVALID_HANDLE_VALUE;
  }

  std::unique_ptr<HandleWrapper> handle;
  {
    ScopedLocker locker(g_handle_wrap_map_mutex);
    handle.reset(new HandleWrapper(dir_handle, path));
  }

  if (!QueueReaddirchanges(handle.get())) {
    return INVALID_HANDLE_VALUE;
  }

  // Wake up the thread to add the new event.
  SetEvent(g_wake_up_event);

  // The pointer is leaked if no error happened.
  return handle.release()->overlapped.hEvent;
}

void PlatformUnwatch(WatcherHandle key) {
  if (PlatformIsHandleValid(key)) {
    HandleWrapper* handle;
    {
      ScopedLocker locker(g_handle_wrap_map_mutex);
      handle = HandleWrapper::Get(key);
      handle->Cancel();
    }

    do {
      SetEvent(g_wake_up_event);
    } while (WaitForSingleObject(g_file_handles_free_event, 50) == WAIT_TIMEOUT);
    delete handle;
  }
}

bool PlatformIsHandleValid(WatcherHandle handle) {
  return handle != INVALID_HANDLE_VALUE;
}

// We have no errno on Windows.
int PlatformInvalidHandleToErrorNumber(WatcherHandle handle) {
  return 0;
}
<commit_msg>Fix Windows build errors for Node.js 12, too.<commit_after>#include <algorithm>
#include <map>
#include <memory>

#include "common.h"

// Size of the buffer to store result of ReadDirectoryChangesW.
static const unsigned int kDirectoryWatcherBufferSize = 4096;

// Object template to create representation of WatcherHandle.
static Nan::Persistent<ObjectTemplate> g_object_template;

// Mutex for the HandleWrapper map.
static uv_mutex_t g_handle_wrap_map_mutex;

// The events to be waited on.
static std::vector<HANDLE> g_events;

// The dummy event to wakeup the thread.
static HANDLE g_wake_up_event;

// The dummy event to ensure we are not waiting on a file handle when destroying it.
static HANDLE g_file_handles_free_event;

struct ScopedLocker {
  explicit ScopedLocker(uv_mutex_t& mutex) : mutex_(&mutex) { uv_mutex_lock(mutex_); }
  ~ScopedLocker() { Unlock(); }

  void Unlock() { uv_mutex_unlock(mutex_); }

  uv_mutex_t* mutex_;
};

struct HandleWrapper {
  HandleWrapper(WatcherHandle handle, const char* path_str)
      : dir_handle(handle),
        path(strlen(path_str)),
        canceled(false) {
    memset(&overlapped, 0, sizeof(overlapped));
    overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_events.push_back(overlapped.hEvent);

    std::copy(path_str, path_str + path.size(), path.data());
    map_[overlapped.hEvent] = this;
  }

  ~HandleWrapper() {
    if (!canceled) {
      Cancel();
    }

    CloseHandle(dir_handle);
    CloseHandle(overlapped.hEvent);
  }

  void Cancel() {
    canceled = true;
    CancelIoEx(dir_handle, &overlapped);
    g_events.erase(std::remove(g_events.begin(), g_events.end(), overlapped.hEvent), g_events.end());
    map_.erase(overlapped.hEvent);
  }

  WatcherHandle dir_handle;
  std::vector<char> path;
  bool canceled;
  OVERLAPPED overlapped;
  char buffer[kDirectoryWatcherBufferSize];

  static HandleWrapper* Get(HANDLE key) { return map_[key]; }

  static std::map<WatcherHandle, HandleWrapper*> map_;
};

std::map<WatcherHandle, HandleWrapper*> HandleWrapper::map_;

struct WatcherEvent {
  EVENT_TYPE type;
  WatcherHandle handle;
  std::vector<char> new_path;
  std::vector<char> old_path;
};

static bool QueueReaddirchanges(HandleWrapper* handle) {
  return ReadDirectoryChangesW(handle->dir_handle,
                               handle->buffer,
                               kDirectoryWatcherBufferSize,
                               FALSE,
                               FILE_NOTIFY_CHANGE_FILE_NAME      |
                                 FILE_NOTIFY_CHANGE_DIR_NAME     |
                                 FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                                 FILE_NOTIFY_CHANGE_SIZE         |
                                 FILE_NOTIFY_CHANGE_LAST_WRITE   |
                                 FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                                 FILE_NOTIFY_CHANGE_CREATION     |
                                 FILE_NOTIFY_CHANGE_SECURITY,
                               NULL,
                               &handle->overlapped,
                               NULL) == TRUE;
}

Local<Value> WatcherHandleToV8Value(WatcherHandle handle) {
  Local<v8::Context> context = Nan::GetCurrentContext();
  Local<Value> value = Nan::New(g_object_template)->NewInstance(context).ToLocalChecked();
  Nan::SetInternalFieldPointer(value->ToObject(context).ToLocalChecked(), 0, handle);
  return value;
}

WatcherHandle V8ValueToWatcherHandle(Local<Value> value) {
  return reinterpret_cast<WatcherHandle>(Nan::GetInternalFieldPointer(
    value->ToObject(Nan::GetCurrentContext()).ToLocalChecked(), 0));
}

bool IsV8ValueWatcherHandle(Local<Value> value) {
  return value->IsObject() &&
    value->ToObject(Nan::GetCurrentContext()).ToLocalChecked()->InternalFieldCount() == 1;
}

void PlatformInit() {
  uv_mutex_init(&g_handle_wrap_map_mutex);

  g_file_handles_free_event = CreateEvent(NULL, TRUE, TRUE, NULL);
  g_wake_up_event = CreateEvent(NULL, FALSE, FALSE, NULL);
  g_events.push_back(g_wake_up_event);

  g_object_template.Reset(Nan::New<ObjectTemplate>());
  Nan::New(g_object_template)->SetInternalFieldCount(1);

  WakeupNewThread();
}

void PlatformThread() {
  while (true) {
    // Do not use g_events directly, since reallocation could happen when there
    // are new watchers adding to g_events when WaitForMultipleObjects is still
    // polling.
    ScopedLocker locker(g_handle_wrap_map_mutex);
    std::vector<HANDLE> copied_events(g_events);
    locker.Unlock();

    ResetEvent(g_file_handles_free_event);
    DWORD r = WaitForMultipleObjects(copied_events.size(),
                                     copied_events.data(),
                                     FALSE,
                                     INFINITE);
    SetEvent(g_file_handles_free_event);
    int i = r - WAIT_OBJECT_0;
    if (i >= 0 && i < copied_events.size()) {
      // It's a wake up event, there is no fs events.
      if (copied_events[i] == g_wake_up_event)
        continue;

      ScopedLocker locker(g_handle_wrap_map_mutex);

      HandleWrapper* handle = HandleWrapper::Get(copied_events[i]);
      if (!handle || handle->canceled)
        continue;

      DWORD bytes_transferred;
      if (!GetOverlappedResult(handle->dir_handle, &handle->overlapped, &bytes_transferred, FALSE))
        continue;
      if (bytes_transferred == 0)
        continue;

      std::vector<char> old_path;
      std::vector<WatcherEvent> events;

      DWORD offset = 0;
      while (true) {
        FILE_NOTIFY_INFORMATION* file_info =
            reinterpret_cast<FILE_NOTIFY_INFORMATION*>(handle->buffer + offset);

        // Emit events for children.
        EVENT_TYPE event = EVENT_NONE;
        switch (file_info->Action) {
          case FILE_ACTION_ADDED:
            event = EVENT_CHILD_CREATE;
            break;
          case FILE_ACTION_REMOVED:
            event = EVENT_CHILD_DELETE;
            break;
          case FILE_ACTION_RENAMED_OLD_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_RENAMED_NEW_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_MODIFIED:
            event = EVENT_CHILD_CHANGE;
            break;
        }

        if (event != EVENT_NONE) {
          // The FileNameLength is in "bytes", but the WideCharToMultiByte
          // requires the length to be in "characters"!
          int file_name_length_in_characters =
              file_info->FileNameLength / sizeof(wchar_t);

          char filename[MAX_PATH] = { 0 };
          int size = WideCharToMultiByte(CP_UTF8,
                                         0,
                                         file_info->FileName,
                                         file_name_length_in_characters,
                                         filename,
                                         MAX_PATH,
                                         NULL,
                                         NULL);

          // Convert file name to file path, same with:
          // path = handle->path + '\\' + filename
          std::vector<char> path(handle->path.size() + 1 + size);
          std::vector<char>::iterator iter = path.begin();
          iter = std::copy(handle->path.begin(), handle->path.end(), iter);
          *(iter++) = '\\';
          std::copy(filename, filename + size, iter);

          if (file_info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            // Do not send rename event until the NEW_NAME event, but still keep
            // a record of old name.
            old_path.swap(path);
          } else if (file_info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            e.old_path.swap(old_path);
            events.push_back(e);
          } else {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            events.push_back(e);
          }
        }

        if (file_info->NextEntryOffset == 0) break;
        offset += file_info->NextEntryOffset;
      }

      // Restart the monitor, it was reset after each call.
      QueueReaddirchanges(handle);

      locker.Unlock();

      for (size_t i = 0; i < events.size(); ++i)
        PostEventAndWait(events[i].type,
                         events[i].handle,
                         events[i].new_path,
                         events[i].old_path);
    }
  }
}

WatcherHandle PlatformWatch(const char* path) {
  wchar_t wpath[MAX_PATH] = { 0 };
  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);

  // Requires a directory, file watching is emulated in js.
  DWORD attr = GetFileAttributesW(wpath);
  if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
    return INVALID_HANDLE_VALUE;
  }

  WatcherHandle dir_handle = CreateFileW(wpath,
                                         FILE_LIST_DIRECTORY,
                                         FILE_SHARE_READ | FILE_SHARE_DELETE |
                                           FILE_SHARE_WRITE,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_FLAG_BACKUP_SEMANTICS |
                                           FILE_FLAG_OVERLAPPED,
                                         NULL);
  if (!PlatformIsHandleValid(dir_handle)) {
    return INVALID_HANDLE_VALUE;
  }

  std::unique_ptr<HandleWrapper> handle;
  {
    ScopedLocker locker(g_handle_wrap_map_mutex);
    handle.reset(new HandleWrapper(dir_handle, path));
  }

  if (!QueueReaddirchanges(handle.get())) {
    return INVALID_HANDLE_VALUE;
  }

  // Wake up the thread to add the new event.
  SetEvent(g_wake_up_event);

  // The pointer is leaked if no error happened.
  return handle.release()->overlapped.hEvent;
}

void PlatformUnwatch(WatcherHandle key) {
  if (PlatformIsHandleValid(key)) {
    HandleWrapper* handle;
    {
      ScopedLocker locker(g_handle_wrap_map_mutex);
      handle = HandleWrapper::Get(key);
      handle->Cancel();
    }

    do {
      SetEvent(g_wake_up_event);
    } while (WaitForSingleObject(g_file_handles_free_event, 50) == WAIT_TIMEOUT);
    delete handle;
  }
}

bool PlatformIsHandleValid(WatcherHandle handle) {
  return handle != INVALID_HANDLE_VALUE;
}

// We have no errno on Windows.
int PlatformInvalidHandleToErrorNumber(WatcherHandle handle) {
  return 0;
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <map>
#include <memory>

#include "common.h"

// Size of the buffer to store result of ReadDirectoryChangesW.
static const unsigned int kDirectoryWatcherBufferSize = 4096;

// Object template to create representation of WatcherHandle.
static Nan::Persistent<ObjectTemplate> g_object_template;

// Mutex for the HandleWrapper map.
static uv_mutex_t g_handle_wrap_map_mutex;

// The events to be waited on.
static std::vector<HANDLE> g_events;

// The dummy event to wakeup the thread.
static HANDLE g_wake_up_event;

struct ScopedLocker {
  explicit ScopedLocker(uv_mutex_t& mutex) : mutex_(&mutex) { uv_mutex_lock(mutex_); }
  ~ScopedLocker() { Unlock(); }

  void Unlock() { uv_mutex_unlock(mutex_); }

  uv_mutex_t* mutex_;
};

struct HandleWrapper {
  HandleWrapper(WatcherHandle handle, const char* path_str)
      : dir_handle(handle),
        path(strlen(path_str)),
        canceled(false) {
    memset(&overlapped, 0, sizeof(overlapped));
    overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_events.push_back(overlapped.hEvent);

    std::copy(path_str, path_str + path.size(), path.data());
    map_[overlapped.hEvent] = this;
  }

  ~HandleWrapper() {
    CloseFile();

    map_.erase(overlapped.hEvent);
    CloseHandle(overlapped.hEvent);
    g_events.erase(
        std::remove(g_events.begin(), g_events.end(), overlapped.hEvent),
        g_events.end());
  }

  void CloseFile() {
    if (dir_handle != INVALID_HANDLE_VALUE) {
      CloseHandle(dir_handle);
      dir_handle = INVALID_HANDLE_VALUE;
    }
  }

  WatcherHandle dir_handle;
  std::vector<char> path;
  bool canceled;
  OVERLAPPED overlapped;
  char buffer[kDirectoryWatcherBufferSize];

  static HandleWrapper* Get(HANDLE key) { return map_[key]; }

  static std::map<WatcherHandle, HandleWrapper*> map_;
};

std::map<WatcherHandle, HandleWrapper*> HandleWrapper::map_;

struct WatcherEvent {
  EVENT_TYPE type;
  WatcherHandle handle;
  std::vector<char> new_path;
  std::vector<char> old_path;
};

static bool QueueReaddirchanges(HandleWrapper* handle) {
  return ReadDirectoryChangesW(handle->dir_handle,
                               handle->buffer,
                               kDirectoryWatcherBufferSize,
                               FALSE,
                               FILE_NOTIFY_CHANGE_FILE_NAME      |
                                 FILE_NOTIFY_CHANGE_DIR_NAME     |
                                 FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                                 FILE_NOTIFY_CHANGE_SIZE         |
                                 FILE_NOTIFY_CHANGE_LAST_WRITE   |
                                 FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                                 FILE_NOTIFY_CHANGE_CREATION     |
                                 FILE_NOTIFY_CHANGE_SECURITY,
                               NULL,
                               &handle->overlapped,
                               NULL) == TRUE;
}

Local<Value> WatcherHandleToV8Value(WatcherHandle handle) {
  Local<Value> value = Nan::New(g_object_template)->NewInstance();
  Nan::SetInternalFieldPointer(value->ToObject(), 0, handle);
  return value;
}

WatcherHandle V8ValueToWatcherHandle(Local<Value> value) {
  return reinterpret_cast<WatcherHandle>(Nan::GetInternalFieldPointer(
      value->ToObject(), 0));
}

bool IsV8ValueWatcherHandle(Local<Value> value) {
  return value->IsObject() && value->ToObject()->InternalFieldCount() == 1;
}

void PlatformInit() {
  uv_mutex_init(&g_handle_wrap_map_mutex);

  g_wake_up_event = CreateEvent(NULL, FALSE, FALSE, NULL);
  g_events.push_back(g_wake_up_event);

  g_object_template.Reset(ObjectTemplate::New());
  Nan::New(g_object_template)->SetInternalFieldCount(1);

  WakeupNewThread();
}

void PlatformThread() {
  while (true) {
    // Do not use g_events directly, since reallocation could happen when there
    // are new watchers adding to g_events when WaitForMultipleObjects is still
    // polling.
    ScopedLocker locker(g_handle_wrap_map_mutex);
    std::vector<HANDLE> copied_events(g_events);
    locker.Unlock();

    DWORD r = WaitForMultipleObjects(copied_events.size(),
                                     copied_events.data(),
                                     FALSE,
                                     INFINITE);
    int i = r - WAIT_OBJECT_0;
    if (i >= 0 && i < copied_events.size()) {
      // It's a wake up event, there is no fs events.
      if (copied_events[i] == g_wake_up_event)
        continue;

      ScopedLocker locker(g_handle_wrap_map_mutex);

      HandleWrapper* handle = HandleWrapper::Get(copied_events[i]);
      if (!handle)
        continue;

      if (handle->canceled) {
        delete handle;
        continue;
      }

      DWORD bytes;
      if (GetOverlappedResult(handle->dir_handle,
                              &handle->overlapped,
                              &bytes,
                              FALSE) == FALSE)
        continue;

      std::vector<char> old_path;
      std::vector<WatcherEvent> events;

      DWORD offset = 0;
      while (true) {
        FILE_NOTIFY_INFORMATION* file_info =
            reinterpret_cast<FILE_NOTIFY_INFORMATION*>(handle->buffer + offset);

        // Emit events for children.
        EVENT_TYPE event = EVENT_NONE;
        switch (file_info->Action) {
          case FILE_ACTION_ADDED:
            event = EVENT_CHILD_CREATE;
            break;
          case FILE_ACTION_REMOVED:
            event = EVENT_CHILD_DELETE;
            break;
          case FILE_ACTION_RENAMED_OLD_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_RENAMED_NEW_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_MODIFIED:
            event = EVENT_CHILD_CHANGE;
            break;
        }

        if (event != EVENT_NONE) {
          // The FileNameLength is in "bytes", but the WideCharToMultiByte
          // requires the length to be in "characters"!
          int file_name_length_in_characters =
              file_info->FileNameLength / sizeof(wchar_t);

          char filename[MAX_PATH] = { 0 };
          int size = WideCharToMultiByte(CP_UTF8,
                                         0,
                                         file_info->FileName,
                                         file_name_length_in_characters,
                                         filename,
                                         MAX_PATH,
                                         NULL,
                                         NULL);

          // Convert file name to file path, same with:
          // path = handle->path + '\\' + filename
          std::vector<char> path(handle->path.size() + 1 + size);
          std::vector<char>::iterator iter = path.begin();
          iter = std::copy(handle->path.begin(), handle->path.end(), iter);
          *(iter++) = '\\';
          std::copy(filename, filename + size, iter);

          if (file_info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            // Do not send rename event until the NEW_NAME event, but still keep
            // a record of old name.
            old_path.swap(path);
          } else if (file_info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            e.old_path.swap(old_path);
            events.push_back(e);
          } else {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            events.push_back(e);
          }
        }

        if (file_info->NextEntryOffset == 0) break;
        offset += file_info->NextEntryOffset;
      }

      // Restart the monitor, it was reset after each call.
      QueueReaddirchanges(handle);

      locker.Unlock();

      for (size_t i = 0; i < events.size(); ++i)
        PostEventAndWait(events[i].type,
                         events[i].handle,
                         events[i].new_path,
                         events[i].old_path);
    }
  }
}

WatcherHandle PlatformWatch(const char* path) {
  wchar_t wpath[MAX_PATH] = { 0 };
  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);

  // Requires a directory, file watching is emulated in js.
  DWORD attr = GetFileAttributesW(wpath);
  if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
    return INVALID_HANDLE_VALUE;
  }

  WatcherHandle dir_handle = CreateFileW(wpath,
                                         FILE_LIST_DIRECTORY,
                                         FILE_SHARE_READ | FILE_SHARE_DELETE |
                                           FILE_SHARE_WRITE,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_FLAG_BACKUP_SEMANTICS |
                                           FILE_FLAG_OVERLAPPED,
                                         NULL);
  if (!PlatformIsHandleValid(dir_handle)) {
    return INVALID_HANDLE_VALUE;
  }

  std::unique_ptr<HandleWrapper> handle;
  {
    ScopedLocker locker(g_handle_wrap_map_mutex);
    handle.reset(new HandleWrapper(dir_handle, path));
  }

  if (!QueueReaddirchanges(handle.get())) {
    return INVALID_HANDLE_VALUE;
  }

  // Wake up the thread to add the new event.
  SetEvent(g_wake_up_event);

  // The pointer is leaked if no error happened.
  return handle.release()->overlapped.hEvent;
}

void PlatformUnwatch(WatcherHandle key) {
  if (PlatformIsHandleValid(key)) {
    ScopedLocker locker(g_handle_wrap_map_mutex);

    HandleWrapper* handle = HandleWrapper::Get(key);
    handle->canceled = true;
    CancelIoEx(handle->dir_handle, &handle->overlapped);
    handle->CloseFile();
  }
}

bool PlatformIsHandleValid(WatcherHandle handle) {
  return handle != INVALID_HANDLE_VALUE;
}

// We have no errno on Windows.
int PlatformInvalidHandleToErrorNumber(WatcherHandle handle) {
  return 0;
}
<commit_msg>Fix race condition on Windows<commit_after>#include <algorithm>
#include <map>
#include <memory>

#include "common.h"

// Size of the buffer to store result of ReadDirectoryChangesW.
static const unsigned int kDirectoryWatcherBufferSize = 4096;

// Object template to create representation of WatcherHandle.
static Nan::Persistent<ObjectTemplate> g_object_template;

// Mutex for the HandleWrapper map.
static uv_mutex_t g_handle_wrap_map_mutex;

// The events to be waited on.
static std::vector<HANDLE> g_events;

// The dummy event to wakeup the thread.
static HANDLE g_wake_up_event;

// The dummy event to ensure we are not waiting on a file handle when destroying it.
static HANDLE g_file_handles_free_event;

struct ScopedLocker {
  explicit ScopedLocker(uv_mutex_t& mutex) : mutex_(&mutex) { uv_mutex_lock(mutex_); }
  ~ScopedLocker() { Unlock(); }

  void Unlock() { uv_mutex_unlock(mutex_); }

  uv_mutex_t* mutex_;
};

struct HandleWrapper {
  HandleWrapper(WatcherHandle handle, const char* path_str)
      : dir_handle(handle),
        path(strlen(path_str)),
        canceled(false) {
    memset(&overlapped, 0, sizeof(overlapped));
    overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_events.push_back(overlapped.hEvent);

    std::copy(path_str, path_str + path.size(), path.data());
    map_[overlapped.hEvent] = this;
  }

  ~HandleWrapper() {
    if (!canceled) {
      Cancel();
    }

    CloseHandle(dir_handle);
    CloseHandle(overlapped.hEvent);
  }

  void Cancel() {
    canceled = true;
    CancelIoEx(dir_handle, &overlapped);
    g_events.erase(std::remove(g_events.begin(), g_events.end(), overlapped.hEvent), g_events.end());
    map_.erase(overlapped.hEvent);
  }

  WatcherHandle dir_handle;
  std::vector<char> path;
  bool canceled;
  OVERLAPPED overlapped;
  char buffer[kDirectoryWatcherBufferSize];

  static HandleWrapper* Get(HANDLE key) { return map_[key]; }

  static std::map<WatcherHandle, HandleWrapper*> map_;
};

std::map<WatcherHandle, HandleWrapper*> HandleWrapper::map_;

struct WatcherEvent {
  EVENT_TYPE type;
  WatcherHandle handle;
  std::vector<char> new_path;
  std::vector<char> old_path;
};

static bool QueueReaddirchanges(HandleWrapper* handle) {
  return ReadDirectoryChangesW(handle->dir_handle,
                               handle->buffer,
                               kDirectoryWatcherBufferSize,
                               FALSE,
                               FILE_NOTIFY_CHANGE_FILE_NAME      |
                                 FILE_NOTIFY_CHANGE_DIR_NAME     |
                                 FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                                 FILE_NOTIFY_CHANGE_SIZE         |
                                 FILE_NOTIFY_CHANGE_LAST_WRITE   |
                                 FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                                 FILE_NOTIFY_CHANGE_CREATION     |
                                 FILE_NOTIFY_CHANGE_SECURITY,
                               NULL,
                               &handle->overlapped,
                               NULL) == TRUE;
}

Local<Value> WatcherHandleToV8Value(WatcherHandle handle) {
  Local<Value> value = Nan::New(g_object_template)->NewInstance();
  Nan::SetInternalFieldPointer(value->ToObject(), 0, handle);
  return value;
}

WatcherHandle V8ValueToWatcherHandle(Local<Value> value) {
  return reinterpret_cast<WatcherHandle>(Nan::GetInternalFieldPointer(
      value->ToObject(), 0));
}

bool IsV8ValueWatcherHandle(Local<Value> value) {
  return value->IsObject() && value->ToObject()->InternalFieldCount() == 1;
}

void PlatformInit() {
  uv_mutex_init(&g_handle_wrap_map_mutex);

  g_file_handles_free_event = CreateEvent(NULL, TRUE, TRUE, NULL);
  g_wake_up_event = CreateEvent(NULL, FALSE, FALSE, NULL);
  g_events.push_back(g_wake_up_event);

  g_object_template.Reset(ObjectTemplate::New());
  Nan::New(g_object_template)->SetInternalFieldCount(1);

  WakeupNewThread();
}

void PlatformThread() {
  while (true) {
    // Do not use g_events directly, since reallocation could happen when there
    // are new watchers adding to g_events when WaitForMultipleObjects is still
    // polling.
    ScopedLocker locker(g_handle_wrap_map_mutex);
    std::vector<HANDLE> copied_events(g_events);
    locker.Unlock();

    ResetEvent(g_file_handles_free_event);
    DWORD r = WaitForMultipleObjects(copied_events.size(),
                                     copied_events.data(),
                                     FALSE,
                                     INFINITE);
    SetEvent(g_file_handles_free_event);
    int i = r - WAIT_OBJECT_0;
    if (i >= 0 && i < copied_events.size()) {
      // It's a wake up event, there is no fs events.
      if (copied_events[i] == g_wake_up_event)
        continue;

      ScopedLocker locker(g_handle_wrap_map_mutex);

      HandleWrapper* handle = HandleWrapper::Get(copied_events[i]);
      if (!handle || handle->canceled)
        continue;

      DWORD bytes;
      if (GetOverlappedResult(handle->dir_handle,
                              &handle->overlapped,
                              &bytes,
                              FALSE) == FALSE)
        continue;

      std::vector<char> old_path;
      std::vector<WatcherEvent> events;

      DWORD offset = 0;
      while (true) {
        FILE_NOTIFY_INFORMATION* file_info =
            reinterpret_cast<FILE_NOTIFY_INFORMATION*>(handle->buffer + offset);

        // Emit events for children.
        EVENT_TYPE event = EVENT_NONE;
        switch (file_info->Action) {
          case FILE_ACTION_ADDED:
            event = EVENT_CHILD_CREATE;
            break;
          case FILE_ACTION_REMOVED:
            event = EVENT_CHILD_DELETE;
            break;
          case FILE_ACTION_RENAMED_OLD_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_RENAMED_NEW_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_MODIFIED:
            event = EVENT_CHILD_CHANGE;
            break;
        }

        if (event != EVENT_NONE) {
          // The FileNameLength is in "bytes", but the WideCharToMultiByte
          // requires the length to be in "characters"!
          int file_name_length_in_characters =
              file_info->FileNameLength / sizeof(wchar_t);

          char filename[MAX_PATH] = { 0 };
          int size = WideCharToMultiByte(CP_UTF8,
                                         0,
                                         file_info->FileName,
                                         file_name_length_in_characters,
                                         filename,
                                         MAX_PATH,
                                         NULL,
                                         NULL);

          // Convert file name to file path, same with:
          // path = handle->path + '\\' + filename
          std::vector<char> path(handle->path.size() + 1 + size);
          std::vector<char>::iterator iter = path.begin();
          iter = std::copy(handle->path.begin(), handle->path.end(), iter);
          *(iter++) = '\\';
          std::copy(filename, filename + size, iter);

          if (file_info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            // Do not send rename event until the NEW_NAME event, but still keep
            // a record of old name.
            old_path.swap(path);
          } else if (file_info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            e.old_path.swap(old_path);
            events.push_back(e);
          } else {
            WatcherEvent e = { event, handle->overlapped.hEvent };
            e.new_path.swap(path);
            events.push_back(e);
          }
        }

        if (file_info->NextEntryOffset == 0) break;
        offset += file_info->NextEntryOffset;
      }

      // Restart the monitor, it was reset after each call.
      QueueReaddirchanges(handle);

      locker.Unlock();

      for (size_t i = 0; i < events.size(); ++i)
        PostEventAndWait(events[i].type,
                         events[i].handle,
                         events[i].new_path,
                         events[i].old_path);
    }
  }
}

WatcherHandle PlatformWatch(const char* path) {
  wchar_t wpath[MAX_PATH] = { 0 };
  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);

  // Requires a directory, file watching is emulated in js.
  DWORD attr = GetFileAttributesW(wpath);
  if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
    return INVALID_HANDLE_VALUE;
  }

  WatcherHandle dir_handle = CreateFileW(wpath,
                                         FILE_LIST_DIRECTORY,
                                         FILE_SHARE_READ | FILE_SHARE_DELETE |
                                           FILE_SHARE_WRITE,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_FLAG_BACKUP_SEMANTICS |
                                           FILE_FLAG_OVERLAPPED,
                                         NULL);
  if (!PlatformIsHandleValid(dir_handle)) {
    return INVALID_HANDLE_VALUE;
  }

  std::unique_ptr<HandleWrapper> handle;
  {
    ScopedLocker locker(g_handle_wrap_map_mutex);
    handle.reset(new HandleWrapper(dir_handle, path));
  }

  if (!QueueReaddirchanges(handle.get())) {
    return INVALID_HANDLE_VALUE;
  }

  // Wake up the thread to add the new event.
  SetEvent(g_wake_up_event);

  // The pointer is leaked if no error happened.
  return handle.release()->overlapped.hEvent;
}

void PlatformUnwatch(WatcherHandle key) {
  if (PlatformIsHandleValid(key)) {
    HandleWrapper* handle;
    {
      ScopedLocker locker(g_handle_wrap_map_mutex);
      handle = HandleWrapper::Get(key);
      handle->Cancel();
    }

    do {
      SetEvent(g_wake_up_event);
    } while (WaitForSingleObject(g_file_handles_free_event, 50) == WAIT_TIMEOUT);
    delete handle;
  }
}

bool PlatformIsHandleValid(WatcherHandle handle) {
  return handle != INVALID_HANDLE_VALUE;
}

// We have no errno on Windows.
int PlatformInvalidHandleToErrorNumber(WatcherHandle handle) {
  return 0;
}
<|endoftext|>


<commit_before>#include "../src/smiles.h"
#include "../src/fileio/molecules.h"

#include "test.h"

using namespace Helium;

void test_parse_smiles()
{
  HeMol mol;

  hemol_from_smiles("C", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(12, get_mass(mol, get_atom(mol, 0)));
  COMPARE(0, get_charge(mol, get_atom(mol, 0)));
  COMPARE(4, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("N", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(7, get_element(mol, get_atom(mol, 0)));
  COMPARE(14, get_mass(mol, get_atom(mol, 0)));
  COMPARE(0, get_charge(mol, get_atom(mol, 0)));
  COMPARE(3, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("[13CH4]", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(13, get_mass(mol, get_atom(mol, 0)));
  COMPARE(0, get_charge(mol, get_atom(mol, 0)));
  COMPARE(4, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("[NH4+]", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(7, get_element(mol, get_atom(mol, 0)));
  COMPARE(14, get_mass(mol, get_atom(mol, 0)));
  COMPARE(1, get_charge(mol, get_atom(mol, 0)));
  COMPARE(4, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("CC", mol);
  COMPARE(2, num_atoms(mol));
  COMPARE(1, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(6, get_element(mol, get_atom(mol, 1)));
  COMPARE(1, get_order(mol, get_bond(mol, 0)));

  hemol_from_smiles("C=C", mol);
  COMPARE(2, num_atoms(mol));
  COMPARE(1, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(6, get_element(mol, get_atom(mol, 1)));
  COMPARE(2, get_order(mol, get_bond(mol, 0)));
  COMPARE(2, get_bond(mol, 0).order());

  hemol_from_smiles("c1ccccc1", mol);
  COMPARE(6, mol.numAtoms());
  COMPARE(6, mol.numBonds());
}

void test_write_smiles(const std::string &smiles)
{
  HeMol mol = hemol_from_smiles(smiles);
  COMPARE(smiles, write_smiles(mol));
}

int main()
{
  test_parse_smiles();

  // simple chain
  test_write_smiles("CCCCC");
  // branches
  test_write_smiles("CCC(CC)CC");
  test_write_smiles("CCC(CC)(CC)CC");
  test_write_smiles("CCC(CC(CC)CC)CC");
  // rings
  test_write_smiles("C1CCCC1");
  test_write_smiles("C1CCCC1C2CCCC2");
  // aromatic
  test_write_smiles("n1ccccc1");

  test_write_smiles("[13C]");
  test_write_smiles("[C+]");
}
<commit_msg>Test reading multi-component smiles (SMILES.SMILES)<commit_after>#include "../src/smiles.h"
#include "../src/fileio/molecules.h"
#include "../src/algorithms/components.h"

#include "test.h"

using namespace Helium;

void test_parse_smiles()
{
  HeMol mol;

  hemol_from_smiles("C", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(12, get_mass(mol, get_atom(mol, 0)));
  COMPARE(0, get_charge(mol, get_atom(mol, 0)));
  COMPARE(4, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("N", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(7, get_element(mol, get_atom(mol, 0)));
  COMPARE(14, get_mass(mol, get_atom(mol, 0)));
  COMPARE(0, get_charge(mol, get_atom(mol, 0)));
  COMPARE(3, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("[13CH4]", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(13, get_mass(mol, get_atom(mol, 0)));
  COMPARE(0, get_charge(mol, get_atom(mol, 0)));
  COMPARE(4, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("[NH4+]", mol);
  COMPARE(1, num_atoms(mol));
  COMPARE(0, num_bonds(mol));
  COMPARE(7, get_element(mol, get_atom(mol, 0)));
  COMPARE(14, get_mass(mol, get_atom(mol, 0)));
  COMPARE(1, get_charge(mol, get_atom(mol, 0)));
  COMPARE(4, num_hydrogens(mol, get_atom(mol, 0)));

  hemol_from_smiles("CC", mol);
  COMPARE(2, num_atoms(mol));
  COMPARE(1, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(6, get_element(mol, get_atom(mol, 1)));
  COMPARE(1, get_order(mol, get_bond(mol, 0)));

  hemol_from_smiles("C=C", mol);
  COMPARE(2, num_atoms(mol));
  COMPARE(1, num_bonds(mol));
  COMPARE(6, get_element(mol, get_atom(mol, 0)));
  COMPARE(6, get_element(mol, get_atom(mol, 1)));
  COMPARE(2, get_order(mol, get_bond(mol, 0)));
  COMPARE(2, get_bond(mol, 0).order());

  hemol_from_smiles("c1ccccc1", mol);
  COMPARE(6, mol.numAtoms());
  COMPARE(6, mol.numBonds());

  hemol_from_smiles("CC.CC", mol);
  COMPARE(4, mol.numAtoms());
  COMPARE(2, mol.numBonds());
  COMPARE(2, num_connected_components(mol));
}

void test_write_smiles(const std::string &smiles)
{
  HeMol mol = hemol_from_smiles(smiles);
  COMPARE(smiles, write_smiles(mol));
}

int main()
{
  test_parse_smiles();

  // simple chain
  test_write_smiles("CCCCC");
  // branches
  test_write_smiles("CCC(CC)CC");
  test_write_smiles("CCC(CC)(CC)CC");
  test_write_smiles("CCC(CC(CC)CC)CC");
  // rings
  test_write_smiles("C1CCCC1");
  test_write_smiles("C1CCCC1C2CCCC2");
  // aromatic
  test_write_smiles("n1ccccc1");

  test_write_smiles("[13C]");
  test_write_smiles("[C+]");
}
<|endoftext|>


<commit_before>#include "catch.hpp"

#include <shadow.hpp>

#include <algorithm>
#include <vector>

void
fun1(void)
{
}

// value parameter
void
fun2(int i)
{
}

int
fun3(const int& i)
{
    return i * 2;
}

void
fun4(int& i)
{
    i *= 2;
}

int
fun5(const int* i)
{
    return *i * 2;
}

void
fun6(int* i)
{
    *i *= 2;
}

void
fun7(int& i)
{
    i *= 2;
}

void
fun7(double& d)
{
    d *= 2.0;
}

namespace test_space
{
REGISTER_TYPE_BEGIN()
REGISTER_TYPE_END()

REGISTER_FREE_FUNCTION(fun1)
REGISTER_FREE_FUNCTION(fun2)
REGISTER_FREE_FUNCTION(fun3)
REGISTER_FREE_FUNCTION(fun4)
REGISTER_FREE_FUNCTION(fun5)
REGISTER_FREE_FUNCTION(fun6)

REGISTER_FREE_FUNCTION_EXPLICIT(fun7, void, int&)
REGISTER_FREE_FUNCTION_EXPLICIT(fun7, void, double&)

SHADOW_INIT()
}

TEST_CASE("get iterator pair to all available functions",
          "[reflection_manager]")
{
    auto ff_pair = test_space::manager.free_functions();

    SECTION("find fun1")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun1");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun1 through reflection system")
        {
            auto fun1_refl = *found;

            // call with no param overload
            auto res = fun1_refl();

            // call with regular overload
            std::vector<shadow::variable> noargs;
            auto res2 = fun1_refl(noargs.begin(), noargs.end());

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(res2.type().name() == std::string("void"));
        }
    }

    SECTION("find fun2")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun2");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun2 through reflection system")
        {
            auto fun2_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(23)};

            auto res = fun2_refl(args.begin(), args.end());
            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 23);
        }
    }

    SECTION("find fun3")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                using namespace std::string_literals;
                return ff.name() == "fun3"s;
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun3 through reflection system")
        {
            auto fun3_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun3_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("int"));
            REQUIRE(test_space::static_value_cast<int>(res) == 20);
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 10);
        }
    }

    SECTION("find fun4")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                using namespace std::string_literals;
                return ff.name() == "fun4"s;
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun4 through reflection system")
        {
            auto fun4_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun4_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 20);
        }
    }

    SECTION("find fun5")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                using namespace std::string_literals;
                return ff.name() == "fun5"s;
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun5 through reflection system")
        {
            auto fun5_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun5_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("int"));
            REQUIRE(test_space::static_value_cast<int>(res) == 20);
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 10);
        }
    }

    SECTION("find fun6")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun6");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun6 through reflection system")
        {
            auto fun6_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun6_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 20);
        }
    }

    SECTION("find fun7 with int& param")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun7") &&
                       ff.parameter_types().first->name() == std::string("int");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun7(int&) through reflection system")
        {
            auto fun7_refl = *found;

            auto anint = test_space::static_create<int>(200);

            auto res = fun7_refl(&anint, &anint + 1);

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(anint) == 400);
        }
    }

    SECTION("find fun7 with double& param")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun7") &&
                       ff.parameter_types().first->name() ==
                           std::string("double");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun7(double&) through reflection system")
        {
            auto fun7_refl = *found;

            auto adouble = test_space::static_create<double>(1.5);

            auto res = fun7_refl(&adouble, &adouble + 1);

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<double>(adouble) == Approx(3.0));
        }
    }
}
<commit_msg>Add unit test for overloaded free function taking pointer param. 	modified:   tests/test_free_function_api.cpp<commit_after>#include "catch.hpp"

#include <shadow.hpp>

#include <algorithm>
#include <vector>

void
fun1(void)
{
}

// value parameter
void
fun2(int i)
{
}

int
fun3(const int& i)
{
    return i * 2;
}

void
fun4(int& i)
{
    i *= 2;
}

int
fun5(const int* i)
{
    return *i * 2;
}

void
fun6(int* i)
{
    *i *= 2;
}

void
fun7(int& i)
{
    i *= 2;
}

void
fun7(double& d)
{
    d *= 2.0;
}

void
fun7(char* c)
{
    *c += 2;
}

namespace test_space
{
REGISTER_TYPE_BEGIN()
REGISTER_TYPE_END()

REGISTER_FREE_FUNCTION(fun1)
REGISTER_FREE_FUNCTION(fun2)
REGISTER_FREE_FUNCTION(fun3)
REGISTER_FREE_FUNCTION(fun4)
REGISTER_FREE_FUNCTION(fun5)
REGISTER_FREE_FUNCTION(fun6)

REGISTER_FREE_FUNCTION_EXPLICIT(fun7, void, int&)
REGISTER_FREE_FUNCTION_EXPLICIT(fun7, void, double&)
REGISTER_FREE_FUNCTION_EXPLICIT(fun7, void, char*)

SHADOW_INIT()
}

TEST_CASE("get iterator pair to all available functions",
          "[reflection_manager]")
{
    auto ff_pair = test_space::manager.free_functions();

    SECTION("find fun1")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun1");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun1 through reflection system")
        {
            auto fun1_refl = *found;

            // call with no param overload
            auto res = fun1_refl();

            // call with regular overload
            std::vector<shadow::variable> noargs;
            auto res2 = fun1_refl(noargs.begin(), noargs.end());

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(res2.type().name() == std::string("void"));
        }
    }

    SECTION("find fun2")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun2");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun2 through reflection system")
        {
            auto fun2_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(23)};

            auto res = fun2_refl(args.begin(), args.end());
            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 23);
        }
    }

    SECTION("find fun3")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                using namespace std::string_literals;
                return ff.name() == "fun3"s;
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun3 through reflection system")
        {
            auto fun3_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun3_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("int"));
            REQUIRE(test_space::static_value_cast<int>(res) == 20);
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 10);
        }
    }

    SECTION("find fun4")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                using namespace std::string_literals;
                return ff.name() == "fun4"s;
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun4 through reflection system")
        {
            auto fun4_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun4_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 20);
        }
    }

    SECTION("find fun5")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                using namespace std::string_literals;
                return ff.name() == "fun5"s;
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun5 through reflection system")
        {
            auto fun5_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun5_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("int"));
            REQUIRE(test_space::static_value_cast<int>(res) == 20);
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 10);
        }
    }

    SECTION("find fun6")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun6");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun6 through reflection system")
        {
            auto fun6_refl = *found;

            std::vector<shadow::variable> args{
                test_space::static_create<int>(10)};

            auto res = fun6_refl(args.begin(), args.end());

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(args[0]) == 20);
        }
    }

    SECTION("find fun7 with int& param")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun7") &&
                       ff.parameter_types().first->name() == std::string("int");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun7(int&) through reflection system")
        {
            auto fun7_refl = *found;

            auto anint = test_space::static_create<int>(200);

            auto res = fun7_refl(&anint, &anint + 1);

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<int>(anint) == 400);
        }
    }

    SECTION("find fun7 with double& param")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun7") &&
                       ff.parameter_types().first->name() ==
                           std::string("double");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun7(double&) through reflection system")
        {
            auto fun7_refl = *found;

            auto adouble = test_space::static_create<double>(1.5);

            auto res = fun7_refl(&adouble, &adouble + 1);

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<double>(adouble) ==
                    Approx(3.0));
        }
    }

    SECTION("find fun7 with char* param")
    {
        auto found =
            std::find_if(ff_pair.first, ff_pair.second, [](const auto& ff) {
                return ff.name() == std::string("fun7") &&
                       ff.parameter_types().first->name() ==
                           std::string("char");
            });

        REQUIRE(found != ff_pair.second);

        SECTION("call fun7(char*) through reflection system")
        {
            auto fun7_refl = *found;

            auto achar = test_space::static_create<char>('a');

            auto res = fun7_refl(&achar, &achar + 1);

            REQUIRE(res.type().name() == std::string("void"));
            REQUIRE(test_space::static_value_cast<char>(achar) == 'c');
        }
    }
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#if defined(__APPLE__)
#include <pthread.h>
#elif defined(__linux__)
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#endif

#include "phosphor/platform/thread.h"

namespace phosphor {
    namespace platform {
        THREAD_LOCAL uint64_t thread_id = 0;

        uint64_t getCurrentThreadID() {
#if defined(__APPLE__)
            return pthread_mach_thread_np(pthread_self());
#elif defined(__linux__)
            return syscall(__NR_gettid);
#elif defined(_WIN32)
            return GetCurrentThreadId(void);
#else
#error Unsupported platform, no way to get threadid
#endif
        }
    }
}
<commit_msg>Fix Windows GetCurrentThreadID call<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#if defined(__APPLE__)
#include <pthread.h>
#elif defined(__linux__)
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#endif

#include "phosphor/platform/thread.h"

namespace phosphor {
    namespace platform {
        THREAD_LOCAL uint64_t thread_id = 0;

        uint64_t getCurrentThreadID() {
#if defined(__APPLE__)
            return pthread_mach_thread_np(pthread_self());
#elif defined(__linux__)
            return syscall(__NR_gettid);
#elif defined(_WIN32)
            return GetCurrentThreadId();
#else
#error Unsupported platform, no way to get threadid
#endif
        }
    }
}
<|endoftext|>