<commit_before>/**************************************************************************
 * newbucket.hxx -- new bucket routines for better world modeling
 *
 * Written by Curtis L. Olson, started February 1999.
 *
 * Copyright (C) 1999  Curtis L. Olson - http://www.flightgear.org/~curt/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * $Id$
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#include <cmath>
#include <cstdio> // some platforms need this for ::snprintf
#include <iostream>

#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>

#include "newbucket.hxx"

// default constructor
SGBucket::SGBucket() :
    lon(-1000),
    lat(-1000),
    x(0),
    y(0)
{
}

bool SGBucket::isValid() const
{
    // The most northerly valid latitude is 89, not 90. There is no tile
    // whose *bottom* latitude is 90. Similar there is no tile whose left egde
    // is 180 longitude.
    return (lon >= -180) &&
            (lon < 180) &&
            (lat >= -90) &&
            (lat < 90) &&
            (x < 8) && (y < 8);
}

void SGBucket::make_bad()
{
    lon = -1000;
    lat = -1000;
}

#ifndef NO_DEPRECATED_API

// constructor for specified location
SGBucket::SGBucket(const double dlon, const double dlat) {
    set_bucket(dlon, dlat);
}
#endif

SGBucket::SGBucket(const SGGeod& geod) {
    set_bucket(geod.getLongitudeDeg(),
                   geod.getLatitudeDeg());
}

// Parse a unique scenery tile index and find the lon, lat, x, and y
SGBucket::SGBucket(const long int bindex) {
    long int index = bindex;

    lon = index >> 14;
    index -= lon << 14;
    lon -= 180;

    lat = index >> 6;
    index -= lat << 6;
    lat -= 90;

    y = index >> 3;
    index -= y << 3;

    x = index;
}

/* Calculate the greatest integral value less than
 * or equal to the given value (floor(x)),
 * but attribute coordinates close to the boundary to the next
 * (increasing) integral
 */
static int floorWithEpsilon(double x)
{
    double diff = x - static_cast<int>(x);
    if ( (x >= 0.0) || (fabs(diff) < SG_EPSILON) ) {
        return static_cast<int>(x);
    } else {
        return static_cast<int>(x) - 1;
    }
}

#ifndef NO_DEPRECATED_API

void SGBucket::set_bucket(double dlon, double dlat)
{
    innerSet(dlon, dlat);
}

void SGBucket::set_bucket(const SGGeod& geod)
{
    innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg());
}

#endif

// Set the bucket params for the specified lat and lon
void SGBucket::innerSet( double dlon, double dlat )
{
    if ((dlon < -180.0) || (dlon >= 180.0)) {
        SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon);
        dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon);
    }

    if ((dlat < -90.0) || (dlat > 90.0)) {
        SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat);
        dlat = SGMiscd::clip(dlat, -90.0, 90.0);
    }

    //
    // longitude first
    //
    double span = sg_bucket_span( dlat );
    // we do NOT need to special case lon=180 here, since
    // normalizePeriodic will never return 180; it will
    // return -180, which is what we want.
    lon = floorWithEpsilon(dlon);

    // find subdivision or super lon if needed
    if ( span <= 1.0 ) {
        /* We have more than one tile per degree of
         * longitude, so we need an x offset.
         */
        x = (int)((dlon - lon) / span);
    } else {
        /* We have one or more degrees per tile,
         * so we need to find the base longitude
         * of that tile.
         *
         * First we calculate the integral base longitude
         * (e.g. -85.5 => -86) and then find the greatest
         * multiple of span that is less than or equal to
         * that longitude.
         *
         * That way, the Greenwich Meridian is always
         * a tile border.
         */
        lon=static_cast<int>(floor(lon / span) * span);
        x = 0;
    }

    //
    // then latitude
    //
    lat = floorWithEpsilon(dlat);

    // special case when passing in the north pole point (possibly due to
    // clipping latitude above). Ensures we generate a valid bucket in this
    // scenario
    if (lat == 90) {
        lat = 89;
        y = 7;
    } else {
        /* Latitude base and offset are easier, as
         * tiles always are 1/8 degree of latitude wide.
         */
        y = (int)((dlat - lat) * 8);
    }
}

// Build the path name for this bucket
std::string SGBucket::gen_base_path() const {
    // long int index;
    int top_lon, top_lat, main_lon, main_lat;
    char hem, pole;
    char raw_path[256];

    top_lon = lon / 10;
    main_lon = lon;
    if ( (lon < 0) && (top_lon * 10 != lon) ) {
	top_lon -= 1;
    }
    top_lon *= 10;
    if ( top_lon >= 0 ) {
	hem = 'e';
    } else {
	hem = 'w';
	top_lon *= -1;
    }
    if ( main_lon < 0 ) {
	main_lon *= -1;
    }

    top_lat = lat / 10;
    main_lat = lat;
    if ( (lat < 0) && (top_lat * 10 != lat) ) {
	top_lat -= 1;
    }
    top_lat *= 10;
    if ( top_lat >= 0 ) {
	pole = 'n';
    } else {
	pole = 's';
	top_lat *= -1;
    }
    if ( main_lat < 0 ) {
	main_lat *= -1;
    }

    ::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
	    hem, top_lon, pole, top_lat,
	    hem, main_lon, pole, main_lat);

    SGPath path( raw_path );

    return path.str();
}

// return width of the tile in degrees
double SGBucket::get_width() const {
    return sg_bucket_span( get_center_lat() );
}

// return height of the tile in degrees
double SGBucket::get_height() const {
    return SG_BUCKET_SPAN;
}

double SGBucket::get_highest_lat() const
{
    unsigned char adjustedY = y;
    if (lat >= 0) {
        // tile is north of the equator, so we want the top edge. Add one
        // to y to achieve this.
        ++adjustedY;
    }

	return lat + (adjustedY / 8.0);
}

// return width of the tile in meters. This function is used by the
// tile-manager to estimate how many tiles are in the view distance, so
// we care about the smallest width, which occurs at the highest latitude.
double SGBucket::get_width_m() const
{
    double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS;
    double cos_lat = cos( clat_rad );
    if (fabs(cos_lat) < SG_EPSILON) {
        // happens for polar tiles, since we pass in a latitude of 90
        // return an arbitrary small value so all tiles are loaded
        return 10.0;
    }

    double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
    double local_perimeter = local_radius * SGD_2PI;
    double degree_width = local_perimeter / 360.0;

    return get_width() * degree_width;
}

// return height of the tile in meters
double SGBucket::get_height_m() const {
    double perimeter = SG_EQUATORIAL_RADIUS_M * SGD_2PI;
    double degree_height = perimeter / 360.0;

    return SG_BUCKET_SPAN * degree_height;
}

SGBucket SGBucket::sibling(int dx, int dy) const
{
    if (!isValid()) {
        SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket");
        return SGBucket();
    }

    double clat = get_center_lat() + dy * SG_BUCKET_SPAN;
    // return invalid here instead of clipping, so callers can discard
    // invalid buckets without having to check if it's an existing one
    if ((clat < -90.0) || (clat > 90.0)) {
        return SGBucket();
    }

    // find the lon span for the new latitude
    double span = sg_bucket_span( clat );

    double tmp = get_center_lon() + dx * span;
    tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp);

    SGBucket b;
    b.innerSet(tmp, clat);
    return b;
}

std::string SGBucket::gen_index_str() const
{
	char tmp[20];
	::snprintf(tmp, 20, "%ld",
                 (((long)lon + 180) << 14) + ((lat + 90) << 6)
                 + (y << 3) + x);
	return (std::string)tmp;
}

#ifndef NO_DEPRECATED_API
// find the bucket which is offset by the specified tile units in the
// X & Y direction.  We need the current lon and lat to resolve
// ambiguities when going from a wider tile to a narrower one above or
// below.  This assumes that we are feeding in
SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) {
    SGBucket result( dlon, dlat );
    double clat = result.get_center_lat() + dy * SG_BUCKET_SPAN;

    // walk dy units in the lat direction
    result.set_bucket( dlon, clat );

    // find the lon span for the new latitude
    double span = sg_bucket_span( clat );

    // walk dx units in the lon direction
    double tmp = dlon + dx * span;
    while ( tmp < -180.0 ) {
	tmp += 360.0;
    }
    while ( tmp >= 180.0 ) {
	tmp -= 360.0;
    }
    result.set_bucket( tmp, clat );

    return result;
}
#endif

// calculate the offset between two buckets
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {

    // Latitude difference
    double c1_lat = b1.get_center_lat();
    double c2_lat = b2.get_center_lat();
    double diff_lat = c2_lat - c1_lat;

#ifdef HAVE_RINT
    *dy = (int)rint( diff_lat / SG_BUCKET_SPAN );
#else
    if ( diff_lat > 0 ) {
	*dy = (int)( diff_lat / SG_BUCKET_SPAN + 0.5 );
    } else {
	*dy = (int)( diff_lat / SG_BUCKET_SPAN - 0.5 );
    }
#endif

    // longitude difference
    double diff_lon=0.0;
    double span=0.0;

    SGBucket tmp_bucket;
    // To handle crossing the bucket size boundary
    //  we need to account for different size buckets.

    if ( sg_bucket_span(c1_lat) <= sg_bucket_span(c2_lat) )
    {
	span = sg_bucket_span(c1_lat);
    } else {
	span = sg_bucket_span(c2_lat);
    }

    diff_lon = b2.get_center_lon() - b1.get_center_lon();

    if (diff_lon <0.0)
    {
       diff_lon -= b1.get_width()*0.5 + b2.get_width()*0.5 - span;
    }
    else
    {
       diff_lon += b1.get_width()*0.5 + b2.get_width()*0.5 - span;
    }

#ifdef HAVE_RINT
    *dx = (int)rint( diff_lon / span );
#else
    if ( diff_lon > 0 ) {
	*dx = (int)( diff_lon / span + 0.5 );
    } else {
	*dx = (int)( diff_lon / span - 0.5 );
    }
#endif
}

