<commit_before>// copyright

#include "cif++/Config.h"

#include <atomic>
#include <mutex>

#include <boost/thread.hpp>

#include "cif++/DistanceMap.h"
#include "cif++/CifUtils.h"

using namespace std;

namespace mmcif
{

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

vector<clipper::RTop_orth> AlternativeSites(const clipper::Spacegroup& spacegroup,
	const clipper::Cell& cell)
{
	vector<clipper::RTop_orth> result;

	for (int i = 0; i < spacegroup.num_symops(); ++i)
	{
		const auto& symop = spacegroup.symop(i);

		for (int u: { -1, 0, 1})
			for (int v: { -1, 0, 1})
				for (int w: { -1, 0, 1})
				{
					result.push_back(
						clipper::RTop_frac(
							symop.rot(), symop.trn() + clipper::Vec3<>(u, v, w)
						).rtop_orth(cell));
				}
	}

	return result;
}

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

DistanceMap::DistanceMap(const Structure& p, const clipper::Spacegroup& spacegroup, const clipper::Cell& cell)
	: dim(0)
{
	const float kMaxDistance = 5, kMaxDistanceSQ = kMaxDistance * kMaxDistance;

	auto atoms = p.atoms();
	dim = atoms.size();

	vector<clipper::Coord_orth> locations(dim);
	vector<bool> isWater(dim, false);

	// bounding box
	Point pMin(numeric_limits<float>::max(), numeric_limits<float>::max(), numeric_limits<float>::max()),
		  pMax(numeric_limits<float>::min(), numeric_limits<float>::min(), numeric_limits<float>::min());

	for (auto& atom: atoms)
	{
		size_t ix = index.size();
		index[atom.id()] = ix;

		locations[ix] = atom.location();
		isWater[ix] = atom.isWater();

		auto p = atom.location();

		if (pMin.mX > p.mX)
			pMin.mX = p.mX;
		if (pMin.mY > p.mY)
			pMin.mY = p.mY;
		if (pMin.mZ > p.mZ)
			pMin.mZ = p.mZ;

		if (pMax.mX < p.mX)
			pMax.mX = p.mX;
		if (pMax.mY < p.mY)
			pMax.mY = p.mY;
		if (pMax.mZ < p.mZ)
			pMax.mZ = p.mZ;
	};

	pMin -= kMaxDistance;	// extend bounding box
	pMax += kMaxDistance;

	vector<clipper::RTop_orth> rtOrth = AlternativeSites(spacegroup, cell);

	cif::Progress progress(locations.size() - 1, "Creating distance map");

	boost::thread_group t;
	size_t N = boost::thread::hardware_concurrency();
	atomic<size_t> next(0);
	mutex m;

	for (size_t i = 0; i < N; ++i)
		t.create_thread([&]()
		{
			for (;;)
			{
				size_t i = next++;

				if (i >= locations.size())
					break;

				for (size_t j = i + 1; j < locations.size(); ++j)
				{
//					if (not (isWater[i] or isWater[j]))
//						continue;

					// find nearest location based on spacegroup/cell
					double minR2 = numeric_limits<double>::max();

					for (const auto& rt: rtOrth)
					{
						auto p = locations[j].transform(rt);

						if (p[0] < pMin.mX or p[1] < pMin.mY or p[2] < pMin.mZ or
						 	p[0] > pMax.mX or p[1] > pMax.mY or p[2] > pMax.mZ)
						{
						 	continue;
						}

						double r2 = (locations[i] - p).lengthsq();
						if (minR2 > r2)
							minR2 = r2;
					}

					if (minR2 < kMaxDistanceSQ)
					{
						float d = sqrt(minR2);
						auto k = make_tuple(i, j);

						lock_guard<mutex> lock(m);
						dist[k] = d;
					}
				}

				progress.consumed(1);
			}
		});

	t.join_all();
}

DistanceMap::DistanceMap(const vector<Atom>& atoms)
	: dim(0)
{
	dim = atoms.size();

	for (auto& atom: atoms)
	{
		size_t ix = index.size();
		index[atom.id()] = ix;
	};

	for (size_t i = 0; i < dim; ++i)
	{
		for (size_t j = i + 1; j < dim; ++j)
		{
			dist[make_tuple(i, j)] = Distance(atoms[i].location(), atoms[j].location());
		}
	}
}

float DistanceMap::operator()(const Atom& a, const Atom& b) const
{
	size_t ixa, ixb;

	try
	{
		ixa = index.at(a.id());
	}
	catch (const out_of_range& ex)
	{
		throw runtime_error("atom " + a.id() + " not found in distance map");
	}

	try
	{
		ixb = index.at(b.id());
	}
	catch (const out_of_range& ex)
	{
		throw runtime_error("atom " + b.id() + " not found in distance map");
	}

	if (ixb < ixa)
		swap(ixa, ixb);

	tuple<size_t,size_t> k{ ixa, ixb };

	auto ii = dist.find(k);

	float result = 100;

	if (ii != dist.end())
		result = ii->second;

	return result;
}

vector<Atom> DistanceMap::near(const Atom& a, float maxDistance) const
{
	vector<Atom> result;
	const File& f = a.getFile();

	size_t ixa;
	try
	{
		ixa = index.at(a.id());
	}
	catch (const out_of_range& ex)
	{
		throw runtime_error("atom " + a.id() + " not found in distance map");
	}

	for (auto& i: index)
	{
		size_t ixb = i.second;

		if (ixb == ixa)
			continue;

		auto ii =
			ixa < ixb ?
				dist.find(make_tuple(ixa, ixb)) :
				dist.find(make_tuple(ixb, ixa));

		if (ii != dist.end() and ii->second <= maxDistance)
			result.emplace_back(f, i.first);
	}

	return result;
}

}
<commit_msg>betere distance berekening<commit_after>// copyright

#include "cif++/Config.h"

#include <atomic>
#include <mutex>

#include <boost/thread.hpp>

#include "cif++/DistanceMap.h"
#include "cif++/CifUtils.h"

using namespace std;

