<commit_before>#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <random>
#include <sstream>
#include <string>
#include <unistd.h>
#include "DataBuffer.hpp"
#include "FoundationDSL.hpp"
#include "GenericProgressBar.hpp"
#include "HexUtil.hpp"
#include "MainEC.hpp"
#include "SHA_1.hpp"
#include "SHA_224.hpp"
#include "SHA_256.hpp"
#include "SHA_384.hpp"
#include "SHA_512.hpp"
#include "SHA_512_224.hpp"
#include "SHA_512_256.hpp"

using namespace snarkfront;
using namespace std;

void printUsage(const char* exeName) {
    cout << "usage: " << exeName
         << " -c BN128|Edwards -b 1|224|256|384|512|512_224|512_256 [-r]" << endl
         << endl
         << "text from standard input:" << endl
         << "echo \"abc\" | " << exeName
         << " -c BN128|Edwards -b 1|224|256|384|512|512_224|512_256" << endl
         << endl
         << "random data:" << endl
         << exeName
         << " -c BN128|Edwards -b 1|224|256|384|512|512_224|512_256 -r" << endl;

    exit(EXIT_FAILURE);
}

template <typename ZK_SHA, typename EVAL_SHA>
bool runTest(const bool stdInput)
{
    DataBufferStream buf;

    if (stdInput) {
        // fill message block(s) from standard input
        cin >> buf;

        ZK_SHA::padMessage(buf);

    } else {
        // fill entire message block with random data
        random_device rd;
        const size_t M = sizeof(typename EVAL_SHA::WordType) / sizeof(uint32_t);
        for (size_t i = 0; i < 16 * M; ++i)
            buf->push32(rd());

        // no padding is not SHA-2 standard (compression function only)
    }

    // print buffer
    HexDumper dump(cout);
    dump.print(buf);

    // compute message digest (adds padding if necessary)
    const auto zk_digest = digest(ZK_SHA(), buf);
    const auto eval_digest = digest(EVAL_SHA(), buf);

    assert(zk_digest.size() == eval_digest.size());

    DataBuffer<PrintHex> hexpr(cout, false);

    // compare message digest values
    bool ok = true;
    for (size_t i = 0; i < zk_digest.size(); ++i) {
        if (zk_digest[i]->value() != eval_digest[i]) {
            ok = false;
            cout << "digest[" << i << "] error zk: ";
            hexpr.push(zk_digest[i]->value());
            cout << " test: ";
            hexpr.push(eval_digest[i]);
            cout << endl;
        }
    }

    // message digest proof constraint
    assert_true(zk_digest == eval_digest);

    cout << "digest " << asciiHex(eval_digest, true) << endl;

    return ok;
}

template <typename PAIRING>
bool runTest(const string& shaBits, const bool stdInput)
{
    reset<PAIRING>();

    bool valueOK = false;
    typedef typename PAIRING::Fr FR;

    if ("1" == shaBits) {
        valueOK = runTest<zk::SHA1<FR>, eval::SHA1>(stdInput);
    } else if ("224" == shaBits) {
        valueOK = runTest<zk::SHA224<FR>, eval::SHA224>(stdInput);
    } else if ("256" == shaBits) {
        valueOK = runTest<zk::SHA256<FR>, eval::SHA256>(stdInput);
    } else if ("384" == shaBits) {
        valueOK = runTest<zk::SHA384<FR>, eval::SHA384>(stdInput);
    } else if ("512" == shaBits) {
        valueOK = runTest<zk::SHA512<FR>, eval::SHA512>(stdInput);
    } else if ("512_224" == shaBits) {
        valueOK = runTest<zk::SHA512_224<FR>, eval::SHA512_224>(stdInput);
    } else if ("512_256" == shaBits) {
        valueOK = runTest<zk::SHA512_256<FR>, eval::SHA512_256>(stdInput);
    }

    cout << "variable count " << variable_count<PAIRING>() << endl;

    GenericProgressBar progress1(cerr), progress2(cerr, 50);

    cerr << "generate key pair";
    const auto key = keypair<PAIRING>(progress2);
    cerr << endl;

    const auto in = input<PAIRING>();

    cerr << "generate proof";
    const auto p = proof(key, progress2);
    cerr << endl;

    cerr << "verify proof ";
    const bool proofOK = verify(key, in, p, progress1);
    cerr << endl;

    return valueOK && proofOK;
}

int main(int argc, char *argv[])
{
    // command line switches
    string ellipticCurve, shaBits;
    bool stdInput = true;
    int opt;
    while (-1 != (opt = getopt(argc, argv, "c:b:r"))) {
        switch (opt) {
        case ('c') :
            ellipticCurve = optarg;
            break;
        case ('b') :
            shaBits = optarg;
            break;
        case ('r') :
            stdInput = false; // use random data
            break;
        }
    }

    bool result;

    if ("BN128" == ellipticCurve) {
        // Barreto-Naehrig 128 bits
        init_BN128();
        result = runTest<BN128_PAIRING>(shaBits, stdInput);

    } else if ("Edwards" == ellipticCurve) {
        // Edwards 80 bits
        init_Edwards();
        result = runTest<EDWARDS_PAIRING>(shaBits, stdInput);

    } else {
        // no elliptic curve specified
        printUsage(argv[0]);
    }

    cout << "test " << (result ? "passed" : "failed") << endl;

    exit(EXIT_SUCCESS);
}
<commit_msg>use snarkfront.hpp<commit_after>#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <random>
#include <sstream>
#include <string>
#include <unistd.h>
#include "snarkfront.hpp"

using namespace snarkfront;
using namespace std;

void printUsage(const char* exeName) {
    cout << "usage: " << exeName
         << " -c BN128|Edwards -b 1|224|256|384|512|512_224|512_256 [-r]" << endl
         << endl
         << "text from standard input:" << endl
         << "echo \"abc\" | " << exeName
         << " -c BN128|Edwards -b 1|224|256|384|512|512_224|512_256" << endl
         << endl
         << "random data:" << endl
         << exeName
         << " -c BN128|Edwards -b 1|224|256|384|512|512_224|512_256 -r" << endl;

    exit(EXIT_FAILURE);
}

template <typename ZK_SHA, typename EVAL_SHA>
bool runTest(const bool stdInput)
{
    DataBufferStream buf;

    if (stdInput) {
        // fill message block(s) from standard input
        cin >> buf;

        ZK_SHA::padMessage(buf);

    } else {
        // fill entire message block with random data
        random_device rd;
        const size_t M = sizeof(typename EVAL_SHA::WordType) / sizeof(uint32_t);
        for (size_t i = 0; i < 16 * M; ++i)
            buf->push32(rd());

        // no padding is not SHA-2 standard (compression function only)
    }

    // print buffer
    HexDumper dump(cout);
    dump.print(buf);

    // compute message digest (adds padding if necessary)
    const auto zk_digest = digest(ZK_SHA(), buf);
    const auto eval_digest = digest(EVAL_SHA(), buf);

    assert(zk_digest.size() == eval_digest.size());

    DataBuffer<PrintHex> hexpr(cout, false);

    // compare message digest values
    bool ok = true;
    for (size_t i = 0; i < zk_digest.size(); ++i) {
        if (zk_digest[i]->value() != eval_digest[i]) {
            ok = false;
            cout << "digest[" << i << "] error zk: ";
            hexpr.push(zk_digest[i]->value());
            cout << " test: ";
            hexpr.push(eval_digest[i]);
            cout << endl;
        }
    }

    // message digest proof constraint
    assert_true(zk_digest == eval_digest);

    cout << "digest " << asciiHex(eval_digest, true) << endl;

    return ok;
}

template <typename PAIRING>
bool runTest(const string& shaBits, const bool stdInput)
{
    reset<PAIRING>();

    bool valueOK = false;
    typedef typename PAIRING::Fr FR;

    if ("1" == shaBits) {
        valueOK = runTest<zk::SHA1<FR>, eval::SHA1>(stdInput);
    } else if ("224" == shaBits) {
        valueOK = runTest<zk::SHA224<FR>, eval::SHA224>(stdInput);
    } else if ("256" == shaBits) {
        valueOK = runTest<zk::SHA256<FR>, eval::SHA256>(stdInput);
    } else if ("384" == shaBits) {
        valueOK = runTest<zk::SHA384<FR>, eval::SHA384>(stdInput);
    } else if ("512" == shaBits) {
        valueOK = runTest<zk::SHA512<FR>, eval::SHA512>(stdInput);
    } else if ("512_224" == shaBits) {
        valueOK = runTest<zk::SHA512_224<FR>, eval::SHA512_224>(stdInput);
    } else if ("512_256" == shaBits) {
        valueOK = runTest<zk::SHA512_256<FR>, eval::SHA512_256>(stdInput);
    }

    cout << "variable count " << variable_count<PAIRING>() << endl;

    GenericProgressBar progress1(cerr), progress2(cerr, 50);

    cerr << "generate key pair";
    const auto key = keypair<PAIRING>(progress2);
    cerr << endl;

    const auto in = input<PAIRING>();

    cerr << "generate proof";
    const auto p = proof(key, progress2);
    cerr << endl;

    cerr << "verify proof ";
    const bool proofOK = verify(key, in, p, progress1);
    cerr << endl;

    return valueOK && proofOK;
}

int main(int argc, char *argv[])
{
    // command line switches
    string ellipticCurve, shaBits;
    bool stdInput = true;
    int opt;
    while (-1 != (opt = getopt(argc, argv, "c:b:r"))) {
        switch (opt) {
        case ('c') :
            ellipticCurve = optarg;
            break;
        case ('b') :
            shaBits = optarg;
            break;
        case ('r') :
            stdInput = false; // use random data
            break;
        }
    }

    bool result;

    if ("BN128" == ellipticCurve) {
        // Barreto-Naehrig 128 bits
        init_BN128();
        result = runTest<BN128_PAIRING>(shaBits, stdInput);

    } else if ("Edwards" == ellipticCurve) {
        // Edwards 80 bits
        init_Edwards();
        result = runTest<EDWARDS_PAIRING>(shaBits, stdInput);

    } else {
        // no elliptic curve specified
        printUsage(argv[0]);
    }

    cout << "test " << (result ? "passed" : "failed") << endl;

    exit(EXIT_SUCCESS);
}
<|endoftext|>


<commit_before>
#include <iostream>
#include <sstream>
#include <random>
#include <functional>
#include <memory>

#include <gui/application.h>
#include <gui/cocoa_style.h>
#include <gui/container.h>
#include <gui/layouts.h>
#include <gui/label.h>
#include <gui/button.h>
#include <gui/slider.h>
#include <gui/background_color.h>
#include <gui/scroll_area.h>
#include <gui/tree_node.h>
#include <gui/line_edit.h>