void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list ) {
    double lon, lat, span;

    for (lat = min.getLatitudeDeg(); lat <= max.getLatitudeDeg(); lat += SG_BUCKET_SPAN) {
        span = sg_bucket_span( lat );
        for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span)
        {
            SGBucket b(SGGeod::fromDeg(lon, lat));
            if (!b.isValid()) {
                continue;
            }

            list.push_back(b);
        }
    }
}

std::ostream& operator<< ( std::ostream& out, const SGBucket& b )
{
    return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
}

<commit_msg>Fix new SGBucket API<commit_after>/**************************************************************************
 * newbucket.hxx -- new bucket routines for better world modeling
 *
 * Written by Curtis L. Olson, started February 1999.
 *
 * Copyright (C) 1999  Curtis L. Olson - http://www.flightgear.org/~curt/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * $Id$
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#include <cmath>
#include <cstdio> // some platforms need this for ::snprintf
#include <iostream>

#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>

#include "newbucket.hxx"

// default constructor
SGBucket::SGBucket() :
    lon(-1000),
    lat(-1000),
    x(0),
    y(0)
{
}

bool SGBucket::isValid() const
{
    // The most northerly valid latitude is 89, not 90. There is no tile
    // whose *bottom* latitude is 90. Similar there is no tile whose left egde
    // is 180 longitude.
    return (lon >= -180) &&
            (lon < 180) &&
            (lat >= -90) &&
            (lat < 90) &&
            (x < 8) && (y < 8);
}

void SGBucket::make_bad()
{
    lon = -1000;
    lat = -1000;
}

#ifndef NO_DEPRECATED_API

// constructor for specified location
SGBucket::SGBucket(const double dlon, const double dlat) {
    set_bucket(dlon, dlat);
}
#endif

SGBucket::SGBucket(const SGGeod& geod) {
    innerSet(geod.getLongitudeDeg(),
             geod.getLatitudeDeg());
}

// Parse a unique scenery tile index and find the lon, lat, x, and y
SGBucket::SGBucket(const long int bindex) {
    long int index = bindex;

    lon = index >> 14;
    index -= lon << 14;
    lon -= 180;

    lat = index >> 6;
    index -= lat << 6;
    lat -= 90;

    y = index >> 3;
    index -= y << 3;

    x = index;
}

/* Calculate the greatest integral value less than
 * or equal to the given value (floor(x)),
 * but attribute coordinates close to the boundary to the next
 * (increasing) integral
 */
static int floorWithEpsilon(double x)
{
    double diff = x - static_cast<int>(x);
    if ( (x >= 0.0) || (fabs(diff) < SG_EPSILON) ) {
        return static_cast<int>(x);
    } else {
        return static_cast<int>(x) - 1;
    }
}

#ifndef NO_DEPRECATED_API

void SGBucket::set_bucket(double dlon, double dlat)
{
    innerSet(dlon, dlat);
}

void SGBucket::set_bucket(const SGGeod& geod)
{
    innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg());
}

#endif

// Set the bucket params for the specified lat and lon
void SGBucket::innerSet( double dlon, double dlat )
{
    if ((dlon < -180.0) || (dlon >= 180.0)) {
        SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon);
        dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon);
    }

    if ((dlat < -90.0) || (dlat > 90.0)) {
        SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat);
        dlat = SGMiscd::clip(dlat, -90.0, 90.0);
    }

    //
    // longitude first
    //
    double span = sg_bucket_span( dlat );
    // we do NOT need to special case lon=180 here, since
    // normalizePeriodic will never return 180; it will
    // return -180, which is what we want.
    lon = floorWithEpsilon(dlon);

    // find subdivision or super lon if needed
    if ( span <= 1.0 ) {
        /* We have more than one tile per degree of
         * longitude, so we need an x offset.
         */
        x = (int)((dlon - lon) / span);
    } else {
        /* We have one or more degrees per tile,
         * so we need to find the base longitude
         * of that tile.
         *
         * First we calculate the integral base longitude
         * (e.g. -85.5 => -86) and then find the greatest
         * multiple of span that is less than or equal to
         * that longitude.
         *
         * That way, the Greenwich Meridian is always
         * a tile border.
         */
        lon=static_cast<int>(floor(lon / span) * span);
        x = 0;
    }

    //
    // then latitude
    //
    lat = floorWithEpsilon(dlat);

    // special case when passing in the north pole point (possibly due to
    // clipping latitude above). Ensures we generate a valid bucket in this
    // scenario
    if (lat == 90) {
        lat = 89;
        y = 7;
    } else {
        /* Latitude base and offset are easier, as
         * tiles always are 1/8 degree of latitude wide.
         */
        y = (int)((dlat - lat) * 8);
    }
}

// Build the path name for this bucket
std::string SGBucket::gen_base_path() const {
    // long int index;
    int top_lon, top_lat, main_lon, main_lat;
    char hem, pole;
    char raw_path[256];

    top_lon = lon / 10;
    main_lon = lon;
    if ( (lon < 0) && (top_lon * 10 != lon) ) {
	top_lon -= 1;
    }
    top_lon *= 10;
    if ( top_lon >= 0 ) {
	hem = 'e';
    } else {
	hem = 'w';
	top_lon *= -1;
    }
    if ( main_lon < 0 ) {
	main_lon *= -1;
    }

    top_lat = lat / 10;
    main_lat = lat;
    if ( (lat < 0) && (top_lat * 10 != lat) ) {
	top_lat -= 1;
    }
    top_lat *= 10;
    if ( top_lat >= 0 ) {
	pole = 'n';
    } else {
	pole = 's';
	top_lat *= -1;
    }
    if ( main_lat < 0 ) {
	main_lat *= -1;
    }

    ::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
	    hem, top_lon, pole, top_lat,
	    hem, main_lon, pole, main_lat);

    SGPath path( raw_path );

    return path.str();
}

// return width of the tile in degrees
double SGBucket::get_width() const {
    return sg_bucket_span( get_center_lat() );
}

// return height of the tile in degrees
double SGBucket::get_height() const {
    return SG_BUCKET_SPAN;
}

double SGBucket::get_highest_lat() const
{
    unsigned char adjustedY = y;
    if (lat >= 0) {
        // tile is north of the equator, so we want the top edge. Add one
        // to y to achieve this.
        ++adjustedY;
    }

	return lat + (adjustedY / 8.0);
}

// return width of the tile in meters. This function is used by the
// tile-manager to estimate how many tiles are in the view distance, so
// we care about the smallest width, which occurs at the highest latitude.
double SGBucket::get_width_m() const
{
    double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS;
    double cos_lat = cos( clat_rad );
    if (fabs(cos_lat) < SG_EPSILON) {
        // happens for polar tiles, since we pass in a latitude of 90
        // return an arbitrary small value so all tiles are loaded
        return 10.0;
    }

    double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
    double local_perimeter = local_radius * SGD_2PI;
    double degree_width = local_perimeter / 360.0;

    return get_width() * degree_width;
}

// return height of the tile in meters
double SGBucket::get_height_m() const {
    double perimeter = SG_EQUATORIAL_RADIUS_M * SGD_2PI;
    double degree_height = perimeter / 360.0;

    return SG_BUCKET_SPAN * degree_height;
}

SGBucket SGBucket::sibling(int dx, int dy) const
{
    if (!isValid()) {
        SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket");
        return SGBucket();
    }

    double clat = get_center_lat() + dy * SG_BUCKET_SPAN;
    // return invalid here instead of clipping, so callers can discard
    // invalid buckets without having to check if it's an existing one
    if ((clat < -90.0) || (clat > 90.0)) {
        return SGBucket();
    }

    // find the lon span for the new latitude
    double span = sg_bucket_span( clat );

    double tmp = get_center_lon() + dx * span;
    tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp);

    SGBucket b;
    b.innerSet(tmp, clat);
    return b;
}

std::string SGBucket::gen_index_str() const
{
	char tmp[20];
	::snprintf(tmp, 20, "%ld",
                 (((long)lon + 180) << 14) + ((lat + 90) << 6)
                 + (y << 3) + x);
	return (std::string)tmp;
}

#ifndef NO_DEPRECATED_API
// find the bucket which is offset by the specified tile units in the
// X & Y direction.  We need the current lon and lat to resolve
// ambiguities when going from a wider tile to a narrower one above or
// below.  This assumes that we are feeding in
SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) {
    SGBucket result( dlon, dlat );
    double clat = result.get_center_lat() + dy * SG_BUCKET_SPAN;

    // walk dy units in the lat direction
    result.set_bucket( dlon, clat );

    // find the lon span for the new latitude
    double span = sg_bucket_span( clat );

    // walk dx units in the lon direction
    double tmp = dlon + dx * span;
    while ( tmp < -180.0 ) {
	tmp += 360.0;
    }
    while ( tmp >= 180.0 ) {
	tmp -= 360.0;
    }
    result.set_bucket( tmp, clat );

    return result;
}
#endif

