<commit_before>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2009-2010  Sandro Santilli <strk@kbt.io>
 * Copyright (C) 2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 ***********************************************************************
 *
 * Last port: operation/overlay/snap/LineStringSnapper.java r320 (JTS-1.12)
 *
 * NOTE: algorithm changed to improve output quality by reducing
 *       probability of self-intersections
 *
 **********************************************************************/

#include <geos/operation/overlay/snap/LineStringSnapper.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateList.h>
#include <geos/util/UniqueCoordinateArrayFilter.h>
#include <geos/geom/LineSegment.h>

#include <vector>
#include <memory>

#ifndef GEOS_DEBUG
#define GEOS_DEBUG 0
#endif

#if GEOS_DEBUG
#include <iostream>
#include <iomanip>
using std::cerr;
using std::endl;
#endif

//using namespace std;
using namespace geos::geom;

namespace geos {
namespace operation { // geos.operation
namespace overlay { // geos.operation.overlay
namespace snap { // geos.operation.overlay.snap

/*public*/
std::unique_ptr<Coordinate::Vect>
LineStringSnapper::snapTo(const geom::Coordinate::ConstVect& snapPts)
{
	geom::CoordinateList coordList(srcPts);

	snapVertices(coordList, snapPts);
	snapSegments(coordList, snapPts);

	return coordList.toCoordinateArray();
}

/*private*/
CoordinateList::iterator
LineStringSnapper::findVertexToSnap(
			const Coordinate& snapPt,
			CoordinateList::iterator from,
			CoordinateList::iterator too_far)
{
	double minDist = snapTolerance; // make sure the first closer then
	                                // snapTolerance is accepted
	CoordinateList::iterator match=too_far;

	for ( ; from != too_far; ++from)
	{
    Coordinate& c0 = *from;

#if GEOS_DEBUG
cerr << " Checking vertex " << c0 << endl;
#endif

		double dist = c0.distance(snapPt);
		if ( dist >= minDist) {
#if GEOS_DEBUG
cerr << "   snap point distance " << dist
     << " not smaller than tolerance "
     << snapTolerance << " or previous closest "
     << minDist << endl;
#endif
      continue;
    }

#if GEOS_DEBUG
    cerr << "   snap point distance " << dist << " within tolerance "
         << snapTolerance << " and closer than previous candidate "
         << minDist << endl;
#endif

    if ( dist == 0.0 ) return from; // can't find any closer

    match = from;
    minDist = dist;

	}

	return match;
}

/*private*/
void
LineStringSnapper::snapVertices(geom::CoordinateList& srcCoords,
			const geom::Coordinate::ConstVect& snapPts)
{
  // nothing to do if there are no source coords..
  if ( srcCoords.empty() ) return;

#if GEOS_DEBUG
cerr << "Snapping vertices of: " << srcCoords << endl;
#endif

	for ( Coordinate::ConstVect::const_iterator
			it=snapPts.begin(), end=snapPts.end();
			it != end;
			++it)
	{
		assert(*it);
		const Coordinate& snapPt = *(*it);

#if GEOS_DEBUG
cerr << "Checking for a vertex to snap to snapPt " << snapPt << endl;
#endif

		CoordinateList::iterator too_far = srcCoords.end();
    if ( isClosed ) --too_far;
		CoordinateList::iterator vertpos =
			findVertexToSnap(snapPt, srcCoords.begin(), too_far);
		if ( vertpos == too_far)
		{
#if GEOS_DEBUG
cerr << " No vertex to snap" << endl;
#endif
			continue;
		}

#if GEOS_DEBUG
cerr << " Vertex to be snapped found, snapping" << endl;
#endif
    *vertpos = snapPt;

    // keep final closing point in synch (rings only)
    if (vertpos == srcCoords.begin() && isClosed)
    {
      vertpos = srcCoords.end(); --vertpos;
#if GEOS_DEBUG
cerr << " Snapped vertex was first in a closed line, also snapping last" << endl;
#endif
      *vertpos = snapPt;
    }

#if GEOS_DEBUG
cerr << " After snapping of vertex " << snapPt << ", srcCoors are: " << srcCoords << endl;
#endif

	}

#if GEOS_DEBUG
cerr << " After vertices snapping, srcCoors are: " << srcCoords << endl;
#endif

}

/*private*/
Coordinate::ConstVect::const_iterator
LineStringSnapper::findSnapForVertex(const Coordinate& pt,
			const Coordinate::ConstVect& snapPts)
{
	Coordinate::ConstVect::const_iterator end = snapPts.end();
	Coordinate::ConstVect::const_iterator candidate = end;
	double minDist = snapTolerance;

	// TODO: use std::find_if
	for ( Coordinate::ConstVect::const_iterator
			it=snapPts.begin();
			it != end;
			++it)
	{
		assert(*it);
		const Coordinate& snapPt = *(*it);

		if ( snapPt.equals2D(pt) )
		{
#if GEOS_DEBUG
cerr << " points are equal, returning not-found " << endl;
#endif
			return end;
		}

		double dist = snapPt.distance(pt);
#if GEOS_DEBUG
cerr << " distance from snap point " << snapPt << ": " << dist << endl;
#endif

		if ( dist < minDist )
		{
      minDist = dist;
      candidate = it;
		}
	}

#if GEOS_DEBUG
  if ( candidate == end ) {
cerr << " no snap point within distance, returning not-found" << endl;
  }
#endif

	return candidate;
}

/*private*/
void
LineStringSnapper::snapSegments(geom::CoordinateList& srcCoords,
			const geom::Coordinate::ConstVect& snapPts)
{

  // nothing to do if there are no source coords..
  if ( srcCoords.empty() ) return;

#if GEOS_DEBUG
cerr << "Snapping segments of: " << srcCoords << endl;
#endif

	for ( Coordinate::ConstVect::const_iterator
			it=snapPts.begin(), end=snapPts.end();
			it != end;
			++it)
	{
		assert(*it);
		const Coordinate& snapPt = *(*it);

#if GEOS_DEBUG
cerr << "Checking for a segment to snap to snapPt " << snapPt << endl;
#endif

		CoordinateList::iterator too_far = srcCoords.end(); --too_far;
		CoordinateList::iterator segpos =
			findSegmentToSnap(snapPt, srcCoords.begin(), too_far);
		if ( segpos == too_far)
		{
#if GEOS_DEBUG
cerr << " No segment to snap" << endl;
#endif
			continue;
		}

    /* Check if the snap point falls outside of the segment */
    // If the snap point is outside, this means that an endpoint
    // was not snap where it should have been
    // so what we should do is re-snap the endpoint to this
    // snapPt and then snap the closest between this and
    // previous (for pf < 0.0) or next (for pf > 1.0) segment
    // to the old endpoint.
    //     --strk May 2013
    //
    // TODO: simplify this code, make more readable
    //
    CoordinateList::iterator to = segpos; ++to;
    LineSegment seg(*segpos, *to);
    double pf = seg.projectionFactor(snapPt);
    if ( pf >= 1.0 ) {
#if GEOS_DEBUG
      cerr << " Segment to be snapped is closer on his end point" << endl;
#endif
      Coordinate newSnapPt = seg.p1;
      *to = seg.p1 = snapPt;
      // now snap from-to (segpos) or to-next (segpos++) to newSnapPt
      if ( to == too_far ) {
        if ( isClosed ) {
#if GEOS_DEBUG
          cerr << " His end point is the last one, but is closed " << endl;
#endif
          *(srcCoords.begin()) = snapPt; // sync to start point
          to = srcCoords.begin();
        } else {
#if GEOS_DEBUG
          cerr << " His end point is the last one, inserting " << newSnapPt << " before it" << endl;
#endif
          srcCoords.insert(to, newSnapPt);
          continue;
        }
      }
      ++to;
      LineSegment nextSeg(seg.p1, *to);
      if ( nextSeg.distance(newSnapPt) < seg.distance(newSnapPt) ) {
#if GEOS_DEBUG
        cerr << " Next segment closer, inserting " << newSnapPt << " into " << nextSeg << endl;
#endif
        // insert into next segment
        srcCoords.insert(to, newSnapPt);
      } else {
#if GEOS_DEBUG
        cerr << " This segment closer, inserting " << newSnapPt << " into " << seg << endl;
#endif
        // insert must happen one-past first point (before next point)
        ++segpos;
        srcCoords.insert(segpos, newSnapPt);
      }
    }
    else if ( pf <= 0.0 ) {
#if GEOS_DEBUG
      cerr << " Segment to be snapped is closer on his start point" << endl;
#endif
      Coordinate newSnapPt = seg.p0;
      *segpos = seg.p0 = snapPt;
      // now snap prev-from (--segpos) or from-to (segpos) to newSnapPt
      if ( segpos == srcCoords.begin() ) {
        if ( isClosed ) {
#if GEOS_DEBUG
          cerr << " His start point is the first one, but is closed " << endl;
#endif
          segpos = srcCoords.end(); --segpos;
          *segpos = snapPt; // sync to end point
        } else {
#if GEOS_DEBUG
          cerr << " His start point is the first one, inserting " << newSnapPt << " before it" << endl;
#endif
          ++segpos;
          srcCoords.insert(segpos, newSnapPt);
          continue;
        }
      }

#if GEOS_DEBUG
cerr << " Before seg-snapping, srcCoors are: " << srcCoords << endl;
#endif

      --segpos;
      LineSegment prevSeg(*segpos, seg.p0);
      if ( prevSeg.distance(newSnapPt) < seg.distance(newSnapPt) ) {
#if GEOS_DEBUG
        cerr << " Prev segment closer, inserting " << newSnapPt << " into "
             << prevSeg << endl;
#endif
        // insert into prev segment
        ++segpos;
        srcCoords.insert(segpos, newSnapPt);
      } else {
#if GEOS_DEBUG
        cerr << " This segment closer, inserting " << newSnapPt << " into " << seg << endl;
#endif
        // insert must happen one-past first point (before next point)
        srcCoords.insert(to, newSnapPt);
      }
    }
    else {
      //assert(pf != 0.0);
#if GEOS_DEBUG
cerr << " Segment to be snapped found, projection factor is " << pf << ", inserting point" << endl;
#endif
      // insert must happen one-past first point (before next point)
      ++segpos;
      srcCoords.insert(segpos, snapPt);
    }
	}

#if GEOS_DEBUG
cerr << " After segment snapping, srcCoors are: " << srcCoords << endl;
#endif

}

/*private*/
/* NOTE: this is called findSegmentIndexToSnap in JTS */
CoordinateList::iterator
LineStringSnapper::findSegmentToSnap(
			const Coordinate& snapPt,
			CoordinateList::iterator from,
			CoordinateList::iterator too_far)
{
	LineSegment seg;
	double minDist = snapTolerance; // make sure the first closer then
	                                // snapTolerance is accepted
	CoordinateList::iterator match=too_far;

	// TODO: use std::find_if
	for ( ; from != too_far; ++from)
	{
		seg.p0 = *from;
		CoordinateList::iterator to = from;
		++to;
		seg.p1 = *to;

#if GEOS_DEBUG
cerr << " Checking segment " << seg << endl;
#endif

		/**
		 * Check if the snap pt is equal to one of
		 * the segment endpoints.
		 *
		 * If the snap pt is already in the src list,
		 * don't snap at all (unless allowSnappingToSourceVertices
		 * is set to true)
		 */
		if ( seg.p0.equals2D(snapPt) || seg.p1.equals2D(snapPt) )
		{

			if (allowSnappingToSourceVertices) {
#if GEOS_DEBUG
cerr << "   snap point matches a segment endpoint, checking next segment"
     << endl;
#endif
				continue;
			} else {
#if GEOS_DEBUG
cerr << "   snap point matches a segment endpoint, giving up seek" << endl;
#endif
				return too_far;
			}
		}

		double dist = seg.distance(snapPt);
		if ( dist >= minDist) {
#if GEOS_DEBUG
cerr << "   snap point distance " << dist
     << " not smaller than tolerance "
     << snapTolerance << " or previous closest "
     << minDist << endl;
#endif
      continue;
    }

#if GEOS_DEBUG
    cerr << "   snap point distance " << dist << " within tolerance "
         << snapTolerance << " and closer than previous candidate "
         << minDist << endl;
#endif

    if ( dist == 0.0 ) return from; // can't find any closer

    match = from;
    minDist = dist;

	}

	return match;
}

} // namespace geos.operation.snap
} // namespace geos.operation.overlay
} // namespace geos.operation
} // namespace geos