namespace {

struct test_record : public model::record
{
	field<std::string> title = field<std::string>( this );
	field<double> rating = field<double>( this, 0.5 );
};

std::shared_ptr<test_record> testrec = std::make_shared<test_record>();

////////////////////////////////////////

std::shared_ptr<gui::form> build_form( direction dir )
{
	auto container = std::make_shared<gui::form>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	auto button = std::make_shared<gui::button>( "Click Me" );
	button->when_activated += []()
	{
		testrec->title = "Goodbye World";
	};

	container->add( std::make_shared<gui::label>( model::make_datum( testrec, testrec->title ) ), button );
	container->add( std::make_shared<gui::label>( "What" ), std::make_shared<gui::slider>( model::make_datum( testrec, testrec->rating ) ) );
	container->add( std::make_shared<gui::label>( "Who" ), std::make_shared<gui::slider>( model::make_datum( testrec, testrec->rating ) ) );
	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::background> build_grid( direction dir )
{
	auto container = std::make_shared<gui::grid>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	std::string name( "A" );
	std::vector<std::shared_ptr<gui::widget>> row;
	const double weights[5] = { 0.25, 0.5, 1.0, 0.5, 0.25 };
	for ( size_t y = 0; y < 5; ++y )
	{
		row.clear();
		for ( size_t x = 0; x < 5; ++x )
		{
			row.push_back( std::make_shared<gui::label>( name, alignment::CENTER ) );
			++name[0];
		}
		container->add_row( row, weights[y] );
	}
	for ( size_t y = 0; y < 5; ++y )
		container->set_column_weight( y, weights[y] );

	auto bg = std::make_shared<gui::background_color>();// { 1, 0, 1, 1 }, container );
	bg->set_color( { 1, 0, 1, 1 } );
	bg->set_widget( container );

	return bg;
}

////////////////////////////////////////

std::shared_ptr<gui::simple_container> build_box( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	std::string name( "A" );
	std::vector<std::shared_ptr<gui::widget>> row;
	for ( size_t y = 0; y < 5; ++y )
	{
		container->add( std::make_shared<gui::label>( name, alignment::CENTER ) );
		++name[0];
	}

	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::tree_node> build_tree( direction dir )
{
	auto container = std::make_shared<gui::tree_node>( 24.0, dir );
	container->set_root( std::make_shared<gui::label>( "Root" ) );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	std::string name( "A" );
	for ( size_t y = 0; y < 3; ++y )
	{
		auto node = std::make_shared<gui::tree_node>( 24.0, dir );
		if( y == 1 )
		{
			auto button = std::make_shared<gui::button>( "+" );
			button->when_activated += [=]( void )
			{
				node->set_collapsed( !node->collapsed() );
			};
			node->set_root( button );
		}
		else
			node->set_root( std::make_shared<gui::label>( name ) );
		std::string sub( "1" );
		for ( size_t x = 0; x < 3; ++x )
		{
			node->add( std::make_shared<gui::label>( sub ) );
			node->set_spacing( 12, 6 );
			++sub[0];
		}
		container->add( node );
		++name[0];
	}

	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::simple_container> build_edit( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	container->add( std::make_shared<gui::label>( model::make_datum( testrec, testrec->title ) ) );
	container->add( std::make_shared<gui::line_edit>( model::make_datum( testrec, testrec->title ) ) );
	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::widget> build_all( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );

	std::vector<std::shared_ptr<gui::widget>> list = { build_form( dir ), build_grid( dir ), build_box( dir ), build_tree( dir ) };

	for ( auto c: list )
		container->add( c );
	container->set_weight( 1, 1.0 );

	auto scroll = std::make_shared<gui::scroll_area>( gui::scroll_behavior::NONE, gui::scroll_behavior::BOUND );
	scroll->set_widget( container );

	return scroll;
}

////////////////////////////////////////

int safemain( int argc, char **argv )
{
	testrec->title = "Hello World";

	auto app = std::make_shared<gui::application>();
	app->push();
	app->set_style( std::make_shared<gui::cocoa_style>() );

	auto win = app->new_window();
	win->set_title( app->active_platform() );

	std::string test = "form";
	if ( argc > 1 )
		test = argv[1];

	std::string dirname = "box";
	if ( argc > 2 )
		dirname = argv[2];
	direction dir = direction::DOWN;
	if ( dirname == "down" )
		dir = direction::DOWN;
	else if ( dirname == "up" )
		dir = direction::UP;
	else if ( dirname == "left" )
		dir = direction::LEFT;
	else if ( dirname == "right" )
		dir = direction::RIGHT;

	if ( test == "form" )
		win->set_widget( build_form( dir ) );
	else if ( test == "grid" )
		win->set_widget( build_grid( dir ) );
	else if ( test == "box" )
		win->set_widget( build_box( dir ) );
	else if ( test == "tree" )
		win->set_widget( build_tree( dir ) );
	else if ( test == "edit" )
		win->set_widget( build_edit( dir ) );
	else if ( test == "all" )
		win->set_widget( build_all( dir ) );
	else
		throw std::runtime_error( "unknown test" );

	win->show();
	int code = app->run();
	app->pop();
	return code;
}

////////////////////////////////////////

}

////////////////////////////////////////

int main( int argc, char *argv[] )
{
	int ret = -1;
	try
	{
		ret = safemain( argc, argv );
	}
	catch ( std::exception &e )
	{
		print_exception( std::cerr, e );
	}
	return ret;
}

////////////////////////////////////////

<commit_msg>Added color picker test.<commit_after>
#include <iostream>
#include <sstream>
#include <random>
#include <functional>
#include <memory>

#include <gui/application.h>
#include <gui/cocoa_style.h>
#include <gui/container.h>
#include <gui/layouts.h>
#include <gui/label.h>
#include <gui/button.h>
#include <gui/slider.h>
#include <gui/background_color.h>
#include <gui/scroll_area.h>
#include <gui/tree_node.h>
#include <gui/line_edit.h>
#include <gui/color_picker.h>

namespace {

struct test_record : public model::record
{
	field<std::string> title = field<std::string>( this );
	field<double> rating = field<double>( this, 0.5 );
};

std::shared_ptr<test_record> testrec = std::make_shared<test_record>();

////////////////////////////////////////

std::shared_ptr<gui::form> build_form( direction dir )
{
	auto container = std::make_shared<gui::form>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	auto button = std::make_shared<gui::button>( "Click Me" );
	button->when_activated += []()
	{
		testrec->title = "Goodbye World";
	};

	container->add( std::make_shared<gui::label>( model::make_datum( testrec, testrec->title ) ), button );
	container->add( std::make_shared<gui::label>( "What" ), std::make_shared<gui::slider>( model::make_datum( testrec, testrec->rating ) ) );
	container->add( std::make_shared<gui::label>( "Who" ), std::make_shared<gui::slider>( model::make_datum( testrec, testrec->rating ) ) );
	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::background> build_grid( direction dir )
{
	auto container = std::make_shared<gui::grid>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	std::string name( "A" );
	std::vector<std::shared_ptr<gui::widget>> row;
	const double weights[5] = { 0.25, 0.5, 1.0, 0.5, 0.25 };
	for ( size_t y = 0; y < 5; ++y )
	{
		row.clear();
		for ( size_t x = 0; x < 5; ++x )
		{
			row.push_back( std::make_shared<gui::label>( name, alignment::CENTER ) );
			++name[0];
		}
		container->add_row( row, weights[y] );
	}
	for ( size_t y = 0; y < 5; ++y )
		container->set_column_weight( y, weights[y] );

	auto bg = std::make_shared<gui::background_color>();// { 1, 0, 1, 1 }, container );
	bg->set_color( { 1, 0, 1, 1 } );
	bg->set_widget( container );

	return bg;
}

////////////////////////////////////////

std::shared_ptr<gui::simple_container> build_box( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	std::string name( "A" );
	std::vector<std::shared_ptr<gui::widget>> row;
	for ( size_t y = 0; y < 5; ++y )
	{
		container->add( std::make_shared<gui::label>( name, alignment::CENTER ) );
		++name[0];
	}

	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::tree_node> build_tree( direction dir )
{
	auto container = std::make_shared<gui::tree_node>( 24.0, dir );
	container->set_root( std::make_shared<gui::label>( "Root" ) );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	std::string name( "A" );
	for ( size_t y = 0; y < 3; ++y )
	{
		auto node = std::make_shared<gui::tree_node>( 24.0, dir );
		if( y == 1 )
		{
			auto button = std::make_shared<gui::button>( "+" );
			button->when_activated += [=]( void )
			{
				node->set_collapsed( !node->collapsed() );
			};
			node->set_root( button );
		}
		else
			node->set_root( std::make_shared<gui::label>( name ) );
		std::string sub( "1" );
		for ( size_t x = 0; x < 3; ++x )
		{
			node->add( std::make_shared<gui::label>( sub ) );
			node->set_spacing( 12, 6 );
			++sub[0];
		}
		container->add( node );
		++name[0];
	}

	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::simple_container> build_edit( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	container->add( std::make_shared<gui::label>( model::make_datum( testrec, testrec->title ) ) );
	container->add( std::make_shared<gui::line_edit>( model::make_datum( testrec, testrec->title ) ) );
	return container;
}

////////////////////////////////////////

std::shared_ptr<gui::widget> build_all( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );

	std::vector<std::shared_ptr<gui::widget>> list = { build_form( dir ), build_grid( dir ), build_box( dir ), build_tree( dir ) };

	for ( auto c: list )
		container->add( c );
	container->set_weight( 1, 1.0 );

	auto scroll = std::make_shared<gui::scroll_area>( gui::scroll_behavior::NONE, gui::scroll_behavior::BOUND );
	scroll->set_widget( container );

	return scroll;
}

////////////////////////////////////////

std::shared_ptr<gui::simple_container> build_color( direction dir )
{
	auto container = std::make_shared<gui::simple_container>( dir );
	container->set_spacing( 12, 6 );
	container->set_pad( 12.5, 12.5, 12.5, 12.5 );

	container->add( std::make_shared<gui::color_picker>( draw::color::space::HSL ), 1.0 );
	container->add( std::make_shared<gui::color_picker>( draw::color::space::LAB ), 1.0 );

	return container;
}

////////////////////////////////////////

int safemain( int argc, char **argv )
{
	testrec->title = "Hello World";

	auto app = std::make_shared<gui::application>();
	app->push();
	app->set_style( std::make_shared<gui::cocoa_style>() );

	auto win = app->new_window();
	win->set_title( app->active_platform() );

	std::string test = "form";
	if ( argc > 1 )
		test = argv[1];

	std::string dirname = "box";
	if ( argc > 2 )
		dirname = argv[2];
	direction dir = direction::DOWN;
	if ( dirname == "down" )
		dir = direction::DOWN;
	else if ( dirname == "up" )
		dir = direction::UP;
	else if ( dirname == "left" )
		dir = direction::LEFT;
	else if ( dirname == "right" )
		dir = direction::RIGHT;

	if ( test == "form" )
		win->set_widget( build_form( dir ) );
	else if ( test == "grid" )
		win->set_widget( build_grid( dir ) );
	else if ( test == "box" )
		win->set_widget( build_box( dir ) );
	else if ( test == "tree" )
		win->set_widget( build_tree( dir ) );
	else if ( test == "edit" )
		win->set_widget( build_edit( dir ) );
	else if ( test == "color" )
		win->set_widget( build_color( dir ) );
	else if ( test == "all" )
		win->set_widget( build_all( dir ) );
	else
		throw std::runtime_error( "unknown test" );

	win->show();
	int code = app->run();
	app->pop();
	return code;
}

////////////////////////////////////////

}

////////////////////////////////////////

int main( int argc, char *argv[] )
{
	int ret = -1;
	try
	{
		ret = safemain( argc, argv );
	}
	catch ( std::exception &e )
	{
		print_exception( std::cerr, e );
	}
	return ret;
}

////////////////////////////////////////

<|endoftext|>


<commit_before>/**
 * @file fileio.cpp
 * @author Zach Marquez
 * @copyright Copyright 2014 Mediafire
 */
#include "fileio.hpp"

#include <cstdlib>
#include <cstdio>
#include <limits>

#include "boost/filesystem.hpp"

#include "./error.hpp"
#include "./string.hpp"

using mf::utils::file_io_error;

namespace mf {
namespace utils {

namespace {
const uint64_t skDefaultBlockSize = 64 * uint64_t(1) << 20;  // 64 MB
}  // anonymous namespace

const uint64_t FileIO::skDefaultReadSize = uint64_t(1)<<30;  // 1 GB

FileIO::Pointer FileIO::Open(
        const std::string& path_str,
        const std::string& open_mode,
        std::error_code * error
    )
{
    if (error)
        error->clear();

#if _WIN32
    std::wstring wide_path = bytes_to_wide(path_str.c_str());
    auto path = boost::filesystem::path(wide_path);
#else
    auto path = boost::filesystem::path(path_str);
#endif

    return Open(path, open_mode, error);
}

FileIO::Pointer FileIO::Open(
        boost::filesystem::path path,
        const std::string& open_mode,
        std::error_code * error
    )
{
    if (error)
        error->clear();

#if _WIN32
    std::wstring wide_open_mode = bytes_to_wide(open_mode.c_str());
    FILE* file_handle = _wfopen(
            path.wstring().c_str(),
            wide_open_mode.c_str()
        );
#else
    FILE* file_handle = fopen(
        path.string().c_str(),
        open_mode.c_str());
#endif

    if (file_handle != nullptr)
    {
        boost::system::error_code bec;
        uint64_t size_hint = boost::filesystem::file_size(path, bec);
        if (bec || size_hint == 0)
            size_hint = skDefaultBlockSize;

        return FileIO::Pointer(new FileIO(
                file_handle,
                size_hint
        ));
    }
    else
    {
        if ( error != nullptr )
            *error = std::error_code(errno, std::system_category());
        return FileIO::Pointer();
    }
}

FileIO::FileIO(
        FILE* file_handle,
        uint64_t size_hint
    ) :
    file_handle_(file_handle),
    size_hint_(size_hint)
{
    assert( size_hint_ != std::numeric_limits<uint64_t>::max() );
}

FileIO::~FileIO()
{
    fclose(file_handle_);
}

uint64_t FileIO::Read(
        void* buffer,
        uint64_t buffer_size,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    uint64_t bytes_read = fread(buffer, 1, buffer_size, file_handle_);

    // Detect errors if requested
    if ( error != nullptr && bytes_read != buffer_size )
    {
        if ( feof(file_handle_) )
        {
            *error = make_error_code(file_io_error::EndOfFile);
        }
        else if ( ferror(file_handle_) )
        {
            *error = make_error_code(file_io_error::StreamError);
        }
        else
        {
            assert( ! "Unknown error in FileIO::Read" );
            *error = make_error_code(file_io_error::UnknownError);
        }
    }

    return bytes_read;
}

std::string FileIO::ReadString(
        uint64_t desired_bytes,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    // Initialize
    std::string buffer;
    uint64_t total_bytes_read = 0;
    std::error_code read_error;

    try {
        // Calculate initial read size
        uint64_t read_size = std::min(desired_bytes, size_hint_ + 1);

        // Loop until we've read all desired or encounter an error
        while ( ! read_error && total_bytes_read < desired_bytes )
        {
            // Resize buffer
            buffer.resize(buffer.size() + read_size);

            // Calculate buffer position
            char* buffer_pos = &(&buffer.front())[total_bytes_read];

            // Read file
            uint64_t bytes_read = Read(buffer_pos, read_size, &read_error);
            total_bytes_read += bytes_read;

            // Ensure reads are complete or error
            assert( bytes_read == read_size || read_error );

            // Calculate next read size
            read_size = std::min(desired_bytes - total_bytes_read, skDefaultBlockSize);
        }
    }
    catch (const std::bad_alloc& a)
    {
        // std::bad_alloc can be thrown by std::string::resize if insufficient
        // memory
        read_error = make_error_code(file_io_error::NotEnoughMemory);
    }
    catch (const std::length_error&)
    {
        // std::length_error can be thrown by std::string::resize if arguments
        // is larger than string max size
        read_error = make_error_code(file_io_error::BufferTooLarge);
    }

    // Shrink buffer to actual bytes read
    buffer.resize(total_bytes_read);

    // Set return status
    if ( error != nullptr )
        *error = read_error;

    // Return read bytes
    return buffer;
}

std::string FileIO::ReadLine(
        std::error_code * error
    )
{
    if (error)
        error->clear();

    uint64_t bytes_consumed = 0;
    std::string lineData;

    // Read characters until we encounter an error, a newline, or we have read
    // too many bytes.
    int inputChar = fgetc(file_handle_);
    while ( inputChar != EOF && inputChar != '\n'
        && bytes_consumed < skDefaultReadSize )
    {
        lineData.push_back(static_cast<char>(inputChar));
        inputChar = fgetc(file_handle_);
        ++bytes_consumed;
    }

    // Set error condition
    if ( error != nullptr )
    {
        if ( inputChar == EOF )
        {   // Read error
            if ( feof(file_handle_) )
            {
                *error = make_error_code(file_io_error::EndOfFile);
            }
            else if ( ferror(file_handle_) )
            {
                *error = make_error_code(file_io_error::StreamError);
            }
            else
            {
                assert( ! "Unknown error in FileIO::Read" );
                *error = make_error_code(file_io_error::UnknownError);
            }
        }
        else if ( bytes_consumed >= skDefaultReadSize )
        {   // Reached our limit
            *error = make_error_code(file_io_error::LineTooLong);
        }
        else
        {   // No error
            assert( inputChar == '\n' );
        }
    }

    return lineData;
}

uint64_t FileIO::Write(
        const void* buffer,
        uint64_t buffer_size,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    uint64_t bytes_written = fwrite(buffer, 1, buffer_size, file_handle_);

    // Detect errors if requested
    if ( error != nullptr && bytes_written != buffer_size )
    {
        if ( ferror(file_handle_) )
        {
            *error = make_error_code(file_io_error::StreamError);
        }
        else
        {
            assert( ! "Unknown error in FileIO::Write" );
            *error = make_error_code(file_io_error::UnknownError);
        }
    }

    return bytes_written;
}

uint64_t FileIO::WriteString(
        const std::string& buffer,
        std::error_code * error
    )
{
    return Write(buffer.data(), buffer.size(), error);
}

void FileIO::Seek(
        const SeekAnchor anchor,
        const uint64_t offset,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    const int origin = [&anchor](){
        switch(anchor)
        {
            case SeekAnchor::Current:
                return SEEK_CUR;
                break;
            case SeekAnchor::Beginning:
                return SEEK_SET;
                break;
            case SeekAnchor::End:
                return SEEK_END;
                break;
            default:
                assert(!"Invalid anchor");
                return SEEK_CUR;
        }}();

#ifdef _WIN32
    const int res = fseeko64(file_handle_, offset, origin);
#else
    const int res = fseeko(file_handle_, offset, origin);
#endif

    if ( error != nullptr && res != 0 )
    {
        if ( ferror(file_handle_) )
        {
            *error = make_error_code(file_io_error::StreamError);
        }
        else
        {
            assert( ! "Unknown error in FileIO::Seek" );
            *error = make_error_code(file_io_error::UnknownError);
        }
    }
}

uint64_t FileIO::Tell()
{
#ifdef _WIN32
    uint64_t position = ftello64(file_handle_);
#else
    uint64_t position = ftello(file_handle_);
#endif

    return position;
}

void FileIO::Flush()
{
    fflush(file_handle_);
}

}  // namespace utils
}  // namespace mf
<commit_msg>BUG: Wrong type used<commit_after>/**
 * @file fileio.cpp
 * @author Zach Marquez
 * @copyright Copyright 2014 Mediafire
 */
#include "fileio.hpp"

#include <cstdlib>
#include <cstdio>
#include <limits>

#include "boost/filesystem.hpp"

#include "./error.hpp"
#include "./string.hpp"

using mf::utils::file_io_error;

namespace mf {
namespace utils {

namespace {
const uint64_t skDefaultBlockSize = 64 * uint64_t(1) << 20;  // 64 MB
}  // anonymous namespace

const uint64_t FileIO::skDefaultReadSize = uint64_t(1)<<30;  // 1 GB

FileIO::Pointer FileIO::Open(
        const std::string& path_str,
        const std::string& open_mode,
        std::error_code * error
    )
{
    if (error)
        error->clear();

#if _WIN32
    std::wstring wide_path = bytes_to_wide(path_str);
    auto path = boost::filesystem::path(wide_path);
#else
    auto path = boost::filesystem::path(path_str);
#endif

    return Open(path, open_mode, error);
}

FileIO::Pointer FileIO::Open(
        boost::filesystem::path path,
        const std::string& open_mode,
        std::error_code * error
    )
{
    if (error)
        error->clear();

#if _WIN32
    std::wstring wide_open_mode = bytes_to_wide(open_mode);
    FILE* file_handle = _wfopen(
            path.wstring().c_str(),
            wide_open_mode.c_str()
        );
#else
    FILE* file_handle = fopen(
        path.string().c_str(),
        open_mode.c_str());
#endif

    if (file_handle != nullptr)
    {
        boost::system::error_code bec;
        uint64_t size_hint = boost::filesystem::file_size(path, bec);
        if (bec || size_hint == 0)
            size_hint = skDefaultBlockSize;

        return FileIO::Pointer(new FileIO(
                file_handle,
                size_hint
        ));
    }
    else
    {
        if ( error != nullptr )
            *error = std::error_code(errno, std::system_category());
        return FileIO::Pointer();
    }
}

FileIO::FileIO(
        FILE* file_handle,
        uint64_t size_hint
    ) :
    file_handle_(file_handle),
    size_hint_(size_hint)
{
    assert( size_hint_ != std::numeric_limits<uint64_t>::max() );
}

FileIO::~FileIO()
{
    fclose(file_handle_);
}

uint64_t FileIO::Read(
        void* buffer,
        uint64_t buffer_size,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    uint64_t bytes_read = fread(buffer, 1, buffer_size, file_handle_);

    // Detect errors if requested
    if ( error != nullptr && bytes_read != buffer_size )
    {
        if ( feof(file_handle_) )
        {
            *error = make_error_code(file_io_error::EndOfFile);
        }
        else if ( ferror(file_handle_) )
        {
            *error = make_error_code(file_io_error::StreamError);
        }
        else
        {
            assert( ! "Unknown error in FileIO::Read" );
            *error = make_error_code(file_io_error::UnknownError);
        }
    }

    return bytes_read;
}

std::string FileIO::ReadString(
        uint64_t desired_bytes,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    // Initialize
    std::string buffer;
    uint64_t total_bytes_read = 0;
    std::error_code read_error;

    try {
        // Calculate initial read size
        uint64_t read_size = std::min(desired_bytes, size_hint_ + 1);

        // Loop until we've read all desired or encounter an error
        while ( ! read_error && total_bytes_read < desired_bytes )
        {
            // Resize buffer
            buffer.resize(buffer.size() + read_size);

            // Calculate buffer position
            char* buffer_pos = &(&buffer.front())[total_bytes_read];

            // Read file
            uint64_t bytes_read = Read(buffer_pos, read_size, &read_error);
            total_bytes_read += bytes_read;

            // Ensure reads are complete or error
            assert( bytes_read == read_size || read_error );

            // Calculate next read size
            read_size = std::min(desired_bytes - total_bytes_read, skDefaultBlockSize);
        }
    }
    catch (const std::bad_alloc& a)
    {
        // std::bad_alloc can be thrown by std::string::resize if insufficient
        // memory
        read_error = make_error_code(file_io_error::NotEnoughMemory);
    }
    catch (const std::length_error&)
    {
        // std::length_error can be thrown by std::string::resize if arguments
        // is larger than string max size
        read_error = make_error_code(file_io_error::BufferTooLarge);
    }

    // Shrink buffer to actual bytes read
    buffer.resize(total_bytes_read);

    // Set return status
    if ( error != nullptr )
        *error = read_error;

    // Return read bytes
    return buffer;
}

std::string FileIO::ReadLine(
        std::error_code * error
    )
{
    if (error)
        error->clear();

    uint64_t bytes_consumed = 0;
    std::string lineData;

    // Read characters until we encounter an error, a newline, or we have read
    // too many bytes.
    int inputChar = fgetc(file_handle_);
    while ( inputChar != EOF && inputChar != '\n'
        && bytes_consumed < skDefaultReadSize )
    {
        lineData.push_back(static_cast<char>(inputChar));
        inputChar = fgetc(file_handle_);
        ++bytes_consumed;
    }

    // Set error condition
    if ( error != nullptr )
    {
        if ( inputChar == EOF )
        {   // Read error
            if ( feof(file_handle_) )
            {
                *error = make_error_code(file_io_error::EndOfFile);
            }
            else if ( ferror(file_handle_) )
            {
                *error = make_error_code(file_io_error::StreamError);
            }
            else
            {
                assert( ! "Unknown error in FileIO::Read" );
                *error = make_error_code(file_io_error::UnknownError);
            }
        }
        else if ( bytes_consumed >= skDefaultReadSize )
        {   // Reached our limit
            *error = make_error_code(file_io_error::LineTooLong);
        }
        else
        {   // No error
            assert( inputChar == '\n' );
        }
    }

    return lineData;
}

uint64_t FileIO::Write(
        const void* buffer,
        uint64_t buffer_size,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    uint64_t bytes_written = fwrite(buffer, 1, buffer_size, file_handle_);

    // Detect errors if requested
    if ( error != nullptr && bytes_written != buffer_size )
    {
        if ( ferror(file_handle_) )
        {
            *error = make_error_code(file_io_error::StreamError);
        }
        else
        {
            assert( ! "Unknown error in FileIO::Write" );
            *error = make_error_code(file_io_error::UnknownError);
        }
    }

    return bytes_written;
}

uint64_t FileIO::WriteString(
        const std::string& buffer,
        std::error_code * error
    )
{
    return Write(buffer.data(), buffer.size(), error);
}

void FileIO::Seek(
        const SeekAnchor anchor,
        const uint64_t offset,
        std::error_code * error
    )
{
    if (error)
        error->clear();

    const int origin = [&anchor](){
        switch(anchor)
        {
            case SeekAnchor::Current:
                return SEEK_CUR;
                break;
            case SeekAnchor::Beginning:
                return SEEK_SET;
                break;
            case SeekAnchor::End:
                return SEEK_END;
                break;
            default:
                assert(!"Invalid anchor");
                return SEEK_CUR;
        }}();

#ifdef _WIN32
    const int res = fseeko64(file_handle_, offset, origin);
#else
    const int res = fseeko(file_handle_, offset, origin);
#endif

    if ( error != nullptr && res != 0 )
    {
        if ( ferror(file_handle_) )
        {
            *error = make_error_code(file_io_error::StreamError);
        }
        else
        {
            assert( ! "Unknown error in FileIO::Seek" );
            *error = make_error_code(file_io_error::UnknownError);
        }
    }
}

uint64_t FileIO::Tell()
{
#ifdef _WIN32
    uint64_t position = ftello64(file_handle_);
#else
    uint64_t position = ftello(file_handle_);
#endif

    return position;
}

void FileIO::Flush()
{
    fflush(file_handle_);
}

}  // namespace utils
}  // namespace mf
<|endoftext|>


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

#include "base/memory/discardable_memory.h"

#include <sys/mman.h>
#include <unistd.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/synchronization/lock.h"
#include "third_party/ashmem/ashmem.h"

namespace base {
namespace {

// Protects |g_num_discardable_memory| below.
base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock =
    LAZY_INSTANCE_INITIALIZER;

// Total number of discardable memory in the process.
int g_num_discardable_memory = 0;

// Upper limit on the number of discardable memory to avoid hitting file
// descriptor limit.
const int kDiscardableMemoryNumLimit = 128;

bool CreateAshmemRegion(const char* name,
                        size_t size,
                        int* out_fd,
                        void** out_address) {
  base::AutoLock lock(g_discardable_memory_lock.Get());
  if (g_num_discardable_memory + 1 > kDiscardableMemoryNumLimit)
    return false;
  int fd = ashmem_create_region(name, size);
  if (fd < 0) {
    DLOG(ERROR) << "ashmem_create_region() failed";
    return false;
  }
  file_util::ScopedFD fd_closer(&fd);

  const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
  if (err < 0) {
    DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
    return false;
  }

  // There is a problem using MAP_PRIVATE here. As we are constantly calling
  // Lock() and Unlock(), data could get lost if they are not written to the
  // underlying file when Unlock() gets called.
  void* const address = mmap(
      NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (address == MAP_FAILED) {
    DPLOG(ERROR) << "Failed to map memory.";
    return false;
  }

  ignore_result(fd_closer.release());
  ++g_num_discardable_memory;
  *out_fd = fd;
  *out_address = address;
  return true;
}

bool DeleteAshmemRegion(int fd, size_t size, void* address) {
  base::AutoLock lock(g_discardable_memory_lock.Get());
  --g_num_discardable_memory;
  if (munmap(address, size) == -1) {
    DPLOG(ERROR) << "Failed to unmap memory.";
    close(fd);
    return false;
  }
  return close(fd) == 0;
}

LockDiscardableMemoryStatus LockAshmemRegion(int fd,
                                             size_t off,
                                             size_t size,
                                             const void* address) {
  const int result = ashmem_pin_region(fd, off, size);
  return result == ASHMEM_WAS_PURGED ?
      DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
}

bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) {
  const int failed = ashmem_unpin_region(fd, off, size);
  if (failed)
    DLOG(ERROR) << "Failed to unpin memory.";
  return !failed;
}

class DiscardableMemoryAndroid : public DiscardableMemory {
 public:
  DiscardableMemoryAndroid(int fd, void* address, size_t size)
      : fd_(fd),
        memory_(address),
        size_(size) {
    DCHECK_GE(fd_, 0);
    DCHECK(memory_);
  }

  virtual ~DiscardableMemoryAndroid() {
    DeleteAshmemRegion(fd_, size_, memory_);
  }

  // DiscardableMemory:
  virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
    if (!memory_ && !MapRegion())
      return DISCARDABLE_MEMORY_FAILED;
    return LockAshmemRegion(fd_, 0, size_, memory_);
  }

  virtual void Unlock() OVERRIDE {
    UnmapRegion();
    UnlockAshmemRegion(fd_, 0, size_, memory_);
  }

  virtual void* Memory() const OVERRIDE {
    DCHECK(memory_) << "Trying to access unmapped memory";
    return memory_;
  }

 private:
  // TODO(pliard): http://crbug.com/311633. Remove the two methods below once
  // memory measurement infrastructure supports unpinned ashmem purging.
  bool MapRegion() {
    DCHECK(!memory_) << "Region already mapped";
    void* const address = mmap(
        NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
    if (address == MAP_FAILED) {
      DPLOG(ERROR) << "Failed to map memory.";
      return false;
    }
    memory_ = address;
    return true;
  }

  void UnmapRegion() {
    DCHECK(memory_) << "Region already unmapped";
    if (munmap(memory_, size_) == -1)
      DPLOG(ERROR) << "Failed to unmap memory.";
    memory_ = NULL;
  }

  const int fd_;
  void* memory_;
  const size_t size_;

  DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid);
};

}  // namespace

// static
bool DiscardableMemory::SupportedNatively() {
  return true;
}

// static
scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
    size_t size) {
  // Pinning & unpinning works with page granularity therefore align the size
  // upfront.
  const size_t kPageSize = 4096;
  const size_t mask = ~(kPageSize - 1);
  size = (size + kPageSize - 1) & mask;
  int fd;
  void* address;
  if (!CreateAshmemRegion("", size, &fd, &address))
    return scoped_ptr<DiscardableMemory>();
  return scoped_ptr<DiscardableMemory>(
      new DiscardableMemoryAndroid(fd, address, size));
}

// static
bool DiscardableMemory::PurgeForTestingSupported() {
  return false;
}

// static
void DiscardableMemory::PurgeForTesting() {
  NOTIMPLEMENTED();
}

}  // namespace base
<commit_msg>Revert "Unmap unpinned ashmem regions."<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/memory/discardable_memory.h"

#include <sys/mman.h>
#include <unistd.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/synchronization/lock.h"
#include "third_party/ashmem/ashmem.h"

namespace base {
namespace {

// Protects |g_num_discardable_memory| below.
base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock =
    LAZY_INSTANCE_INITIALIZER;

// Total number of discardable memory in the process.
int g_num_discardable_memory = 0;

// Upper limit on the number of discardable memory to avoid hitting file
// descriptor limit.
const int kDiscardableMemoryNumLimit = 128;

bool CreateAshmemRegion(const char* name,
                        size_t size,
                        int* out_fd,
                        void** out_address) {
  base::AutoLock lock(g_discardable_memory_lock.Get());
  if (g_num_discardable_memory + 1 > kDiscardableMemoryNumLimit)
    return false;
  int fd = ashmem_create_region(name, size);
  if (fd < 0) {
    DLOG(ERROR) << "ashmem_create_region() failed";
    return false;
  }
  file_util::ScopedFD fd_closer(&fd);

  const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
  if (err < 0) {
    DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
    return false;
  }

  // There is a problem using MAP_PRIVATE here. As we are constantly calling
  // Lock() and Unlock(), data could get lost if they are not written to the
  // underlying file when Unlock() gets called.
  void* const address = mmap(
      NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (address == MAP_FAILED) {
    DPLOG(ERROR) << "Failed to map memory.";
    return false;
  }

  ignore_result(fd_closer.release());
  ++g_num_discardable_memory;
  *out_fd = fd;
  *out_address = address;
  return true;
}

bool DeleteAshmemRegion(int fd, size_t size, void* address) {
  base::AutoLock lock(g_discardable_memory_lock.Get());
  --g_num_discardable_memory;
  if (munmap(address, size) == -1) {
    DPLOG(ERROR) << "Failed to unmap memory.";
    close(fd);
    return false;
  }
  return close(fd) == 0;
}

LockDiscardableMemoryStatus LockAshmemRegion(int fd,
                                             size_t off,
                                             size_t size,
                                             const void* address) {
  const int result = ashmem_pin_region(fd, off, size);
  DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE));
  return result == ASHMEM_WAS_PURGED ?
      DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
}

bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) {
  const int failed = ashmem_unpin_region(fd, off, size);
  if (failed)
    DLOG(ERROR) << "Failed to unpin memory.";
  // This allows us to catch accesses to unlocked memory.
  DCHECK_EQ(0, mprotect(address, size, PROT_NONE));
  return !failed;
}

class DiscardableMemoryAndroid : public DiscardableMemory {
 public:
  DiscardableMemoryAndroid(int fd, void* address, size_t size)
      : fd_(fd),
        memory_(address),
        size_(size) {
    DCHECK_GE(fd_, 0);
    DCHECK(memory_);
  }

  virtual ~DiscardableMemoryAndroid() {
    DeleteAshmemRegion(fd_, size_, memory_);
  }

  // DiscardableMemory:
  virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
    return LockAshmemRegion(fd_, 0, size_, memory_);
  }

  virtual void Unlock() OVERRIDE {
    UnlockAshmemRegion(fd_, 0, size_, memory_);
  }

  virtual void* Memory() const OVERRIDE {
    return memory_;
  }

 private:
  const int fd_;
  void* const memory_;
  const size_t size_;

  DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid);
};

}  // namespace