// calculate the offset between two buckets
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {

    // Latitude difference
    double c1_lat = b1.get_center_lat();
    double c2_lat = b2.get_center_lat();
    double diff_lat = c2_lat - c1_lat;

#ifdef HAVE_RINT
    *dy = (int)rint( diff_lat / SG_BUCKET_SPAN );
#else
    if ( diff_lat > 0 ) {
	*dy = (int)( diff_lat / SG_BUCKET_SPAN + 0.5 );
    } else {
	*dy = (int)( diff_lat / SG_BUCKET_SPAN - 0.5 );
    }
#endif

    // longitude difference
    double diff_lon=0.0;
    double span=0.0;

    SGBucket tmp_bucket;
    // To handle crossing the bucket size boundary
    //  we need to account for different size buckets.

    if ( sg_bucket_span(c1_lat) <= sg_bucket_span(c2_lat) )
    {
	span = sg_bucket_span(c1_lat);
    } else {
	span = sg_bucket_span(c2_lat);
    }

    diff_lon = b2.get_center_lon() - b1.get_center_lon();

    if (diff_lon <0.0)
    {
       diff_lon -= b1.get_width()*0.5 + b2.get_width()*0.5 - span;
    }
    else
    {
       diff_lon += b1.get_width()*0.5 + b2.get_width()*0.5 - span;
    }

#ifdef HAVE_RINT
    *dx = (int)rint( diff_lon / span );
#else
    if ( diff_lon > 0 ) {
	*dx = (int)( diff_lon / span + 0.5 );
    } else {
	*dx = (int)( diff_lon / span - 0.5 );
    }
#endif
}

void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list ) {
    double lon, lat, span;

    for (lat = min.getLatitudeDeg(); lat <= max.getLatitudeDeg(); lat += SG_BUCKET_SPAN) {
        span = sg_bucket_span( lat );
        for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span)
        {
            SGBucket b(SGGeod::fromDeg(lon, lat));
            if (!b.isValid()) {
                continue;
            }

            list.push_back(b);
        }
    }
}

std::ostream& operator<< ( std::ostream& out, const SGBucket& b )
{
    return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
}

<|endoftext|>


<commit_before>#include "xchainer/array_body.h"

#include <cassert>
#include <cstdint>
#include <memory>

#include "xchainer/array.h"
#include "xchainer/array_node.h"
#include "xchainer/backward.h"
#include "xchainer/error.h"
#include "xchainer/graph.h"

namespace xchainer {
namespace internal {

ArrayBody::ArrayBody(Shape shape, Strides strides, Dtype dtype, Device& device, std::shared_ptr<void> data, int64_t offset)
    : shape_{std::move(shape)}, strides_{std::move(strides)}, dtype_{dtype}, device_{device}, data_{std::move(data)}, offset_{offset} {}

const std::shared_ptr<ArrayNode>& ArrayBody::AddNode(std::shared_ptr<ArrayNode> array_node) {
    AssertConsistency();
    assert(this == array_node->GetBody().get());
    if (std::any_of(nodes_.begin(), nodes_.end(), [&array_node](const std::shared_ptr<ArrayNode>& existing_node) {
            return existing_node->graph_id() == array_node->graph_id();
        })) {
        throw XchainerError{"Duplicate graph registration: '", array_node->graph_id(), "'."};
    }

    nodes_.emplace_back(std::move(array_node));
    grads_.emplace_back(std::make_unique<nonstd::optional<Array>>(nonstd::nullopt));

    AssertConsistency();
    return nodes_.back();
}

void ArrayBody::AssertConsistency() const {
#ifndef NDEBUG
    assert(nodes_.size() == grads_.size());
    for (size_t i = 0; i < nodes_.size(); ++i) {
        const std::shared_ptr<ArrayNode>& array_node = nodes_[i];
        const nonstd::optional<Array>& grad = *grads_[i];
        assert(array_node != nullptr);
        assert(this == array_node->GetBody().get());
        if (grad.has_value()) {
            assert(grad->body() != nullptr);
            assert(grad->shape() == array_node->shape());
            assert(grad->dtype() == array_node->dtype());
            assert(&grad->device() == &array_node->device());
        }
    }
#endif /* NDEBUG */
}

nonstd::optional<size_t> ArrayBody::GetNodeIndex(const GraphId& graph_id) const {
    AssertConsistency();
    for (size_t i = 0; i < nodes_.size(); ++i) {
        if (nodes_[i]->graph_id() == graph_id) {
            return i;
        }
    }
    return nonstd::nullopt;
}

void ArrayBody::SetGrad(Array grad, const GraphId& graph_id) {
    nonstd::optional<Array>* target_grad = GetGrad(graph_id);
    assert(target_grad != nullptr);
    internal::SetGrad(*target_grad, std::move(grad), shape_, dtype_, device_);
}

void ArrayBody::AccumulateGrad(Array partial_grad, const GraphId& graph_id) {
    nonstd::optional<Array>* target_grad = GetGrad(graph_id);
    assert(target_grad != nullptr);
    internal::AccumulateGrad(*target_grad, std::move(partial_grad), shape_, dtype_, device_);
}

void ArrayBody::ClearGrad(const GraphId& graph_id) {
    nonstd::optional<Array>* grad = GetGrad(graph_id);
    if (grad == nullptr) {
        throw XchainerError{"Array does not belong to the graph: '", graph_id, "'."};
    }
    grad->reset();
}

template <typename ThisPtr, typename ReturnType>
ReturnType ArrayBody::GetGradImpl(ThisPtr this_ptr, const GraphId& graph_id) {
    this_ptr->AssertConsistency();

    nonstd::optional<size_t> i = this_ptr->GetNodeIndex(graph_id);
    if (!i.has_value()) {
        return nullptr;
    }
    assert(*i < this_ptr->grads_.size());
    return this_ptr->grads_[*i].get();
}

template nonstd::optional<Array>* ArrayBody::GetGradImpl<ArrayBody*, nonstd::optional<Array>*>(ArrayBody*, const GraphId&);
template const nonstd::optional<Array>* ArrayBody::GetGradImpl<const ArrayBody*, const nonstd::optional<Array>*>(
        const ArrayBody*, const GraphId&);

}  // namespace internal
}  // namespace xchainer
<commit_msg>Remove duplicated check<commit_after>#include "xchainer/array_body.h"

#include <cassert>
#include <cstdint>
#include <memory>

#include "xchainer/array.h"
#include "xchainer/array_node.h"
#include "xchainer/backward.h"
#include "xchainer/error.h"
#include "xchainer/graph.h"

namespace xchainer {
namespace internal {

ArrayBody::ArrayBody(Shape shape, Strides strides, Dtype dtype, Device& device, std::shared_ptr<void> data, int64_t offset)
    : shape_{std::move(shape)}, strides_{std::move(strides)}, dtype_{dtype}, device_{device}, data_{std::move(data)}, offset_{offset} {}

const std::shared_ptr<ArrayNode>& ArrayBody::AddNode(std::shared_ptr<ArrayNode> array_node) {
    AssertConsistency();
    assert(this == array_node->GetBody().get());
    if (std::any_of(nodes_.begin(), nodes_.end(), [&array_node](const std::shared_ptr<ArrayNode>& existing_node) {
            return existing_node->graph_id() == array_node->graph_id();
        })) {
        throw XchainerError{"Duplicate graph registration: '", array_node->graph_id(), "'."};
    }

    nodes_.emplace_back(std::move(array_node));
    grads_.emplace_back(std::make_unique<nonstd::optional<Array>>(nonstd::nullopt));

    AssertConsistency();
    return nodes_.back();
}

void ArrayBody::AssertConsistency() const {
#ifndef NDEBUG
    assert(nodes_.size() == grads_.size());
    for (size_t i = 0; i < nodes_.size(); ++i) {
        const std::shared_ptr<ArrayNode>& array_node = nodes_[i];
        const nonstd::optional<Array>& grad = *grads_[i];
        assert(array_node != nullptr);
        assert(this == array_node->GetBody().get());
        if (grad.has_value()) {
            assert(grad->body() != nullptr);
            assert(grad->shape() == array_node->shape());
            assert(grad->dtype() == array_node->dtype());
            assert(&grad->device() == &array_node->device());
        }
    }
#endif /* NDEBUG */
}

nonstd::optional<size_t> ArrayBody::GetNodeIndex(const GraphId& graph_id) const {
    for (size_t i = 0; i < nodes_.size(); ++i) {
        if (nodes_[i]->graph_id() == graph_id) {
            return i;
        }
    }
    return nonstd::nullopt;
}

void ArrayBody::SetGrad(Array grad, const GraphId& graph_id) {
    nonstd::optional<Array>* target_grad = GetGrad(graph_id);
    assert(target_grad != nullptr);
    internal::SetGrad(*target_grad, std::move(grad), shape_, dtype_, device_);
}

void ArrayBody::AccumulateGrad(Array partial_grad, const GraphId& graph_id) {
    nonstd::optional<Array>* target_grad = GetGrad(graph_id);
    assert(target_grad != nullptr);
    internal::AccumulateGrad(*target_grad, std::move(partial_grad), shape_, dtype_, device_);
}

void ArrayBody::ClearGrad(const GraphId& graph_id) {
    nonstd::optional<Array>* grad = GetGrad(graph_id);
    if (grad == nullptr) {
        throw XchainerError{"Array does not belong to the graph: '", graph_id, "'."};
    }
    grad->reset();
}

template <typename ThisPtr, typename ReturnType>
ReturnType ArrayBody::GetGradImpl(ThisPtr this_ptr, const GraphId& graph_id) {
    this_ptr->AssertConsistency();

    nonstd::optional<size_t> i = this_ptr->GetNodeIndex(graph_id);
    if (!i.has_value()) {
        return nullptr;
    }
    assert(*i < this_ptr->grads_.size());
    return this_ptr->grads_[*i].get();
}

template nonstd::optional<Array>* ArrayBody::GetGradImpl<ArrayBody*, nonstd::optional<Array>*>(ArrayBody*, const GraphId&);
template const nonstd::optional<Array>* ArrayBody::GetGradImpl<const ArrayBody*, const nonstd::optional<Array>*>(
        const ArrayBody*, const GraphId&);

}  // namespace internal
}  // namespace xchainer
<|endoftext|>