namespace mmcif
{

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

inline ostream& operator<<(ostream& os, const Atom& a)
{
	os << a.labelAsymId() << ':' << a.labelSeqId() << '/' << a.labelAtomId();

	return os;
}

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

vector<clipper::RTop_orth> AlternativeSites(const clipper::Spacegroup& spacegroup,
	const clipper::Cell& cell)
{
	vector<clipper::RTop_orth> result;

	for (int i = 0; i < spacegroup.num_symops(); ++i)
	{
		const auto& symop = spacegroup.symop(i);

		for (int u: { -1, 0, 1})
			for (int v: { -1, 0, 1})
				for (int w: { -1, 0, 1})
				{
					result.push_back(
						clipper::RTop_frac(
							symop.rot(), symop.trn() + clipper::Vec3<>(u, v, w)
						).rtop_orth(cell));
				}
	}

	return result;
}

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

DistanceMap::DistanceMap(const Structure& p, const clipper::Spacegroup& spacegroup, const clipper::Cell& cell)
	: dim(0)
{
	const float kMaxDistance = 5, kMaxDistanceSQ = kMaxDistance * kMaxDistance;

	auto atoms = p.atoms();
	dim = atoms.size();

	vector<clipper::Coord_orth> locations(dim);
	vector<bool> isWater(dim, false);

	// bounding box
	Point pMin(numeric_limits<float>::max(), numeric_limits<float>::max(), numeric_limits<float>::max()),
		  pMax(numeric_limits<float>::min(), numeric_limits<float>::min(), numeric_limits<float>::min());

	for (auto& atom: atoms)
	{
		size_t ix = index.size();
		index[atom.id()] = ix;

		locations[ix] = atom.location();
		isWater[ix] = atom.isWater();

		auto p = atom.location();

		if (pMin.mX > p.mX)
			pMin.mX = p.mX;
		if (pMin.mY > p.mY)
			pMin.mY = p.mY;
		if (pMin.mZ > p.mZ)
			pMin.mZ = p.mZ;

		if (pMax.mX < p.mX)
			pMax.mX = p.mX;
		if (pMax.mY < p.mY)
			pMax.mY = p.mY;
		if (pMax.mZ < p.mZ)
			pMax.mZ = p.mZ;
	};

	pMin -= kMaxDistance;	// extend bounding box
	pMax += kMaxDistance;

	vector<clipper::RTop_orth>
		rtOrth = AlternativeSites(spacegroup, cell);

	cif::Progress progress(locations.size() - 1, "Creating distance map");

	boost::thread_group t;
	size_t N = boost::thread::hardware_concurrency();
	atomic<size_t> next(0);
	mutex m;

	for (size_t i = 0; i < N; ++i)
		t.create_thread([&]()
		{
			for (;;)
			{
				size_t i = next++;

				if (i >= locations.size())
					break;

				clipper::Coord_orth pi = locations[i];
				pi[0] = fmod(pi[0], cell.a());
				pi[1] = fmod(pi[1], cell.b());
				pi[2] = fmod(pi[2], cell.c());

				for (size_t j = i + 1; j < locations.size(); ++j)
				{
//					if (not (isWater[i] or isWater[j]))
//						continue;

					// find nearest location based on spacegroup/cell
					double minR2 = numeric_limits<double>::max();

					for (auto rt: rtOrth)
					{
						auto pj = locations[j];

						pj[0] = fmod(pj[0], cell.a());
						pj[1] = fmod(pj[1], cell.b());
						pj[2] = fmod(pj[2], cell.c());

						pj = pj.transform(rt);
						double r2 = (pi - pj).lengthsq();

						if (minR2 > r2)
							minR2 = r2;

#if DEBUG_VOOR_BART
						if (r2 < 3.5 * 3.5 and not rt.equals(clipper::RTop<>::identity(), 0.1))
							cout << "symmetry contact between "
								 << atoms[i] << " at " << pi << " and "
								 << atoms[j] << " at " << pj << endl;
#endif
					}

					if (minR2 < kMaxDistanceSQ)
					{
						float d = sqrt(minR2);
						auto k = make_tuple(i, j);

						lock_guard<mutex> lock(m);
						dist[k] = d;
					}
				}

				progress.consumed(1);
			}
		});

	t.join_all();
}

DistanceMap::DistanceMap(const vector<Atom>& atoms)
	: dim(0)
{
	dim = atoms.size();

	for (auto& atom: atoms)
	{
		size_t ix = index.size();
		index[atom.id()] = ix;
	};

	for (size_t i = 0; i < dim; ++i)
	{
		for (size_t j = i + 1; j < dim; ++j)
		{
			dist[make_tuple(i, j)] = Distance(atoms[i].location(), atoms[j].location());
		}
	}
}

float DistanceMap::operator()(const Atom& a, const Atom& b) const
{
	size_t ixa, ixb;

	try
	{
		ixa = index.at(a.id());
	}
	catch (const out_of_range& ex)
	{
		throw runtime_error("atom " + a.id() + " not found in distance map");
	}

	try
	{
		ixb = index.at(b.id());
	}
	catch (const out_of_range& ex)
	{
		throw runtime_error("atom " + b.id() + " not found in distance map");
	}

	if (ixb < ixa)
		swap(ixa, ixb);

	tuple<size_t,size_t> k{ ixa, ixb };

	auto ii = dist.find(k);

	float result = 100;

	if (ii != dist.end())
		result = ii->second;

	return result;
}

vector<Atom> DistanceMap::near(const Atom& a, float maxDistance) const
{
	vector<Atom> result;
	const File& f = a.getFile();

	size_t ixa;
	try
	{
		ixa = index.at(a.id());
	}
	catch (const out_of_range& ex)
	{
		throw runtime_error("atom " + a.id() + " not found in distance map");
	}

	for (auto& i: index)
	{
		size_t ixb = i.second;

		if (ixb == ixa)
			continue;

		auto ii =
			ixa < ixb ?
				dist.find(make_tuple(ixa, ixb)) :
				dist.find(make_tuple(ixb, ixa));

		if (ii != dist.end() and ii->second <= maxDistance)
			result.emplace_back(f, i.first);
	}

	return result;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BENG_PROXY_UNUSED_ISTREAM_PTR_HXX
#define BENG_PROXY_UNUSED_ISTREAM_PTR_HXX

#include "istream.hxx"

#include <utility>

/**
 * This class holds a pointer to an unused #Istream and auto-closes
 * it.  It can be moved to other instances, until it is finally
 * "stolen" using Steal() to actually use it.
 */
class UnusedIstreamPtr {
    Istream *stream = nullptr;

public:
    UnusedIstreamPtr() = default;

    explicit UnusedIstreamPtr(Istream *_stream)
        :stream(_stream) {}

    UnusedIstreamPtr(UnusedIstreamPtr &&src):stream(src.stream) {
        src.stream = nullptr;
    }

    ~UnusedIstreamPtr() {
        if (stream != nullptr)
            stream->CloseUnused();
    }

    UnusedIstreamPtr &operator=(UnusedIstreamPtr &&src) {
        using std::swap;
        swap(stream, src.stream);
        return *this;
    }

    Istream *Steal() {
        return std::exchange(stream, nullptr);
    }

    void Clear() {
        auto *s = Steal();
        if (s != nullptr)
            s->Close();
    }
};

#endif
<commit_msg>istream/UnusedPtr: use std::exchange()<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BENG_PROXY_UNUSED_ISTREAM_PTR_HXX
#define BENG_PROXY_UNUSED_ISTREAM_PTR_HXX

#include "istream.hxx"

#include <utility>

/**
 * This class holds a pointer to an unused #Istream and auto-closes
 * it.  It can be moved to other instances, until it is finally
 * "stolen" using Steal() to actually use it.
 */
class UnusedIstreamPtr {
    Istream *stream = nullptr;

public:
    UnusedIstreamPtr() = default;

    explicit UnusedIstreamPtr(Istream *_stream)
        :stream(_stream) {}

    UnusedIstreamPtr(UnusedIstreamPtr &&src)
        :stream(std::exchange(src.stream, nullptr)) {}

    ~UnusedIstreamPtr() {
        if (stream != nullptr)
            stream->CloseUnused();
    }

    UnusedIstreamPtr &operator=(UnusedIstreamPtr &&src) {
        using std::swap;
        swap(stream, src.stream);
        return *this;
    }

    Istream *Steal() {
        return std::exchange(stream, nullptr);
    }

    void Clear() {
        auto *s = Steal();
        if (s != nullptr)
            s->Close();
    }
};

#endif
<|endoftext|>


<commit_before>#include "vvvsourcegraph.hpp"
#include <memory>
using namespace clang;
using namespace std;

shared_ptr<SemanticVertex>
getSemanticVertexFromStmt(const Stmt* stmt, Graph& graph, const clang::ASTContext& context)
{
    SemanticVertex* ret = 0;
    if(stmt) switch(stmt->getStmtClass()){
                case Stmt::CompoundStmtClass: ret = new BlockCompound(graph, stmt, context);    break;
                case Stmt::IfStmtClass:       ret = new BlockIf(      graph, stmt, context);    break;
                case Stmt::SwitchStmtClass:   ret = new BlockSwitch(  graph, stmt, context);    break;
                case Stmt::CaseStmtClass:     ret = new BlockCase(    graph, stmt, context);    break;
                case Stmt::DefaultStmtClass:  ret = new BlockDefault( graph, stmt, context);    break;
                case Stmt::ForStmtClass:      ret = new BlockFor(     graph, stmt, context);    break;
                case Stmt::WhileStmtClass:    ret = new BlockWhile(   graph, stmt, context);    break;
                case Stmt::DoStmtClass:       ret = new BlockDoWhile( graph, stmt, context);    break;
                case Stmt::CallExprClass:     ret = new BlockCall(    graph, stmt, context);    break;
                case Stmt::ReturnStmtClass:   ret = new BlockReturn(  graph, stmt, context);    break;
                case Stmt::BreakStmtClass:    ret = new BlockBreak(  graph );                   break;
                case Stmt::ContinueStmtClass: ret = new BlockContinue( graph );                 break;
                default:                      ret = new BlockSimple(  graph, stmt, context);    break; }
    return shared_ptr<SemanticVertex>(ret);
}

void BlockIf::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addConditionVertex( graph, decl2str( stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto thenStmt = getSemanticVertexFromStmt( stmt->getThen(), graph, context );
    auto elseStmt = getSemanticVertexFromStmt( stmt->getElse(), graph, context );

    thenStmt->expand( vertex, end, onReturn, onBreak, onContinue);
    if( elseStmt )
        elseStmt->expand( vertex, end, onReturn, onBreak, onContinue);
    else {
        edge_t e = boost::add_edge( vertex, end, graph).first;
        graph[e].text = "false"; }
    auto outedges = boost::out_edges( vertex, graph );
    auto i = outedges.first;
    edge_t trueBranch =  *(i++);
    edge_t falseBranch = *i;
    graph[ trueBranch ].text = "true";
    graph[ falseBranch ].text = "false";

}

void BlockCall::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addProcessVertex( graph, decl2str( stmt, context ) );
    boost::add_edge( begin, vertex, graph );
    boost::add_edge( vertex, end, graph );
}

void BlockReturn::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addProcessVertex( graph, std::string("return ") + decl2str( stmt->getRetValue(), context )  );
    boost::add_edge( begin, vertex, graph);
    boost::add_edge( vertex, onReturn, graph);
}

void BlockSimple::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addProcessVertex( graph, decl2str( stmt, context ) );
    boost::add_edge( begin, vertex, graph );
    boost::add_edge( vertex, end, graph );
}

static std::vector<std::shared_ptr<SemanticVertex>>
groupChildren(const std::vector<const Stmt*>& stmts,
            Graph& graph, const ASTContext& context)
{
    using namespace std;
    using uptrSemVert = shared_ptr<SemanticVertex>;

    std::vector<uptrSemVert> ret;
    std::vector<uptrSemVert> oneBlock;

    auto addOneBlock = [&oneBlock, &ret, &graph, &context]() {
        if(oneBlock.size()) {
            ret.push_back( uptrSemVert(new BlockSimpleCompound(graph, oneBlock, context) ) );
            oneBlock.clear();} };

    for( auto& stmt : stmts ) {
        auto sv = getSemanticVertexFromStmt( stmt, graph, context);
        if (sv->getType() != SEMANTIC_BLOCK_TYPE::SIMPLE) {
            addOneBlock();
            ret.push_back( std::move(sv) );}
        else oneBlock.push_back( std::move(sv) ); }
    addOneBlock();

    return ret;
}

void BlockCompound::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    auto children = getCompoundStmtChildren( stmt );

    const auto groupedChildren = groupChildren(children, graph, context);
    const auto numChildren = groupedChildren.size();

    switch(numChildren){
        case 0: boost::add_edge(begin,end,graph); break;
        case 1: groupedChildren[0]->expand(begin,end,onReturn, onBreak, onContinue); break;
        default:
                {
                    size_t i = numChildren-1;
                    groupedChildren[i]->expand(begin,end,onReturn,onBreak,onContinue);
                    boost::remove_edge( begin, groupedChildren[i]->getVertex(), graph);
                    do
                    {
                        --i;
                        groupedChildren[i]->expand(begin,groupedChildren[i+1]->getVertex(),onReturn,onBreak,onContinue);
                        boost::remove_edge( begin, groupedChildren[i]->getVertex(), graph);
                    }while(i!=0);
                    boost::add_edge( begin, groupedChildren[0]->getVertex(), graph);
                }
                break;};
    vertex = groupedChildren[0]->getVertex();
}