<commit_msg>Add interrupts to the snap operation<commit_after>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2009-2010  Sandro Santilli <strk@kbt.io>
 * Copyright (C) 2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 ***********************************************************************
 *
 * Last port: operation/overlay/snap/LineStringSnapper.java r320 (JTS-1.12)
 *
 * NOTE: algorithm changed to improve output quality by reducing
 *       probability of self-intersections
 *
 **********************************************************************/

#include <geos/operation/overlay/snap/LineStringSnapper.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateList.h>
#include <geos/util/UniqueCoordinateArrayFilter.h>
#include <geos/geom/LineSegment.h>
#include <geos/util/Interrupt.h>

#include <vector>
#include <memory>

#ifndef GEOS_DEBUG
#define GEOS_DEBUG 0
#endif

#if GEOS_DEBUG
#include <iostream>
#include <iomanip>
using std::cerr;
using std::endl;
#endif

//using namespace std;
using namespace geos::geom;

namespace geos {
namespace operation { // geos.operation
namespace overlay { // geos.operation.overlay
namespace snap { // geos.operation.overlay.snap

/*public*/
std::unique_ptr<Coordinate::Vect>
LineStringSnapper::snapTo(const geom::Coordinate::ConstVect& snapPts)
{
	geom::CoordinateList coordList(srcPts);

	snapVertices(coordList, snapPts);
	snapSegments(coordList, snapPts);

	return coordList.toCoordinateArray();
}

/*private*/
CoordinateList::iterator
LineStringSnapper::findVertexToSnap(
			const Coordinate& snapPt,
			CoordinateList::iterator from,
			CoordinateList::iterator too_far)
{
	double minDist = snapTolerance; // make sure the first closer then
	                                // snapTolerance is accepted
	CoordinateList::iterator match=too_far;

	for ( ; from != too_far; ++from)
	{
    Coordinate& c0 = *from;

#if GEOS_DEBUG
cerr << " Checking vertex " << c0 << endl;
#endif

		double dist = c0.distance(snapPt);
		if ( dist >= minDist) {
#if GEOS_DEBUG
cerr << "   snap point distance " << dist
     << " not smaller than tolerance "
     << snapTolerance << " or previous closest "
     << minDist << endl;
#endif
      continue;
    }

#if GEOS_DEBUG
    cerr << "   snap point distance " << dist << " within tolerance "
         << snapTolerance << " and closer than previous candidate "
         << minDist << endl;
#endif

    if ( dist == 0.0 ) return from; // can't find any closer

    match = from;
    minDist = dist;

	}

	return match;
}

/*private*/
void
LineStringSnapper::snapVertices(geom::CoordinateList& srcCoords,
			const geom::Coordinate::ConstVect& snapPts)
{
  // nothing to do if there are no source coords..
  if ( srcCoords.empty() ) return;

#if GEOS_DEBUG
cerr << "Snapping vertices of: " << srcCoords << endl;
#endif

	for ( Coordinate::ConstVect::const_iterator
			it=snapPts.begin(), end=snapPts.end();
			it != end;
			++it)
	{
	    GEOS_CHECK_FOR_INTERRUPTS();
		assert(*it);
		const Coordinate& snapPt = *(*it);

#if GEOS_DEBUG
cerr << "Checking for a vertex to snap to snapPt " << snapPt << endl;
#endif

		CoordinateList::iterator too_far = srcCoords.end();
    if ( isClosed ) --too_far;
		CoordinateList::iterator vertpos =
			findVertexToSnap(snapPt, srcCoords.begin(), too_far);
		if ( vertpos == too_far)
		{
#if GEOS_DEBUG
cerr << " No vertex to snap" << endl;
#endif
			continue;
		}

#if GEOS_DEBUG
cerr << " Vertex to be snapped found, snapping" << endl;
#endif
    *vertpos = snapPt;

    // keep final closing point in synch (rings only)
    if (vertpos == srcCoords.begin() && isClosed)
    {
      vertpos = srcCoords.end(); --vertpos;
#if GEOS_DEBUG
cerr << " Snapped vertex was first in a closed line, also snapping last" << endl;
#endif
      *vertpos = snapPt;
    }

#if GEOS_DEBUG
cerr << " After snapping of vertex " << snapPt << ", srcCoors are: " << srcCoords << endl;
#endif

	}

#if GEOS_DEBUG
cerr << " After vertices snapping, srcCoors are: " << srcCoords << endl;
#endif

}

/*private*/
Coordinate::ConstVect::const_iterator
LineStringSnapper::findSnapForVertex(const Coordinate& pt,
			const Coordinate::ConstVect& snapPts)
{
	Coordinate::ConstVect::const_iterator end = snapPts.end();
	Coordinate::ConstVect::const_iterator candidate = end;
	double minDist = snapTolerance;

	// TODO: use std::find_if
	for ( Coordinate::ConstVect::const_iterator
			it=snapPts.begin();
			it != end;
			++it)
	{
		assert(*it);
		const Coordinate& snapPt = *(*it);

		if ( snapPt.equals2D(pt) )
		{
#if GEOS_DEBUG
cerr << " points are equal, returning not-found " << endl;
#endif
			return end;
		}

		double dist = snapPt.distance(pt);
#if GEOS_DEBUG
cerr << " distance from snap point " << snapPt << ": " << dist << endl;
#endif

		if ( dist < minDist )
		{
      minDist = dist;
      candidate = it;
		}
	}

#if GEOS_DEBUG
  if ( candidate == end ) {
cerr << " no snap point within distance, returning not-found" << endl;
  }
#endif

	return candidate;
}

/*private*/
void
LineStringSnapper::snapSegments(geom::CoordinateList& srcCoords,
			const geom::Coordinate::ConstVect& snapPts)
{

  // nothing to do if there are no source coords..
  if ( srcCoords.empty() ) return;

	GEOS_CHECK_FOR_INTERRUPTS();

#if GEOS_DEBUG
cerr << "Snapping segments of: " << srcCoords << endl;
#endif

	for ( Coordinate::ConstVect::const_iterator
			it=snapPts.begin(), end=snapPts.end();
			it != end;
			++it)
	{
		assert(*it);
		const Coordinate& snapPt = *(*it);

#if GEOS_DEBUG
cerr << "Checking for a segment to snap to snapPt " << snapPt << endl;
#endif

		CoordinateList::iterator too_far = srcCoords.end(); --too_far;
		CoordinateList::iterator segpos =
			findSegmentToSnap(snapPt, srcCoords.begin(), too_far);
		if ( segpos == too_far)
		{
#if GEOS_DEBUG
cerr << " No segment to snap" << endl;
#endif
			continue;
		}

    /* Check if the snap point falls outside of the segment */
    // If the snap point is outside, this means that an endpoint
    // was not snap where it should have been
    // so what we should do is re-snap the endpoint to this
    // snapPt and then snap the closest between this and
    // previous (for pf < 0.0) or next (for pf > 1.0) segment
    // to the old endpoint.
    //     --strk May 2013
    //
    // TODO: simplify this code, make more readable
    //
    CoordinateList::iterator to = segpos; ++to;
    LineSegment seg(*segpos, *to);
    double pf = seg.projectionFactor(snapPt);
    if ( pf >= 1.0 ) {
#if GEOS_DEBUG
      cerr << " Segment to be snapped is closer on his end point" << endl;
#endif
      Coordinate newSnapPt = seg.p1;
      *to = seg.p1 = snapPt;
      // now snap from-to (segpos) or to-next (segpos++) to newSnapPt
      if ( to == too_far ) {
        if ( isClosed ) {
#if GEOS_DEBUG
          cerr << " His end point is the last one, but is closed " << endl;
#endif
          *(srcCoords.begin()) = snapPt; // sync to start point
          to = srcCoords.begin();
        } else {
#if GEOS_DEBUG
          cerr << " His end point is the last one, inserting " << newSnapPt << " before it" << endl;
#endif
          srcCoords.insert(to, newSnapPt);
          continue;
        }
      }
      ++to;
      LineSegment nextSeg(seg.p1, *to);
      if ( nextSeg.distance(newSnapPt) < seg.distance(newSnapPt) ) {
#if GEOS_DEBUG
        cerr << " Next segment closer, inserting " << newSnapPt << " into " << nextSeg << endl;
#endif
        // insert into next segment
        srcCoords.insert(to, newSnapPt);
      } else {
#if GEOS_DEBUG
        cerr << " This segment closer, inserting " << newSnapPt << " into " << seg << endl;
#endif
        // insert must happen one-past first point (before next point)
        ++segpos;
        srcCoords.insert(segpos, newSnapPt);
      }
    }
    else if ( pf <= 0.0 ) {
#if GEOS_DEBUG
      cerr << " Segment to be snapped is closer on his start point" << endl;
#endif
      Coordinate newSnapPt = seg.p0;
      *segpos = seg.p0 = snapPt;
      // now snap prev-from (--segpos) or from-to (segpos) to newSnapPt
      if ( segpos == srcCoords.begin() ) {
        if ( isClosed ) {
#if GEOS_DEBUG
          cerr << " His start point is the first one, but is closed " << endl;
#endif
          segpos = srcCoords.end(); --segpos;
          *segpos = snapPt; // sync to end point
        } else {
#if GEOS_DEBUG
          cerr << " His start point is the first one, inserting " << newSnapPt << " before it" << endl;
#endif
          ++segpos;
          srcCoords.insert(segpos, newSnapPt);
          continue;
        }
      }

#if GEOS_DEBUG
cerr << " Before seg-snapping, srcCoors are: " << srcCoords << endl;
#endif

      --segpos;
      LineSegment prevSeg(*segpos, seg.p0);
      if ( prevSeg.distance(newSnapPt) < seg.distance(newSnapPt) ) {
#if GEOS_DEBUG
        cerr << " Prev segment closer, inserting " << newSnapPt << " into "
             << prevSeg << endl;
#endif
        // insert into prev segment
        ++segpos;
        srcCoords.insert(segpos, newSnapPt);
      } else {
#if GEOS_DEBUG
        cerr << " This segment closer, inserting " << newSnapPt << " into " << seg << endl;
#endif
        // insert must happen one-past first point (before next point)
        srcCoords.insert(to, newSnapPt);
      }
    }
    else {
      //assert(pf != 0.0);
#if GEOS_DEBUG
cerr << " Segment to be snapped found, projection factor is " << pf << ", inserting point" << endl;
#endif
      // insert must happen one-past first point (before next point)
      ++segpos;
      srcCoords.insert(segpos, snapPt);
    }
	}

#if GEOS_DEBUG
cerr << " After segment snapping, srcCoors are: " << srcCoords << endl;
#endif

}

/*private*/
/* NOTE: this is called findSegmentIndexToSnap in JTS */
CoordinateList::iterator
LineStringSnapper::findSegmentToSnap(
			const Coordinate& snapPt,
			CoordinateList::iterator from,
			CoordinateList::iterator too_far)
{
	LineSegment seg;
	double minDist = snapTolerance; // make sure the first closer then
	                                // snapTolerance is accepted
	CoordinateList::iterator match=too_far;

	// TODO: use std::find_if
	for ( ; from != too_far; ++from)
	{
		seg.p0 = *from;
		CoordinateList::iterator to = from;
		++to;
		seg.p1 = *to;

#if GEOS_DEBUG
cerr << " Checking segment " << seg << endl;
#endif

		/**
		 * Check if the snap pt is equal to one of
		 * the segment endpoints.
		 *
		 * If the snap pt is already in the src list,
		 * don't snap at all (unless allowSnappingToSourceVertices
		 * is set to true)
		 */
		if ( seg.p0.equals2D(snapPt) || seg.p1.equals2D(snapPt) )
		{

			if (allowSnappingToSourceVertices) {
#if GEOS_DEBUG
cerr << "   snap point matches a segment endpoint, checking next segment"
     << endl;
#endif
				continue;
			} else {
#if GEOS_DEBUG
cerr << "   snap point matches a segment endpoint, giving up seek" << endl;
#endif
				return too_far;
			}
		}

		double dist = seg.distance(snapPt);
		if ( dist >= minDist) {
#if GEOS_DEBUG
cerr << "   snap point distance " << dist
     << " not smaller than tolerance "
     << snapTolerance << " or previous closest "
     << minDist << endl;
#endif
      continue;
    }

#if GEOS_DEBUG
    cerr << "   snap point distance " << dist << " within tolerance "
         << snapTolerance << " and closer than previous candidate "
         << minDist << endl;
#endif

    if ( dist == 0.0 ) return from; // can't find any closer

    match = from;
    minDist = dist;

	}

	return match;
}

} // namespace geos.operation.snap
} // namespace geos.operation.overlay
} // namespace geos.operation
} // namespace geos

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: db.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 12:19:11 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "db.hxx"