<commit_before>/* *
* Copyright (c) 2013 Julio Gutierrez
*
* Permission is hereby granted, free of charge, to any person obtaining a co$
* this software and associated documentation files (the "Software"), to deal$
* the Software without restriction, including without limitation the rights $
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop$
* the Software, and to permit persons to whom the Software is furnished to d$
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in$
* 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, F$
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR$
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHE$
* 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 <stdio.h>
#include <raspboop/Raspboop.h>

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

	raspboop::Init(raspboop::WIRING);

}
<commit_msg>sensors: main implementation<commit_after>/* *
* Copyright (c) 2013 Julio Gutierrez
*
* Permission is hereby granted, free of charge, to any person obtaining a co$
* this software and associated documentation files (the "Software"), to deal$
* the Software without restriction, including without limitation the rights $
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop$
* the Software, and to permit persons to whom the Software is furnished to d$
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in$
* 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, F$
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR$
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHE$
* 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 <stdio.h>
#include <raspboop/Raspboop.h>

using raspboop::HCSR04;
using raspboop::HCSR501;

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

#define SIG 6
#define TRIG 4
#define ECHO 5

	raspboop::Init(raspboop::WIRING);

	HCSR04* DistanceSensor = HCSR04::Create(ECHO, TRIG);
	HCSR501* InfraredSensor = HCSR501::Create(SIG);

	while(true)
	{

		InfraredSensor->Sense();
		DistanceSensor->Sense();

		printf("Motion Detected: %d", InfraredSensor->IsSignalled());
		printf("Distance: %0.2f cm", DistanceSensor->GetDistance());

		delay(1000);
	}

	return 0;
}
<|endoftext|>


<commit_before>/* carbon_connector.cc
   Jeremy Barnes, 3 August 2011
   Copyright (c) 2011 Datacratic.  All rights reserved.

*/

#include "soa/service/carbon_connector.h"
#include "ace/INET_Addr.h"
#include "jml/arch/exception.h"
#include "jml/arch/format.h"
#include <iostream>
#include "jml/arch/cmp_xchg.h"
#include "jml/arch/atomic_ops.h"
#include "jml/arch/timers.h"
#include "jml/arch/futex.h"
#include "jml/utils/floating_point.h"
#include "jml/utils/smart_ptr_utils.h"
#include "jml/utils/exc_assert.h"
#include <boost/tuple/tuple.hpp>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <poll.h>

using namespace std;
using namespace ML;

namespace Datacratic {

/*****************************************************************************/
/* MULTI AGGREGATOR                                                          */
/*****************************************************************************/

MultiAggregator::
MultiAggregator()
    : doShutdown(false), doDump(false), dumpInterval(0.0)
{
}

MultiAggregator::
MultiAggregator(const std::string & path,
                const OutputFn & output,
                double dumpInterval,
                std::function<void ()> onStop)
    : doShutdown(false), doDump(false)
{
    open(path, output, dumpInterval, onStop);
}

MultiAggregator::
~MultiAggregator()
{
    shutdown();
}

void
MultiAggregator::
open(const std::string & path,
     const OutputFn & output,
     double dumpInterval,
     std::function<void ()> onStop)
{
    shutdown();

    doShutdown = doDump = false;
    if (dumpInterval < 1.0) {
        dumpInterval = 1.0;
    }
    this->dumpInterval = dumpInterval;
    this->onStop = onStop;

    if (path == "") prefix = "";
    else prefix = path + ".";

    if (output)
        outputFn = output;
    else
        outputFn = [&] (const std::vector<StatReading> & values)
            {
                this->doStat(values);
            };

    dumpingThread.reset
        (new std::thread(std::bind(&MultiAggregator::runDumpingThread,
                                       this)));
}

void
MultiAggregator::
stop()
{
    shutdown();
}

void
MultiAggregator::
doStat(const std::vector<StatReading> & values) const
{
    outputFn(values);
}

StatAggregator * createNewCounter()
{
    return new CounterAggregator();
}

StatAggregator * createNewStableLevel()
{
    return new GaugeAggregator(GaugeAggregator::StableLevel);
}

StatAggregator * createNewLevel()
{
    return new GaugeAggregator(GaugeAggregator::Level);
}

StatAggregator * createNewOutcome()
{
    return new GaugeAggregator(GaugeAggregator::Outcome);
}

void
MultiAggregator::
record(const std::string & stat,
       EventType type,
       float value)
{
    switch (type) {
    case ET_HIT:          recordHit(stat);                break;
    case ET_COUNT:        recordCount(stat, value);       break;
    case ET_STABLE_LEVEL: recordStableLevel(stat, value); break;
    case ET_LEVEL:        recordLevel(stat, value);       break;
    case ET_OUTCOME:      recordOutcome(stat, value);     break;
    default:
        cerr << "warning: unknown stat type" << endl;
    }
}

void
MultiAggregator::
recordHit(const std::string & stat)
{
    getAggregator(stat, createNewCounter).record(1.0);
}

void
MultiAggregator::
recordCount(const std::string & stat, float quantity)
{
    getAggregator(stat, createNewCounter).record(quantity);
}

void
MultiAggregator::
recordStableLevel(const std::string & stat, float value)
{
    getAggregator(stat, createNewStableLevel).record(value);
}

void
MultiAggregator::
recordLevel(const std::string & stat, float value)
{
    getAggregator(stat, createNewLevel).record(value);
}

void
MultiAggregator::
recordOutcome(const std::string & stat, float value)
{
    getAggregator(stat, createNewOutcome).record(value);
}

void
MultiAggregator::
dump()
{
    {
        std::lock_guard<std::mutex> lock(m);
        doDump = true;
    }

    cond.notify_all();
}

void
MultiAggregator::
dumpSync(std::ostream & stream) const
{
    std::unique_lock<Lock> guard(this->lock);

    for (auto & s: stats) {
        auto vals = s.second->read(s.first);
        for (auto v: vals) {
            stream << v.name << ":\t" << v.value << endl;
        }
    }
}

void
MultiAggregator::
shutdown()
{
    if (dumpingThread) {
        if (onPreShutdown) onPreShutdown();

        {
            std::lock_guard<std::mutex> lock(m);
            doShutdown = true;
        }

        cond.notify_all();

        dumpingThread->join();
        dumpingThread.reset();

        if (onPostShutdown) onPostShutdown();

        if (onStop) onStop();
    }
}

StatAggregator &
MultiAggregator::
getAggregator(const std::string & stat,
              StatAggregator * (*createFn) ())
{
    if (!lookupCache.get())
        lookupCache.reset(new LookupCache());

    auto found = lookupCache->find(stat);
    if (found != lookupCache->end())
        return *found->second->second;

    // Get the read lock to look for the aggregator
    std::unique_lock<Lock> guard(lock);

    auto found2 = stats.find(stat);

    if (found2 != stats.end()) {
        guard.unlock();

        (*lookupCache)[stat] = found2;

        return *found2->second;
    }

    guard.unlock();

    // Get the write lock to add it to the aggregator
    std::unique_lock<Lock> guard2(lock);

    // Add it in
    found2 = stats.insert(make_pair(stat, std::shared_ptr<StatAggregator>(createFn()))).first;

    guard2.unlock();
    (*lookupCache)[stat] = found2;
    return *found2->second;
}

void
MultiAggregator::
runDumpingThread()
{

    size_t current = 0;

    for (;;) {
        std::unique_lock<std::mutex> lock(m);

        Date now = Date::now();
        Date nextWakeup = now.plusSeconds(1.0);
        if (cond.wait_until(lock, nextWakeup.toStd(), [&] { return doShutdown.load(); }))
            break;

        // Get the read lock to extract a list of stats to dump
        vector<Stats::iterator> toDump;
        {
            std::unique_lock<Lock> guard(this->lock);
            toDump.reserve(stats.size());
            for (auto it = stats.begin(), end = stats.end(); it != end;  ++it)
                toDump.push_back(it);
        }

        ++current;
        bool dumpNow = doDump.exchange(false) ||
                       (current % static_cast<size_t>(dumpInterval)) == 0;

        // Now dump them without the lock held. Note that we still need to call
        // read every second even if we're not flushing to carbon.
        for (auto it = toDump.begin(), end = toDump.end(); it != end;  ++it) {

            try {
                auto stat = (*it)->second->read((*it)->first);
                if (dumpNow) doStat(std::move(stat));
            } catch (const std::exception & exc) {
                cerr << "error writing stat: " << exc.what() << endl;
            }
        }
    }
}

/*****************************************************************************/
/* CARBON CONNECTOR                                                        */
/*****************************************************************************/

CarbonConnector::
CarbonConnector()
{
}

CarbonConnector::
CarbonConnector(const std::string & carbonAddr,
                const std::string & path,
                double dumpInterval,
                std::function<void ()> onStop)
{
    open(carbonAddr, path, dumpInterval, onStop);
}

CarbonConnector::
CarbonConnector(const std::vector<std::string> & carbonAddrs,
                const std::string & path,
                double dumpInterval,
                std::function<void ()> onStop)
{
    open(carbonAddrs, path,dumpInterval,  onStop);
}

CarbonConnector::
~CarbonConnector()
{
    doShutdown();
}

void
CarbonConnector::
open(const std::string & carbonAddr,
     const std::string & path,
     double dumpInterval,
     std::function<void ()> onStop)
{
    return open(vector<string>({carbonAddr}), path, dumpInterval, onStop);
}

void
CarbonConnector::
open(const std::vector<std::string> & carbonAddrs,
     const std::string & path,
     double dumpInterval,
     std::function<void ()> onStop)
{
    stop();

    int numConnections = 0;

    connections.clear();
    for (unsigned i = 0;  i < carbonAddrs.size();  ++i) {
        connections.push_back(std::make_shared<Connection>
                                    (carbonAddrs[i]));
        string error = connections.back()->connect();
        if (connections.back()->fd == -1) {
            cerr << "error connecting to Carbon at " << carbonAddrs[i]
                 << ": " << error << endl;
        }
        else ++numConnections;
    }

    if (numConnections == 0)
        throw ML::Exception("unable to connect to any Carbon instances");

    this->onPostShutdown = std::bind(&CarbonConnector::doShutdown, this);

    MultiAggregator::open(path, OutputFn(), dumpInterval, onStop);
}

void
CarbonConnector::
doShutdown()
{
    stop();
    connections.clear();
}

void
CarbonConnector::
doStat(const std::vector<StatReading> & values) const
{
    if (connections.empty())
        return;

    std::string message;

    for (unsigned i = 0;  i < values.size();  ++i) {
        message += ML::format("%s%s %.5f %lld\n",
                              prefix.c_str(), values[i].name.c_str(),
                              values[i].value,
                              (unsigned long long)
                              values[i].timestamp.secondsSinceEpoch());
    }

    for (unsigned i = 0;  i < connections.size();  ++i)
        connections[i]->send(message);
}

CarbonConnector::Connection::
~Connection()
{
    close();

    if (reconnectionThread) {
        shutdown = 1;
        futex_wake(shutdown);
        reconnectionThread->join();
        reconnectionThread.reset();
    }
}

void
CarbonConnector::Connection::
close()
{
    if (fd != -1)
        ::close(fd);
    fd = -1;
}

std::string
CarbonConnector::Connection::
connect()
{
    if (fd != -1)
        throw ML::Exception("error connecting");

    ip = ACE_INET_Addr(addr.c_str());

    cerr << "connecting to Carbon at "
         << ip.get_host_addr() << ":" << ip.get_port_number()
         << " (" << ip.get_host_name() << ")" << endl;

    int tmpFd = socket(AF_INET, SOCK_STREAM, 0);
    int res = ::connect(tmpFd,
                        (sockaddr *)ip.get_addr(),
                        ip.get_addr_size());

    int saved_errno = errno;

    if (res == -1) {
        ::close(tmpFd);
        return ML::format("connect to carbon at %s:%d (%s): %s",
                          ip.get_host_addr(),
                          ip.get_port_number(),
                          ip.get_host_name(),
                          strerror(saved_errno));
    }

    fd = tmpFd;

    return "";
}

void
CarbonConnector::Connection::
send(const std::string & message)
{
    //cerr << "STAT: " << message << endl;
    //return;
    if (message.empty())
        return;

    //cerr << "sending to " << addr << " on " << fd << " " << message << endl;

    if (fd == -1) {
        if (reconnectionThreadActive) return;
        throw ML::Exception("send with fd -1 and no thread active");
    }

    size_t done = 0;

    for (;;) {
        int sendRes = ::send(fd, message.c_str() + done,
                             message.size() - done,
                             MSG_DONTWAIT | MSG_NOSIGNAL);

        if (sendRes > 0) {
            done += sendRes;
            if (done == message.size())
                return;  // done; normal case
            else if (done > message.size())
                throw ML::Exception("logic error sending message to Carbon");
            else continue;  // do the rest of the message
        }

        if (sendRes != -1)
            throw ML::Exception("invalid return code from send");

        // Error handling
        if (errno == EINTR)
            continue;  // retry
        else if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // Would block (something that we don't want).  Select on the
            // socket for the timeout before giving up.
            struct pollfd events[] = {
                { fd, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0 }
            };

            int res = poll(events, 1, 500 /* 500ms max timeout */);
            if (res == 1 && events[0].revents == POLLOUT) {
                // Ready to send
                continue;  // we can now send it
            }
            else if (res == -1) {
                // error in epoll call
                int saved_errno = errno;
                cerr << "error on epoll with CarbonConnector " << addr
                     << ": " << strerror(saved_errno) << endl;
            }
            else if (res == 0) {
                // nothing ready... must be a timeout
                cerr << "timeout sending to CarbonConnector at " << addr
                     << endl;
            }
            else if (res == 1 && events[0].revents & ~POLLOUT) {
                // Disconnection or error... need to reconnect
                cerr << "disconnection sending to CarbonConnector at " << addr
                     << endl;
            }
            else {
                // Logic error; we should never get here
                throw ML::Exception("logic error in carbon connector");
            }
        } else {
            // Error in sending
            int saved_errno = errno;
            cerr << "error sending to CarbonConnector " << addr
                 << ": " << strerror(saved_errno) << endl;
        }
        break;
    }