// static
bool DiscardableMemory::SupportedNatively() {
  return true;
}

// static
scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
    size_t size) {
  // Pinning & unpinning works with page granularity therefore align the size
  // upfront.
  const size_t kPageSize = 4096;
  const size_t mask = ~(kPageSize - 1);
  size = (size + kPageSize - 1) & mask;
  int fd;
  void* address;
  if (!CreateAshmemRegion("", size, &fd, &address))
    return scoped_ptr<DiscardableMemory>();
  return scoped_ptr<DiscardableMemory>(
      new DiscardableMemoryAndroid(fd, address, size));
}

// static
bool DiscardableMemory::PurgeForTestingSupported() {
  return false;
}

// static
void DiscardableMemory::PurgeForTesting() {
  NOTIMPLEMENTED();
}

}  // namespace base
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkRect.h"
#include <algorithm>
#include <vector>

using std::vector;

struct Contour {
    enum class Direction {  // SkPath::Direction doesn't have 'none' state
        kCCW = -1,
        kNone,
        kCW,
    };

    Contour(const SkRect& bounds, int lastStart, int verbStart)
        : fBounds(bounds)
        , fVerbStart(lastStart)
        , fVerbEnd(verbStart) {
    }

    vector<Contour*> fChildren;
    const SkRect fBounds;
    SkPoint fMinXY{SK_ScalarMax, SK_ScalarMax};
    const int fVerbStart;
    const int fVerbEnd;
    Direction fDirection{Direction::kNone};
    bool fContained{false};
    bool fReverse{false};
};