void BlockSimpleCompound::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    std::string caption;
    for(const auto& stmt: stmts)
        caption += "\n" + stmt->toString();
    vertex = addProcessVertex( graph, caption );
    boost::add_edge( begin, vertex, graph);
    boost::add_edge( vertex,   end, graph);
}

void BlockFor::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addLoopOpenVertex( graph, decl2str( stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto endLoopVertex = addLoopCloseVertex(graph, decl2str(stmt->getInc(),context) );
    boost::add_edge( endLoopVertex, end, graph);

    auto body = getSemanticVertexFromStmt( stmt->getBody(), graph, context);
    body->expand( vertex, endLoopVertex, onReturn, end, endLoopVertex );
}

void BlockWhile::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addLoopOpenVertex( graph, decl2str( stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto endLoopVertex = addLoopCloseVertex(graph, "");
    boost::add_edge( endLoopVertex, end, graph);

    auto body = getSemanticVertexFromStmt( stmt->getBody(), graph, context);
    body->expand( vertex, endLoopVertex, onReturn, end, endLoopVertex );
}

void BlockDoWhile::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addLoopOpenVertex( graph, "" );
    boost::add_edge( begin, vertex, graph);

    auto endLoopVertex = addLoopCloseVertex( graph, decl2str( stmt->getCond(), context));
    boost::add_edge( endLoopVertex, end, graph);

    auto body = getSemanticVertexFromStmt( stmt->getBody(), graph, context);
    body->expand( vertex, endLoopVertex, onReturn, end, endLoopVertex );
}

void BlockSwitch::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addConditionVertex( graph, decl2str(stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto children = getCompoundStmtChildren( stmt->getBody() );
    const auto groupedChildren = groupChildren(children, graph, context);
    const auto numChildren = groupedChildren.size();

    switch(numChildren){
        case 0: boost::add_edge(vertex,end,graph); break;
        case 1: groupedChildren[0]->expand(vertex,end,onReturn, end, onContinue); break;
        default: {  size_t i = numChildren-1;
                    if(groupedChildren[i]->getType() != SEMANTIC_BLOCK_TYPE::BREAK)
                    {
                        groupedChildren[i]->expand(vertex,end,onReturn,end,onContinue);
                        boost::remove_edge(vertex,groupedChildren[i]->getVertex(), graph);
                    }
                    do {
                        --i;
                        auto& currentChild  = groupedChildren[i];
                        auto& nextChild     = groupedChildren[i+1];
                        const auto localend = (nextChild->getType() == SEMANTIC_BLOCK_TYPE::BREAK) ? end :
                                                                                nextChild->getVertex();
                        const auto currentChildType = currentChild->getType();
                        if(currentChildType != SEMANTIC_BLOCK_TYPE::BREAK){
                            currentChild->expand(vertex, localend, onReturn, end, onContinue);  // TODO: connect children to each other, not to vertex
                            if(     currentChildType != SEMANTIC_BLOCK_TYPE::CASE
                                &&  currentChildType != SEMANTIC_BLOCK_TYPE::DEFAULT)
                                boost::remove_edge(vertex, currentChild->getVertex(), graph); }
                    }while(i!=0);
                }
                break;};
}

static std::pair<vector<string>,const Stmt*> getConditions(const CaseStmt* stmt, const ASTContext& context)
{
    std::pair<vector<string>, const Stmt*> ret;

    auto currentStmt = stmt;
    for(;;){
        ret.first.push_back( decl2str(currentStmt->getLHS(), context) );
        auto subStmt = currentStmt->getSubStmt();
        if( subStmt->getStmtClass() == Stmt::CaseStmtClass )
            currentStmt = static_cast<const CaseStmt*>(subStmt);
        else {
            currentStmt = nullptr;
            ret.second  = subStmt;
            break;} }

    return ret;
}

static
string joinStringsWith( const vector<string>& v, const string& delimiter)
{
    string ret;
    const auto numStrings = v.size();
    const auto numStrings_1 = numStrings-1;
    for(size_t i = 0; i < numStrings; ++i) {
        ret += v[i];
        if( i!= numStrings_1) ret += delimiter; }

    return ret;
}

void BlockCase::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    auto condition = getConditions( stmt, context );

    auto statements = getSemanticVertexFromStmt( condition.second, graph, context);
    statements->expand( begin, end, onReturn, onBreak, onContinue );

    vertex = statements->getVertex();
    boost::remove_edge( begin, vertex, graph );
    auto beginvertex = boost::add_edge( begin, vertex, graph).first;
    graph[beginvertex].text = joinStringsWith( condition.first, ", " );

/*
    vertex = addConditionVertex( graph, "CASE");
    auto beginvertex = boost::add_edge( begin, vertex, graph).first;
    boost::add_edge( vertex, end, graph);
    graph[beginvertex].text = joinStringsWith( condition.first, ", " );
    */
}

void BlockDefault::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addConditionVertex( graph, "default: ");
    boost::add_edge( begin, vertex, graph);
    boost::add_edge( vertex, end, graph);
}

void BlockBreak::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    boost::add_edge( begin, onBreak, graph);
}

void BlockContinue::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    boost::add_edge( begin, onContinue, graph);
}

<commit_msg>implement 'default'-statement<commit_after>#include "vvvsourcegraph.hpp"
#include <memory>
using namespace clang;
using namespace std;

shared_ptr<SemanticVertex>
getSemanticVertexFromStmt(const Stmt* stmt, Graph& graph, const clang::ASTContext& context)
{
    SemanticVertex* ret = 0;
    if(stmt) switch(stmt->getStmtClass()){
                case Stmt::CompoundStmtClass: ret = new BlockCompound(graph, stmt, context);    break;
                case Stmt::IfStmtClass:       ret = new BlockIf(      graph, stmt, context);    break;
                case Stmt::SwitchStmtClass:   ret = new BlockSwitch(  graph, stmt, context);    break;
                case Stmt::CaseStmtClass:     ret = new BlockCase(    graph, stmt, context);    break;
                case Stmt::DefaultStmtClass:  ret = new BlockDefault( graph, stmt, context);    break;
                case Stmt::ForStmtClass:      ret = new BlockFor(     graph, stmt, context);    break;
                case Stmt::WhileStmtClass:    ret = new BlockWhile(   graph, stmt, context);    break;
                case Stmt::DoStmtClass:       ret = new BlockDoWhile( graph, stmt, context);    break;
                case Stmt::CallExprClass:     ret = new BlockCall(    graph, stmt, context);    break;
                case Stmt::ReturnStmtClass:   ret = new BlockReturn(  graph, stmt, context);    break;
                case Stmt::BreakStmtClass:    ret = new BlockBreak(  graph );                   break;
                case Stmt::ContinueStmtClass: ret = new BlockContinue( graph );                 break;
                default:                      ret = new BlockSimple(  graph, stmt, context);    break; }
    return shared_ptr<SemanticVertex>(ret);
}

void BlockIf::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addConditionVertex( graph, decl2str( stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto thenStmt = getSemanticVertexFromStmt( stmt->getThen(), graph, context );
    auto elseStmt = getSemanticVertexFromStmt( stmt->getElse(), graph, context );

    thenStmt->expand( vertex, end, onReturn, onBreak, onContinue);
    if( elseStmt )
        elseStmt->expand( vertex, end, onReturn, onBreak, onContinue);
    else {
        edge_t e = boost::add_edge( vertex, end, graph).first;
        graph[e].text = "false"; }
    auto outedges = boost::out_edges( vertex, graph );
    auto i = outedges.first;
    edge_t trueBranch =  *(i++);
    edge_t falseBranch = *i;
    graph[ trueBranch ].text = "true";
    graph[ falseBranch ].text = "false";

}

void BlockCall::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addProcessVertex( graph, decl2str( stmt, context ) );
    boost::add_edge( begin, vertex, graph );
    boost::add_edge( vertex, end, graph );
}

void BlockReturn::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addProcessVertex( graph, std::string("return ") + decl2str( stmt->getRetValue(), context )  );
    boost::add_edge( begin, vertex, graph);
    boost::add_edge( vertex, onReturn, graph);
}

void BlockSimple::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addProcessVertex( graph, decl2str( stmt, context ) );
    boost::add_edge( begin, vertex, graph );
    boost::add_edge( vertex, end, graph );
}

static std::vector<std::shared_ptr<SemanticVertex>>
groupChildren(const std::vector<const Stmt*>& stmts,
            Graph& graph, const ASTContext& context)
{
    using namespace std;
    using uptrSemVert = shared_ptr<SemanticVertex>;

    std::vector<uptrSemVert> ret;
    std::vector<uptrSemVert> oneBlock;

    auto addOneBlock = [&oneBlock, &ret, &graph, &context]() {
        if(oneBlock.size()) {
            ret.push_back( uptrSemVert(new BlockSimpleCompound(graph, oneBlock, context) ) );
            oneBlock.clear();} };

    for( auto& stmt : stmts ) {
        auto sv = getSemanticVertexFromStmt( stmt, graph, context);
        if (sv->getType() != SEMANTIC_BLOCK_TYPE::SIMPLE) {
            addOneBlock();
            ret.push_back( std::move(sv) );}
        else oneBlock.push_back( std::move(sv) ); }
    addOneBlock();

    return ret;
}