    reconnect();
}

void
CarbonConnector::Connection::
reconnect()
{
    close();

    cerr << "reconnecting to " << addr << endl;

    if (reconnectionThreadJoinable && reconnectionThread) {
        reconnectionThread->join();
        reconnectionThread.reset();
    }

    reconnectionThreadActive = false;
    reconnectionThreadJoinable = false;

    reconnectionThread
        = std::make_shared<std::thread>
            (std::bind(&Connection::runReconnectionThread, this));

}

void
CarbonConnector::Connection::
runReconnectionThread()
{
    cerr << "started reconnection thread" << endl;

    reconnectionThreadActive = true;

    // Close the current connection
    if (fd != -1)
        close();

    double meanWaitTime = 0.5;  // half a second

    while (!shutdown) {
        string error = connect();
        if (fd != -1) break;

        cerr << "error reconnecting to " << addr << ": " << error
             << endl;

        double r = (random() % 10001) / 10000.0;
        double waitTime = meanWaitTime * (0.5 + r);

        if (meanWaitTime < 8.0)
            meanWaitTime *= 2;

        // Wait for the given time before we attempt reconnection
        // again.
        futex_wait(shutdown, 0, waitTime);
    }

    reconnectionThreadActive = false;
    reconnectionThreadJoinable = true;
}

} // namespace Datacratic
<commit_msg>Fixed drift and timestamps in periodic carbon dumps.<commit_after>/* carbon_connector.cc
   Jeremy Barnes, 3 August 2011
   Copyright (c) 2011 Datacratic.  All rights reserved.

*/

#include "soa/service/carbon_connector.h"
#include "ace/INET_Addr.h"
#include "jml/arch/exception.h"
#include "jml/arch/format.h"
#include <iostream>
#include "jml/arch/cmp_xchg.h"
#include "jml/arch/atomic_ops.h"
#include "jml/arch/timers.h"
#include "jml/arch/futex.h"
#include "jml/utils/floating_point.h"
#include "jml/utils/smart_ptr_utils.h"
#include "jml/utils/exc_assert.h"
#include <boost/tuple/tuple.hpp>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <poll.h>

using namespace std;
using namespace ML;