static const int kPtCount[] = { 1, 1, 2, 2, 3, 0 };
static const int kPtIndex[] = { 0, 1, 1, 1, 1, 0 };

static Contour::Direction to_direction(SkScalar dy) {
    return dy > 0 ? Contour::Direction::kCCW : dy < 0 ? Contour::Direction::kCW :
            Contour::Direction::kNone;
}

static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) {
    SkRect bounds;
    bounds.set(pts, kPtCount[verb] + 1);
    if (bounds.fTop > edge.fY) {
        return 0;
    }
    if (bounds.fBottom <= edge.fY) {  // check to see if y is at line end to avoid double counting
        return 0;
    }
    if (bounds.fLeft >= edge.fX) {
        return 0;
    }
    int winding = 0;
    double tVals[3];
    Contour::Direction directions[3];
    // must intersect horz ray with curve in case it intersects more than once
    int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals);
    SkASSERT(between(0, count, 3));
    // remove results to the right of edge
    for (int index = 0; index < count; ) {
        SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX;
        if (intersectX < edge.fX) {
            ++index;
            continue;
        }
        if (intersectX > edge.fX) {
            tVals[index] = tVals[--count];
            continue;
        }
        // if intersect x equals edge x, we need to determine if pts is to the left or right of edge
        if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) {
            ++index;
            continue;
        }
        // TODO : other cases need discriminating. need op angle code to figure it out
        // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep.
        // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases.
        if (intersectX < edge.fX) {
            tVals[index] = tVals[--count];
            continue;
        }
    }
    // use first derivative to determine if intersection is contributing +1 or -1 to winding
    for (int index = 0; index < count; ++index) {
        directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY);
    }
    for (int index = 0; index < count; ++index) {
        // skip intersections that end at edge and go up
        if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) {
            continue;
        }
        winding += (int) directions[index];
    }
    return winding;  // note winding indicates containership, not contour direction
}