void BlockCompound::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    auto children = getCompoundStmtChildren( stmt );

    const auto groupedChildren = groupChildren(children, graph, context);
    const auto numChildren = groupedChildren.size();

    switch(numChildren){
        case 0: boost::add_edge(begin,end,graph); break;
        case 1: groupedChildren[0]->expand(begin,end,onReturn, onBreak, onContinue); break;
        default:
                {
                    size_t i = numChildren-1;
                    groupedChildren[i]->expand(begin,end,onReturn,onBreak,onContinue);
                    boost::remove_edge( begin, groupedChildren[i]->getVertex(), graph);
                    do
                    {
                        --i;
                        groupedChildren[i]->expand(begin,groupedChildren[i+1]->getVertex(),onReturn,onBreak,onContinue);
                        boost::remove_edge( begin, groupedChildren[i]->getVertex(), graph);
                    }while(i!=0);
                    boost::add_edge( begin, groupedChildren[0]->getVertex(), graph);
                }
                break;};
    vertex = groupedChildren[0]->getVertex();
}

void BlockSimpleCompound::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    std::string caption;
    for(const auto& stmt: stmts)
        caption += "\n" + stmt->toString();
    vertex = addProcessVertex( graph, caption );
    boost::add_edge( begin, vertex, graph);
    boost::add_edge( vertex,   end, graph);
}

void BlockFor::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addLoopOpenVertex( graph, decl2str( stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto endLoopVertex = addLoopCloseVertex(graph, decl2str(stmt->getInc(),context) );
    boost::add_edge( endLoopVertex, end, graph);

    auto body = getSemanticVertexFromStmt( stmt->getBody(), graph, context);
    body->expand( vertex, endLoopVertex, onReturn, end, endLoopVertex );
}

void BlockWhile::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addLoopOpenVertex( graph, decl2str( stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto endLoopVertex = addLoopCloseVertex(graph, "");
    boost::add_edge( endLoopVertex, end, graph);

    auto body = getSemanticVertexFromStmt( stmt->getBody(), graph, context);
    body->expand( vertex, endLoopVertex, onReturn, end, endLoopVertex );
}

void BlockDoWhile::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addLoopOpenVertex( graph, "" );
    boost::add_edge( begin, vertex, graph);

    auto endLoopVertex = addLoopCloseVertex( graph, decl2str( stmt->getCond(), context));
    boost::add_edge( endLoopVertex, end, graph);

    auto body = getSemanticVertexFromStmt( stmt->getBody(), graph, context);
    body->expand( vertex, endLoopVertex, onReturn, end, endLoopVertex );
}

void BlockSwitch::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    vertex = addConditionVertex( graph, decl2str(stmt->getCond(), context) );
    boost::add_edge( begin, vertex, graph);

    auto children = getCompoundStmtChildren( stmt->getBody() );
    const auto groupedChildren = groupChildren(children, graph, context);
    const auto numChildren = groupedChildren.size();

    switch(numChildren){
        case 0: boost::add_edge(vertex,end,graph); break;
        case 1: groupedChildren[0]->expand(vertex,end,onReturn, end, onContinue); break;
        default: {  size_t i = numChildren-1;
                    if(groupedChildren[i]->getType() != SEMANTIC_BLOCK_TYPE::BREAK)
                    {
                        groupedChildren[i]->expand(vertex,end,onReturn,end,onContinue);
                        boost::remove_edge(vertex,groupedChildren[i]->getVertex(), graph);
                    }
                    do {
                        --i;
                        auto& currentChild  = groupedChildren[i];
                        auto& nextChild     = groupedChildren[i+1];
                        const auto localend = (nextChild->getType() == SEMANTIC_BLOCK_TYPE::BREAK) ? end :
                                                                                nextChild->getVertex();
                        const auto currentChildType = currentChild->getType();
                        if(currentChildType != SEMANTIC_BLOCK_TYPE::BREAK){
                            currentChild->expand(vertex, localend, onReturn, end, onContinue);  // TODO: connect children to each other, not to vertex
                            if(     currentChildType != SEMANTIC_BLOCK_TYPE::CASE
                                &&  currentChildType != SEMANTIC_BLOCK_TYPE::DEFAULT)
                                boost::remove_edge(vertex, currentChild->getVertex(), graph); }
                    }while(i!=0);
                }
                break;};
}

static std::pair<vector<string>,const Stmt*> getConditions(const CaseStmt* stmt, const ASTContext& context)
{
    std::pair<vector<string>, const Stmt*> ret;

    auto currentStmt = stmt;
    for(;;){
        ret.first.push_back( decl2str(currentStmt->getLHS(), context) );
        auto subStmt = currentStmt->getSubStmt();
        if( subStmt->getStmtClass() == Stmt::CaseStmtClass )
            currentStmt = static_cast<const CaseStmt*>(subStmt);
        else {
            currentStmt = nullptr;
            ret.second  = subStmt;
            break;} }

    return ret;
}

static
string joinStringsWith( const vector<string>& v, const string& delimiter)
{
    string ret;
    const auto numStrings = v.size();
    const auto numStrings_1 = numStrings-1;
    for(size_t i = 0; i < numStrings; ++i) {
        ret += v[i];
        if( i!= numStrings_1) ret += delimiter; }

    return ret;
}

void BlockCase::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    auto condition = getConditions( stmt, context );

    auto statements = getSemanticVertexFromStmt( condition.second, graph, context);
         statements->expand( begin, end, onReturn, onBreak, onContinue );

    vertex = statements->getVertex();
    boost::remove_edge( begin, vertex, graph );
    auto beginvertex = boost::add_edge( begin, vertex, graph).first;
    graph[beginvertex].text = joinStringsWith( condition.first, ", " );
}

void BlockDefault::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    auto statements = getSemanticVertexFromStmt( stmt->getSubStmt(), graph, context);
         statements->expand( begin, end, onReturn, onBreak, onContinue );

    vertex = statements->getVertex();
    boost::remove_edge( begin, vertex, graph );
    auto beginvertex = boost::add_edge( begin, vertex, graph).first;
    graph[beginvertex].text = "default";
}

void BlockBreak::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    boost::add_edge( begin, onBreak, graph);
}

void BlockContinue::expand(vertex_t begin, vertex_t end, vertex_t onReturn, vertex_t onBreak, vertex_t onContinue)
{
    boost::add_edge( begin, onContinue, graph);
}

<|endoftext|>


<commit_before>#include <iomanip>
#include <memory>
#include <chrono>
#include <vector>
#include <tuple>

#include <hip/hip_runtime.h>
#include <hip/hip_runtime_api.h>

#include "tensor.h"
#include "miopen_helper.h"
#include "conv_problems.h"

class miopenCNN {
    TensorDescriptor4d<float> x_desc_;
    TensorDescriptor4d<float> h_desc_;

    FilterDescriptor4d<float> w_desc_;

    std::vector<int> output_dims_;
    int num_repeats_;

    size_t fwd_workspace_size_;
    size_t bwd_inputs_workspace_size_;
    size_t bwd_params_workspace_size_;

    Tensor<float> fwd_workspace_;
    Tensor<float> bwd_inputs_workspace_;
    Tensor<float> bwd_params_workspace_;

    Tensor<float> h;

    miopenConvFwdAlgorithm_t fwd_algo_;
    miopenConvBwdDataAlgorithm_t bwd_inputs_algo_;
    miopenConvBwdWeightsAlgorithm_t bwd_params_algo_;

    const float alpha_ = 1.f;
    const float beta_  = 0.f;

    ConvolutionDescriptor conv_desc_;
    MIOpenHandle miopen_handle_;

public:

    miopenCNN(int _w, int _h, int c, int n, int k, int r, int s,
             int pad_w, int pad_h, int wstride, int hstride, Tensor<float> x, Tensor<float> w)
             :
        miopen_handle_(),
        x_desc_(n, c, _h, _w),
        w_desc_(k, c, r, s),
        conv_desc_(pad_h, pad_w, hstride, wstride)
    {
        int out_h, out_w, out_c, out_n;

        // Get output dimensions
        CHECK_MIOPEN_ERROR(miopenGetConvolutionForwardOutputDim(conv_desc_.desc(),
                                                                x_desc_.desc(),
                                                                w_desc_.desc(),
                                                                &out_n,
                                                                &out_c,
                                                                &out_h,
                                                                &out_w));

        h_desc_ = TensorDescriptor4d<float>(out_n, out_c, out_h, out_w);

        output_dims_ = {out_w, out_h, out_c, out_n};

        h = zeros<float>(output_dims_);

        // Set fwd workspace size
        CHECK_MIOPEN_ERROR(miopenConvolutionForwardGetWorkSpaceSize(
                    miopen_handle_.handle(),
                    w_desc_.desc(),
                                                          x_desc_.desc(),
                                                          conv_desc_.desc(),
                                                          h_desc_.desc(),
                                                          &fwd_workspace_size_));

        std::vector<int> u = std::vector<int>{static_cast<int>(fwd_workspace_size_ / sizeof(float)), 1};

        fwd_workspace_ = zeros<float>(u);

        const int requestAlgoCount = 1;
        int returnedAlgoCount;
        miopenConvAlgoPerf_t perfResults;

        CHECK_MIOPEN_ERROR(miopenFindConvolutionForwardAlgorithm(
          miopen_handle_.handle(),
          x_desc_.desc(),
          x.begin(),
          w_desc_.desc(),
          w.begin(),
          conv_desc_.desc(),
          h_desc_.desc(),
          h.begin(),
          requestAlgoCount,
          &returnedAlgoCount,
          &perfResults,
          fwd_workspace_.begin(),
          fwd_workspace_size_,
          false
        ));

        fwd_algo_ = perfResults.fwd_algo;

    CHECK_MIOPEN_ERROR(miopenConvolutionBackwardWeightsGetWorkSpaceSize(
                miopen_handle_.handle(),
                                                  h_desc_.desc(),
                                                  x_desc_.desc(),
                                                  conv_desc_.desc(),
                                                  w_desc_.desc(),
                                                  &bwd_params_workspace_size_));
    u = std::vector<int>{static_cast<int>(bwd_params_workspace_size_ / sizeof(float)), 1};
    bwd_params_workspace_ = zeros<float>(u);

    CHECK_MIOPEN_ERROR(miopenFindConvolutionBackwardWeightsAlgorithm(
      miopen_handle_.handle(),
      h_desc_.desc(),
      h.begin(),
      x_desc_.desc(),
      x.begin(),
      conv_desc_.desc(),
      w_desc_.desc(),
      w.begin(),
      requestAlgoCount,
      &returnedAlgoCount,
      &perfResults,
      bwd_params_workspace_.begin(),
      bwd_params_workspace_size_,
      false
    ));

    bwd_params_algo_ = perfResults.bwd_weights_algo;

    CHECK_MIOPEN_ERROR(miopenConvolutionBackwardDataGetWorkSpaceSize(
                miopen_handle_.handle(),
                                                  h_desc_.desc(),
                                                  w_desc_.desc(),
                                                  conv_desc_.desc(),
                                                  x_desc_.desc(),
                                                  &bwd_inputs_workspace_size_));

    u = std::vector<int>{static_cast<int>(bwd_inputs_workspace_size_ / sizeof(float)), 1};
    bwd_inputs_workspace_ = zeros<float>(u);

        CHECK_MIOPEN_ERROR(miopenFindConvolutionBackwardDataAlgorithm(
          miopen_handle_.handle(),
          h_desc_.desc(),
          h.begin(),
          w_desc_.desc(),
          w.begin(),
          conv_desc_.desc(),
          x_desc_.desc(),
          x.begin(),
          requestAlgoCount,
          &returnedAlgoCount,
          &perfResults,
          bwd_inputs_workspace_.begin(),
          bwd_inputs_workspace_size_,
          false
        ));

        bwd_inputs_algo_ = perfResults.bwd_data_algo;

    }