#include <rtl/alloc.h>
#include <cstring>

namespace berkeleydbproxy {

//----------------------------------------------------------------------------
    namespace db_internal
    {
        static void raise_error(int dberr, const char * where);

        static inline int check_error(int dberr, const char * where)
        {
            // if (dberr) raise_error(dberr,where);
            return dberr;
        }
    }

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

DbEnv::DbEnv(u_int32_t flags)
: m_pDBENV(0)
{
    db_internal::check_error( db_env_create(&m_pDBENV,flags), "DbEnv::DbEnv" );
}

DbEnv::~DbEnv()
{
    if (m_pDBENV)
    {
        // should not happen
        // TODO: add assert
        m_pDBENV->close(m_pDBENV,0);
    }
}

int DbEnv::open(const char *db_home, u_int32_t flags, int mode)
{
  return db_internal::check_error( m_pDBENV->open(m_pDBENV,db_home,flags,mode), "DbEnv::open" );
}

void DbEnv::close(u_int32_t flags)
{
    int error = m_pDBENV->close(m_pDBENV,flags);
    m_pDBENV = 0;

    db_internal::check_error(error, "DbEnv::close");
}

int DbEnv::set_alloc(db_malloc_fcn_type app_malloc,
                     db_realloc_fcn_type app_realloc,
                     db_free_fcn_type app_free)
{
    int err = m_pDBENV->set_alloc(m_pDBENV,app_malloc,app_realloc,app_free);
    return db_internal::check_error(err,"Db::set_alloc");
}
//----------------------------------------------------------------------------

int Db::set_alloc(   db_malloc_fcn_type app_malloc,
                     db_realloc_fcn_type app_realloc,
                     db_free_fcn_type app_free)
{
    int err = m_pDBP->set_alloc(m_pDBP,app_malloc,app_realloc,app_free);
    return db_internal::check_error(err,"Db::set_alloc");
}

Db::Db(DbEnv* pDbenv,u_int32_t flags)
: m_pDBP(0)
{
    db_internal::check_error( db_create(&m_pDBP,pDbenv ? pDbenv->m_pDBENV:0,flags),"Db::Db" );
}

Db::~Db()
{
    if (m_pDBP)
    {
        // should not happen
        // TODO: add assert
    }

}

int Db::close(u_int32_t flags)
{
    int error = m_pDBP->close(m_pDBP,flags);
    m_pDBP = 0;
    return db_internal::check_error(error,"Db::close");
}

int Db::open(DB_TXN *txnid,
             const char *file,
             const char *database,
             DBTYPE type,
             u_int32_t flags,
             int mode)
{
    int err = m_pDBP->open(m_pDBP,txnid,file,database,type,flags,mode);
    return db_internal::check_error( err,"Db::open" );
}

int Db::get(DB_TXN *txnid, Dbt *key, Dbt *data, u_int32_t flags)
{
    int err = m_pDBP->get(m_pDBP,txnid,key,data,flags);

    // these are non-exceptional outcomes
    if (err != DB_NOTFOUND && err != DB_KEYEMPTY)
        db_internal::check_error( err,"Db::get" );

    return err;
}

int Db::put(DB_TXN* txnid, Dbt *key, Dbt *data, u_int32_t flags)
{
    int err = m_pDBP->put(m_pDBP,txnid,key,data,flags);

    if (err != DB_KEYEXIST) // this is a non-exceptional outcome
        db_internal::check_error( err,"Db::put" );
    return err;
}

int Db::cursor(DB_TXN *txnid, Dbc **cursorp, u_int32_t flags)
{
    DBC * dbc = 0;
    int error = m_pDBP->cursor(m_pDBP,txnid,&dbc,flags);

    if (!db_internal::check_error(error,"Db::cursor"))
        *cursorp = new Dbc(dbc);

    return error;
}

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

Dbc::Dbc(DBC * dbc)
: m_pDBC(dbc)
{
}

Dbc::~Dbc()
{
}

int Dbc::close()
{
    int err = m_pDBC->c_close(m_pDBC);
    delete this;
    return db_internal::check_error( err,"Dbcursor::close" );
}

int Dbc::get(Dbt *key, Dbt *data, u_int32_t flags)
{
    int err = m_pDBC->c_get(m_pDBC,key,data,flags);

    // these are non-exceptional outcomes
    if (err != DB_NOTFOUND && err != DB_KEYEMPTY)
        db_internal::check_error( err, "Dbcursor::get" );

    return err;
}

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

Dbt::Dbt()
{
    using namespace std;
    DBT * thispod = this;
    memset(thispod, 0, sizeof *thispod);
}

Dbt::Dbt(void *data_arg, u_int32_t size_arg)
{
    using namespace std;
    DBT * thispod = this;
    memset(thispod, 0, sizeof *thispod);
    this->set_data(data_arg);
    this->set_size(size_arg);
}

Dbt::Dbt(const Dbt & other)
{
    using namespace std;
    const DBT *otherpod = &other;
    DBT *thispod = this;
    memcpy(thispod, otherpod, sizeof *thispod);
}

Dbt& Dbt::operator = (const Dbt & other)
{
    if (this != &other)
    {
        using namespace std;
        const DBT *otherpod = &other;
        DBT *thispod = this;
        memcpy(thispod, otherpod, sizeof *thispod);
    }
    return *this;
}

Dbt::~Dbt()
{
}

void * Dbt::get_data() const
{
    return this->data;
}

void Dbt::set_data(void *value)
{
    this->data = value;
}

u_int32_t Dbt::get_size() const
{
    return this->size;
}

void Dbt::set_size(u_int32_t value)
{
    this->size = value;
}

void Dbt::set_flags(u_int32_t value)
{
    this->flags = value;
}

//----------------------------------------------------------------------------
void db_internal::raise_error(int dberr, const char * where)
{
    if (!where) where = "<unknown>";

    const char * dberrmsg = db_strerror(dberr);
    if (!dberrmsg || !*dberrmsg) dberrmsg = "<unknown DB error>";

    rtl::OString msg = where;
    msg += ": ";
    msg += dberrmsg;

    throw DbException(msg);
}

//----------------------------------------------------------------------------
} // namespace ecomp

<commit_msg>INTEGRATION: CWS warnings01 (1.3.18); FILE MERGED 2005/12/15 16:36:42 ab 1.3.18.1: #i53898# Removed warnings for unxlngi6/unxlngi6.pro<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: db.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-20 00:39:06 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "db.hxx"

#include <rtl/alloc.h>
#include <cstring>

namespace berkeleydbproxy {

//----------------------------------------------------------------------------
    namespace db_internal
    {
        // static void raise_error(int dberr, const char * where);