static SkScalar conic_weight(const SkPath::Iter& iter, SkPath::Verb verb) {
    return SkPath::kConic_Verb == verb ? iter.conicWeight() : 1;
}

static SkPoint left_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight,
        Contour::Direction* direction) {
    SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
    SkPoint result;
    double dy;
    double t SK_INIT_TO_AVOID_WARNING;
    int roots = 0;
    if (SkPath::kLine_Verb == verb) {
        result = pts[0].fX < pts[1].fX ? pts[0] : pts[1];
        dy = pts[1].fY - pts[0].fY;
    } else if (SkPath::kQuad_Verb == verb) {
        SkDQuad quad;
        quad.set(pts);
        if (!quad.monotonicInX()) {
            roots = SkDQuad::FindExtrema(&quad[0].fX, &t);
        }
        if (roots) {
            result = quad.ptAtT(t).asSkPoint();
        } else {
            result = pts[0].fX < pts[2].fX ? pts[0] : pts[2];
            t = pts[0].fX < pts[2].fX ? 0 : 1;
        }
        dy = quad.dxdyAtT(t).fY;
    } else if (SkPath::kConic_Verb == verb) {
        SkDConic conic;
        conic.set(pts, weight);
        if (!conic.monotonicInX()) {
            roots = SkDConic::FindExtrema(&conic[0].fX, weight, &t);
        }
        if (roots) {
            result = conic.ptAtT(t).asSkPoint();
        } else {
            result = pts[0].fX < pts[2].fX ? pts[0] : pts[2];
            t = pts[0].fX < pts[2].fX ? 0 : 1;
        }
        dy = conic.dxdyAtT(t).fY;
    } else {
        SkASSERT(SkPath::kCubic_Verb == verb);
        SkDCubic cubic;
        cubic.set(pts);
        if (!cubic.monotonicInX()) {
            double tValues[2];
            roots = SkDCubic::FindExtrema(&cubic[0].fX, tValues);
            SkASSERT(roots <= 2);
            for (int index = 0; index < roots; ++index) {
                SkPoint temp = cubic.ptAtT(tValues[index]).asSkPoint();
                if (0 == index || result.fX > temp.fX) {
                    result = temp;
                    t = tValues[index];
                }
            }
        }
        if (roots) {
            result = cubic.ptAtT(t).asSkPoint();
        } else {
            result = pts[0].fX < pts[3].fX ? pts[0] : pts[3];
            t = pts[0].fX < pts[3].fX ? 0 : 1;
        }
        dy = cubic.dxdyAtT(t).fY;
    }
    *direction = to_direction(dy);
    return result;
}

class OpAsWinding {
public:
    enum class Edge {
        kInitial,
        kCompare,
    };

    OpAsWinding(const SkPath& path)
        : fPath(path) {
    }

    void contourBounds(vector<Contour>* containers) {
        SkRect bounds;
        bounds.setEmpty();
        SkPath::RawIter iter(fPath);
        SkPoint pts[4];
        SkPath::Verb verb;
        int lastStart = 0;
        int verbStart = 0;
        do {
            verb = iter.next(pts);
            if (SkPath::kMove_Verb == verb) {
                if (!bounds.isEmpty()) {
                    containers->emplace_back(bounds, lastStart, verbStart);
                    lastStart = verbStart;
               }
               bounds.setBounds(&pts[kPtIndex[verb]], kPtCount[verb]);
            }
            if (SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb) {
                SkRect verbBounds;
                verbBounds.setBounds(&pts[kPtIndex[verb]], kPtCount[verb]);
                bounds.joinPossiblyEmptyRect(verbBounds);
            }
            ++verbStart;
        } while (SkPath::kDone_Verb != verb);
        if (!bounds.isEmpty()) {
            containers->emplace_back(bounds, lastStart, verbStart);
        }
    }