    Tensor<float> getOutputTensor(){ return h; }

    std::vector<int> get_output_dims() { return output_dims_; }

    std::string get_fwd_algo_string() {
        if (fwd_algo_ == miopenConvolutionFwdAlgoGEMM)
            return " ConvolutionFwdAlgoGEMM";
        else if (fwd_algo_ == miopenConvolutionFwdAlgoDirect)
            return " ConvolutionFwdAlgoDirect";
        else if (fwd_algo_ == miopenConvolutionFwdAlgoFFT)
            return " ConvolutionFwdAlgoFFT";
        else if (fwd_algo_ == miopenConvolutionFwdAlgoWinograd)
            return " ConvolutionFwdAlgoWinograd";
        else {
            std::stringstream ss;
            ss << "Illegal algorithm passed to get_fwd_algo_string. Algo: " << fwd_algo_ << std::endl;
            throw std::runtime_error(ss.str());
        }
    }

    void forward(Tensor<float> x, Tensor<float> filter, Tensor<float> h) {

        // Convolution forward.
        CHECK_MIOPEN_ERROR(miopenConvolutionForward(miopen_handle_.handle(),
                                                  &alpha_,
                                                  x_desc_.desc(),
                                                  x.begin(),
                                                  w_desc_.desc(),
                                                  filter.begin(),
                                                  conv_desc_.desc(),
                                                  fwd_algo_,
                                                  &beta_,
                                                  h_desc_.desc(),
                                                  h.begin(),
                                                  fwd_workspace_.begin(),
                                                  fwd_workspace_size_
                                                ));

    }

    void backward_params(Tensor<float> x, Tensor<float> delta, Tensor<float> dW) {

        CHECK_MIOPEN_ERROR(miopenConvolutionBackwardWeights(miopen_handle_.handle(),
                                                         &alpha_,
                                                         h_desc_.desc(),
                                                         delta.begin(),
                                                         x_desc_.desc(),
                                                         x.begin(),
                                                         conv_desc_.desc(),
                                                         bwd_params_algo_,
                                                         &beta_,
                                                         w_desc_.desc(),
                                                         dW.begin(),
                                                         bwd_params_workspace_.begin(),
                                                         bwd_params_workspace_size_
                                                       ));

    }

    void backward_inputs(Tensor<float> filter, Tensor<float> delta, Tensor<float> dX) {

        CHECK_MIOPEN_ERROR(miopenConvolutionBackwardData(miopen_handle_.handle(),
                                                      &alpha_,
                                                      h_desc_.desc(),
                                                      delta.begin(),
                                                      w_desc_.desc(),
                                                      filter.begin(),
                                                      conv_desc_.desc(),
                                                      bwd_inputs_algo_,
                                                      &beta_,
                                                      x_desc_.desc(),
                                                      dX.begin(),
                                                      bwd_inputs_workspace_.begin(),
                                                      bwd_inputs_workspace_size_
                                                    ));

    }
};

std::tuple<int, int, int, std::string> time_cnn(
         int k, int c, int r, int s,
         int n, int h, int w,
         int pad_h, int pad_w,
         int hstride, int wstride,
         int num_repeats
        ) {

    // Allocate memory for filter
    auto filter = rand<float>(std::vector<int>{r, s, c, k});

    // Allocate memory for input
    auto input = rand<float>(std::vector<int>{w, h, c, n});
    miopenCNN cnn(w, h, c, n, k, r, s, pad_w, pad_h, wstride, hstride, input, filter);

    // Allocate memory for output tensor
    auto output = cnn.getOutputTensor();

    std::string fwd_algo_s = cnn.get_fwd_algo_string();

    //Warm up
    cnn.forward(input, filter, output);

    hipDeviceSynchronize();
    auto start = std::chrono::steady_clock::now();

    for (int i = 0; i < num_repeats; ++i) {
        cnn.forward(input, filter, output);
    }

    hipDeviceSynchronize();
    auto end = std::chrono::steady_clock::now();
    int fwd_time = static_cast<int>(std::chrono::duration<double, std::micro>(end - start).count() / num_repeats);

    // Allocate memory for backward pass wrt weights
    auto delta = rand<float>(cnn.get_output_dims());
    auto dW = zeros<float>(std::vector<int>{r, s, c, k});

    // Warm up backward
    cnn.backward_params(input, delta, dW);

    hipDeviceSynchronize();
    start = std::chrono::steady_clock::now();

    for (int i = 0; i < num_repeats; ++i) {
        // Backward pass wrt weights
        cnn.backward_params(input, delta, dW);
    }

    hipDeviceSynchronize();
    end = std::chrono::steady_clock::now();

    int bwd_params_time = static_cast<int>(std::chrono::duration<double, std::micro>(end - start).count() / num_repeats);

    //Allocate memory for backward pass wrt inputs
    auto dX = zeros<float>(std::vector<int>{w, h, c, n});

    //Warm up backward inputs
    cnn.backward_inputs(filter, delta, dX);

    hipDeviceSynchronize();
    start = std::chrono::steady_clock::now();

    for (int i = 0; i < num_repeats; ++i) {
        // Backward pass wrt weights
        cnn.backward_inputs(filter, delta, dX);

    }

    hipDeviceSynchronize();
    end = std::chrono::steady_clock::now();

    int bwd_inputs_time = static_cast<int>(std::chrono::duration<double, std::micro>(end - start).count() / num_repeats);

    return std::tuple<int, int, int, std::string>(fwd_time, bwd_inputs_time, bwd_params_time, fwd_algo_s);

}

int main(int argc, char **argv) {

    int num_repeats = 100;

    hipFree(0);

    if (argc > 1)
        num_repeats = atoi(argv[1]);

    std::cout << std::setw(30) << "Times" << std::endl;
    std::cout << std::setfill('-') << std::setw(190) << "-" << std::endl;
    std::cout << std::setfill(' ');
    std::cout << "   w      h      c      n      k      r      s    pad_w  pad_h    stride_w  stride_h    fwd_time (usec)  bwd_inputs_time (usec)  bwd_params_time (usec)  total_time (usec)   fwd_algo " << std::endl;
    std::cout << std::setfill('-') << std::setw(190) << "-" << std::endl;
    std::cout << std::setfill(' ');

    int total_fwd_time=0, total_bwd_inputs_time=0, total_bwd_params_time=0;
    for (const auto &problem : training_set) {

        // Filter parameters
        int k, c, r, s;

        // Input parameters
        int n, w, h;

        // Padding
        int pad_w, pad_h;

        // Stride
        int wstride, hstride;

        std::tie(w, h, c, n, k, r, s, pad_w, pad_h, wstride, hstride) = problem;

        int fwd_time, bwd_inputs_time, bwd_params_time;
        std::string fwd_algo_s;

        std::tie(fwd_time, bwd_inputs_time, bwd_params_time, fwd_algo_s) =
            time_cnn(k, c, r, s, n, h, w, pad_h, pad_w, hstride, wstride, num_repeats);

        std::cout << std::setw(5) << w;
        std::cout << std::setw(7) << h;
        std::cout << std::setw(7) << c;
        std::cout << std::setw(7) << n;
        std::cout << std::setw(7) << k;
        std::cout << std::setw(7) << r;
        std::cout << std::setw(7) << s;
        std::cout << std::setw(7) << pad_w;
        std::cout << std::setw(8) << pad_h;
        std::cout << std::setw(10) << wstride;
        std::cout << std::setw(10) << hstride;
        std::cout << std::setw(14) << std::setprecision(7) << fwd_time;
        std::cout << std::setw(24) << std::setprecision(7) << bwd_inputs_time;
        std::cout << std::setw(24) << std::setprecision(7) << bwd_params_time;
        std::cout << std::setw(19) << std::setprecision(8) << fwd_time + bwd_inputs_time + bwd_params_time;

        std::cout << std::setw(25) << fwd_algo_s;

        std::cout << std::endl;

        total_fwd_time += fwd_time;
        total_bwd_inputs_time += bwd_inputs_time;
        total_bwd_params_time += bwd_params_time;

    }

    std::cout << std::setw(82) << "Totals" ;
    std::cout << std::setw(14) << std::setprecision(7) << total_fwd_time;
    std::cout << std::setw(24) << std::setprecision(7) << total_bwd_inputs_time;
    std::cout << std::setw(24) << std::setprecision(7) << total_bwd_params_time;
    std::cout << std::setw(19) << std::setprecision(8) << total_fwd_time + total_bwd_inputs_time + total_bwd_params_time;
    std::cout << std::endl;

    return 0;

}