        static inline int check_error(int dberr, const char * where)
        {
            (void)where;

            // if (dberr) raise_error(dberr,where);
            return dberr;
        }
    }

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

DbEnv::DbEnv(u_int32_t flags)
: m_pDBENV(0)
{
    db_internal::check_error( db_env_create(&m_pDBENV,flags), "DbEnv::DbEnv" );
}

DbEnv::~DbEnv()
{
    if (m_pDBENV)
    {
        // should not happen
        // TODO: add assert
        m_pDBENV->close(m_pDBENV,0);
    }
}

int DbEnv::open(const char *db_home, u_int32_t flags, int mode)
{
  return db_internal::check_error( m_pDBENV->open(m_pDBENV,db_home,flags,mode), "DbEnv::open" );
}

void DbEnv::close(u_int32_t flags)
{
    int error = m_pDBENV->close(m_pDBENV,flags);
    m_pDBENV = 0;

    db_internal::check_error(error, "DbEnv::close");
}

int DbEnv::set_alloc(db_malloc_fcn_type app_malloc,
                     db_realloc_fcn_type app_realloc,
                     db_free_fcn_type app_free)
{
    int err = m_pDBENV->set_alloc(m_pDBENV,app_malloc,app_realloc,app_free);
    return db_internal::check_error(err,"Db::set_alloc");
}
//----------------------------------------------------------------------------

int Db::set_alloc(   db_malloc_fcn_type app_malloc,
                     db_realloc_fcn_type app_realloc,
                     db_free_fcn_type app_free)
{
    int err = m_pDBP->set_alloc(m_pDBP,app_malloc,app_realloc,app_free);
    return db_internal::check_error(err,"Db::set_alloc");
}

Db::Db(DbEnv* pDbenv,u_int32_t flags)
: m_pDBP(0)
{
    db_internal::check_error( db_create(&m_pDBP,pDbenv ? pDbenv->m_pDBENV:0,flags),"Db::Db" );
}

Db::~Db()
{
    if (m_pDBP)
    {
        // should not happen
        // TODO: add assert
    }

}

int Db::close(u_int32_t flags)
{
    int error = m_pDBP->close(m_pDBP,flags);
    m_pDBP = 0;
    return db_internal::check_error(error,"Db::close");
}

int Db::open(DB_TXN *txnid,
             const char *file,
             const char *database,
             DBTYPE type,
             u_int32_t flags,
             int mode)
{
    int err = m_pDBP->open(m_pDBP,txnid,file,database,type,flags,mode);
    return db_internal::check_error( err,"Db::open" );
}

int Db::get(DB_TXN *txnid, Dbt *key, Dbt *data, u_int32_t flags)
{
    int err = m_pDBP->get(m_pDBP,txnid,key,data,flags);

    // these are non-exceptional outcomes
    if (err != DB_NOTFOUND && err != DB_KEYEMPTY)
        db_internal::check_error( err,"Db::get" );

    return err;
}

int Db::put(DB_TXN* txnid, Dbt *key, Dbt *data, u_int32_t flags)
{
    int err = m_pDBP->put(m_pDBP,txnid,key,data,flags);

    if (err != DB_KEYEXIST) // this is a non-exceptional outcome
        db_internal::check_error( err,"Db::put" );
    return err;
}

int Db::cursor(DB_TXN *txnid, Dbc **cursorp, u_int32_t flags)
{
    DBC * dbc = 0;
    int error = m_pDBP->cursor(m_pDBP,txnid,&dbc,flags);

    if (!db_internal::check_error(error,"Db::cursor"))
        *cursorp = new Dbc(dbc);

    return error;
}

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

Dbc::Dbc(DBC * dbc)
: m_pDBC(dbc)
{
}

Dbc::~Dbc()
{
}

int Dbc::close()
{
    int err = m_pDBC->c_close(m_pDBC);
    delete this;
    return db_internal::check_error( err,"Dbcursor::close" );
}

int Dbc::get(Dbt *key, Dbt *data, u_int32_t flags)
{
    int err = m_pDBC->c_get(m_pDBC,key,data,flags);

    // these are non-exceptional outcomes
    if (err != DB_NOTFOUND && err != DB_KEYEMPTY)
        db_internal::check_error( err, "Dbcursor::get" );

    return err;
}

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

Dbt::Dbt()
{
    using namespace std;
    DBT * thispod = this;
    memset(thispod, 0, sizeof *thispod);
}

Dbt::Dbt(void *data_arg, u_int32_t size_arg)
{
    using namespace std;
    DBT * thispod = this;
    memset(thispod, 0, sizeof *thispod);
    this->set_data(data_arg);
    this->set_size(size_arg);
}

Dbt::Dbt(const Dbt & other)
{
    using namespace std;
    const DBT *otherpod = &other;
    DBT *thispod = this;
    memcpy(thispod, otherpod, sizeof *thispod);
}

Dbt& Dbt::operator = (const Dbt & other)
{
    if (this != &other)
    {
        using namespace std;
        const DBT *otherpod = &other;
        DBT *thispod = this;
        memcpy(thispod, otherpod, sizeof *thispod);
    }
    return *this;
}

Dbt::~Dbt()
{
}

void * Dbt::get_data() const
{
    return this->data;
}

void Dbt::set_data(void *value)
{
    this->data = value;
}

u_int32_t Dbt::get_size() const
{
    return this->size;
}

void Dbt::set_size(u_int32_t value)
{
    this->size = value;
}

void Dbt::set_flags(u_int32_t value)
{
    this->flags = value;
}

//----------------------------------------------------------------------------
/*
void db_internal::raise_error(int dberr, const char * where)
{
    if (!where) where = "<unknown>";

    const char * dberrmsg = db_strerror(dberr);
    if (!dberrmsg || !*dberrmsg) dberrmsg = "<unknown DB error>";

    rtl::OString msg = where;
    msg += ": ";
    msg += dberrmsg;

    throw DbException(msg);
}
*/

//----------------------------------------------------------------------------
} // namespace ecomp

<|endoftext|>


<commit_before>/* Copyright 2012-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman/WatchmanConfig.h"
#include <folly/Synchronized.h>
#include <optional>
#include "watchman/Errors.h"
#include "watchman/watchman.h"

using namespace watchman;

namespace {

struct ConfigState {
  json_ref global_cfg;
  w_string global_config_file_path;
  json_ref arg_cfg;
};
folly::Synchronized<ConfigState> configState;

std::optional<std::pair<json_ref, w_string>> loadSystemConfig() {
  const char* cfg_file = getenv("WATCHMAN_CONFIG_FILE");
#ifdef WATCHMAN_CONFIG_FILE
  if (!cfg_file) {
    cfg_file = WATCHMAN_CONFIG_FILE;
  }
#endif
  if (!cfg_file || cfg_file[0] == '\0') {
    return std::nullopt;
  }

  std::string cfg_file_default = std::string{cfg_file} + ".default";
  const char* current_cfg_file;

  json_ref config;
  try {
    // Try to load system watchman configuration
    try {
      current_cfg_file = cfg_file;
      config = json_load_file(current_cfg_file, 0);
    } catch (const std::system_error& exc) {
      if (exc.code() == watchman::error_code::no_such_file_or_directory) {
        // Fallback to trying to load default watchman configuration if there
        // is no system configuration
        try {
          current_cfg_file = cfg_file_default.c_str();
          config = json_load_file(current_cfg_file, 0);
        } catch (const std::system_error& default_exc) {
          // If there is no default configuration either, just return
          if (default_exc.code() ==
              watchman::error_code::no_such_file_or_directory) {
            return std::nullopt;
          } else {
            throw;
          }
        }
      } else {
        throw;
      }
    }
  } catch (const std::system_error& exc) {
    logf(
        ERR,
        "Failed to load config file {}: {}\n",
        current_cfg_file,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  } catch (const std::exception& exc) {
    logf(
        ERR,
        "Failed to parse config file {}: {}\n",
        current_cfg_file,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  }

  if (!config.isObject()) {
    logf(ERR, "config {} must be a JSON object\n", current_cfg_file);
    return std::nullopt;
  }

  return {{config, current_cfg_file}};
}

std::optional<json_ref> loadUserConfig() {
  const char* home = getenv("HOME");
  if (!home) {
    return std::nullopt;
  }
  auto path = std::string{home} + "/.watchman.json";
  try {
    json_ref config = json_load_file(path.c_str(), 0);
    if (!config.isObject()) {
      logf(ERR, "config {} must be a JSON object\n", path);
      return std::nullopt;
    }
    return config;
  } catch (const std::system_error& exc) {
    if (exc.code() == watchman::error_code::no_such_file_or_directory) {
      return std::nullopt;
    }
    logf(
        ERR,
        "Failed to load config file {}: {}\n",
        path,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  } catch (const std::exception& exc) {
    logf(
        ERR,
        "Failed to parse config file {}: {}\n",
        path,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  }
}

} // namespace

/* Called during shutdown to free things so that we run cleanly
 * under valgrind */
void cfg_shutdown() {
  auto state = configState.wlock();
  state->global_cfg.reset();
  state->arg_cfg.reset();
}

w_string cfg_get_global_config_file_path() {
  return configState.rlock()->global_config_file_path;
}

void cfg_load_global_config_file() {
  auto systemConfig = loadSystemConfig();
  auto userConfig = loadUserConfig();

  auto lockedState = configState.wlock();
  if (systemConfig) {
    lockedState->global_cfg = systemConfig->first;
    lockedState->global_config_file_path = systemConfig->second;
  }

  if (userConfig) {
    if (!lockedState->global_cfg) {
      lockedState->global_cfg = json_object();
    }
    for (auto& [key, value] : userConfig->object()) {
      lockedState->global_cfg.object()[key] = value;
    }
  }
}

void cfg_set_arg(const char* name, const json_ref& val) {
  auto state = configState.wlock();
  if (!state->arg_cfg) {
    state->arg_cfg = json_object();
  }

  state->arg_cfg.set(name, json_ref(val));
}

void cfg_set_global(const char* name, const json_ref& val) {
  auto state = configState.wlock();
  if (!state->global_cfg) {
    state->global_cfg = json_object();
  }

  state->global_cfg.set(name, json_ref(val));
}

static json_ref cfg_get_raw(const char* name, const json_ref* optr) {
  json_ref val;

  if (*optr) {
    val = optr->get_default(name);
  }
  return val;
}

json_ref cfg_get_json(const char* name) {
  json_ref val;
  auto state = configState.rlock();

  // Highest precedence: command line arguments
  val = cfg_get_raw(name, &state->arg_cfg);
  // then: global config options
  if (!val) {
    val = cfg_get_raw(name, &state->global_cfg);
  }
  return val;
}

const char* cfg_get_string(const char* name, const char* defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isString()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a string"));
    }
    return json_string_value(val);
  }

  return defval;
}

// Return true if the json ref is an array of string values
static bool is_array_of_strings(const json_ref& ref) {
  uint32_t i;

  if (!ref.isArray()) {
    return false;
  }

  for (i = 0; i < json_array_size(ref); i++) {
    if (!json_array_get(ref, i).isString()) {
      return false;
    }
  }
  return true;
}

// Given an array of string values, if that array does not contain
// a ".watchmanconfig" entry, prepend it
static void prepend_watchmanconfig_to_array(json_ref& ref) {
  const char* val;

  if (json_array_size(ref) == 0) {
    // json_array_insert_new at index can fail when the array is empty,
    // so just append in this case.
    json_array_append_new(
        ref, typed_string_to_json(".watchmanconfig", W_STRING_UNICODE));
    return;
  }

  val = json_string_value(json_array_get(ref, 0));
  if (!strcmp(val, ".watchmanconfig")) {
    return;
  }
  json_array_insert_new(
      ref, 0, typed_string_to_json(".watchmanconfig", W_STRING_UNICODE));
}

// Compute the effective value of the root_files configuration and
// return a json reference.  The caller must decref the ref when done
// (we may synthesize this value).   Sets enforcing to indicate whether
// we will only allow watches on the root_files.
// The array returned by this function (if not NULL) is guaranteed to
// list .watchmanconfig as its zeroth element.
json_ref cfg_compute_root_files(bool* enforcing) {
  *enforcing = false;

  json_ref ref = cfg_get_json("enforce_root_files");
  if (ref) {
    if (!ref.isBool()) {
      logf(FATAL, "Expected config value enforce_root_files to be boolean\n");
    }
    *enforcing = ref.asBool();
  }

  ref = cfg_get_json("root_files");
  if (ref) {
    if (!is_array_of_strings(ref)) {
      logf(FATAL, "global config root_files must be an array of strings\n");
      *enforcing = false;
      return nullptr;
    }
    prepend_watchmanconfig_to_array(ref);

    return ref;
  }

  // Try legacy root_restrict_files configuration
  ref = cfg_get_json("root_restrict_files");
  if (ref) {
    if (!is_array_of_strings(ref)) {
      logf(
          FATAL,
          "deprecated global config root_restrict_files "
          "must be an array of strings\n");
      *enforcing = false;
      return nullptr;
    }
    prepend_watchmanconfig_to_array(ref);
    *enforcing = true;
    return ref;
  }

  // Synthesize our conservative default value.
  // .watchmanconfig MUST be first
  return json_array(
      {typed_string_to_json(".watchmanconfig"),
       typed_string_to_json(".hg"),
       typed_string_to_json(".git"),
       typed_string_to_json(".svn")});
}

// Produces a string like:  "`foo`, `bar`, and `baz`"
std::string cfg_pretty_print_root_files(const json_ref& root_files) {
  std::string result;
  for (unsigned int i = 0; i < root_files.array().size(); ++i) {
    const auto& r = root_files.array()[i];
    if (i > 1 && i == root_files.array().size() - 1) {
      // We are last in a list of multiple items
      result.append(", and ");
    } else if (i > 0) {
      result.append(", ");
    }
    result.append("`");
    result.append(json_string_value(r));
    result.append("`");
  }
  return result;
}