    int nextEdge(Contour& contour, Edge edge) {
        SkPath::Iter iter(fPath, true);
        SkPoint pts[4];
        SkPath::Verb verb;
        int verbCount = -1;
        int winding = 0;
        do {
            verb = iter.next(pts);
            if (++verbCount < contour.fVerbStart) {
                continue;
            }
            if (verbCount >= contour.fVerbEnd) {
                continue;
            }
            if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) {
                continue;
            }
            bool horizontal = true;
            for (int index = 1; index <= kPtCount[verb]; ++index) {
                if (pts[0].fY != pts[index].fY) {
                    horizontal = false;
                    break;
                }
            }
            if (horizontal) {
                continue;
            }
            if (edge == Edge::kCompare) {
                winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY);
                continue;
            }
            SkASSERT(edge == Edge::kInitial);
            Contour::Direction direction;
            SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb), &direction);
            if (minXY.fX > contour.fMinXY.fX) {
                continue;
            }
            if (minXY.fX == contour.fMinXY.fX) {
                if (minXY.fY != contour.fMinXY.fY) {
                    continue;
                }
                if (direction == contour.fDirection) {
                    continue;
                }
                // incomplete: must sort edges to find the one most to left
                SkDebugf("incomplete\n");
                // TODO: add edges as opangle and sort
            }
            contour.fMinXY = minXY;
            contour.fDirection = direction;
        } while (SkPath::kDone_Verb != verb);
        return winding;
    }

    void containerContains(Contour& contour, Contour& test) {
        // find outside point on lesser contour
        // arbitrarily, choose non-horizontal edge where point <= bounds left
        // note that if leftmost point is control point, may need tight bounds
            // to find edge with minimum-x
        if (SK_ScalarMax == test.fMinXY.fX) {
            this->nextEdge(test, Edge::kInitial);
        }
        // find all edges on greater equal or to the left of one on lesser
        contour.fMinXY = test.fMinXY;
        int winding = this->nextEdge(contour, Edge::kCompare);
        // if edge is up, mark contour cw, otherwise, ccw
        // sum of greater edges direction should be cw, 0, ccw
        SkASSERT(-1 <= winding && winding <= 1);
        test.fContained = winding != 0;
    }

    void inParent(Contour& contour, Contour& parent) {
        // move contour into sibling list contained by parent
        for (auto test : parent.fChildren) {
            if (test->fBounds.contains(contour.fBounds)) {
                inParent(contour, *test);
                return;
            }
        }
        // move parent's children into contour's children if contained by contour
        for (auto iter = parent.fChildren.begin(); iter != parent.fChildren.end(); ) {
            if (contour.fBounds.contains((*iter)->fBounds)) {
                contour.fChildren.push_back(*iter);
                iter = parent.fChildren.erase(iter);
                continue;
            }
            ++iter;
        }
        parent.fChildren.push_back(&contour);
    }

    void checkContainerChildren(Contour* parent, Contour* child) {
        for (auto grandChild : child->fChildren) {
            checkContainerChildren(child, grandChild);
        }
        if (parent) {
            containerContains(*parent, *child);
        }
    }

    bool markReverse(Contour* parent, Contour* child) {
        bool reversed = false;
        for (auto grandChild : child->fChildren) {
            reversed |= markReverse(grandChild->fContained ? child : parent, grandChild);
        }
        if (parent && parent->fDirection == child->fDirection) {
            child->fReverse = true;
            child->fDirection = (Contour::Direction) -(int) child->fDirection;
            return true;
        }
        return reversed;
    }

    void reverseMarkedContours(vector<Contour>& contours, SkPath* result) {
        SkPath::RawIter iter(fPath);
        int verbCount = 0;
        for (auto contour : contours) {
            SkPath reverse;
            SkPath* temp = contour.fReverse ? &reverse : result;
            do {
                SkPoint pts[4];
                switch (iter.next(pts)) {
                    case SkPath::kMove_Verb:
                        temp->moveTo(pts[0]);
                        break;
                    case SkPath::kLine_Verb:
                        temp->lineTo(pts[1]);
                        break;
                    case SkPath::kQuad_Verb:
                        temp->quadTo(pts[1], pts[2]);
                        break;
                    case SkPath::kConic_Verb:
                        temp->conicTo(pts[1], pts[2], iter.conicWeight());
                        break;
                    case SkPath::kCubic_Verb:
                        temp->cubicTo(pts[1], pts[2], pts[3]);
                        break;
                    case SkPath::kClose_Verb:
                        temp->close();
                        break;
                    case SkPath::kDone_Verb:
                        break;
                    default:
                        SkASSERT(0);
                }
            } while (++verbCount < contour.fVerbEnd);
            if (contour.fReverse) {
                result->reverseAddPath(reverse);
            }
        }
    }

private:
    const SkPath& fPath;
};

static bool set_result_path(SkPath* result, const SkPath& path, SkPath::FillType fillType) {
    *result = path;
    result->setFillType(fillType);
    return true;
}

bool SK_API AsWinding(const SkPath& path, SkPath* result) {
    if (!path.isFinite()) {
        return false;
    }
    SkPath::FillType fillType = path.getFillType();
    if (fillType == SkPath::kWinding_FillType
            || fillType == SkPath::kInverseWinding_FillType ) {
        return set_result_path(result, path, fillType);
    }
    fillType = path.isInverseFillType() ? SkPath::kInverseWinding_FillType :
            SkPath::kWinding_FillType;
    if (path.isEmpty() || path.isConvex()) {
        return set_result_path(result, path, fillType);
    }
    // count contours
    vector<Contour> contours;   // one per contour
    OpAsWinding winder(path);
    winder.contourBounds(&contours);
    if (contours.size() <= 1) {
        return set_result_path(result, path, fillType);
    }
    // create contour bounding box tree
    Contour sorted(SkRect(), 0, 0);
    for (auto& contour : contours) {
        winder.inParent(contour, sorted);
    }
    // if sorted has no grandchildren, no child has to fix its children's winding
    if (std::all_of(sorted.fChildren.begin(), sorted.fChildren.end(),
            [](const Contour* contour) -> bool { return !contour->fChildren.size(); } )) {
        return set_result_path(result, path, fillType);
    }
    // starting with outermost and moving inward, see if one path contains another
    for (auto contour : sorted.fChildren) {
        winder.nextEdge(*contour, OpAsWinding::Edge::kInitial);
        winder.checkContainerChildren(nullptr, contour);
    }
    // starting with outermost and moving inward, mark paths to reverse
    bool reversed = false;
    for (auto contour : sorted.fChildren) {
        reversed |= winder.markReverse(nullptr, contour);
    }
    if (!reversed) {
        return set_result_path(result, path, fillType);
    }
    SkPath temp;
    temp.setFillType(fillType);
    winder.reverseMarkedContours(contours, &temp);
    result->swap(temp);
    return true;
}
<commit_msg>fix hanging fuzz in AsWinding<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkRect.h"
#include <algorithm>
#include <vector>

using std::vector;

struct Contour {
    enum class Direction {  // SkPath::Direction doesn't have 'none' state
        kCCW = -1,
        kNone,
        kCW,
    };

    Contour(const SkRect& bounds, int lastStart, int verbStart)
        : fBounds(bounds)
        , fVerbStart(lastStart)
        , fVerbEnd(verbStart) {
    }

    vector<Contour*> fChildren;
    const SkRect fBounds;
    SkPoint fMinXY{SK_ScalarMax, SK_ScalarMax};
    const int fVerbStart;
    const int fVerbEnd;
    Direction fDirection{Direction::kNone};
    bool fContained{false};
    bool fReverse{false};
};

static const int kPtCount[] = { 1, 1, 2, 2, 3, 0 };
static const int kPtIndex[] = { 0, 1, 1, 1, 1, 0 };

static Contour::Direction to_direction(SkScalar dy) {
    return dy > 0 ? Contour::Direction::kCCW : dy < 0 ? Contour::Direction::kCW :
            Contour::Direction::kNone;
}

static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) {
    SkRect bounds;
    bounds.set(pts, kPtCount[verb] + 1);
    if (bounds.fTop > edge.fY) {
        return 0;
    }
    if (bounds.fBottom <= edge.fY) {  // check to see if y is at line end to avoid double counting
        return 0;
    }
    if (bounds.fLeft >= edge.fX) {
        return 0;
    }
    int winding = 0;
    double tVals[3];
    Contour::Direction directions[3];
    // must intersect horz ray with curve in case it intersects more than once
    int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals);
    SkASSERT(between(0, count, 3));
    // remove results to the right of edge
    for (int index = 0; index < count; ) {
        SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX;
        if (intersectX < edge.fX) {
            ++index;
            continue;
        }
        if (intersectX > edge.fX) {
            tVals[index] = tVals[--count];
            continue;
        }
        // if intersect x equals edge x, we need to determine if pts is to the left or right of edge
        if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) {
            ++index;
            continue;
        }
        // TODO : other cases need discriminating. need op angle code to figure it out
        // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep.
        // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases.
        tVals[index] = tVals[--count];
    }
    // use first derivative to determine if intersection is contributing +1 or -1 to winding
    for (int index = 0; index < count; ++index) {
        directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY);
    }
    for (int index = 0; index < count; ++index) {
        // skip intersections that end at edge and go up
        if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) {
            continue;
        }
        winding += (int) directions[index];
    }
    return winding;  // note winding indicates containership, not contour direction
}

static SkScalar conic_weight(const SkPath::Iter& iter, SkPath::Verb verb) {
    return SkPath::kConic_Verb == verb ? iter.conicWeight() : 1;
}

static SkPoint left_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight,
        Contour::Direction* direction) {
    SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
    SkPoint result;
    double dy;
    double t SK_INIT_TO_AVOID_WARNING;
    int roots = 0;
    if (SkPath::kLine_Verb == verb) {
        result = pts[0].fX < pts[1].fX ? pts[0] : pts[1];
        dy = pts[1].fY - pts[0].fY;
    } else if (SkPath::kQuad_Verb == verb) {
        SkDQuad quad;
        quad.set(pts);
        if (!quad.monotonicInX()) {
            roots = SkDQuad::FindExtrema(&quad[0].fX, &t);
        }
        if (roots) {
            result = quad.ptAtT(t).asSkPoint();
        } else {
            result = pts[0].fX < pts[2].fX ? pts[0] : pts[2];
            t = pts[0].fX < pts[2].fX ? 0 : 1;
        }
        dy = quad.dxdyAtT(t).fY;
    } else if (SkPath::kConic_Verb == verb) {
        SkDConic conic;
        conic.set(pts, weight);
        if (!conic.monotonicInX()) {
            roots = SkDConic::FindExtrema(&conic[0].fX, weight, &t);
        }
        if (roots) {
            result = conic.ptAtT(t).asSkPoint();
        } else {
            result = pts[0].fX < pts[2].fX ? pts[0] : pts[2];
            t = pts[0].fX < pts[2].fX ? 0 : 1;
        }
        dy = conic.dxdyAtT(t).fY;
    } else {
        SkASSERT(SkPath::kCubic_Verb == verb);
        SkDCubic cubic;
        cubic.set(pts);
        if (!cubic.monotonicInX()) {
            double tValues[2];
            roots = SkDCubic::FindExtrema(&cubic[0].fX, tValues);
            SkASSERT(roots <= 2);
            for (int index = 0; index < roots; ++index) {
                SkPoint temp = cubic.ptAtT(tValues[index]).asSkPoint();
                if (0 == index || result.fX > temp.fX) {
                    result = temp;
                    t = tValues[index];
                }
            }
        }
        if (roots) {
            result = cubic.ptAtT(t).asSkPoint();
        } else {
            result = pts[0].fX < pts[3].fX ? pts[0] : pts[3];
            t = pts[0].fX < pts[3].fX ? 0 : 1;
        }
        dy = cubic.dxdyAtT(t).fY;
    }
    *direction = to_direction(dy);
    return result;
}

class OpAsWinding {
public:
    enum class Edge {
        kInitial,
        kCompare,
    };

    OpAsWinding(const SkPath& path)
        : fPath(path) {
    }

    void contourBounds(vector<Contour>* containers) {
        SkRect bounds;
        bounds.setEmpty();
        SkPath::RawIter iter(fPath);
        SkPoint pts[4];
        SkPath::Verb verb;
        int lastStart = 0;
        int verbStart = 0;
        do {
            verb = iter.next(pts);
            if (SkPath::kMove_Verb == verb) {
                if (!bounds.isEmpty()) {
                    containers->emplace_back(bounds, lastStart, verbStart);
                    lastStart = verbStart;
               }
               bounds.setBounds(&pts[kPtIndex[verb]], kPtCount[verb]);
            }
            if (SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb) {
                SkRect verbBounds;
                verbBounds.setBounds(&pts[kPtIndex[verb]], kPtCount[verb]);
                bounds.joinPossiblyEmptyRect(verbBounds);
            }
            ++verbStart;
        } while (SkPath::kDone_Verb != verb);
        if (!bounds.isEmpty()) {
            containers->emplace_back(bounds, lastStart, verbStart);
        }
    }