<commit_msg>exchange filter w and h<commit_after>#include <iomanip>
#include <memory>
#include <chrono>
#include <vector>
#include <tuple>

#include <hip/hip_runtime.h>
#include <hip/hip_runtime_api.h>

#include "tensor.h"
#include "miopen_helper.h"
#include "conv_problems.h"

class miopenCNN {
    TensorDescriptor4d<float> x_desc_;
    TensorDescriptor4d<float> h_desc_;

    FilterDescriptor4d<float> w_desc_;

    std::vector<int> output_dims_;
    int num_repeats_;

    size_t fwd_workspace_size_;
    size_t bwd_inputs_workspace_size_;
    size_t bwd_params_workspace_size_;

    Tensor<float> fwd_workspace_;
    Tensor<float> bwd_inputs_workspace_;
    Tensor<float> bwd_params_workspace_;

    Tensor<float> h;

    miopenConvFwdAlgorithm_t fwd_algo_;
    miopenConvBwdDataAlgorithm_t bwd_inputs_algo_;
    miopenConvBwdWeightsAlgorithm_t bwd_params_algo_;

    const float alpha_ = 1.f;
    const float beta_  = 0.f;

    ConvolutionDescriptor conv_desc_;
    MIOpenHandle miopen_handle_;

public:

    miopenCNN(int _w, int _h, int c, int n, int k, int r, int s,
             int pad_w, int pad_h, int wstride, int hstride, Tensor<float> x, Tensor<float> w)
             :
        miopen_handle_(),
        x_desc_(n, c, _h, _w),
        w_desc_(k, c, r, s),
        conv_desc_(pad_h, pad_w, hstride, wstride)
    {
        int out_h, out_w, out_c, out_n;

        // Get output dimensions
        CHECK_MIOPEN_ERROR(miopenGetConvolutionForwardOutputDim(conv_desc_.desc(),
                                                                x_desc_.desc(),
                                                                w_desc_.desc(),
                                                                &out_n,
                                                                &out_c,
                                                                &out_h,
                                                                &out_w));

        h_desc_ = TensorDescriptor4d<float>(out_n, out_c, out_h, out_w);

        output_dims_ = {out_w, out_h, out_c, out_n};

        h = zeros<float>(output_dims_);

        // Set fwd workspace size
        CHECK_MIOPEN_ERROR(miopenConvolutionForwardGetWorkSpaceSize(
                    miopen_handle_.handle(),
                    w_desc_.desc(),
                                                          x_desc_.desc(),
                                                          conv_desc_.desc(),
                                                          h_desc_.desc(),
                                                          &fwd_workspace_size_));

        std::vector<int> u = std::vector<int>{static_cast<int>(fwd_workspace_size_ / sizeof(float)), 1};

        fwd_workspace_ = zeros<float>(u);

        const int requestAlgoCount = 1;
        int returnedAlgoCount;
        miopenConvAlgoPerf_t perfResults;

        CHECK_MIOPEN_ERROR(miopenFindConvolutionForwardAlgorithm(
          miopen_handle_.handle(),
          x_desc_.desc(),
          x.begin(),
          w_desc_.desc(),
          w.begin(),
          conv_desc_.desc(),
          h_desc_.desc(),
          h.begin(),
          requestAlgoCount,
          &returnedAlgoCount,
          &perfResults,
          fwd_workspace_.begin(),
          fwd_workspace_size_,
          false
        ));

        fwd_algo_ = perfResults.fwd_algo;

    CHECK_MIOPEN_ERROR(miopenConvolutionBackwardWeightsGetWorkSpaceSize(
                miopen_handle_.handle(),
                                                  h_desc_.desc(),
                                                  x_desc_.desc(),
                                                  conv_desc_.desc(),
                                                  w_desc_.desc(),
                                                  &bwd_params_workspace_size_));
    u = std::vector<int>{static_cast<int>(bwd_params_workspace_size_ / sizeof(float)), 1};
    bwd_params_workspace_ = zeros<float>(u);

    CHECK_MIOPEN_ERROR(miopenFindConvolutionBackwardWeightsAlgorithm(
      miopen_handle_.handle(),
      h_desc_.desc(),
      h.begin(),
      x_desc_.desc(),
      x.begin(),
      conv_desc_.desc(),
      w_desc_.desc(),
      w.begin(),
      requestAlgoCount,
      &returnedAlgoCount,
      &perfResults,
      bwd_params_workspace_.begin(),
      bwd_params_workspace_size_,
      false
    ));

    bwd_params_algo_ = perfResults.bwd_weights_algo;

    CHECK_MIOPEN_ERROR(miopenConvolutionBackwardDataGetWorkSpaceSize(
                miopen_handle_.handle(),
                                                  h_desc_.desc(),
                                                  w_desc_.desc(),
                                                  conv_desc_.desc(),
                                                  x_desc_.desc(),
                                                  &bwd_inputs_workspace_size_));

    u = std::vector<int>{static_cast<int>(bwd_inputs_workspace_size_ / sizeof(float)), 1};
    bwd_inputs_workspace_ = zeros<float>(u);

        CHECK_MIOPEN_ERROR(miopenFindConvolutionBackwardDataAlgorithm(
          miopen_handle_.handle(),
          h_desc_.desc(),
          h.begin(),
          w_desc_.desc(),
          w.begin(),
          conv_desc_.desc(),
          x_desc_.desc(),
          x.begin(),
          requestAlgoCount,
          &returnedAlgoCount,
          &perfResults,
          bwd_inputs_workspace_.begin(),
          bwd_inputs_workspace_size_,
          false
        ));

        bwd_inputs_algo_ = perfResults.bwd_data_algo;

    }

    Tensor<float> getOutputTensor(){ return h; }

    std::vector<int> get_output_dims() { return output_dims_; }

    std::string get_fwd_algo_string() {
        if (fwd_algo_ == miopenConvolutionFwdAlgoGEMM)
            return " ConvolutionFwdAlgoGEMM";
        else if (fwd_algo_ == miopenConvolutionFwdAlgoDirect)
            return " ConvolutionFwdAlgoDirect";
        else if (fwd_algo_ == miopenConvolutionFwdAlgoFFT)
            return " ConvolutionFwdAlgoFFT";
        else if (fwd_algo_ == miopenConvolutionFwdAlgoWinograd)
            return " ConvolutionFwdAlgoWinograd";
        else {
            std::stringstream ss;
            ss << "Illegal algorithm passed to get_fwd_algo_string. Algo: " << fwd_algo_ << std::endl;
            throw std::runtime_error(ss.str());
        }
    }

    void forward(Tensor<float> x, Tensor<float> filter, Tensor<float> h) {

        // Convolution forward.
        CHECK_MIOPEN_ERROR(miopenConvolutionForward(miopen_handle_.handle(),
                                                  &alpha_,
                                                  x_desc_.desc(),
                                                  x.begin(),
                                                  w_desc_.desc(),
                                                  filter.begin(),
                                                  conv_desc_.desc(),
                                                  fwd_algo_,
                                                  &beta_,
                                                  h_desc_.desc(),
                                                  h.begin(),
                                                  fwd_workspace_.begin(),
                                                  fwd_workspace_size_
                                                ));

    }

    void backward_params(Tensor<float> x, Tensor<float> delta, Tensor<float> dW) {

        CHECK_MIOPEN_ERROR(miopenConvolutionBackwardWeights(miopen_handle_.handle(),
                                                         &alpha_,
                                                         h_desc_.desc(),
                                                         delta.begin(),
                                                         x_desc_.desc(),
                                                         x.begin(),
                                                         conv_desc_.desc(),
                                                         bwd_params_algo_,
                                                         &beta_,
                                                         w_desc_.desc(),
                                                         dW.begin(),
                                                         bwd_params_workspace_.begin(),
                                                         bwd_params_workspace_size_
                                                       ));

    }

    void backward_inputs(Tensor<float> filter, Tensor<float> delta, Tensor<float> dX) {

        CHECK_MIOPEN_ERROR(miopenConvolutionBackwardData(miopen_handle_.handle(),
                                                      &alpha_,
                                                      h_desc_.desc(),
                                                      delta.begin(),
                                                      w_desc_.desc(),
                                                      filter.begin(),
                                                      conv_desc_.desc(),
                                                      bwd_inputs_algo_,
                                                      &beta_,
                                                      x_desc_.desc(),
                                                      dX.begin(),
                                                      bwd_inputs_workspace_.begin(),
                                                      bwd_inputs_workspace_size_
                                                    ));

    }
};