json_int_t cfg_get_int(const char* name, json_int_t defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isInt()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be an integer"));
    }
    return val.asInt();
  }

  return defval;
}

bool cfg_get_bool(const char* name, bool defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isBool()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a boolean"));
    }
    return val.asBool();
  }

  return defval;
}

double cfg_get_double(const char* name, double defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isNumber()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a number"));
    }
    return json_real_value(val);
  }

  return defval;
}

#ifndef _WIN32
#define MAKE_GET_PERM(PROP, SUFFIX)                                 \
  static mode_t get_##PROP##_perm(                                  \
      const char* name,                                             \
      const json_ref& val,                                          \
      bool write_bits,                                              \
      bool execute_bits) {                                          \
    mode_t ret = 0;                                                 \
    auto perm = val.get_default(#PROP);                             \
    if (perm) {                                                     \
      if (!perm.isBool()) {                                         \
        logf(                                                       \
            FATAL,                                                  \
            "Expected config value {}." #PROP " to be a boolean\n", \
            name);                                                  \
      }                                                             \
      if (perm.asBool()) {                                          \
        ret |= S_IR##SUFFIX;                                        \
        if (write_bits) {                                           \
          ret |= S_IW##SUFFIX;                                      \
        }                                                           \
        if (execute_bits) {                                         \
          ret |= S_IX##SUFFIX;                                      \
        }                                                           \
      }                                                             \
    }                                                               \
    return ret;                                                     \
  }

MAKE_GET_PERM(group, GRP)
MAKE_GET_PERM(others, OTH)

/**
 * This function expects the config to be an object containing the keys 'group'
 * and 'others', each a bool.
 */
mode_t cfg_get_perms(const char* name, bool write_bits, bool execute_bits) {
  auto val = cfg_get_json(name);
  mode_t ret = S_IRUSR | S_IWUSR;
  if (execute_bits) {
    ret |= S_IXUSR;
  }

  if (val) {
    if (!val.isObject()) {
      logf(FATAL, "Expected config value {} to be an object\n", name);
    }

    ret |= get_group_perm(name, val, write_bits, execute_bits);
    ret |= get_others_perm(name, val, write_bits, execute_bits);
  }

  return ret;
}
#endif

const char* cfg_get_trouble_url() {
  return cfg_get_string(
      "troubleshooting_url",
      "https://facebook.github.io/watchman/docs/troubleshooting.html");
}

namespace watchman {

Configuration::Configuration(const json_ref& local) : local_(local) {}

json_ref Configuration::get(const char* name) const {
  // Highest precedence: options set locally
  json_ref val;
  if (local_) {
    val = local_.get_default(name);
    if (val) {
      return val;
    }
  }
  auto state = configState.rlock();

  // then: command line arguments
  if (!val) {
    val = cfg_get_raw(name, &state->arg_cfg);
  }
  // then: global config options
  if (!val) {
    val = cfg_get_raw(name, &state->global_cfg);
  }
  return val;
}

const char* Configuration::getString(const char* name, const char* defval)
    const {
  auto val = get(name);

  if (val) {
    if (!val.isString()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a string"));
    }
    return json_string_value(val);
  }

  return defval;
}

json_int_t Configuration::getInt(const char* name, json_int_t defval) const {
  auto val = get(name);

  if (val) {
    if (!val.isInt()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be an integer"));
    }
    return val.asInt();
  }

  return defval;
}

bool Configuration::getBool(const char* name, bool defval) const {
  auto val = get(name);

  if (val) {
    if (!val.isBool()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a boolean"));
    }
    return val.asBool();
  }

  return defval;
}

double Configuration::getDouble(const char* name, double defval) const {
  auto val = get(name);

  if (val) {
    if (!val.isNumber()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a number"));
    }
    return json_real_value(val);
  }

  return defval;
}

} // namespace watchman

/* vim:ts=2:sw=2:et:
 */
<commit_msg>split WatchmanConfig into its own target<commit_after>/* Copyright 2012-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman/WatchmanConfig.h"
#include <folly/ExceptionString.h>
#include <folly/Synchronized.h>
#include <optional>
#include "watchman/Errors.h"
#include "watchman/Logging.h"

using namespace watchman;

namespace {

struct ConfigState {
  json_ref global_cfg;
  w_string global_config_file_path;
  json_ref arg_cfg;
};
folly::Synchronized<ConfigState> configState;

std::optional<std::pair<json_ref, w_string>> loadSystemConfig() {
  const char* cfg_file = getenv("WATCHMAN_CONFIG_FILE");
#ifdef WATCHMAN_CONFIG_FILE
  if (!cfg_file) {
    cfg_file = WATCHMAN_CONFIG_FILE;
  }
#endif
  if (!cfg_file || cfg_file[0] == '\0') {
    return std::nullopt;
  }

  std::string cfg_file_default = std::string{cfg_file} + ".default";
  const char* current_cfg_file;

  json_ref config;
  try {
    // Try to load system watchman configuration
    try {
      current_cfg_file = cfg_file;
      config = json_load_file(current_cfg_file, 0);
    } catch (const std::system_error& exc) {
      if (exc.code() == watchman::error_code::no_such_file_or_directory) {
        // Fallback to trying to load default watchman configuration if there
        // is no system configuration
        try {
          current_cfg_file = cfg_file_default.c_str();
          config = json_load_file(current_cfg_file, 0);
        } catch (const std::system_error& default_exc) {
          // If there is no default configuration either, just return
          if (default_exc.code() ==
              watchman::error_code::no_such_file_or_directory) {
            return std::nullopt;
          } else {
            throw;
          }
        }
      } else {
        throw;
      }
    }
  } catch (const std::system_error& exc) {
    logf(
        ERR,
        "Failed to load config file {}: {}\n",
        current_cfg_file,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  } catch (const std::exception& exc) {
    logf(
        ERR,
        "Failed to parse config file {}: {}\n",
        current_cfg_file,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  }

  if (!config.isObject()) {
    logf(ERR, "config {} must be a JSON object\n", current_cfg_file);
    return std::nullopt;
  }

  return {{config, current_cfg_file}};
}

std::optional<json_ref> loadUserConfig() {
  const char* home = getenv("HOME");
  if (!home) {
    return std::nullopt;
  }
  auto path = std::string{home} + "/.watchman.json";
  try {
    json_ref config = json_load_file(path.c_str(), 0);
    if (!config.isObject()) {
      logf(ERR, "config {} must be a JSON object\n", path);
      return std::nullopt;
    }
    return config;
  } catch (const std::system_error& exc) {
    if (exc.code() == watchman::error_code::no_such_file_or_directory) {
      return std::nullopt;
    }
    logf(
        ERR,
        "Failed to load config file {}: {}\n",
        path,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  } catch (const std::exception& exc) {
    logf(
        ERR,
        "Failed to parse config file {}: {}\n",
        path,
        folly::exceptionStr(exc).toStdString());
    return std::nullopt;
  }
}

} // namespace

/* Called during shutdown to free things so that we run cleanly
 * under valgrind */
void cfg_shutdown() {
  auto state = configState.wlock();
  state->global_cfg.reset();
  state->arg_cfg.reset();
}

w_string cfg_get_global_config_file_path() {
  return configState.rlock()->global_config_file_path;
}

void cfg_load_global_config_file() {
  auto systemConfig = loadSystemConfig();
  auto userConfig = loadUserConfig();

  auto lockedState = configState.wlock();
  if (systemConfig) {
    lockedState->global_cfg = systemConfig->first;
    lockedState->global_config_file_path = systemConfig->second;
  }

  if (userConfig) {
    if (!lockedState->global_cfg) {
      lockedState->global_cfg = json_object();
    }
    for (auto& [key, value] : userConfig->object()) {
      lockedState->global_cfg.object()[key] = value;
    }
  }
}

void cfg_set_arg(const char* name, const json_ref& val) {
  auto state = configState.wlock();
  if (!state->arg_cfg) {
    state->arg_cfg = json_object();
  }

  state->arg_cfg.set(name, json_ref(val));
}

void cfg_set_global(const char* name, const json_ref& val) {
  auto state = configState.wlock();
  if (!state->global_cfg) {
    state->global_cfg = json_object();
  }

  state->global_cfg.set(name, json_ref(val));
}

static json_ref cfg_get_raw(const char* name, const json_ref* optr) {
  json_ref val;

  if (*optr) {
    val = optr->get_default(name);
  }
  return val;
}

json_ref cfg_get_json(const char* name) {
  json_ref val;
  auto state = configState.rlock();

  // Highest precedence: command line arguments
  val = cfg_get_raw(name, &state->arg_cfg);
  // then: global config options
  if (!val) {
    val = cfg_get_raw(name, &state->global_cfg);
  }
  return val;
}

const char* cfg_get_string(const char* name, const char* defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isString()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a string"));
    }
    return json_string_value(val);
  }

  return defval;
}

// Return true if the json ref is an array of string values
static bool is_array_of_strings(const json_ref& ref) {
  uint32_t i;

  if (!ref.isArray()) {
    return false;
  }

  for (i = 0; i < json_array_size(ref); i++) {
    if (!json_array_get(ref, i).isString()) {
      return false;
    }
  }
  return true;
}

// Given an array of string values, if that array does not contain
// a ".watchmanconfig" entry, prepend it
static void prepend_watchmanconfig_to_array(json_ref& ref) {
  const char* val;

  if (json_array_size(ref) == 0) {
    // json_array_insert_new at index can fail when the array is empty,
    // so just append in this case.
    json_array_append_new(
        ref, typed_string_to_json(".watchmanconfig", W_STRING_UNICODE));
    return;
  }

  val = json_string_value(json_array_get(ref, 0));
  if (!strcmp(val, ".watchmanconfig")) {
    return;
  }
  json_array_insert_new(
      ref, 0, typed_string_to_json(".watchmanconfig", W_STRING_UNICODE));
}

// Compute the effective value of the root_files configuration and
// return a json reference.  The caller must decref the ref when done
// (we may synthesize this value).   Sets enforcing to indicate whether
// we will only allow watches on the root_files.
// The array returned by this function (if not NULL) is guaranteed to
// list .watchmanconfig as its zeroth element.
json_ref cfg_compute_root_files(bool* enforcing) {
  *enforcing = false;

  json_ref ref = cfg_get_json("enforce_root_files");
  if (ref) {
    if (!ref.isBool()) {
      logf(FATAL, "Expected config value enforce_root_files to be boolean\n");
    }
    *enforcing = ref.asBool();
  }

  ref = cfg_get_json("root_files");
  if (ref) {
    if (!is_array_of_strings(ref)) {
      logf(FATAL, "global config root_files must be an array of strings\n");
      *enforcing = false;
      return nullptr;
    }
    prepend_watchmanconfig_to_array(ref);

    return ref;
  }

  // Try legacy root_restrict_files configuration
  ref = cfg_get_json("root_restrict_files");
  if (ref) {
    if (!is_array_of_strings(ref)) {
      logf(
          FATAL,
          "deprecated global config root_restrict_files "
          "must be an array of strings\n");
      *enforcing = false;
      return nullptr;
    }
    prepend_watchmanconfig_to_array(ref);
    *enforcing = true;
    return ref;
  }

  // Synthesize our conservative default value.
  // .watchmanconfig MUST be first
  return json_array(
      {typed_string_to_json(".watchmanconfig"),
       typed_string_to_json(".hg"),
       typed_string_to_json(".git"),
       typed_string_to_json(".svn")});
}