namespace Datacratic {

/*****************************************************************************/
/* MULTI AGGREGATOR                                                          */
/*****************************************************************************/

MultiAggregator::
MultiAggregator()
    : doShutdown(false), doDump(false), dumpInterval(0.0)
{
}

MultiAggregator::
MultiAggregator(const std::string & path,
                const OutputFn & output,
                double dumpInterval,
                std::function<void ()> onStop)
    : doShutdown(false), doDump(false)
{
    open(path, output, dumpInterval, onStop);
}

MultiAggregator::
~MultiAggregator()
{
    shutdown();
}

void
MultiAggregator::
open(const std::string & path,
     const OutputFn & output,
     double dumpInterval,
     std::function<void ()> onStop)
{
    shutdown();

    doShutdown = doDump = false;
    if (dumpInterval < 1.0) {
        dumpInterval = 1.0;
    }
    this->dumpInterval = dumpInterval;
    this->onStop = onStop;

    if (path == "") prefix = "";
    else prefix = path + ".";

    if (output)
        outputFn = output;
    else
        outputFn = [&] (const std::vector<StatReading> & values)
            {
                this->doStat(values);
            };

    dumpingThread.reset
        (new std::thread(std::bind(&MultiAggregator::runDumpingThread,
                                       this)));
}

void
MultiAggregator::
stop()
{
    shutdown();
}

void
MultiAggregator::
doStat(const std::vector<StatReading> & values) const
{
    outputFn(values);
}

StatAggregator * createNewCounter()
{
    return new CounterAggregator();
}

StatAggregator * createNewStableLevel()
{
    return new GaugeAggregator(GaugeAggregator::StableLevel);
}

StatAggregator * createNewLevel()
{
    return new GaugeAggregator(GaugeAggregator::Level);
}

StatAggregator * createNewOutcome()
{
    return new GaugeAggregator(GaugeAggregator::Outcome);
}

void
MultiAggregator::
record(const std::string & stat,
       EventType type,
       float value)
{
    switch (type) {
    case ET_HIT:          recordHit(stat);                break;
    case ET_COUNT:        recordCount(stat, value);       break;
    case ET_STABLE_LEVEL: recordStableLevel(stat, value); break;
    case ET_LEVEL:        recordLevel(stat, value);       break;
    case ET_OUTCOME:      recordOutcome(stat, value);     break;
    default:
        cerr << "warning: unknown stat type" << endl;
    }
}

void
MultiAggregator::
recordHit(const std::string & stat)
{
    getAggregator(stat, createNewCounter).record(1.0);
}

void
MultiAggregator::
recordCount(const std::string & stat, float quantity)
{
    getAggregator(stat, createNewCounter).record(quantity);
}

void
MultiAggregator::
recordStableLevel(const std::string & stat, float value)
{
    getAggregator(stat, createNewStableLevel).record(value);
}

void
MultiAggregator::
recordLevel(const std::string & stat, float value)
{
    getAggregator(stat, createNewLevel).record(value);
}

void
MultiAggregator::
recordOutcome(const std::string & stat, float value)
{
    getAggregator(stat, createNewOutcome).record(value);
}

void
MultiAggregator::
dump()
{
    {
        std::lock_guard<std::mutex> lock(m);
        doDump = true;
    }

    cond.notify_all();
}

void
MultiAggregator::
dumpSync(std::ostream & stream) const
{
    std::unique_lock<Lock> guard(this->lock);

    for (auto & s: stats) {
        auto vals = s.second->read(s.first);
        for (auto v: vals) {
            stream << v.name << ":\t" << v.value << endl;
        }
    }
}

void
MultiAggregator::
shutdown()
{
    if (dumpingThread) {
        if (onPreShutdown) onPreShutdown();

        {
            std::lock_guard<std::mutex> lock(m);
            doShutdown = true;
        }

        cond.notify_all();

        dumpingThread->join();
        dumpingThread.reset();

        if (onPostShutdown) onPostShutdown();

        if (onStop) onStop();
    }
}

StatAggregator &
MultiAggregator::
getAggregator(const std::string & stat,
              StatAggregator * (*createFn) ())
{
    if (!lookupCache.get())
        lookupCache.reset(new LookupCache());

    auto found = lookupCache->find(stat);
    if (found != lookupCache->end())
        return *found->second->second;

    // Get the read lock to look for the aggregator
    std::unique_lock<Lock> guard(lock);

    auto found2 = stats.find(stat);

    if (found2 != stats.end()) {
        guard.unlock();

        (*lookupCache)[stat] = found2;

        return *found2->second;
    }

    guard.unlock();

    // Get the write lock to add it to the aggregator
    std::unique_lock<Lock> guard2(lock);

    // Add it in
    found2 = stats.insert(make_pair(stat, std::shared_ptr<StatAggregator>(createFn()))).first;

    guard2.unlock();
    (*lookupCache)[stat] = found2;
    return *found2->second;
}

void
MultiAggregator::
runDumpingThread()
{
    size_t current = 0;
    Date nextWakeup = Date::now();

    for (;;) {
        std::unique_lock<std::mutex> lock(m);

        nextWakeup.addSeconds(1.0);
        if (cond.wait_until(lock, nextWakeup.toStd(), [&] { return doShutdown.load(); }))
            break;

        // Get the read lock to extract a list of stats to dump
        vector<Stats::iterator> toDump;
        {
            std::unique_lock<Lock> guard(this->lock);
            toDump.reserve(stats.size());
            for (auto it = stats.begin(), end = stats.end(); it != end;  ++it)
                toDump.push_back(it);
        }

        ++current;
        bool dumpNow = doDump.exchange(false) ||
                       (current % static_cast<size_t>(dumpInterval)) == 0;

        // Now dump them without the lock held. Note that we still need to call
        // read every second even if we're not flushing to carbon.
        for (auto it = toDump.begin(), end = toDump.end(); it != end;  ++it) {

            try {
                auto stat = (*it)->second->read((*it)->first);

                // Hack: ensures that all timestamps are consistent and that we
                // will not have any gaps within carbon.
                for (auto& s : stat) s.timestamp = nextWakeup;

                if (dumpNow) doStat(std::move(stat));
            } catch (const std::exception & exc) {
                cerr << "error writing stat: " << exc.what() << endl;
            }
        }
    }
}

/*****************************************************************************/
/* CARBON CONNECTOR                                                        */
/*****************************************************************************/

CarbonConnector::
CarbonConnector()
{
}

CarbonConnector::
CarbonConnector(const std::string & carbonAddr,
                const std::string & path,
                double dumpInterval,
                std::function<void ()> onStop)
{
    open(carbonAddr, path, dumpInterval, onStop);
}

CarbonConnector::
CarbonConnector(const std::vector<std::string> & carbonAddrs,
                const std::string & path,
                double dumpInterval,
                std::function<void ()> onStop)
{
    open(carbonAddrs, path,dumpInterval,  onStop);
}

CarbonConnector::
~CarbonConnector()
{
    doShutdown();
}

void
CarbonConnector::
open(const std::string & carbonAddr,
     const std::string & path,
     double dumpInterval,
     std::function<void ()> onStop)
{
    return open(vector<string>({carbonAddr}), path, dumpInterval, onStop);
}

void
CarbonConnector::
open(const std::vector<std::string> & carbonAddrs,
     const std::string & path,
     double dumpInterval,
     std::function<void ()> onStop)
{
    stop();

    int numConnections = 0;

    connections.clear();
    for (unsigned i = 0;  i < carbonAddrs.size();  ++i) {
        connections.push_back(std::make_shared<Connection>
                                    (carbonAddrs[i]));
        string error = connections.back()->connect();
        if (connections.back()->fd == -1) {
            cerr << "error connecting to Carbon at " << carbonAddrs[i]
                 << ": " << error << endl;
        }
        else ++numConnections;
    }

    if (numConnections == 0)
        throw ML::Exception("unable to connect to any Carbon instances");

    this->onPostShutdown = std::bind(&CarbonConnector::doShutdown, this);

    MultiAggregator::open(path, OutputFn(), dumpInterval, onStop);
}

void
CarbonConnector::
doShutdown()
{
    stop();
    connections.clear();
}

void
CarbonConnector::
doStat(const std::vector<StatReading> & values) const
{
    if (connections.empty())
        return;

    std::string message;

    for (unsigned i = 0;  i < values.size();  ++i) {
        message += ML::format("%s%s %.5f %lld\n",
                              prefix.c_str(), values[i].name.c_str(),
                              values[i].value,
                              (unsigned long long)
                              values[i].timestamp.secondsSinceEpoch());
    }

    for (unsigned i = 0;  i < connections.size();  ++i)
        connections[i]->send(message);
}

CarbonConnector::Connection::
~Connection()
{
    close();

    if (reconnectionThread) {
        shutdown = 1;
        futex_wake(shutdown);
        reconnectionThread->join();
        reconnectionThread.reset();
    }
}

void
CarbonConnector::Connection::
close()
{
    if (fd != -1)
        ::close(fd);
    fd = -1;
}

std::string
CarbonConnector::Connection::
connect()
{
    if (fd != -1)
        throw ML::Exception("error connecting");

    ip = ACE_INET_Addr(addr.c_str());

    cerr << "connecting to Carbon at "
         << ip.get_host_addr() << ":" << ip.get_port_number()
         << " (" << ip.get_host_name() << ")" << endl;

    int tmpFd = socket(AF_INET, SOCK_STREAM, 0);
    int res = ::connect(tmpFd,
                        (sockaddr *)ip.get_addr(),
                        ip.get_addr_size());

    int saved_errno = errno;

    if (res == -1) {
        ::close(tmpFd);
        return ML::format("connect to carbon at %s:%d (%s): %s",
                          ip.get_host_addr(),
                          ip.get_port_number(),
                          ip.get_host_name(),
                          strerror(saved_errno));
    }

    fd = tmpFd;

    return "";
}

void
CarbonConnector::Connection::
send(const std::string & message)
{
    //cerr << "STAT: " << message << endl;
    //return;
    if (message.empty())
        return;

    //cerr << "sending to " << addr << " on " << fd << " " << message << endl;

    if (fd == -1) {
        if (reconnectionThreadActive) return;
        throw ML::Exception("send with fd -1 and no thread active");
    }

    size_t done = 0;

    for (;;) {
        int sendRes = ::send(fd, message.c_str() + done,
                             message.size() - done,
                             MSG_DONTWAIT | MSG_NOSIGNAL);

        if (sendRes > 0) {
            done += sendRes;
            if (done == message.size())
                return;  // done; normal case
            else if (done > message.size())
                throw ML::Exception("logic error sending message to Carbon");
            else continue;  // do the rest of the message
        }

        if (sendRes != -1)
            throw ML::Exception("invalid return code from send");

        // Error handling
        if (errno == EINTR)
            continue;  // retry
        else if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // Would block (something that we don't want).  Select on the
            // socket for the timeout before giving up.
            struct pollfd events[] = {
                { fd, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0 }
            };

            int res = poll(events, 1, 500 /* 500ms max timeout */);
            if (res == 1 && events[0].revents == POLLOUT) {
                // Ready to send
                continue;  // we can now send it
            }
            else if (res == -1) {
                // error in epoll call
                int saved_errno = errno;
                cerr << "error on epoll with CarbonConnector " << addr
                     << ": " << strerror(saved_errno) << endl;
            }
            else if (res == 0) {
                // nothing ready... must be a timeout
                cerr << "timeout sending to CarbonConnector at " << addr
                     << endl;
            }
            else if (res == 1 && events[0].revents & ~POLLOUT) {
                // Disconnection or error... need to reconnect
                cerr << "disconnection sending to CarbonConnector at " << addr
                     << endl;
            }
            else {
                // Logic error; we should never get here
                throw ML::Exception("logic error in carbon connector");
            }
        } else {
            // Error in sending
            int saved_errno = errno;
            cerr << "error sending to CarbonConnector " << addr
                 << ": " << strerror(saved_errno) << endl;
        }
        break;
    }

    reconnect();
}

void
CarbonConnector::Connection::
reconnect()
{
    close();

    cerr << "reconnecting to " << addr << endl;

    if (reconnectionThreadJoinable && reconnectionThread) {
        reconnectionThread->join();
        reconnectionThread.reset();
    }

    reconnectionThreadActive = false;
    reconnectionThreadJoinable = false;

    reconnectionThread
        = std::make_shared<std::thread>
            (std::bind(&Connection::runReconnectionThread, this));

}