std::tuple<int, int, int, std::string> time_cnn(
         int k, int c, int r, int s,
         int n, int h, int w,
         int pad_h, int pad_w,
         int hstride, int wstride,
         int num_repeats
        ) {

    // Allocate memory for filter
    auto filter = rand<float>(std::vector<int>{r, s, c, k});

    // Allocate memory for input
    auto input = rand<float>(std::vector<int>{w, h, c, n});
    miopenCNN cnn(w, h, c, n, k, r, s, pad_w, pad_h, wstride, hstride, input, filter);

    // Allocate memory for output tensor
    auto output = cnn.getOutputTensor();

    std::string fwd_algo_s = cnn.get_fwd_algo_string();

    //Warm up
    cnn.forward(input, filter, output);

    hipDeviceSynchronize();
    auto start = std::chrono::steady_clock::now();

    for (int i = 0; i < num_repeats; ++i) {
        cnn.forward(input, filter, output);
    }

    hipDeviceSynchronize();
    auto end = std::chrono::steady_clock::now();
    int fwd_time = static_cast<int>(std::chrono::duration<double, std::micro>(end - start).count() / num_repeats);

    // Allocate memory for backward pass wrt weights
    auto delta = rand<float>(cnn.get_output_dims());
    auto dW = zeros<float>(std::vector<int>{r, s, c, k});

    // Warm up backward
    cnn.backward_params(input, delta, dW);

    hipDeviceSynchronize();
    start = std::chrono::steady_clock::now();

    for (int i = 0; i < num_repeats; ++i) {
        // Backward pass wrt weights
        cnn.backward_params(input, delta, dW);
    }

    hipDeviceSynchronize();
    end = std::chrono::steady_clock::now();

    int bwd_params_time = static_cast<int>(std::chrono::duration<double, std::micro>(end - start).count() / num_repeats);

    //Allocate memory for backward pass wrt inputs
    auto dX = zeros<float>(std::vector<int>{w, h, c, n});

    //Warm up backward inputs
    cnn.backward_inputs(filter, delta, dX);

    hipDeviceSynchronize();
    start = std::chrono::steady_clock::now();

    for (int i = 0; i < num_repeats; ++i) {
        // Backward pass wrt weights
        cnn.backward_inputs(filter, delta, dX);

    }

    hipDeviceSynchronize();
    end = std::chrono::steady_clock::now();

    int bwd_inputs_time = static_cast<int>(std::chrono::duration<double, std::micro>(end - start).count() / num_repeats);

    return std::tuple<int, int, int, std::string>(fwd_time, bwd_inputs_time, bwd_params_time, fwd_algo_s);

}

int main(int argc, char **argv) {

    int num_repeats = 300;

    hipFree(0);

    if (argc > 1)
        num_repeats = atoi(argv[1]);

    std::cout << std::setw(30) << "Times" << std::endl;
    std::cout << std::setfill('-') << std::setw(190) << "-" << std::endl;
    std::cout << std::setfill(' ');
    std::cout << "   w      h      c      n      k      f_w      f_h    pad_w  pad_h    stride_w  stride_h    fwd_time (usec)  bwd_inputs_time (usec)  bwd_params_time (usec)  total_time (usec)   fwd_algo " << std::endl;
    std::cout << std::setfill('-') << std::setw(190) << "-" << std::endl;
    std::cout << std::setfill(' ');

    int total_fwd_time=0, total_bwd_inputs_time=0, total_bwd_params_time=0;
    for (const auto &problem : training_set) {

        // Filter parameters
        int k, c, r, s; // r - filter_h (f_h), s - filter_w (f_w)

        // Input parameters
        int n, w, h;

        // Padding
        int pad_w, pad_h;

        // Stride
        int wstride, hstride;

        std::tie(w, h, c, n, k, s, r, pad_w, pad_h, wstride, hstride) = problem;

        int fwd_time, bwd_inputs_time, bwd_params_time;
        std::string fwd_algo_s;

        std::tie(fwd_time, bwd_inputs_time, bwd_params_time, fwd_algo_s) =
            time_cnn(k, c, r, s, n, h, w, pad_h, pad_w, hstride, wstride, num_repeats);

        std::cout << std::setw(5) << w;
        std::cout << std::setw(7) << h;
        std::cout << std::setw(7) << c;
        std::cout << std::setw(7) << n;
        std::cout << std::setw(7) << k;
        std::cout << std::setw(7) << s;
        std::cout << std::setw(7) << r;
        std::cout << std::setw(7) << pad_w;
        std::cout << std::setw(8) << pad_h;
        std::cout << std::setw(10) << wstride;
        std::cout << std::setw(10) << hstride;
        std::cout << std::setw(14) << std::setprecision(7) << fwd_time;
        std::cout << std::setw(24) << std::setprecision(7) << bwd_inputs_time;
        std::cout << std::setw(24) << std::setprecision(7) << bwd_params_time;
        std::cout << std::setw(19) << std::setprecision(8) << fwd_time + bwd_inputs_time + bwd_params_time;

        std::cout << std::setw(25) << fwd_algo_s;

        std::cout << std::endl;

        total_fwd_time += fwd_time;
        total_bwd_inputs_time += bwd_inputs_time;
        total_bwd_params_time += bwd_params_time;

    }

    std::cout << std::setw(82) << "Totals" ;
    std::cout << std::setw(14) << std::setprecision(7) << total_fwd_time;
    std::cout << std::setw(24) << std::setprecision(7) << total_bwd_inputs_time;
    std::cout << std::setw(24) << std::setprecision(7) << total_bwd_params_time;
    std::cout << std::setw(19) << std::setprecision(8) << total_fwd_time + total_bwd_inputs_time + total_bwd_params_time;
    std::cout << std::endl;

    return 0;

}

<|endoftext|>


<commit_before>// -*- c++ -*-
/* $Id$ */

/* Copyright 2002 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <glibmm/property.h>

#include <glibmm/object.h>
#include <glibmm/class.h>
#include <cstddef>

// Temporary hack till GLib gets fixed.
#undef  G_STRLOC
#define G_STRLOC __FILE__ ":" G_STRINGIFY(__LINE__)

namespace
{

// OK guys, please don't kill me for that.  Let me explain what happens here.
//
// The task:
// ---------
// a) Autogenerate a property ID number for each custom property.  This is an
//    unsigned integer, which doesn't have to be assigned continuously.  I.e.,
//    it can be everything but 0.
// b) If more than one object of the same class is instantiated, then of course
//    the already installed properties must be used.  That means, a property ID
//    must not be associated with a single Glib::Property<> instance.  Rather,
//    the ID has to be associated with the class somehow.
// c) With only a GObject pointer and a property ID (and perhaps GParamSpec*
//    if necessary), it must be possible to acquire a reference to the property
//    wrapper instance.
//
// The current solution:
// ---------------------
// a) Assign an ID to a Glib::PropertyBase by calculating its offset in bytes
//    relative to the beginning of the object's memory.  dynamic_cast<void*>
//    is used to retrieve a pointer to the very beginning of an instance.
// b) Recalculate a specific PropertyBase pointer by adding the property ID
//    (i.e. the byte offset) to the object start pointer.  The result is then
//    just casted to PropertyBase*.
//
// Drawbacks:
// ----------
// a) It's a low-level hack.  Should be portable, yes, but we can only do very
//    limited error checking.
// b) All Glib::Property<> instances are absolutely required to be direct data
//    members of the class that implements the property.  That seems a natural
//    thing to do, but it's questionable whether it should be a requirement.
//
// Advantages:
// -----------
// a) Although low-level, it's extremely easy to implement.  The nasty code is
//    concentrated in only two non-exposed utility functions, and it works
//    just fine.
// b) It's efficient, and the memory footprint is very small too.
// c) I actually tried other ways, too, but ran into dead-ends everywhere.
//    It's probably possible to implement this without calculating offsets,
//    but it'll be very complicated, and involve a lot of qdata pointers to
//    property tables andwhatnot.
//
// We can reimplement this later if necessary.

static unsigned int property_to_id(Glib::ObjectBase& object, Glib::PropertyBase& property)
{
  void *const base_ptr = dynamic_cast<void*>(&object);
  void *const prop_ptr = &property;

  const std::ptrdiff_t offset = static_cast<guint8*>(prop_ptr) - static_cast<guint8*>(base_ptr);

  g_return_val_if_fail(offset > 0 && offset < G_MAXINT, 0);

  return static_cast<unsigned int>(offset);
}

Glib::PropertyBase& property_from_id(Glib::ObjectBase& object, unsigned int property_id)
{
  void *const base_ptr = dynamic_cast<void*>(&object);
  void *const prop_ptr = static_cast<guint8*>(base_ptr) + property_id;

  return *static_cast<Glib::PropertyBase*>(prop_ptr);
}

} // anonymous namespace

namespace Glib
{

void custom_get_property_callback(GObject* object, unsigned int property_id,
                                  GValue* value, GParamSpec* param_spec)
{
  // If the id is zero there is no property to get.
  g_return_if_fail(property_id != 0);

  GType custom_type = G_OBJECT_TYPE(object);

  Class::iface_properties_type* iface_props = static_cast<Class::iface_properties_type*>(
    g_type_get_qdata(custom_type, Class::iface_properties_quark));

  Class::iface_properties_type::size_type iface_props_size = 0;

  if (iface_props)
    iface_props_size = iface_props->size();

  if (property_id <= iface_props_size)
  {
    g_value_copy((*iface_props)[property_id - 1], value);
  }
  else
  {
    if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
    {
      PropertyBase& property =
        property_from_id(*wrapper, property_id - iface_props_size);

      if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
        g_value_copy(property.value_.gobj(), value);
      else
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
    }
  }
}

void custom_set_property_callback(GObject* object, unsigned int property_id,
                                  const GValue* value, GParamSpec* param_spec)
{
  // If the id is zero there is no property to get.
  g_return_if_fail(property_id != 0);

  GType custom_type = G_OBJECT_TYPE(object);

  Class::iface_properties_type* iface_props = static_cast<Class::iface_properties_type*>(
    g_type_get_qdata(custom_type, Class::iface_properties_quark));

  Class::iface_properties_type::size_type iface_props_size = 0;

  if (iface_props)
    iface_props_size = iface_props->size();

  if (property_id <= iface_props_size)
  {
    g_value_copy(value, (*iface_props)[property_id - 1]);
    g_object_notify(object, g_param_spec_get_name(param_spec));
  }
  else
  {
    if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
    {
      PropertyBase& property =
        property_from_id(*wrapper, property_id - iface_props_size);

      if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
      {
        g_value_copy(value, property.value_.gobj());
        g_object_notify(object, g_param_spec_get_name(param_spec));
      }
      else
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
    }
  }
}

/**** Glib::PropertyBase ***************************************************/