    int nextEdge(Contour& contour, Edge edge) {
        SkPath::Iter iter(fPath, true);
        SkPoint pts[4];
        SkPath::Verb verb;
        int verbCount = -1;
        int winding = 0;
        do {
            verb = iter.next(pts);
            if (++verbCount < contour.fVerbStart) {
                continue;
            }
            if (verbCount >= contour.fVerbEnd) {
                continue;
            }
            if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) {
                continue;
            }
            bool horizontal = true;
            for (int index = 1; index <= kPtCount[verb]; ++index) {
                if (pts[0].fY != pts[index].fY) {
                    horizontal = false;
                    break;
                }
            }
            if (horizontal) {
                continue;
            }
            if (edge == Edge::kCompare) {
                winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY);
                continue;
            }
            SkASSERT(edge == Edge::kInitial);
            Contour::Direction direction;
            SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb), &direction);
            if (minXY.fX > contour.fMinXY.fX) {
                continue;
            }
            if (minXY.fX == contour.fMinXY.fX) {
                if (minXY.fY != contour.fMinXY.fY) {
                    continue;
                }
                if (direction == contour.fDirection) {
                    continue;
                }
                // incomplete: must sort edges to find the one most to left
                // File a bug if this code path is triggered and AsWinding was
                // expected to succeed.
                SkDEBUGF("incomplete\n");
                // TODO: add edges as opangle and sort
            }
            contour.fMinXY = minXY;
            contour.fDirection = direction;
        } while (SkPath::kDone_Verb != verb);
        return winding;
    }

    void containerContains(Contour& contour, Contour& test) {
        // find outside point on lesser contour
        // arbitrarily, choose non-horizontal edge where point <= bounds left
        // note that if leftmost point is control point, may need tight bounds
            // to find edge with minimum-x
        if (SK_ScalarMax == test.fMinXY.fX) {
            this->nextEdge(test, Edge::kInitial);
        }
        // find all edges on greater equal or to the left of one on lesser
        contour.fMinXY = test.fMinXY;
        int winding = this->nextEdge(contour, Edge::kCompare);
        // if edge is up, mark contour cw, otherwise, ccw
        // sum of greater edges direction should be cw, 0, ccw
        SkASSERT(-1 <= winding && winding <= 1);
        test.fContained = winding != 0;
    }

    void inParent(Contour& contour, Contour& parent) {
        // move contour into sibling list contained by parent
        for (auto test : parent.fChildren) {
            if (test->fBounds.contains(contour.fBounds)) {
                inParent(contour, *test);
                return;
            }
        }
        // move parent's children into contour's children if contained by contour
        for (auto iter = parent.fChildren.begin(); iter != parent.fChildren.end(); ) {
            if (contour.fBounds.contains((*iter)->fBounds)) {
                contour.fChildren.push_back(*iter);
                iter = parent.fChildren.erase(iter);
                continue;
            }
            ++iter;
        }
        parent.fChildren.push_back(&contour);
    }

    void checkContainerChildren(Contour* parent, Contour* child) {
        for (auto grandChild : child->fChildren) {
            checkContainerChildren(child, grandChild);
        }
        if (parent) {
            containerContains(*parent, *child);
        }
    }

    bool markReverse(Contour* parent, Contour* child) {
        bool reversed = false;
        for (auto grandChild : child->fChildren) {
            reversed |= markReverse(grandChild->fContained ? child : parent, grandChild);
        }
        if (parent && parent->fDirection == child->fDirection) {
            child->fReverse = true;
            child->fDirection = (Contour::Direction) -(int) child->fDirection;
            return true;
        }
        return reversed;
    }

    void reverseMarkedContours(vector<Contour>& contours, SkPath* result) {
        SkPath::RawIter iter(fPath);
        int verbCount = 0;
        for (auto contour : contours) {
            SkPath reverse;
            SkPath* temp = contour.fReverse ? &reverse : result;
            do {
                SkPoint pts[4];
                switch (iter.next(pts)) {
                    case SkPath::kMove_Verb:
                        temp->moveTo(pts[0]);
                        break;
                    case SkPath::kLine_Verb:
                        temp->lineTo(pts[1]);
                        break;
                    case SkPath::kQuad_Verb:
                        temp->quadTo(pts[1], pts[2]);
                        break;
                    case SkPath::kConic_Verb:
                        temp->conicTo(pts[1], pts[2], iter.conicWeight());
                        break;
                    case SkPath::kCubic_Verb:
                        temp->cubicTo(pts[1], pts[2], pts[3]);
                        break;
                    case SkPath::kClose_Verb:
                        temp->close();
                        break;
                    case SkPath::kDone_Verb:
                        break;
                    default:
                        SkASSERT(0);
                }
            } while (++verbCount < contour.fVerbEnd);
            if (contour.fReverse) {
                result->reverseAddPath(reverse);
            }
        }
    }

private:
    const SkPath& fPath;
};

static bool set_result_path(SkPath* result, const SkPath& path, SkPath::FillType fillType) {
    *result = path;
    result->setFillType(fillType);
    return true;
}

bool SK_API AsWinding(const SkPath& path, SkPath* result) {
    if (!path.isFinite()) {
        return false;
    }
    SkPath::FillType fillType = path.getFillType();
    if (fillType == SkPath::kWinding_FillType
            || fillType == SkPath::kInverseWinding_FillType ) {
        return set_result_path(result, path, fillType);
    }
    fillType = path.isInverseFillType() ? SkPath::kInverseWinding_FillType :
            SkPath::kWinding_FillType;
    if (path.isEmpty() || path.isConvex()) {
        return set_result_path(result, path, fillType);
    }
    // count contours
    vector<Contour> contours;   // one per contour
    OpAsWinding winder(path);
    winder.contourBounds(&contours);
    if (contours.size() <= 1) {
        return set_result_path(result, path, fillType);
    }
    // create contour bounding box tree
    Contour sorted(SkRect(), 0, 0);
    for (auto& contour : contours) {
        winder.inParent(contour, sorted);
    }
    // if sorted has no grandchildren, no child has to fix its children's winding
    if (std::all_of(sorted.fChildren.begin(), sorted.fChildren.end(),
            [](const Contour* contour) -> bool { return !contour->fChildren.size(); } )) {
        return set_result_path(result, path, fillType);
    }
    // starting with outermost and moving inward, see if one path contains another
    for (auto contour : sorted.fChildren) {
        winder.nextEdge(*contour, OpAsWinding::Edge::kInitial);
        winder.checkContainerChildren(nullptr, contour);
    }
    // starting with outermost and moving inward, mark paths to reverse
    bool reversed = false;
    for (auto contour : sorted.fChildren) {
        reversed |= winder.markReverse(nullptr, contour);
    }
    if (!reversed) {
        return set_result_path(result, path, fillType);
    }
    SkPath temp;
    temp.setFillType(fillType);
    winder.reverseMarkedContours(contours, &temp);
    result->swap(temp);
    return true;
}
<|endoftext|>


<commit_before>// Copyright 2015 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.

#include "bindings/runtime.h"

#include <algorithm>
#include <chrono>
#include <string.h>
#include <thread>
#include <unordered_map>

#include <include/libplatform/libplatform.h>

#include "base/logging.h"
#include "base/time.h"
#include "bindings/exception_handler.h"
#include "bindings/frame_observer.h"
#include "bindings/global_scope.h"
#include "bindings/profiler.h"
#include "bindings/runtime_modulator.h"
#include "bindings/timer_queue.h"
#include "bindings/utilities.h"

namespace bindings {

namespace {

// Map of v8 Isolates to the associated Runtime instances (weak references).
std::unordered_map<v8::Isolate*, std::weak_ptr<Runtime>> g_runtime_instances_;

// String of the v8 flags that should apply to this virtual machine. See flag-definitions.h in
// the v8 source code for a list of all supported command line flags.
const char kRuntimeFlags[] =
    "--expose_gc "
    "--use_strict "
    "--harmony "

    // Dynamic imports
    "--harmony_dynamic_import "
    "--harmony_import_meta "

    // Arbitrary precision integers (beyond the usual 53 bits)
    "--harmony_bigint "

    // Public and class fields to be usable in class literals
    "--harmony_public_fields "
    "--harmony_class_fields "

    // Array.prototype.values
    "--harmony_array_prototype_values";

// Returns whether |character| represents a line break.
bool IsLineBreak(char character) {
  return character == '\n' || character == '\r';
}

// Returns a string with all line breaks removed from |line|.
std::string RemoveLineBreaks(std::string line) {
  line.erase(std::remove_if(line.begin(), line.end(), IsLineBreak), line.end());
  return line;
}

// Callback for the v8 message handler. Forward the call to the exception handler.
void MessageCallback(v8::Local<v8::Message> message, v8::Local<v8::Value> error) {
  std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(v8::Isolate::GetCurrent());
  if (runtime)
    runtime->GetExceptionHandler()->OnMessage(
        message, error, ExceptionHandler::MessageSource::kScript);
}

// Callback for the v8 fatal error handler. Forward the call to the exception handler.
void FatalErrorCallback(const char* location, const char* message) {
  std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(v8::Isolate::GetCurrent());
  if (runtime)
    runtime->GetExceptionHandler()->OnFatalError(location, message);
}

// Callback for uncaught promise rejection events. Output the exception message to the console
// after all, so that developers can pick up on the error and deal with it.
void PromiseRejectCallback(v8::PromiseRejectMessage message) {
  std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(v8::Isolate::GetCurrent());
  if (!runtime)
    return;

  if (message.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
    runtime->GetExceptionHandler()->RevokeQueuedMessages(message.GetPromise());
    return;
  }

  v8::Local<v8::Value> value = message.GetValue();
  if (value.IsEmpty() || !value->IsNativeError())
    return;

  v8::Local<v8::Message> error_message = v8::Exception::CreateMessage(value);
  if (error_message.IsEmpty())
    return;

  runtime->GetExceptionHandler()->OnMessage(
      error_message, value, ExceptionHandler::MessageSource::kRejectedPromise, message.GetPromise());
}

}  // namespace

// static
std::shared_ptr<Runtime> Runtime::FromIsolate(v8::Isolate* isolate) {
  const auto instance = g_runtime_instances_.find(isolate);
  if (instance == g_runtime_instances_.end())
    return nullptr;

  CHECK(!instance->second.expired());
  return instance->second.lock();
}

// static
std::shared_ptr<Runtime> Runtime::Create(Delegate* runtime_delegate,
                                         plugin::PluginController* plugin_controller) {
  auto instance = std::shared_ptr<Runtime>(
      new Runtime(runtime_delegate, plugin_controller));
  CHECK(instance->isolate());

  g_runtime_instances_[instance->isolate()] = instance;
  return instance;
}

Runtime::Runtime(Delegate* runtime_delegate,
                 plugin::PluginController* plugin_controller)
    : global_scope_(new GlobalScope(plugin_controller)),
      runtime_delegate_(runtime_delegate),
      is_ready_(false),
      frame_counter_start_(::base::monotonicallyIncreasingTime()),
      frame_counter_(0) {
  v8::V8::InitializeICU();

  platform_.reset(v8::platform::CreateDefaultPlatform());
  v8::V8::InitializePlatform(platform_.get());

  v8::V8::SetFlagsFromString(kRuntimeFlags, sizeof(kRuntimeFlags));
  v8::V8::Initialize();

  allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());

  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = allocator_.get();