// Produces a string like:  "`foo`, `bar`, and `baz`"
std::string cfg_pretty_print_root_files(const json_ref& root_files) {
  std::string result;
  for (unsigned int i = 0; i < root_files.array().size(); ++i) {
    const auto& r = root_files.array()[i];
    if (i > 1 && i == root_files.array().size() - 1) {
      // We are last in a list of multiple items
      result.append(", and ");
    } else if (i > 0) {
      result.append(", ");
    }
    result.append("`");
    result.append(json_string_value(r));
    result.append("`");
  }
  return result;
}

json_int_t cfg_get_int(const char* name, json_int_t defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isInt()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be an integer"));
    }
    return val.asInt();
  }

  return defval;
}

bool cfg_get_bool(const char* name, bool defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isBool()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a boolean"));
    }
    return val.asBool();
  }

  return defval;
}

double cfg_get_double(const char* name, double defval) {
  auto val = cfg_get_json(name);

  if (val) {
    if (!val.isNumber()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a number"));
    }
    return json_real_value(val);
  }

  return defval;
}

#ifndef _WIN32
#define MAKE_GET_PERM(PROP, SUFFIX)                                 \
  static mode_t get_##PROP##_perm(                                  \
      const char* name,                                             \
      const json_ref& val,                                          \
      bool write_bits,                                              \
      bool execute_bits) {                                          \
    mode_t ret = 0;                                                 \
    auto perm = val.get_default(#PROP);                             \
    if (perm) {                                                     \
      if (!perm.isBool()) {                                         \
        logf(                                                       \
            FATAL,                                                  \
            "Expected config value {}." #PROP " to be a boolean\n", \
            name);                                                  \
      }                                                             \
      if (perm.asBool()) {                                          \
        ret |= S_IR##SUFFIX;                                        \
        if (write_bits) {                                           \
          ret |= S_IW##SUFFIX;                                      \
        }                                                           \
        if (execute_bits) {                                         \
          ret |= S_IX##SUFFIX;                                      \
        }                                                           \
      }                                                             \
    }                                                               \
    return ret;                                                     \
  }

MAKE_GET_PERM(group, GRP)
MAKE_GET_PERM(others, OTH)

/**
 * This function expects the config to be an object containing the keys 'group'
 * and 'others', each a bool.
 */
mode_t cfg_get_perms(const char* name, bool write_bits, bool execute_bits) {
  auto val = cfg_get_json(name);
  mode_t ret = S_IRUSR | S_IWUSR;
  if (execute_bits) {
    ret |= S_IXUSR;
  }

  if (val) {
    if (!val.isObject()) {
      logf(FATAL, "Expected config value {} to be an object\n", name);
    }

    ret |= get_group_perm(name, val, write_bits, execute_bits);
    ret |= get_others_perm(name, val, write_bits, execute_bits);
  }

  return ret;
}
#endif

const char* cfg_get_trouble_url() {
  return cfg_get_string(
      "troubleshooting_url",
      "https://facebook.github.io/watchman/docs/troubleshooting.html");
}

namespace watchman {

Configuration::Configuration(const json_ref& local) : local_(local) {}

json_ref Configuration::get(const char* name) const {
  // Highest precedence: options set locally
  json_ref val;
  if (local_) {
    val = local_.get_default(name);
    if (val) {
      return val;
    }
  }
  auto state = configState.rlock();

  // then: command line arguments
  if (!val) {
    val = cfg_get_raw(name, &state->arg_cfg);
  }
  // then: global config options
  if (!val) {
    val = cfg_get_raw(name, &state->global_cfg);
  }
  return val;
}

const char* Configuration::getString(const char* name, const char* defval)
    const {
  auto val = get(name);

  if (val) {
    if (!val.isString()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a string"));
    }
    return json_string_value(val);
  }

  return defval;
}

json_int_t Configuration::getInt(const char* name, json_int_t defval) const {
  auto val = get(name);

  if (val) {
    if (!val.isInt()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be an integer"));
    }
    return val.asInt();
  }

  return defval;
}

bool Configuration::getBool(const char* name, bool defval) const {
  auto val = get(name);

  if (val) {
    if (!val.isBool()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a boolean"));
    }
    return val.asBool();
  }

  return defval;
}

double Configuration::getDouble(const char* name, double defval) const {
  auto val = get(name);

  if (val) {
    if (!val.isNumber()) {
      throw std::runtime_error(folly::to<std::string>(
          "Expected config value ", name, " to be a number"));
    }
    return json_real_value(val);
  }

  return defval;
}

} // namespace watchman

/* vim:ts=2:sw=2:et:
 */
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Modules/Legacy/Forward/CalcTMP.h>
#include <Core/Datatypes/DenseMatrix.h>
//#include <Packages/BioPSE/Core/Algorithms/NumApproximation/CalcTMP.h>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Modules::Forward;
using namespace SCIRun::Dataflow::Networks;
//using namespace SCIRun::Core::Algorithms::Forward;

const ModuleLookupInfo CalcTMP::staticInfo_("CalcTMP", "Forward", "SCIRun");

/*
class CalcTMP : public Module, public CalcTMPAlgo {
  public:
    CalcTMP(GuiContext*);
    virtual ~CalcTMP() {}

    virtual void execute();

  private:
    //SCIRunAlgo::CalcTMPAlgo algo_;
    CalcTMPAlgo algo_;
};
*/

CalcTMP::CalcTMP()
  : Module(staticInfo_)
{
  INITIALIZE_PORT(Amplitude);
  INITIALIZE_PORT(Depolarization_Time);
  INITIALIZE_PORT(Depolarization_Slope);
  INITIALIZE_PORT(Plateau_Slope);
  INITIALIZE_PORT(Repolarization_Time);
  INITIALIZE_PORT(Repolarization_Slope);
  INITIALIZE_PORT(Rest_Potential);
  INITIALIZE_PORT(TMPs);
  //algo_.set_progress_reporter(this);
}

void CalcTMP::execute()
{
  auto amplitudes = getOptionalInput(Amplitude);
  auto deps = getOptionalInput(Depolarization_Time);
  auto depslopes = getOptionalInput(Depolarization_Slope);;
  auto platslopes = getOptionalInput(Plateau_Slope);;
  auto reps = getOptionalInput(Repolarization_Time);;
  auto repslopes = getOptionalInput(Repolarization_Slope);;
  auto rests = getOptionalInput(Rest_Potential);;

  MatrixHandle tmps;

  if (needToExecute())
  {
    /* TODO LATER
    algo_.calc_TMPs(amplitudes,
                    deps,
                    depslopes,
                    platslopes,
                    reps,
                    repslopes,
                    rests,
                    500,
                    TMPs);
                    */

    sendOutput(TMPs, tmps);
  }
}
<commit_msg>Turn off UI<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Modules/Legacy/Forward/CalcTMP.h>
#include <Core/Datatypes/DenseMatrix.h>
//#include <Packages/BioPSE/Core/Algorithms/NumApproximation/CalcTMP.h>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Modules::Forward;
using namespace SCIRun::Dataflow::Networks;
//using namespace SCIRun::Core::Algorithms::Forward;

const ModuleLookupInfo CalcTMP::staticInfo_("CalcTMP", "Forward", "SCIRun");

/*
class CalcTMP : public Module, public CalcTMPAlgo {
  public:
    CalcTMP(GuiContext*);
    virtual ~CalcTMP() {}

    virtual void execute();

  private:
    //SCIRunAlgo::CalcTMPAlgo algo_;
    CalcTMPAlgo algo_;
};
*/

CalcTMP::CalcTMP()
  : Module(staticInfo_, false)
{
  INITIALIZE_PORT(Amplitude);
  INITIALIZE_PORT(Depolarization_Time);
  INITIALIZE_PORT(Depolarization_Slope);
  INITIALIZE_PORT(Plateau_Slope);
  INITIALIZE_PORT(Repolarization_Time);
  INITIALIZE_PORT(Repolarization_Slope);
  INITIALIZE_PORT(Rest_Potential);
  INITIALIZE_PORT(TMPs);
  //algo_.set_progress_reporter(this);
}

void CalcTMP::execute()
{
  auto amplitudes = getOptionalInput(Amplitude);
  auto deps = getOptionalInput(Depolarization_Time);
  auto depslopes = getOptionalInput(Depolarization_Slope);;
  auto platslopes = getOptionalInput(Plateau_Slope);;
  auto reps = getOptionalInput(Repolarization_Time);;
  auto repslopes = getOptionalInput(Repolarization_Slope);;
  auto rests = getOptionalInput(Rest_Potential);;

  MatrixHandle tmps;

  if (needToExecute())
  {
    /* TODO LATER
    algo_.calc_TMPs(amplitudes,
                    deps,
                    depslopes,
                    platslopes,
                    reps,
                    repslopes,
                    rests,
                    500,
                    TMPs);
                    */

    sendOutput(TMPs, tmps);
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 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 "ports/mojo_system/node.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "crypto/random.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "ports/mojo_system/core.h"

namespace mojo {
namespace edk {

namespace {

template <typename T>
void GenerateRandomName(T* out) { crypto::RandBytes(out, sizeof(T)); }

ports::NodeName GetRandomNodeName() {
  ports::NodeName name;
  GenerateRandomName(&name);
  return name;
}

}  // namespace

Node::PendingTokenConnection::PendingTokenConnection() {}

Node::PendingTokenConnection::~PendingTokenConnection() {}

Node::ReservedPort::ReservedPort() {}

Node::ReservedPort::~ReservedPort() {}

Node::~Node() {}

Node::Node(Core* core)
    : core_(core),
      name_(GetRandomNodeName()),
      node_(new ports::Node(name_, this)) {
  DLOG(INFO) << "Initializing node " << name_;
}

void Node::ConnectToChild(ScopedPlatformHandle platform_handle) {
  scoped_ptr<NodeChannel> channel(
      new NodeChannel(this, std::move(platform_handle),
                      core_->io_task_runner()));

  ports::NodeName token;
  GenerateRandomName(&token);

  channel->SetRemoteNodeName(token);
  channel->Start();
  channel->AcceptChild(name_, token);

  base::AutoLock lock(lock_);
  pending_children_.insert(std::make_pair(token, std::move(channel)));
}

void Node::ConnectToParent(ScopedPlatformHandle platform_handle) {
  base::AutoLock lock(lock_);
  DCHECK(parent_name_ == ports::kInvalidNodeName);
  DCHECK(!bootstrap_channel_to_parent_);

  scoped_ptr<NodeChannel> channel(
      new NodeChannel(this, std::move(platform_handle),
                      core_->io_task_runner()));
  bootstrap_channel_to_parent_ = std::move(channel);
  bootstrap_channel_to_parent_->Start();
}

void Node::AddPeer(const ports::NodeName& name,
                   scoped_ptr<NodeChannel> channel,
                   bool start_channel) {
  base::AutoLock lock(lock_);
  AddPeerNoLock(name, std::move(channel), start_channel);
}

void Node::DropPeer(const ports::NodeName& name) {
  base::AutoLock lock(lock_);
  DropPeerNoLock(name);
}

void Node::SendPeerEvent(const ports::NodeName& name, ports::Event event) {
  base::AutoLock lock(lock_);
  auto it = peers_.find(name);
  if (it != peers_.end()) {
    it->second->Event(std::move(event));
    return;
  }

  if (parent_name_ == ports::kInvalidNodeName) {
    DLOG(INFO) << "Dropping event for unknown peer: " << name;
    return;
  }

  // If we don't know who the peer is, queue the event for delivery and ask our
  // parent node to introduce us.
  auto& queue = pending_peer_events_[name];
  if (queue.empty()) {
    auto it = peers_.find(parent_name_);
    DCHECK(it != peers_.end());
    it->second->RequestIntroduction(name);
  }
  queue.emplace(std::move(event));
}

void Node::CreateUninitializedPort(ports::PortName* port_name) {
  node_->CreatePort(port_name);
}

void Node::InitializePort(const ports::PortName& port_name,
                          const ports::NodeName& peer_node_name,
                          const ports::PortName& peer_port_name) {
  int rv = node_->InitializePort(port_name, peer_node_name, peer_port_name);
  DCHECK_EQ(rv, ports::OK);
}

void Node::CreatePortPair(ports::PortName* port0, ports::PortName* port1) {
  int rv = node_->CreatePortPair(port0, port1);
  DCHECK_EQ(rv, ports::OK);
}

void Node::SetPortObserver(const ports::PortName& port_name,
                           std::shared_ptr<PortObserver> observer) {
  DCHECK(observer);
  node_->SetUserData(port_name, std::move(observer));
}

int Node::SendMessage(const ports::PortName& port_name,
                      ports::ScopedMessage message) {
  return node_->SendMessage(port_name, std::move(message));
}

void Node::ClosePort(const ports::PortName& port_name) {
  int rv = node_->ClosePort(port_name);
  DCHECK_EQ(rv, ports::OK) << "ClosePort failed: " << rv;
}

void Node::ReservePortForToken(const ports::PortName& port_name,
                               const std::string& token,
                               const base::Closure& on_connect) {
  ReservedPort reservation;
  reservation.local_port = port_name;
  reservation.callback = on_connect;

  base::AutoLock lock(lock_);
  auto result = reserved_ports_.insert(std::make_pair(token, reservation));
  if (!result.second)
    DLOG(ERROR) << "Can't reserve port for duplicate token: " << token;
}

void Node::ConnectToParentPortByToken(const std::string& token,
                                      const ports::PortName& local_port,
                                      const base::Closure& on_connect) {
  base::AutoLock lock(lock_);
  if (parent_name_ != ports::kInvalidNodeName) {
    ConnectToParentPortByTokenNowNoLock(token, local_port, on_connect);
  } else {
    PendingTokenConnection pending_connection;
    pending_connection.port = local_port;
    pending_connection.token = token;
    pending_connection.callback = on_connect;

    pending_token_connections_.push_back(pending_connection);
  }
}

void Node::AddPeerNoLock(const ports::NodeName& name,
                         scoped_ptr<NodeChannel> channel,
                         bool start_channel) {
  DCHECK(name != ports::kInvalidNodeName);
  DCHECK(channel);

  channel->SetRemoteNodeName(name);

  if (peers_.find(name) != peers_.end()) {
    // This can happen normally if two nodes race to be introduced to each
    // other. The losing pipe will be silently closed and introduction should
    // not be affected.
    LOG(INFO) << "Ignoring duplicate peer name " << name;
    return;
  }

  DLOG(INFO) << "Accepting new peer " << name << " on node " << name_;

  OutgoingEventQueue pending_events;
  auto it = pending_peer_events_.find(name);
  if (it != pending_peer_events_.end()) {
    auto& event_queue = it->second;
    while (!event_queue.empty()) {
      channel->Event(std::move(event_queue.front()));
      event_queue.pop();
    }
    pending_peer_events_.erase(it);
  }

  auto result = peers_.insert(std::make_pair(name, std::move(channel)));
  DCHECK(result.second);
}

void Node::DropPeerNoLock(const ports::NodeName& name) {
  auto it = peers_.find(name);
  if (it != peers_.end()) {
    ports::NodeName peer = it->first;
    peers_.erase(it);
    DLOG(INFO) << "Dropped peer " << peer;
  }

  pending_peer_events_.erase(name);
  pending_children_.erase(name);

  {
    base::AutoUnlock unlock(lock_);
    node_->LostConnectionToNode(name);
  }
}

void Node::ConnectToParentPortByTokenNowNoLock(
    const std::string& token,
    const ports::PortName& local_port,
    const base::Closure& on_connect) {
  DCHECK(parent_name_ != ports::kInvalidNodeName);

  auto result = pending_connection_acks_.insert(
      std::make_pair(local_port, on_connect));
  DCHECK(result.second);

  auto it = peers_.find(parent_name_);
  it->second->ConnectToPort(token, local_port);
}

void Node::AcceptEventOnIOThread(ports::Event event) {
  DCHECK(core_->io_task_runner()->RunsTasksOnCurrentThread());
  node_->AcceptEvent(std::move(event));
}

void Node::GenerateRandomPortName(ports::PortName* port_name) {
  GenerateRandomName(port_name);
}

void Node::SendEvent(const ports::NodeName& node, ports::Event event) {
  if (node == name_) {
    core_->io_task_runner()->PostTask(
        FROM_HERE,
        base::Bind(&Node::AcceptEventOnIOThread,
                   base::Unretained(this),
                   base::Passed(&event)));
  } else {
    SendPeerEvent(node, std::move(event));
  }
}

void Node::MessagesAvailable(const ports::PortName& port,
                             std::shared_ptr<ports::UserData> user_data) {
  PortObserver* observer = static_cast<PortObserver*>(user_data.get());
  if (observer)
    observer->OnMessagesAvailable();
}

void Node::OnAcceptChild(const ports::NodeName& from_node,
                         const ports::NodeName& parent_name,
                         const ports::NodeName& token) {
  base::AutoLock lock(lock_);

  if (!bootstrap_channel_to_parent_) {
    DLOG(ERROR) << "Unexpected AcceptChild message from " << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  parent_name_ = parent_name;
  bootstrap_channel_to_parent_->AcceptParent(token, name_);
  AddPeerNoLock(parent_name_, std::move(bootstrap_channel_to_parent_),
                false /* start_channel */);

  // Flush any pending token-based port connections.
  for (const PendingTokenConnection& c : pending_token_connections_)
    ConnectToParentPortByTokenNowNoLock(c.token, c.port, c.callback);

  DLOG(INFO) << "Child " << name_ << " accepted parent " << parent_name;
}

void Node::OnAcceptParent(const ports::NodeName& from_node,
                          const ports::NodeName& token,
                          const ports::NodeName& child_name) {
  base::AutoLock lock(lock_);
  scoped_ptr<NodeChannel> channel;

  auto it = pending_children_.find(from_node);
  if (it == pending_children_.end() || token != from_node) {
    DLOG(ERROR) << "Received unexpected AcceptParent message from "
                << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  channel = std::move(it->second);
  pending_children_.erase(it);

  DCHECK(channel);

  AddPeerNoLock(child_name, std::move(channel), false /* start_channel */);
}

void Node::OnEvent(const ports::NodeName& from_node,
                   ports::Event event) {
  AcceptEventOnIOThread(std::move(event));
}

void Node::OnConnectToPort(const ports::NodeName& from_node,
                           const ports::PortName& connector_port,
                           const std::string& token) {
  base::AutoLock lock(lock_);

  auto port_it = reserved_ports_.find(token);
  auto peer_it = peers_.find(from_node);
  if (port_it == reserved_ports_.end() || peer_it == peers_.end()) {
    DLOG(ERROR) << "Ignoring invalid ConnectToPort from node " << from_node
                << " for token " << token;
    DropPeerNoLock(from_node);
    return;
  }

  ports::PortName parent_port_name = port_it->second.local_port;
  base::Closure callback = port_it->second.callback;
  reserved_ports_.erase(port_it);

  DCHECK(!callback.is_null());

  InitializePort(parent_port_name, from_node, connector_port);
  callback.Run();

  peer_it->second->ConnectToPortAck(connector_port, parent_port_name);
}

void Node::OnConnectToPortAck(const ports::NodeName& from_node,
                              const ports::PortName& connector_port,
                              const ports::PortName& connectee_port) {
  base::AutoLock lock(lock_);

  if (from_node != parent_name_) {
    DLOG(ERROR) << "Ignoring ConnectToPortAck from non-parent node "
                << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  auto it = pending_connection_acks_.find(connector_port);
  DCHECK(it != pending_connection_acks_.end());
  base::Closure callback = it->second;
  pending_connection_acks_.erase(it);

  DCHECK(!callback.is_null());

  InitializePort(connector_port, parent_name_, connectee_port);
  callback.Run();
}

void Node::OnRequestIntroduction(const ports::NodeName& from_node,
                                 const ports::NodeName& name) {
  if (from_node == name || name == ports::kInvalidNodeName) {
    DLOG(ERROR) << "Rejecting invalid OnRequestIntroduction message from "
                << from_node;
    DropPeer(from_node);
    return;
  }

  base::AutoLock lock(lock_);
  auto it = peers_.find(from_node);
  DCHECK(it != peers_.end());

  NodeChannel* requestor = it->second.get();
  it = peers_.find(name);
  if (it == peers_.end()) {
    requestor->Introduce(name, ScopedPlatformHandle());
  } else {
    PlatformChannelPair new_channel;
    requestor->Introduce(name, new_channel.PassServerHandle());
    it->second->Introduce(from_node, new_channel.PassClientHandle());
  }
}

void Node::OnIntroduce(const ports::NodeName& from_node,
                       const ports::NodeName& name,
                       ScopedPlatformHandle channel_handle) {
  base::AutoLock lock(lock_);

  if (from_node != parent_name_) {
    DLOG(INFO) << "Received unexpected Introduce message from node "
               << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  if (!channel_handle.is_valid()) {
    DLOG(ERROR) << "Could not be introduced to peer " << name;
    pending_peer_events_.erase(name);
    return;
  }

  scoped_ptr<NodeChannel> channel(
      new NodeChannel(this, std::move(channel_handle),
                      core_->io_task_runner()));
  AddPeerNoLock(name, std::move(channel), true /* start_channel */);
}

void Node::OnChannelError(const ports::NodeName& from_node) {
  DropPeer(from_node);
}

}  // namespace edk
}  // namespace mojo
<commit_msg>fix peer introduction and a race in child handshake<commit_after>// Copyright 2015 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 "ports/mojo_system/node.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "crypto/random.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "ports/mojo_system/core.h"

namespace mojo {
namespace edk {

namespace {

template <typename T>
void GenerateRandomName(T* out) { crypto::RandBytes(out, sizeof(T)); }

ports::NodeName GetRandomNodeName() {
  ports::NodeName name;
  GenerateRandomName(&name);
  return name;
}

}  // namespace

Node::PendingTokenConnection::PendingTokenConnection() {}

Node::PendingTokenConnection::~PendingTokenConnection() {}

Node::ReservedPort::ReservedPort() {}

Node::ReservedPort::~ReservedPort() {}

Node::~Node() {}

Node::Node(Core* core)
    : core_(core),
      name_(GetRandomNodeName()),
      node_(new ports::Node(name_, this)) {
  DLOG(INFO) << "Initializing node " << name_;
}

void Node::ConnectToChild(ScopedPlatformHandle platform_handle) {
  scoped_ptr<NodeChannel> channel(
      new NodeChannel(this, std::move(platform_handle),
                      core_->io_task_runner()));

  ports::NodeName token;
  GenerateRandomName(&token);

  base::AutoLock lock(lock_);

  channel->SetRemoteNodeName(token);
  channel->Start();
  channel->AcceptChild(name_, token);

  pending_children_.insert(std::make_pair(token, std::move(channel)));
}

void Node::ConnectToParent(ScopedPlatformHandle platform_handle) {
  base::AutoLock lock(lock_);
  DCHECK(parent_name_ == ports::kInvalidNodeName);
  DCHECK(!bootstrap_channel_to_parent_);

  scoped_ptr<NodeChannel> channel(
      new NodeChannel(this, std::move(platform_handle),
                      core_->io_task_runner()));
  bootstrap_channel_to_parent_ = std::move(channel);
  bootstrap_channel_to_parent_->Start();
}

void Node::AddPeer(const ports::NodeName& name,
                   scoped_ptr<NodeChannel> channel,
                   bool start_channel) {
  base::AutoLock lock(lock_);
  AddPeerNoLock(name, std::move(channel), start_channel);
}

void Node::DropPeer(const ports::NodeName& name) {
  base::AutoLock lock(lock_);
  DropPeerNoLock(name);
}

void Node::SendPeerEvent(const ports::NodeName& name, ports::Event event) {
  base::AutoLock lock(lock_);
  auto it = peers_.find(name);
  if (it != peers_.end()) {
    it->second->Event(std::move(event));
    return;
  }

  if (parent_name_ == ports::kInvalidNodeName) {
    DLOG(INFO) << "Dropping event for unknown peer: " << name;
    return;
  }

  // If we don't know who the peer is, queue the event for delivery and ask our
  // parent node to introduce us.
  auto& queue = pending_peer_events_[name];
  if (queue.empty()) {
    auto it = peers_.find(parent_name_);
    DCHECK(it != peers_.end());
    it->second->RequestIntroduction(name);
  }
  queue.emplace(std::move(event));
}

void Node::CreateUninitializedPort(ports::PortName* port_name) {
  node_->CreatePort(port_name);
}

void Node::InitializePort(const ports::PortName& port_name,
                          const ports::NodeName& peer_node_name,
                          const ports::PortName& peer_port_name) {
  int rv = node_->InitializePort(port_name, peer_node_name, peer_port_name);
  DCHECK_EQ(rv, ports::OK);
}

void Node::CreatePortPair(ports::PortName* port0, ports::PortName* port1) {
  int rv = node_->CreatePortPair(port0, port1);
  DCHECK_EQ(rv, ports::OK);
}

void Node::SetPortObserver(const ports::PortName& port_name,
                           std::shared_ptr<PortObserver> observer) {
  DCHECK(observer);
  node_->SetUserData(port_name, std::move(observer));
}

int Node::SendMessage(const ports::PortName& port_name,
                      ports::ScopedMessage message) {
  return node_->SendMessage(port_name, std::move(message));
}

void Node::ClosePort(const ports::PortName& port_name) {
  int rv = node_->ClosePort(port_name);
  DCHECK_EQ(rv, ports::OK) << "ClosePort failed: " << rv;
}

void Node::ReservePortForToken(const ports::PortName& port_name,
                               const std::string& token,
                               const base::Closure& on_connect) {
  ReservedPort reservation;
  reservation.local_port = port_name;
  reservation.callback = on_connect;

  base::AutoLock lock(lock_);
  auto result = reserved_ports_.insert(std::make_pair(token, reservation));
  if (!result.second)
    DLOG(ERROR) << "Can't reserve port for duplicate token: " << token;
}

void Node::ConnectToParentPortByToken(const std::string& token,
                                      const ports::PortName& local_port,
                                      const base::Closure& on_connect) {
  base::AutoLock lock(lock_);
  if (parent_name_ != ports::kInvalidNodeName) {
    ConnectToParentPortByTokenNowNoLock(token, local_port, on_connect);
  } else {
    PendingTokenConnection pending_connection;
    pending_connection.port = local_port;
    pending_connection.token = token;
    pending_connection.callback = on_connect;

    pending_token_connections_.push_back(pending_connection);
  }
}

void Node::AddPeerNoLock(const ports::NodeName& name,
                         scoped_ptr<NodeChannel> channel,
                         bool start_channel) {
  DCHECK(name != ports::kInvalidNodeName);
  DCHECK(channel);

  channel->SetRemoteNodeName(name);

  if (peers_.find(name) != peers_.end()) {
    // This can happen normally if two nodes race to be introduced to each
    // other. The losing pipe will be silently closed and introduction should
    // not be affected.
    LOG(INFO) << "Ignoring duplicate peer name " << name;
    return;
  }

  DLOG(INFO) << "Accepting new peer " << name << " on node " << name_;

  if (start_channel)
    channel->Start();

  OutgoingEventQueue pending_events;
  auto it = pending_peer_events_.find(name);
  if (it != pending_peer_events_.end()) {
    auto& event_queue = it->second;
    while (!event_queue.empty()) {
      channel->Event(std::move(event_queue.front()));
      event_queue.pop();
    }
    pending_peer_events_.erase(it);
  }

  auto result = peers_.insert(std::make_pair(name, std::move(channel)));
  DCHECK(result.second);
}

void Node::DropPeerNoLock(const ports::NodeName& name) {
  auto it = peers_.find(name);
  if (it != peers_.end()) {
    ports::NodeName peer = it->first;
    peers_.erase(it);
    DLOG(INFO) << "Dropped peer " << peer;
  }

  pending_peer_events_.erase(name);
  pending_children_.erase(name);

  {
    base::AutoUnlock unlock(lock_);
    node_->LostConnectionToNode(name);
  }
}

void Node::ConnectToParentPortByTokenNowNoLock(
    const std::string& token,
    const ports::PortName& local_port,
    const base::Closure& on_connect) {
  DCHECK(parent_name_ != ports::kInvalidNodeName);

  auto result = pending_connection_acks_.insert(
      std::make_pair(local_port, on_connect));
  DCHECK(result.second);

  auto it = peers_.find(parent_name_);
  it->second->ConnectToPort(token, local_port);
}

void Node::AcceptEventOnIOThread(ports::Event event) {
  DCHECK(core_->io_task_runner()->RunsTasksOnCurrentThread());
  node_->AcceptEvent(std::move(event));
}

void Node::GenerateRandomPortName(ports::PortName* port_name) {
  GenerateRandomName(port_name);
}

void Node::SendEvent(const ports::NodeName& node, ports::Event event) {
  if (node == name_) {
    core_->io_task_runner()->PostTask(
        FROM_HERE,
        base::Bind(&Node::AcceptEventOnIOThread,
                   base::Unretained(this),
                   base::Passed(&event)));
  } else {
    SendPeerEvent(node, std::move(event));
  }
}

void Node::MessagesAvailable(const ports::PortName& port,
                             std::shared_ptr<ports::UserData> user_data) {
  PortObserver* observer = static_cast<PortObserver*>(user_data.get());
  if (observer)
    observer->OnMessagesAvailable();
}

void Node::OnAcceptChild(const ports::NodeName& from_node,
                         const ports::NodeName& parent_name,
                         const ports::NodeName& token) {
  base::AutoLock lock(lock_);

  if (!bootstrap_channel_to_parent_) {
    DLOG(ERROR) << "Unexpected AcceptChild message from " << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  parent_name_ = parent_name;
  bootstrap_channel_to_parent_->AcceptParent(token, name_);
  AddPeerNoLock(parent_name_, std::move(bootstrap_channel_to_parent_),
                false /* start_channel */);

  // Flush any pending token-based port connections.
  for (const PendingTokenConnection& c : pending_token_connections_)
    ConnectToParentPortByTokenNowNoLock(c.token, c.port, c.callback);

  DLOG(INFO) << "Child " << name_ << " accepted parent " << parent_name;
}

void Node::OnAcceptParent(const ports::NodeName& from_node,
                          const ports::NodeName& token,
                          const ports::NodeName& child_name) {
  base::AutoLock lock(lock_);
  scoped_ptr<NodeChannel> channel;

  auto it = pending_children_.find(from_node);
  if (it == pending_children_.end() || token != from_node) {
    DLOG(ERROR) << "Received unexpected AcceptParent message from "
                << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  channel = std::move(it->second);
  pending_children_.erase(it);

  DCHECK(channel);

  AddPeerNoLock(child_name, std::move(channel), false /* start_channel */);
}

void Node::OnEvent(const ports::NodeName& from_node,
                   ports::Event event) {
  AcceptEventOnIOThread(std::move(event));
}

void Node::OnConnectToPort(const ports::NodeName& from_node,
                           const ports::PortName& connector_port,
                           const std::string& token) {
  base::AutoLock lock(lock_);

  auto port_it = reserved_ports_.find(token);
  auto peer_it = peers_.find(from_node);
  if (port_it == reserved_ports_.end() || peer_it == peers_.end()) {
    DLOG(ERROR) << "Ignoring invalid ConnectToPort from node " << from_node
                << " for token " << token;
    DropPeerNoLock(from_node);
    return;
  }

  ports::PortName parent_port_name = port_it->second.local_port;
  base::Closure callback = port_it->second.callback;
  reserved_ports_.erase(port_it);

  DCHECK(!callback.is_null());

  InitializePort(parent_port_name, from_node, connector_port);
  callback.Run();

  peer_it->second->ConnectToPortAck(connector_port, parent_port_name);
}

void Node::OnConnectToPortAck(const ports::NodeName& from_node,
                              const ports::PortName& connector_port,
                              const ports::PortName& connectee_port) {
  base::AutoLock lock(lock_);

  if (from_node != parent_name_) {
    DLOG(ERROR) << "Ignoring ConnectToPortAck from non-parent node "
                << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  auto it = pending_connection_acks_.find(connector_port);
  DCHECK(it != pending_connection_acks_.end());
  base::Closure callback = it->second;
  pending_connection_acks_.erase(it);

  DCHECK(!callback.is_null());

  InitializePort(connector_port, parent_name_, connectee_port);
  callback.Run();
}

void Node::OnRequestIntroduction(const ports::NodeName& from_node,
                                 const ports::NodeName& name) {
  if (from_node == name || name == ports::kInvalidNodeName) {
    DLOG(ERROR) << "Rejecting invalid OnRequestIntroduction message from "
                << from_node;
    DropPeer(from_node);
    return;
  }

  base::AutoLock lock(lock_);
  auto it = peers_.find(from_node);
  DCHECK(it != peers_.end());

  NodeChannel* requestor = it->second.get();
  it = peers_.find(name);
  if (it == peers_.end()) {
    requestor->Introduce(name, ScopedPlatformHandle());
  } else {
    PlatformChannelPair new_channel;
    requestor->Introduce(name, new_channel.PassServerHandle());
    it->second->Introduce(from_node, new_channel.PassClientHandle());
  }
}

void Node::OnIntroduce(const ports::NodeName& from_node,
                       const ports::NodeName& name,
                       ScopedPlatformHandle channel_handle) {
  base::AutoLock lock(lock_);

  if (from_node != parent_name_) {
    DLOG(INFO) << "Received unexpected Introduce message from node "
               << from_node;
    DropPeerNoLock(from_node);
    return;
  }

  if (!channel_handle.is_valid()) {
    DLOG(ERROR) << "Could not be introduced to peer " << name;
    pending_peer_events_.erase(name);
    return;
  }

  scoped_ptr<NodeChannel> channel(
      new NodeChannel(this, std::move(channel_handle),
                      core_->io_task_runner()));
  AddPeerNoLock(name, std::move(channel), true /* start_channel */);
}

void Node::OnChannelError(const ports::NodeName& from_node) {
  DropPeer(from_node);
}

}  // namespace edk
}  // namespace mojo
<|endoftext|>