PropertyBase::PropertyBase(Glib::Object& object, GType value_type)
:
  object_     (&object),
  value_      (),
  param_spec_ (0)
{
  value_.init(value_type);
}

PropertyBase::~PropertyBase()
{
  if(param_spec_)
    g_param_spec_unref(param_spec_);
}

bool PropertyBase::lookup_property(const Glib::ustring& name)
{
  g_assert(param_spec_ == 0);

  param_spec_ = g_object_class_find_property(G_OBJECT_GET_CLASS(object_->gobj()), name.c_str());

  if(param_spec_)
  {
    g_assert(G_PARAM_SPEC_VALUE_TYPE(param_spec_) == G_VALUE_TYPE(value_.gobj()));
    g_param_spec_ref(param_spec_);
  }

  return (param_spec_ != 0);
}

void PropertyBase::install_property(GParamSpec* param_spec)
{
  g_return_if_fail(param_spec != 0);

  // Ensure that there would not be id clashes with possible existing
  // properties overridden from implemented interfaces if dealing with a custom
  // type by offsetting the generated id with the number of already existing
  // properties.

  GType gtype = G_OBJECT_TYPE(object_->gobj());
  Class::iface_properties_type* iface_props = static_cast<Class::iface_properties_type*>(
    g_type_get_qdata(gtype, Class::iface_properties_quark));

  Class::iface_properties_type::size_type iface_props_size = 0;
  if (iface_props)
    iface_props_size = iface_props->size();

  const unsigned int property_id = property_to_id(*object_, *this) + iface_props_size;

  g_object_class_install_property(G_OBJECT_GET_CLASS(object_->gobj()), property_id, param_spec);

  param_spec_ = param_spec;
  g_param_spec_ref(param_spec_);
}

const char* PropertyBase::get_name_internal() const
{
  const char *const name = g_param_spec_get_name(param_spec_);
  g_return_val_if_fail(name != 0, "");
  return name;
}

Glib::ustring PropertyBase::get_name() const
{
  return Glib::ustring(get_name_internal());
}

void PropertyBase::notify()
{
  g_object_notify(object_->gobj(), g_param_spec_get_name(param_spec_));
}

} // namespace Glib
<commit_msg>PropertyBase: Use g_object_notify_by_pspec() instead of g_object_notify()<commit_after>// -*- c++ -*-
/* $Id$ */

/* Copyright 2002 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <glibmm/property.h>

#include <glibmm/object.h>
#include <glibmm/class.h>
#include <cstddef>

// Temporary hack till GLib gets fixed.
#undef  G_STRLOC
#define G_STRLOC __FILE__ ":" G_STRINGIFY(__LINE__)

namespace
{

// OK guys, please don't kill me for that.  Let me explain what happens here.
//
// The task:
// ---------
// a) Autogenerate a property ID number for each custom property.  This is an
//    unsigned integer, which doesn't have to be assigned continuously.  I.e.,
//    it can be everything but 0.
// b) If more than one object of the same class is instantiated, then of course
//    the already installed properties must be used.  That means, a property ID
//    must not be associated with a single Glib::Property<> instance.  Rather,
//    the ID has to be associated with the class somehow.
// c) With only a GObject pointer and a property ID (and perhaps GParamSpec*
//    if necessary), it must be possible to acquire a reference to the property
//    wrapper instance.
//
// The current solution:
// ---------------------
// a) Assign an ID to a Glib::PropertyBase by calculating its offset in bytes
//    relative to the beginning of the object's memory.  dynamic_cast<void*>
//    is used to retrieve a pointer to the very beginning of an instance.
// b) Recalculate a specific PropertyBase pointer by adding the property ID
//    (i.e. the byte offset) to the object start pointer.  The result is then
//    just casted to PropertyBase*.
//
// Drawbacks:
// ----------
// a) It's a low-level hack.  Should be portable, yes, but we can only do very
//    limited error checking.
// b) All Glib::Property<> instances are absolutely required to be direct data
//    members of the class that implements the property.  That seems a natural
//    thing to do, but it's questionable whether it should be a requirement.
//
// Advantages:
// -----------
// a) Although low-level, it's extremely easy to implement.  The nasty code is
//    concentrated in only two non-exposed utility functions, and it works
//    just fine.
// b) It's efficient, and the memory footprint is very small too.
// c) I actually tried other ways, too, but ran into dead-ends everywhere.
//    It's probably possible to implement this without calculating offsets,
//    but it'll be very complicated, and involve a lot of qdata pointers to
//    property tables andwhatnot.
//
// We can reimplement this later if necessary.

static unsigned int property_to_id(Glib::ObjectBase& object, Glib::PropertyBase& property)
{
  void *const base_ptr = dynamic_cast<void*>(&object);
  void *const prop_ptr = &property;

  const std::ptrdiff_t offset = static_cast<guint8*>(prop_ptr) - static_cast<guint8*>(base_ptr);

  g_return_val_if_fail(offset > 0 && offset < G_MAXINT, 0);

  return static_cast<unsigned int>(offset);
}

Glib::PropertyBase& property_from_id(Glib::ObjectBase& object, unsigned int property_id)
{
  void *const base_ptr = dynamic_cast<void*>(&object);
  void *const prop_ptr = static_cast<guint8*>(base_ptr) + property_id;

  return *static_cast<Glib::PropertyBase*>(prop_ptr);
}

} // anonymous namespace

namespace Glib
{

void custom_get_property_callback(GObject* object, unsigned int property_id,
                                  GValue* value, GParamSpec* param_spec)
{
  // If the id is zero there is no property to get.
  g_return_if_fail(property_id != 0);

  GType custom_type = G_OBJECT_TYPE(object);

  Class::iface_properties_type* iface_props = static_cast<Class::iface_properties_type*>(
    g_type_get_qdata(custom_type, Class::iface_properties_quark));

  Class::iface_properties_type::size_type iface_props_size = 0;

  if (iface_props)
    iface_props_size = iface_props->size();

  if (property_id <= iface_props_size)
  {
    g_value_copy((*iface_props)[property_id - 1], value);
  }
  else
  {
    if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
    {
      PropertyBase& property =
        property_from_id(*wrapper, property_id - iface_props_size);

      if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
        g_value_copy(property.value_.gobj(), value);
      else
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
    }
  }
}

void custom_set_property_callback(GObject* object, unsigned int property_id,
                                  const GValue* value, GParamSpec* param_spec)
{
  // If the id is zero there is no property to get.
  g_return_if_fail(property_id != 0);

  GType custom_type = G_OBJECT_TYPE(object);

  Class::iface_properties_type* iface_props = static_cast<Class::iface_properties_type*>(
    g_type_get_qdata(custom_type, Class::iface_properties_quark));

  Class::iface_properties_type::size_type iface_props_size = 0;

  if (iface_props)
    iface_props_size = iface_props->size();

  if (property_id <= iface_props_size)
  {
    g_value_copy(value, (*iface_props)[property_id - 1]);
    g_object_notify_by_pspec(object, param_spec);
  }
  else
  {
    if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
    {
      PropertyBase& property =
        property_from_id(*wrapper, property_id - iface_props_size);

      if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
      {
        g_value_copy(value, property.value_.gobj());
        g_object_notify_by_pspec(object, param_spec);
      }
      else
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
    }
  }
}

/**** Glib::PropertyBase ***************************************************/

PropertyBase::PropertyBase(Glib::Object& object, GType value_type)
:
  object_     (&object),
  value_      (),
  param_spec_ (0)
{
  value_.init(value_type);
}

PropertyBase::~PropertyBase()
{
  if(param_spec_)
    g_param_spec_unref(param_spec_);
}

bool PropertyBase::lookup_property(const Glib::ustring& name)
{
  g_assert(param_spec_ == 0);

  param_spec_ = g_object_class_find_property(G_OBJECT_GET_CLASS(object_->gobj()), name.c_str());

  if(param_spec_)
  {
    g_assert(G_PARAM_SPEC_VALUE_TYPE(param_spec_) == G_VALUE_TYPE(value_.gobj()));
    g_param_spec_ref(param_spec_);
  }

  return (param_spec_ != 0);
}

void PropertyBase::install_property(GParamSpec* param_spec)
{
  g_return_if_fail(param_spec != 0);

  // Ensure that there would not be id clashes with possible existing
  // properties overridden from implemented interfaces if dealing with a custom
  // type by offsetting the generated id with the number of already existing
  // properties.

  GType gtype = G_OBJECT_TYPE(object_->gobj());
  Class::iface_properties_type* iface_props = static_cast<Class::iface_properties_type*>(
    g_type_get_qdata(gtype, Class::iface_properties_quark));

  Class::iface_properties_type::size_type iface_props_size = 0;
  if (iface_props)
    iface_props_size = iface_props->size();

  const unsigned int property_id = property_to_id(*object_, *this) + iface_props_size;

  g_object_class_install_property(G_OBJECT_GET_CLASS(object_->gobj()), property_id, param_spec);

  param_spec_ = param_spec;
  g_param_spec_ref(param_spec_);
}

const char* PropertyBase::get_name_internal() const
{
  const char *const name = g_param_spec_get_name(param_spec_);
  g_return_val_if_fail(name != 0, "");
  return name;
}

Glib::ustring PropertyBase::get_name() const
{
  return Glib::ustring(get_name_internal());
}

void PropertyBase::notify()
{
  g_object_notify_by_pspec(object_->gobj(), param_spec_);
}

} // namespace Glib
<|endoftext|>