  isolate_ = v8::Isolate::New(create_params);
  isolate_scope_.reset(new v8::Isolate::Scope(isolate_));

  exception_handler_.reset(new ExceptionHandler(this, runtime_delegate_));
  isolate_->SetCaptureStackTraceForUncaughtExceptions(true, 15);
  isolate_->AddMessageListener(MessageCallback);
  isolate_->SetFatalErrorHandler(FatalErrorCallback);
  isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
  isolate_->SetPromiseRejectCallback(PromiseRejectCallback);
  isolate_->SetHostImportModuleDynamicallyCallback(
      &RuntimeModulator::ImportModuleDynamicallyCallback);

  profiler_.reset(new Profiler(isolate_));
  timer_queue_.reset(new TimerQueue(this));

  source_directory_ = base::FilePath::CurrentDirectory().Append("javascript");
}

Runtime::~Runtime() {
  global_scope_.reset();
  timer_queue_.reset();
  modulator_.reset();

  isolate_scope_.reset();
  isolate_->Dispose();

  v8::V8::Dispose();
  v8::V8::ShutdownPlatform();
}

void Runtime::Initialize() {
  v8::HandleScope handle_scope(isolate_);

  v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);

  // The global scope is being applied in two steps. First we install all the prototypes (which
  // essentially are functions that may be instantiated), then we install objects, which may
  // include instantiations of previously installed prototypes (e.g. the console global).
  global_scope_->InstallPrototypes(global);

  v8::Local<v8::Context> context = v8::Context::New(isolate_, nullptr, global);

  v8::Context::Scope context_scope(context);
  global_scope_->InstallObjects(context->Global());
  global_scope_->Finalize();

  context_.Reset(isolate_, context);

  modulator_ = std::make_unique<RuntimeModulator>(isolate_, source_directory_);
  modulator_->LoadModule(context, base::FilePath() /* referrer */, "main.js");
}

void Runtime::SpinUntilReady() {
  while (!is_ready_) {
    std::this_thread::sleep_for(std::chrono::microseconds(500));
    OnFrame();
  }
}

void Runtime::SetReady() {
  is_ready_ = true;
}

void Runtime::GetAndResetFrameCounter(double* duration, double* average_fps) {
  DCHECK(duration && average_fps);

  double current = ::base::monotonicallyIncreasingTime();

  *duration = current - frame_counter_start_;
  *average_fps = frame_counter_ / (*duration / 1000);

  frame_counter_start_ = current;
  frame_counter_ = 0;
}

void Runtime::OnFrame() {
  ++frame_counter_;

  double current_time = ::base::monotonicallyIncreasingTime();

  for (FrameObserver* observer : frame_observers_)
    observer->OnFrame();

  if (profiler_->IsActive())
    profiler_->OnFrame(current_time);

  timer_queue_->Run(current_time);

  isolate_->RunMicrotasks();

  exception_handler_->FlushMessageQueue();
}

void Runtime::AddFrameObserver(FrameObserver* observer) {
  frame_observers_.insert(observer);
}

void Runtime::RemoveFrameObserver(FrameObserver* observer) {
  frame_observers_.erase(observer);
}

}  // namespace bindings
<commit_msg>std::make_unique -> bare new for Linux<commit_after>// Copyright 2015 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.

#include "bindings/runtime.h"

#include <algorithm>
#include <chrono>
#include <string.h>
#include <thread>
#include <unordered_map>

#include <include/libplatform/libplatform.h>

#include "base/logging.h"
#include "base/time.h"
#include "bindings/exception_handler.h"
#include "bindings/frame_observer.h"
#include "bindings/global_scope.h"
#include "bindings/profiler.h"
#include "bindings/runtime_modulator.h"
#include "bindings/timer_queue.h"
#include "bindings/utilities.h"

namespace bindings {

namespace {

// Map of v8 Isolates to the associated Runtime instances (weak references).
std::unordered_map<v8::Isolate*, std::weak_ptr<Runtime>> g_runtime_instances_;

// String of the v8 flags that should apply to this virtual machine. See flag-definitions.h in
// the v8 source code for a list of all supported command line flags.
const char kRuntimeFlags[] =
    "--expose_gc "
    "--use_strict "
    "--harmony "

    // Dynamic imports
    "--harmony_dynamic_import "
    "--harmony_import_meta "

    // Arbitrary precision integers (beyond the usual 53 bits)
    "--harmony_bigint "

    // Public and class fields to be usable in class literals
    "--harmony_public_fields "
    "--harmony_class_fields "

    // Array.prototype.values
    "--harmony_array_prototype_values";

// Returns whether |character| represents a line break.
bool IsLineBreak(char character) {
  return character == '\n' || character == '\r';
}

// Returns a string with all line breaks removed from |line|.
std::string RemoveLineBreaks(std::string line) {
  line.erase(std::remove_if(line.begin(), line.end(), IsLineBreak), line.end());
  return line;
}

// Callback for the v8 message handler. Forward the call to the exception handler.
void MessageCallback(v8::Local<v8::Message> message, v8::Local<v8::Value> error) {
  std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(v8::Isolate::GetCurrent());
  if (runtime)
    runtime->GetExceptionHandler()->OnMessage(
        message, error, ExceptionHandler::MessageSource::kScript);
}

// Callback for the v8 fatal error handler. Forward the call to the exception handler.
void FatalErrorCallback(const char* location, const char* message) {
  std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(v8::Isolate::GetCurrent());
  if (runtime)
    runtime->GetExceptionHandler()->OnFatalError(location, message);
}

// Callback for uncaught promise rejection events. Output the exception message to the console
// after all, so that developers can pick up on the error and deal with it.
void PromiseRejectCallback(v8::PromiseRejectMessage message) {
  std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(v8::Isolate::GetCurrent());
  if (!runtime)
    return;

  if (message.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
    runtime->GetExceptionHandler()->RevokeQueuedMessages(message.GetPromise());
    return;
  }

  v8::Local<v8::Value> value = message.GetValue();
  if (value.IsEmpty() || !value->IsNativeError())
    return;

  v8::Local<v8::Message> error_message = v8::Exception::CreateMessage(value);
  if (error_message.IsEmpty())
    return;

  runtime->GetExceptionHandler()->OnMessage(
      error_message, value, ExceptionHandler::MessageSource::kRejectedPromise, message.GetPromise());
}

}  // namespace

// static
std::shared_ptr<Runtime> Runtime::FromIsolate(v8::Isolate* isolate) {
  const auto instance = g_runtime_instances_.find(isolate);
  if (instance == g_runtime_instances_.end())
    return nullptr;

  CHECK(!instance->second.expired());
  return instance->second.lock();
}

// static
std::shared_ptr<Runtime> Runtime::Create(Delegate* runtime_delegate,
                                         plugin::PluginController* plugin_controller) {
  auto instance = std::shared_ptr<Runtime>(
      new Runtime(runtime_delegate, plugin_controller));
  CHECK(instance->isolate());

  g_runtime_instances_[instance->isolate()] = instance;
  return instance;
}

Runtime::Runtime(Delegate* runtime_delegate,
                 plugin::PluginController* plugin_controller)
    : global_scope_(new GlobalScope(plugin_controller)),
      runtime_delegate_(runtime_delegate),
      is_ready_(false),
      frame_counter_start_(::base::monotonicallyIncreasingTime()),
      frame_counter_(0) {
  v8::V8::InitializeICU();

  platform_.reset(v8::platform::CreateDefaultPlatform());
  v8::V8::InitializePlatform(platform_.get());

  v8::V8::SetFlagsFromString(kRuntimeFlags, sizeof(kRuntimeFlags));
  v8::V8::Initialize();

  allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());

  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = allocator_.get();

  isolate_ = v8::Isolate::New(create_params);
  isolate_scope_.reset(new v8::Isolate::Scope(isolate_));

  exception_handler_.reset(new ExceptionHandler(this, runtime_delegate_));
  isolate_->SetCaptureStackTraceForUncaughtExceptions(true, 15);
  isolate_->AddMessageListener(MessageCallback);
  isolate_->SetFatalErrorHandler(FatalErrorCallback);
  isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
  isolate_->SetPromiseRejectCallback(PromiseRejectCallback);
  isolate_->SetHostImportModuleDynamicallyCallback(
      &RuntimeModulator::ImportModuleDynamicallyCallback);

  profiler_.reset(new Profiler(isolate_));
  timer_queue_.reset(new TimerQueue(this));

  source_directory_ = base::FilePath::CurrentDirectory().Append("javascript");
}

Runtime::~Runtime() {
  global_scope_.reset();
  timer_queue_.reset();
  modulator_.reset();

  isolate_scope_.reset();
  isolate_->Dispose();

  v8::V8::Dispose();
  v8::V8::ShutdownPlatform();
}

void Runtime::Initialize() {
  v8::HandleScope handle_scope(isolate_);

  v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);

  // The global scope is being applied in two steps. First we install all the prototypes (which
  // essentially are functions that may be instantiated), then we install objects, which may
  // include instantiations of previously installed prototypes (e.g. the console global).
  global_scope_->InstallPrototypes(global);

  v8::Local<v8::Context> context = v8::Context::New(isolate_, nullptr, global);

  v8::Context::Scope context_scope(context);
  global_scope_->InstallObjects(context->Global());
  global_scope_->Finalize();

  context_.Reset(isolate_, context);

  modulator_.reset(new RuntimeModulator(isolate_, source_directory_));
  modulator_->LoadModule(context, base::FilePath() /* referrer */, "main.js");
}

void Runtime::SpinUntilReady() {
  while (!is_ready_) {
    std::this_thread::sleep_for(std::chrono::microseconds(500));
    OnFrame();
  }
}

void Runtime::SetReady() {
  is_ready_ = true;
}

void Runtime::GetAndResetFrameCounter(double* duration, double* average_fps) {
  DCHECK(duration && average_fps);

  double current = ::base::monotonicallyIncreasingTime();

  *duration = current - frame_counter_start_;
  *average_fps = frame_counter_ / (*duration / 1000);

  frame_counter_start_ = current;
  frame_counter_ = 0;
}

void Runtime::OnFrame() {
  ++frame_counter_;

  double current_time = ::base::monotonicallyIncreasingTime();

  for (FrameObserver* observer : frame_observers_)
    observer->OnFrame();

  if (profiler_->IsActive())
    profiler_->OnFrame(current_time);

  timer_queue_->Run(current_time);

  isolate_->RunMicrotasks();

  exception_handler_->FlushMessageQueue();
}

void Runtime::AddFrameObserver(FrameObserver* observer) {
  frame_observers_.insert(observer);
}

void Runtime::RemoveFrameObserver(FrameObserver* observer) {
  frame_observers_.erase(observer);
}

}  // namespace bindings
<|endoftext|>