void
CarbonConnector::Connection::
runReconnectionThread()
{
    cerr << "started reconnection thread" << endl;

    reconnectionThreadActive = true;

    // Close the current connection
    if (fd != -1)
        close();

    double meanWaitTime = 0.5;  // half a second

    while (!shutdown) {
        string error = connect();
        if (fd != -1) break;

        cerr << "error reconnecting to " << addr << ": " << error
             << endl;

        double r = (random() % 10001) / 10000.0;
        double waitTime = meanWaitTime * (0.5 + r);

        if (meanWaitTime < 8.0)
            meanWaitTime *= 2;

        // Wait for the given time before we attempt reconnection
        // again.
        futex_wait(shutdown, 0, waitTime);
    }

    reconnectionThreadActive = false;
    reconnectionThreadJoinable = true;
}

} // namespace Datacratic
<|endoftext|>


<commit_before>#include "include/buffer.h"
#include "include/encoding.h"

#include "gtest/gtest.h"
#include "stdlib.h"

#define MAX_TEST 1000000

TEST(BufferList, EmptyAppend) {
  bufferlist bl;
  bufferptr ptr;
  bl.push_back(ptr);
  ASSERT_EQ(bl.begin().end(), 1);
}

TEST(BufferList, TestPtrAppend) {
  bufferlist bl;
  char correct[MAX_TEST];
  int curpos = 0;
  int length = random() % 5 > 0 ? random() % 1000 : 0;
  while (curpos + length < MAX_TEST) {
    if (!length) {
      bufferptr ptr;
      bl.push_back(ptr);
    } else {
      char *current = correct + curpos;
      for (int i = 0; i < length; ++i) {
        char next = random() % 255;
        correct[curpos++] = next;
      }
      bufferptr ptr(current, length);
      bl.append(ptr);
    }
    length = random() % 5 > 0 ? random() % 1000 : 0;
  }
  ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
}

TEST(BufferList, TestDirectAppend) {
  bufferlist bl;
  char correct[MAX_TEST];
  int curpos = 0;
  int length = random() % 5 > 0 ? random() % 1000 : 0;
  while (curpos + length < MAX_TEST) {
    char *current = correct + curpos;
    for (int i = 0; i < length; ++i) {
      char next = random() % 255;
      correct[curpos++] = next;
    }
    bl.append(current, length);
    length = random() % 5 > 0 ? random() % 1000 : 0;
  }
  ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
}
<commit_msg>test/bufferlist: add copy_all test<commit_after>#include "include/buffer.h"
#include "include/encoding.h"

#include "gtest/gtest.h"
#include "stdlib.h"

#define MAX_TEST 1000000

TEST(BufferList, EmptyAppend) {
  bufferlist bl;
  bufferptr ptr;
  bl.push_back(ptr);
  ASSERT_EQ(bl.begin().end(), 1);
}

TEST(BufferList, TestPtrAppend) {
  bufferlist bl;
  char correct[MAX_TEST];
  int curpos = 0;
  int length = random() % 5 > 0 ? random() % 1000 : 0;
  while (curpos + length < MAX_TEST) {
    if (!length) {
      bufferptr ptr;
      bl.push_back(ptr);
    } else {
      char *current = correct + curpos;
      for (int i = 0; i < length; ++i) {
        char next = random() % 255;
        correct[curpos++] = next;
      }
      bufferptr ptr(current, length);
      bl.append(ptr);
    }
    length = random() % 5 > 0 ? random() % 1000 : 0;
  }
  ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
}

TEST(BufferList, TestDirectAppend) {
  bufferlist bl;
  char correct[MAX_TEST];
  int curpos = 0;
  int length = random() % 5 > 0 ? random() % 1000 : 0;
  while (curpos + length < MAX_TEST) {
    char *current = correct + curpos;
    for (int i = 0; i < length; ++i) {
      char next = random() % 255;
      correct[curpos++] = next;
    }
    bl.append(current, length);
    length = random() % 5 > 0 ? random() % 1000 : 0;
  }
  ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
}

TEST(BufferList, TestCopyAll) {
  const static size_t BIG_SZ = 10737414;
  unsigned char big[BIG_SZ];
  unsigned char c = 0;
  for (size_t i = 0; i < BIG_SZ; ++i) {
    big[i] = c++;
  }
  bufferlist bl;
  bl.append((const char*)big, BIG_SZ);
  bufferlist::iterator i = bl.begin();
  bufferlist bl2;
  i.copy_all(bl2);
  ASSERT_EQ(bl2.length(), BIG_SZ);
  unsigned char big2[BIG_SZ];
  bl2.copy(0, BIG_SZ, (char*)big2);
  ASSERT_EQ(memcmp(big, big2, BIG_SZ), 0);
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "addrman.h"
#include "chainparams.h"
#include "hash.h"
#include "net.h"
#include "netbase.h"
#include "serialize.h"
#include "streams.h"

#include <cstdint>

#include "test/test_pivx.h"

#include <string>

#include <boost/test/unit_test.hpp>

class CAddrManSerializationMock : public CAddrMan
{
public:
    virtual void Serialize(CDataStream::CBaseDataStream& s) const = 0;

    //! Ensure that bucket placement is always the same for testing purposes.
    void MakeDeterministic()
    {
        nKey.SetNull();
        SeedInsecureRand(true);
    }
};

class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream::CBaseDataStream& s) const
    {
        CAddrMan::Serialize(s);
    }
};

class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream::CBaseDataStream& s) const
    {
        // Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
        unsigned char nVersion = 1;
        s << nVersion;
        s << ((unsigned char)32);
        s << nKey;
        s << 10; // nNew
        s << 10; // nTried

        int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
        s << nUBuckets;

        CService serv;
        Lookup("252.1.1.1", serv, 7777, false);
        CAddress addr = CAddress(serv, NODE_NONE);
        CNetAddr resolved;
        LookupHost("252.2.2.2", resolved, false);
        CAddrInfo info = CAddrInfo(addr, resolved);
        s << info;
    }
};

CDataStream AddrmanToStream(CAddrManSerializationMock& addrman)
{
    CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
    ssPeersIn << Params().MessageStart();
    ssPeersIn << addrman;
    std::string str = ssPeersIn.str();
    std::vector<unsigned char> vchData(str.begin(), str.end());
    return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}

BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(cnode_listen_port)
{
    // test default
    uint16_t port = GetListenPort();
    BOOST_CHECK(port == Params().GetDefaultPort());
    // test set port
    uint16_t altPort = 12345;
    BOOST_CHECK(gArgs.SoftSetArg("-port", std::to_string(altPort)));
    port = GetListenPort();
    BOOST_CHECK(port == altPort);
}

BOOST_AUTO_TEST_CASE(caddrdb_read)
{
    CAddrManUncorrupted addrmanUncorrupted;
    addrmanUncorrupted.MakeDeterministic();

    CService addr1, addr2, addr3;
    BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
    BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
    BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));
    BOOST_CHECK(Lookup(std::string("250.7.3.3", 9), addr3, 9999, false));
    BOOST_CHECK(!Lookup(std::string("250.7.3.3\0example.com", 21), addr3, 9999, false));

    // Add three addresses to new table.
    CService source;
    Lookup("252.5.1.1", source, 8333, false);
    addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source);
    addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source);
    addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source);

    // Test that the de-serialization does not throw an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;

    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }

    BOOST_CHECK(addrman1.size() == 3);
    BOOST_CHECK(exceptionThrown == false);

    // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
    CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    adb.Read(addrman2, ssPeers2);
    BOOST_CHECK(addrman2.size() == 3);
}

BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
    CAddrManCorrupted addrmanCorrupted;
    addrmanCorrupted.MakeDeterministic();

    // Test that the de-serialization of corrupted addrman throws an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;
    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }
    // Even through de-serialization failed addrman is not left in a clean state.
    BOOST_CHECK(addrman1.size() == 1);
    BOOST_CHECK(exceptionThrown);

    // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
    CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    adb.Read(addrman2, ssPeers2);
    BOOST_CHECK(addrman2.size() == 0);
}

BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
    SOCKET hSocket = INVALID_SOCKET;
    NodeId id = 0;
    int height = 0;

    in_addr ipv4Addr;
    ipv4Addr.s_addr = 0xa0b0c001;

    CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
    std::string pszDest = "";
    bool fInboundIn = false;

    // Test that fFeeler is false by default.
    std::unique_ptr<CNode> pnode1(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, pszDest, fInboundIn));
    BOOST_CHECK(pnode1->fInbound == false);
    BOOST_CHECK(pnode1->fFeeler == false);

    fInboundIn = true;
    std::unique_ptr<CNode> pnode2(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, pszDest, fInboundIn));
    BOOST_CHECK(pnode2->fInbound == true);
    BOOST_CHECK(pnode2->fFeeler == false);
}

// prior to PR #14728, this test triggers an undefined behavior
BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
{
    // set up local addresses; all that's necessary to reproduce the bug is
    // that a normal IPv4 address is among the entries, but if this address is
    // !IsRoutable the undefined behavior is easier to trigger deterministically
    {
        LOCK(cs_mapLocalHost);
        in_addr ipv4AddrLocal;
        ipv4AddrLocal.s_addr = 0x0100007f;
        CNetAddr addr = CNetAddr(ipv4AddrLocal);
        LocalServiceInfo lsi;
        lsi.nScore = 23;
        lsi.nPort = 42;
        mapLocalHost[addr] = lsi;
    }

    // create a peer with an IPv4 address
    in_addr ipv4AddrPeer;
    ipv4AddrPeer.s_addr = 0xa0b0c001;
    CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
    std::unique_ptr<CNode> pnode = std::make_unique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, std::string{}, false);
    pnode->fSuccessfullyConnected.store(true);

    // the peer claims to be reaching us via IPv6
    in6_addr ipv6AddrLocal;
    memset(ipv6AddrLocal.s6_addr, 0, 16);
    ipv6AddrLocal.s6_addr[0] = 0xcc;
    CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
    pnode->SetAddrLocal(addrLocal);

    // before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
    AdvertiseLocal(&*pnode);

    // suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
    BOOST_CHECK(1);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);

    SetReachable(NET_IPV4, false);
    SetReachable(NET_IPV6, false);
    SetReachable(NET_ONION, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false);

    SetReachable(NET_IPV4, true);
    SetReachable(NET_IPV6, true);
    SetReachable(NET_ONION, true);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);

    SetReachable(NET_UNROUTABLE, false);
    SetReachable(NET_INTERNAL, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); // Ignored for both networks
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);
}

CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4)
{
    unsigned char ip[] = {p1, p2, p3, p4};

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sockaddr_in)); // initialize the memory block
    memcpy(&(sa.sin_addr), &ip, sizeof(ip));
    return CNetAddr(sa.sin_addr);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr)
{
    CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1

    SetReachable(NET_IPV4, true);
    BOOST_CHECK_EQUAL(IsReachable(addr), true);

    SetReachable(NET_IPV4, false);
    BOOST_CHECK_EQUAL(IsReachable(addr), false);

    SetReachable(NET_IPV4, true); // have to reset this, because this is stateful.
}

BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
{
    CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000

    SetReachable(NET_IPV4, true);

    BOOST_CHECK_EQUAL(IsLocal(addr), false);
    BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true);
    BOOST_CHECK_EQUAL(IsLocal(addr), true);

    RemoveLocal(addr);
    BOOST_CHECK_EQUAL(IsLocal(addr), false);
}

BOOST_AUTO_TEST_CASE(PoissonNextSend)
{
    g_mock_deterministic_tests = true;

    int64_t now = 5000;
    int average_interval_seconds = 600;

    auto poisson = ::PoissonNextSend(now, average_interval_seconds);
    std::chrono::microseconds poisson_chrono = ::PoissonNextSend(std::chrono::microseconds{now}, std::chrono::seconds{average_interval_seconds});

    BOOST_CHECK_EQUAL(poisson, poisson_chrono.count());

    g_mock_deterministic_tests = false;
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>test: Do not instantiate CAddrDB for static call<commit_after>// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "addrman.h"
#include "chainparams.h"
#include "hash.h"
#include "net.h"
#include "netbase.h"
#include "serialize.h"
#include "streams.h"

#include <cstdint>

#include "test/test_pivx.h"

#include <string>

#include <boost/test/unit_test.hpp>

class CAddrManSerializationMock : public CAddrMan
{
public:
    virtual void Serialize(CDataStream::CBaseDataStream& s) const = 0;

    //! Ensure that bucket placement is always the same for testing purposes.
    void MakeDeterministic()
    {
        nKey.SetNull();
        SeedInsecureRand(true);
    }
};

class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream::CBaseDataStream& s) const
    {
        CAddrMan::Serialize(s);
    }
};

class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream::CBaseDataStream& s) const
    {
        // Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
        unsigned char nVersion = 1;
        s << nVersion;
        s << ((unsigned char)32);
        s << nKey;
        s << 10; // nNew
        s << 10; // nTried

        int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
        s << nUBuckets;

        CService serv;
        Lookup("252.1.1.1", serv, 7777, false);
        CAddress addr = CAddress(serv, NODE_NONE);
        CNetAddr resolved;
        LookupHost("252.2.2.2", resolved, false);
        CAddrInfo info = CAddrInfo(addr, resolved);
        s << info;
    }
};

CDataStream AddrmanToStream(CAddrManSerializationMock& addrman)
{
    CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
    ssPeersIn << Params().MessageStart();
    ssPeersIn << addrman;
    std::string str = ssPeersIn.str();
    std::vector<unsigned char> vchData(str.begin(), str.end());
    return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}

BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(cnode_listen_port)
{
    // test default
    uint16_t port = GetListenPort();
    BOOST_CHECK(port == Params().GetDefaultPort());
    // test set port
    uint16_t altPort = 12345;
    BOOST_CHECK(gArgs.SoftSetArg("-port", std::to_string(altPort)));
    port = GetListenPort();
    BOOST_CHECK(port == altPort);
}

BOOST_AUTO_TEST_CASE(caddrdb_read)
{
    CAddrManUncorrupted addrmanUncorrupted;
    addrmanUncorrupted.MakeDeterministic();

    CService addr1, addr2, addr3;
    BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
    BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
    BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));
    BOOST_CHECK(Lookup(std::string("250.7.3.3", 9), addr3, 9999, false));
    BOOST_CHECK(!Lookup(std::string("250.7.3.3\0example.com", 21), addr3, 9999, false));

    // Add three addresses to new table.
    CService source;
    Lookup("252.5.1.1", source, 8333, false);
    addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source);
    addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source);
    addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source);

    // Test that the de-serialization does not throw an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;

    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }

    BOOST_CHECK(addrman1.size() == 3);
    BOOST_CHECK(exceptionThrown == false);

    // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
    CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);

    CAddrMan addrman2;
    BOOST_CHECK(addrman2.size() == 0);
    BOOST_CHECK(CAddrDB::Read(addrman2, ssPeers2));
    BOOST_CHECK(addrman2.size() == 3);
}

BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
    CAddrManCorrupted addrmanCorrupted;
    addrmanCorrupted.MakeDeterministic();

    // Test that the de-serialization of corrupted addrman throws an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;
    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }
    // Even through de-serialization failed addrman is not left in a clean state.
    BOOST_CHECK(addrman1.size() == 1);
    BOOST_CHECK(exceptionThrown);

    // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
    CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);

    CAddrMan addrman2;
    BOOST_CHECK(addrman2.size() == 0);
    BOOST_CHECK(!CAddrDB::Read(addrman2, ssPeers2));
    BOOST_CHECK(addrman2.size() == 0);
}

BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
    SOCKET hSocket = INVALID_SOCKET;
    NodeId id = 0;
    int height = 0;

    in_addr ipv4Addr;
    ipv4Addr.s_addr = 0xa0b0c001;

    CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
    std::string pszDest = "";
    bool fInboundIn = false;

    // Test that fFeeler is false by default.
    std::unique_ptr<CNode> pnode1(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, pszDest, fInboundIn));
    BOOST_CHECK(pnode1->fInbound == false);
    BOOST_CHECK(pnode1->fFeeler == false);

    fInboundIn = true;
    std::unique_ptr<CNode> pnode2(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, pszDest, fInboundIn));
    BOOST_CHECK(pnode2->fInbound == true);
    BOOST_CHECK(pnode2->fFeeler == false);
}

// prior to PR #14728, this test triggers an undefined behavior
BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
{
    // set up local addresses; all that's necessary to reproduce the bug is
    // that a normal IPv4 address is among the entries, but if this address is
    // !IsRoutable the undefined behavior is easier to trigger deterministically
    {
        LOCK(cs_mapLocalHost);
        in_addr ipv4AddrLocal;
        ipv4AddrLocal.s_addr = 0x0100007f;
        CNetAddr addr = CNetAddr(ipv4AddrLocal);
        LocalServiceInfo lsi;
        lsi.nScore = 23;
        lsi.nPort = 42;
        mapLocalHost[addr] = lsi;
    }

    // create a peer with an IPv4 address
    in_addr ipv4AddrPeer;
    ipv4AddrPeer.s_addr = 0xa0b0c001;
    CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
    std::unique_ptr<CNode> pnode = std::make_unique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, std::string{}, false);
    pnode->fSuccessfullyConnected.store(true);

    // the peer claims to be reaching us via IPv6
    in6_addr ipv6AddrLocal;
    memset(ipv6AddrLocal.s6_addr, 0, 16);
    ipv6AddrLocal.s6_addr[0] = 0xcc;
    CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
    pnode->SetAddrLocal(addrLocal);

    // before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
    AdvertiseLocal(&*pnode);

    // suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
    BOOST_CHECK(1);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);

    SetReachable(NET_IPV4, false);
    SetReachable(NET_IPV6, false);
    SetReachable(NET_ONION, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false);

    SetReachable(NET_IPV4, true);
    SetReachable(NET_IPV6, true);
    SetReachable(NET_ONION, true);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);

    SetReachable(NET_UNROUTABLE, false);
    SetReachable(NET_INTERNAL, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); // Ignored for both networks
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);
}

CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4)
{
    unsigned char ip[] = {p1, p2, p3, p4};

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sockaddr_in)); // initialize the memory block
    memcpy(&(sa.sin_addr), &ip, sizeof(ip));
    return CNetAddr(sa.sin_addr);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr)
{
    CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1

    SetReachable(NET_IPV4, true);
    BOOST_CHECK_EQUAL(IsReachable(addr), true);

    SetReachable(NET_IPV4, false);
    BOOST_CHECK_EQUAL(IsReachable(addr), false);

    SetReachable(NET_IPV4, true); // have to reset this, because this is stateful.
}

BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
{
    CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000

    SetReachable(NET_IPV4, true);

    BOOST_CHECK_EQUAL(IsLocal(addr), false);
    BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true);
    BOOST_CHECK_EQUAL(IsLocal(addr), true);

    RemoveLocal(addr);
    BOOST_CHECK_EQUAL(IsLocal(addr), false);
}

BOOST_AUTO_TEST_CASE(PoissonNextSend)
{
    g_mock_deterministic_tests = true;

    int64_t now = 5000;
    int average_interval_seconds = 600;

    auto poisson = ::PoissonNextSend(now, average_interval_seconds);
    std::chrono::microseconds poisson_chrono = ::PoissonNextSend(std::chrono::microseconds{now}, std::chrono::seconds{average_interval_seconds});

    BOOST_CHECK_EQUAL(poisson, poisson_chrono.count());

    g_mock_deterministic_tests = false;
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>