<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 - curt@flightgear.org
 *
 * 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 Library 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.
 *
 * $Id$
 **************************************************************************/

/** \file newbucket.hxx
 * A class and associated utiltity functions to manage world scenery tiling.
 */

#ifndef _NEWBUCKET_HXX
#define _NEWBUCKET_HXX

#include <simgear/compiler.h>
#include <simgear/constants.h>

#ifdef SG_HAVE_STD_INCLUDES
#  include <cmath>
#  include <cstdio> // sprintf()
#else
#  include <math.h>
#  include <stdio.h> // sprintf()
#endif

#include STL_IOSTREAM

// I don't understand ... <math.h> or <cmath> should be included
// already depending on how you defined SG_HAVE_STD_INCLUDES, but I
// can go ahead and add this -- CLO
#ifdef __MWERKS__
SG_USING_STD(sprintf);
SG_USING_STD(fabs);
#endif

#include STL_STRING

SG_USING_STD(string);
SG_USING_STD(ostream);

/**
 * standard size of a bucket in degrees (1/8 of a degree)
 */
#define SG_BUCKET_SPAN      0.125

/**
 * half of a standard SG_BUCKET_SPAN
 */
#define SG_HALF_BUCKET_SPAN ( 0.5 * SG_BUCKET_SPAN )

// return the horizontal tile span factor based on latitude
static double sg_bucket_span( double l ) {
    if ( l >= 89.0 ) {
	return 360.0;
    } else if ( l >= 88.0 ) {
	return 8.0;
    } else if ( l >= 86.0 ) {
	return 4.0;
    } else if ( l >= 83.0 ) {
	return 2.0;
    } else if ( l >= 76.0 ) {
	return 1.0;
    } else if ( l >= 62.0 ) {
	return 0.5;
    } else if ( l >= 22.0 ) {
	return 0.25;
    } else if ( l >= -22.0 ) {
	return 0.125;
    } else if ( l >= -62.0 ) {
	return 0.25;
    } else if ( l >= -76.0 ) {
	return 0.5;
    } else if ( l >= -83.0 ) {
	return 1.0;
    } else if ( l >= -86.0 ) {
	return 2.0;
    } else if ( l >= -88.0 ) {
	return 4.0;
    } else if ( l >= -89.0 ) {
	return 8.0;
    } else {
	return 360.0;
    }
}

/**
 * A class to manage world scenery tiling.
 * This class encapsulates the world tiling scheme.  It provides ways
 * to calculate a unique tile index from a lat/lon, and it can provide
 * information such as the dimensions of a given tile.
 */

class SGBucket {

private:
    double cx, cy;  // centerpoint (lon, lat) in degrees of bucket
    int lon;        // longitude index (-180 to 179)
    int lat;        // latitude index (-90 to 89)
    int x;          // x subdivision (0 to 7)
    int y;          // y subdivision (0 to 7)

public:

    /**
     * Default constructor.
     */
    SGBucket();

    /**
     * Construct a bucket given a specific location.
     * @param dlon longitude specified in degrees
     * @param dlat latitude specified in degrees
     */
    SGBucket(const double dlon, const double dlat);

    /** Construct a bucket.
     *  @param is_good if false, create an invalid bucket.  This is
     *  useful * if you are comparing cur_bucket to last_bucket and
     *  you want to * make sure last_bucket starts out as something
     *  impossible.
     */
    SGBucket(const bool is_good);

    /** Construct a bucket given a unique bucket index number.
     * @param bindex unique bucket index
     */
    SGBucket(const long int bindex);

    /**
     * Default destructor.
     */
    ~SGBucket();

    /**
     * Reset a bucket to represent a new lat and lon
     * @param dlon longitude specified in degrees
     * @param dlat latitude specified in degrees
     */
    void set_bucket( double dlon, double dlat );

    /**
     * Reset a bucket to represent a new lat and lon
     * @param lonlat an array of double[2] holding lon and lat
     * (specified) in degrees
     */
    void set_bucket( double *lonlat );

    /**
     * Create an impossible bucket.
     * This is useful if you are comparing cur_bucket to last_bucket
     * and you want to make sure last_bucket starts out as something
     * impossible.
     */
    inline void make_bad() {
	set_bucket(0.0, 0.0);
	lon = -1000;
    }

    /**
     * Generate the unique scenery tile index for this bucket
     *
     * The index is constructed as follows:
     *
     * 9 bits - to represent 360 degrees of longitude (-180 to 179)
     * 8 bits - to represent 180 degrees of latitude (-90 to 89)
     *
     * Each 1 degree by 1 degree tile is further broken down into an 8x8
     * grid.  So we also need:
     *
     * 3 bits - to represent x (0 to 7)
     * 3 bits - to represent y (0 to 7)
     * @return tile index
     */
    inline long int gen_index() const {
	return ((lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x;
    }

    /**
     * Generate the unique scenery tile index for this bucket in ascii
     * string form.
     * @return tile index in string form
     */
    inline string gen_index_str() const {
	char tmp[20];
	sprintf(tmp, "%ld",
                (((long)lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x);
	return (string)tmp;
    }

    /**
     * Build the base path name for this bucket.
     * @return base path in string form
     */
    string gen_base_path() const;

    /**
     * @return the center lon of a tile.
     */
    inline double get_center_lon() const {
	double span = sg_bucket_span( lat + y / 8.0 + SG_HALF_BUCKET_SPAN );

	if ( span >= 1.0 ) {
	    return lon + span / 2.0;
	} else {
	    return lon + x * span + span / 2.0;
	}
    }

    /**
     * @return the center lat of a tile.
     */
    inline double get_center_lat() const {
	return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
    }

    /**
     * @return the width of the tile in degrees.
     */
    double get_width() const;

    /**
     * @return the height of the tile in degrees.
     */
    double get_height() const;

    /**
     * @return the width of the tile in meters.
     */
    double get_width_m() const;

    /**
     * @return the height of the tile in meters.
     */
    double get_height_m() const;

    // Informational methods.

    /**
     * @return the lon of the lower left corner of
     * the 1x1 chunk containing this tile.
     */
    inline int get_lon() const { return lon; }

    /**
     * @return the lat of the lower left corner of
     * the 1x1 chunk containing this tile.
     */
    inline int get_lat() const { return lat; }

    /**
     * @return the x coord within the 1x1 degree chunk this tile.
     */
    inline int get_x() const { return x; }

    /**
     * @return the y coord within the 1x1 degree chunk this tile.
     */
    inline int get_y() const { return y; }

    // friends

    friend ostream& operator<< ( ostream&, const SGBucket& );
    friend bool operator== ( const SGBucket&, const SGBucket& );
};

/**
 * \relates SGBucket
 * Return the bucket which is offset from the specified dlon, dlat by
 * the specified tile units in the X & Y direction.
 * @param dlon starting lon in degrees
 * @param dlat starting lat in degrees
 * @param x number of bucket units to offset in x (lon) direction
 * @param y number of bucket units to offset in y (lat) direction
 * @return offset bucket
 */
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );

/**
 * \relates SGBucket
 * Calculate the offset between two buckets (in quantity of buckets).
 * @param b1 bucket 1
 * @param b2 bucket 2
 * @param dx offset distance (lon) in tile units
 * @param dy offset distance (lat) in tile units
 */
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );

/**
 * Write the bucket lon, lat, x, and y to the output stream.
 * @param out output stream
 * @param b bucket
 */
inline ostream&
operator<< ( ostream& out, const SGBucket& b )
{
    return out << b.lon << ":" << b.x << ", " << b.lat << ":" << b.y;
}

/**
 * Compare two bucket structures for equality.
 * @param b1 bucket 1
 * @param b2 bucket 2
 * @return comparison result
 */
inline bool
operator== ( const SGBucket& b1, const SGBucket& b2 )
{
    return ( b1.lon == b2.lon &&
	     b1.lat == b2.lat &&
	     b1.x == b2.x &&
	     b1.y == b2.y );
}

#endif // _NEWBUCKET_HXX

<commit_msg>Give these two methods slightly less misleading names.<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 - curt@flightgear.org
 *
 * 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 Library 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.
 *
 * $Id$
 **************************************************************************/

/** \file newbucket.hxx
 * A class and associated utiltity functions to manage world scenery tiling.
 */

#ifndef _NEWBUCKET_HXX
#define _NEWBUCKET_HXX

#include <simgear/compiler.h>
#include <simgear/constants.h>

#ifdef SG_HAVE_STD_INCLUDES
#  include <cmath>
#  include <cstdio> // sprintf()
#else
#  include <math.h>
#  include <stdio.h> // sprintf()
#endif

#include STL_IOSTREAM

// I don't understand ... <math.h> or <cmath> should be included
// already depending on how you defined SG_HAVE_STD_INCLUDES, but I
// can go ahead and add this -- CLO
#ifdef __MWERKS__
SG_USING_STD(sprintf);
SG_USING_STD(fabs);
#endif

#include STL_STRING

SG_USING_STD(string);
SG_USING_STD(ostream);

/**
 * standard size of a bucket in degrees (1/8 of a degree)
 */
#define SG_BUCKET_SPAN      0.125

/**
 * half of a standard SG_BUCKET_SPAN
 */
#define SG_HALF_BUCKET_SPAN ( 0.5 * SG_BUCKET_SPAN )

// return the horizontal tile span factor based on latitude
static double sg_bucket_span( double l ) {
    if ( l >= 89.0 ) {
	return 360.0;
    } else if ( l >= 88.0 ) {
	return 8.0;
    } else if ( l >= 86.0 ) {
	return 4.0;
    } else if ( l >= 83.0 ) {
	return 2.0;
    } else if ( l >= 76.0 ) {
	return 1.0;
    } else if ( l >= 62.0 ) {
	return 0.5;
    } else if ( l >= 22.0 ) {
	return 0.25;
    } else if ( l >= -22.0 ) {
	return 0.125;
    } else if ( l >= -62.0 ) {
	return 0.25;
    } else if ( l >= -76.0 ) {
	return 0.5;
    } else if ( l >= -83.0 ) {
	return 1.0;
    } else if ( l >= -86.0 ) {
	return 2.0;
    } else if ( l >= -88.0 ) {
	return 4.0;
    } else if ( l >= -89.0 ) {
	return 8.0;
    } else {
	return 360.0;
    }
}

/**
 * A class to manage world scenery tiling.
 * This class encapsulates the world tiling scheme.  It provides ways
 * to calculate a unique tile index from a lat/lon, and it can provide
 * information such as the dimensions of a given tile.
 */

class SGBucket {

private:
    double cx, cy;  // centerpoint (lon, lat) in degrees of bucket
    int lon;        // longitude index (-180 to 179)
    int lat;        // latitude index (-90 to 89)
    int x;          // x subdivision (0 to 7)
    int y;          // y subdivision (0 to 7)

public:

    /**
     * Default constructor.
     */
    SGBucket();

    /**
     * Construct a bucket given a specific location.
     * @param dlon longitude specified in degrees
     * @param dlat latitude specified in degrees
     */
    SGBucket(const double dlon, const double dlat);

    /** Construct a bucket.
     *  @param is_good if false, create an invalid bucket.  This is
     *  useful * if you are comparing cur_bucket to last_bucket and
     *  you want to * make sure last_bucket starts out as something
     *  impossible.
     */
    SGBucket(const bool is_good);

    /** Construct a bucket given a unique bucket index number.
     * @param bindex unique bucket index
     */
    SGBucket(const long int bindex);

    /**
     * Default destructor.
     */
    ~SGBucket();

    /**
     * Reset a bucket to represent a new lat and lon
     * @param dlon longitude specified in degrees
     * @param dlat latitude specified in degrees
     */
    void set_bucket( double dlon, double dlat );

    /**
     * Reset a bucket to represent a new lat and lon
     * @param lonlat an array of double[2] holding lon and lat
     * (specified) in degrees
     */
    void set_bucket( double *lonlat );

    /**
     * Create an impossible bucket.
     * This is useful if you are comparing cur_bucket to last_bucket
     * and you want to make sure last_bucket starts out as something
     * impossible.
     */
    inline void make_bad() {
	set_bucket(0.0, 0.0);
	lon = -1000;
    }

    /**
     * Generate the unique scenery tile index for this bucket
     *
     * The index is constructed as follows:
     *
     * 9 bits - to represent 360 degrees of longitude (-180 to 179)
     * 8 bits - to represent 180 degrees of latitude (-90 to 89)
     *
     * Each 1 degree by 1 degree tile is further broken down into an 8x8
     * grid.  So we also need:
     *
     * 3 bits - to represent x (0 to 7)
     * 3 bits - to represent y (0 to 7)
     * @return tile index
     */
    inline long int gen_index() const {
	return ((lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x;
    }

    /**
     * Generate the unique scenery tile index for this bucket in ascii
     * string form.
     * @return tile index in string form
     */
    inline string gen_index_str() const {
	char tmp[20];
	sprintf(tmp, "%ld",
                (((long)lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x);
	return (string)tmp;
    }

    /**
     * Build the base path name for this bucket.
     * @return base path in string form
     */
    string gen_base_path() const;

    /**
     * @return the center lon of a tile.
     */
    inline double get_center_lon() const {
	double span = sg_bucket_span( lat + y / 8.0 + SG_HALF_BUCKET_SPAN );

	if ( span >= 1.0 ) {
	    return lon + span / 2.0;
	} else {
	    return lon + x * span + span / 2.0;
	}
    }

    /**
     * @return the center lat of a tile.
     */
    inline double get_center_lat() const {
	return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
    }

    /**
     * @return the width of the tile in degrees.
     */
    double get_width() const;

    /**
     * @return the height of the tile in degrees.
     */
    double get_height() const;

    /**
     * @return the width of the tile in meters.
     */
    double get_width_m() const;

    /**
     * @return the height of the tile in meters.
     */
    double get_height_m() const;

    // Informational methods.

    /**
     * @return the lon of the lower left corner of
     * the 1x1 chunk containing this tile.
     */
    inline int get_chunk_lon() const { return lon; }

    /**
     * @return the lat of the lower left corner of
     * the 1x1 chunk containing this tile.
     */
    inline int get_chunk_lat() const { return lat; }

    /**
     * @return the x coord within the 1x1 degree chunk this tile.
     */
    inline int get_x() const { return x; }

    /**
     * @return the y coord within the 1x1 degree chunk this tile.
     */
    inline int get_y() const { return y; }

    // friends

    friend ostream& operator<< ( ostream&, const SGBucket& );
    friend bool operator== ( const SGBucket&, const SGBucket& );
};

/**
 * \relates SGBucket
 * Return the bucket which is offset from the specified dlon, dlat by
 * the specified tile units in the X & Y direction.
 * @param dlon starting lon in degrees
 * @param dlat starting lat in degrees
 * @param x number of bucket units to offset in x (lon) direction
 * @param y number of bucket units to offset in y (lat) direction
 * @return offset bucket
 */
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );

/**
 * \relates SGBucket
 * Calculate the offset between two buckets (in quantity of buckets).
 * @param b1 bucket 1
 * @param b2 bucket 2
 * @param dx offset distance (lon) in tile units
 * @param dy offset distance (lat) in tile units
 */
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );

/**
 * Write the bucket lon, lat, x, and y to the output stream.
 * @param out output stream
 * @param b bucket
 */
inline ostream&
operator<< ( ostream& out, const SGBucket& b )
{
    return out << b.lon << ":" << b.x << ", " << b.lat << ":" << b.y;
}

/**
 * Compare two bucket structures for equality.
 * @param b1 bucket 1
 * @param b2 bucket 2
 * @return comparison result
 */
inline bool
operator== ( const SGBucket& b1, const SGBucket& b2 )
{
    return ( b1.lon == b2.lon &&
	     b1.lat == b2.lat &&
	     b1.x == b2.x &&
	     b1.y == b2.y );
}

#endif // _NEWBUCKET_HXX

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * 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 "strings.hh"

#include <cassert>            // for assert
#include <cmath>              // for std::log, std::pow
#include <vector>             // for std::vector

#include "colors.h"

class Humanize {
	long double base;
	long double div;
	std::vector<long double> scaling;
	std::vector<std::string_view> units;
	std::vector<std::string_view> colors;
	size_t needle;

public:
	Humanize(
		long double base_,
		std::vector<long double>&& scaling_,
		std::vector<std::string_view>&& units_,
		std::vector<std::string_view>&& colors_
	) :
		base(base_),
		div(std::log(base)),
		scaling(std::move(scaling_)),
		units(std::move(units_)),
		colors(std::move(colors_)),
		needle(std::distance(scaling.begin(), std::find(scaling.begin(), scaling.end(), 0)))
	{
		assert(scaling.size() == units.size());
		assert(colors.size() == units.size() + 1);
		assert(needle >= 0);
		assert(needle < units.size());
		std::transform(scaling.begin(), scaling.end(), scaling.begin(), [&](long double s) {
			return std::pow(base, s);
		});
	}

	std::string operator()(long double delta, const char* prefix, bool colored, long double rounding) const {
		if (delta < 0) {
			delta = -delta;
		}

		long double num = delta;
		ssize_t last = units.size();

		ssize_t order = (delta == 0) ? last - 1 : -std::floor(std::log(delta) / div);
		order += needle;
		if (order < 0) {
			order = 0;
		} else if (order >= last) {
			order = last - 1;
		}

		num = std::round(rounding * num / scaling[order]) / rounding;
		auto& unit = units[order];

		if (colored) {
			auto& color = colors[order];
			auto& reset = colors[last];
			return strings::format("{}{}{}{}{}", color, prefix, static_cast<double>(num), unit, reset);
		}

		return strings::format("{}{}{}", prefix, static_cast<double>(num), unit);
	}
};

// MEDIUM_SEA_GREEN  -> rgb(60, 179, 113)
// MEDIUM_SEA_GREEN  -> rgb(60, 179, 113)
// SEA_GREEN         -> rgb(46, 139, 87);
// OLIVE_DRAB        -> rgb(107, 142, 35)
// OLIVE             -> rgb(128, 128, 0)
// DARK_GOLDEN_ROD   -> rgb(184, 134, 11);
// PERU              -> rgb(205, 133, 63);
// SADDLE_BROWN      -> rgb(139, 69, 19);
// BROWN             -> rgb(165, 42, 42);

static inline std::string _from_bytes(size_t bytes, const char* prefix, bool colored) {
	static const Humanize humanize(
		1024,
		{ 8, 7, 6, 5, 4, 3, 2, 1, 0 },
		{ "YiB", "ZiB", "EiB", "PiB", "TiB", "GiB", "MiB", "KiB", "B" },
		{ BROWN, BROWN, BROWN, BROWN, BROWN, PERU, OLIVE, SEA_GREEN, MEDIUM_SEA_GREEN, CLEAR_COLOR }
	);
	return humanize(bytes, prefix, colored, 10.0L);
}

std::string strings::from_bytes(size_t bytes, const char* prefix, bool colored) {
	return _from_bytes(bytes, prefix, colored);
}

static inline std::string _from_small_time(long double seconds, const char* prefix, bool colored) {
	static const Humanize humanize(
		1000,
		{ 0, -1, -2, -3, -4 },
		{ "s", "ms", R"(µs)", "ns", "ps" },
		{ OLIVE, OLIVE_DRAB, SEA_GREEN, MEDIUM_SEA_GREEN, MEDIUM_SEA_GREEN, CLEAR_COLOR }
	);
	return humanize(seconds, prefix, colored, 1000.0L);
}

std::string strings::from_small_time(long double seconds, const char* prefix, bool colored) {
	return _from_small_time(seconds, prefix, colored);
}

static inline std::string _from_time(long double seconds, const char* prefix, bool colored) {
	static const Humanize humanize(
		60,
		{ 2, 1, 0 },
		{ "hrs", "min", "s" },
		{ SADDLE_BROWN, PERU, DARK_GOLDEN_ROD, CLEAR_COLOR }
	);
	return humanize(seconds, prefix, colored, 100.0L);
}

std::string strings::from_time(long double seconds, const char* prefix, bool colored) {
	return _from_time(seconds, prefix, colored);
}

static inline std::string _from_delta(long double nanoseconds, const char* prefix, bool colored) {
	long double seconds = nanoseconds / 1e9;  // convert nanoseconds to seconds (as a double)
	return (seconds < 1) ? _from_small_time(seconds, prefix, colored) : _from_time(seconds, prefix, colored);
}

std::string strings::from_delta(long double nanoseconds, const char* prefix, bool colored) {
	return _from_delta(nanoseconds, prefix, colored);
}
<commit_msg>Humanize: Add extra checks<commit_after>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * 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 "strings.hh"

#include <cassert>            // for assert
#include <cmath>              // for std::log, std::pow
#include <vector>             // for std::vector

#include "colors.h"

class Humanize {
	long double base;
	long double div;
	std::vector<long double> scaling;
	std::vector<std::string_view> units;
	std::vector<std::string_view> colors;
	size_t needle;

public:
	Humanize(
		long double base_,
		std::vector<long double>&& scaling_,
		std::vector<std::string_view>&& units_,
		std::vector<std::string_view>&& colors_
	) :
		base(base_),
		div(std::log(base)),
		scaling(std::move(scaling_)),
		units(std::move(units_)),
		colors(std::move(colors_)),
		needle(std::distance(scaling.begin(), std::find(scaling.begin(), scaling.end(), 0)))
	{
		assert(base > 0);
		assert(units.size() > 0);
		assert(scaling.size() == units.size());
		assert(colors.size() == units.size() + 1);
		assert(needle >= 0);
		assert(needle < units.size());
		std::transform(scaling.begin(), scaling.end(), scaling.begin(), [&](long double s) {
			return std::pow(base, s);
		});
	}

	std::string operator()(long double delta, const char* prefix, bool colored, long double rounding) const {
		long double num = delta;

		if (delta < 0) {
			delta = -delta;
		}

		ssize_t last = units.size();
		ssize_t order = (delta == 0) ? last - 1 : -std::floor(std::log(delta) / div);
		order += needle;
		if (order >= last) {
			order = last - 1;
		}
		if (order < 0) {
			order = 0;
		}

		num = std::round(rounding * num / scaling[order]) / rounding;
		auto& unit = units[order];

		if (colored) {
			auto& color = colors[order];
			auto& reset = colors[last];
			return strings::format("{}{}{}{}{}", color, prefix, static_cast<double>(num), unit, reset);
		}

		return strings::format("{}{}{}", prefix, static_cast<double>(num), unit);
	}
};

// MEDIUM_SEA_GREEN  -> rgb(60, 179, 113)
// MEDIUM_SEA_GREEN  -> rgb(60, 179, 113)
// SEA_GREEN         -> rgb(46, 139, 87);
// OLIVE_DRAB        -> rgb(107, 142, 35)
// OLIVE             -> rgb(128, 128, 0)
// DARK_GOLDEN_ROD   -> rgb(184, 134, 11);
// PERU              -> rgb(205, 133, 63);
// SADDLE_BROWN      -> rgb(139, 69, 19);
// BROWN             -> rgb(165, 42, 42);

static inline std::string
_from_bytes(size_t bytes, const char* prefix, bool colored)
{
	static const Humanize humanize(
		1024,
		{ 8, 7, 6, 5, 4, 3, 2, 1, 0 },
		{ "YiB", "ZiB", "EiB", "PiB", "TiB", "GiB", "MiB", "KiB", "B" },
		{ BROWN, BROWN, BROWN, BROWN, BROWN, PERU, OLIVE, SEA_GREEN, MEDIUM_SEA_GREEN, CLEAR_COLOR }
	);
	return humanize(bytes, prefix, colored, 10.0L);
}

std::string
strings::from_bytes(size_t bytes, const char* prefix, bool colored)
{
	return _from_bytes(bytes, prefix, colored);
}

static inline std::string
_from_small_time(long double seconds, const char* prefix, bool colored)
{
	static const Humanize humanize(
		1000,
		{ 0, -1, -2, -3, -4 },
		{ "s", "ms", R"(µs)", "ns", "ps" },
		{ OLIVE, OLIVE_DRAB, SEA_GREEN, MEDIUM_SEA_GREEN, MEDIUM_SEA_GREEN, CLEAR_COLOR }
	);
	return humanize(seconds, prefix, colored, 1000.0L);
}

std::string
strings::from_small_time(long double seconds, const char* prefix, bool colored)
{
	return _from_small_time(seconds, prefix, colored);
}

static inline std::string
_from_time(long double seconds, const char* prefix, bool colored)
{
	static const Humanize humanize(
		60,
		{ 2, 1, 0 },
		{ "hrs", "min", "s" },
		{ SADDLE_BROWN, PERU, DARK_GOLDEN_ROD, CLEAR_COLOR }
	);
	return humanize(seconds, prefix, colored, 100.0L);
}

std::string
strings::from_time(long double seconds, const char* prefix, bool colored)
{
	return _from_time(seconds, prefix, colored);
}

static inline std::string
_from_delta(long double nanoseconds, const char* prefix, bool colored)
{
	long double seconds = nanoseconds / 1e9;  // convert nanoseconds to seconds (as a double)
	return (seconds < 1) ? _from_small_time(seconds, prefix, colored) : _from_time(seconds, prefix, colored);
}

std::string
strings::from_delta(long double nanoseconds, const char* prefix, bool colored)
{
	return _from_delta(nanoseconds, prefix, colored);
}
<|endoftext|>


<commit_before>#include "config.h"
#include "mstring.h"
#include "upath.h"
#include <stdio.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>

char const *ApplicationName = "strtest";
static const char source[] = __FILE__;

#define equal(p, s)     (0 == strcmp(cstring(p), cstring(s)))

#define expect(u, s)    if (++testsrun, (u) == mstring(s) && equal(u, s)) \
        ++passed; else test_failed(cstring(u), cstring(s), __LINE__)

#define assert(u, b)    if (++testsrun, (b)) ++passed; else \
        test_failed(cstring(u), #b, __LINE__)

#define ispath(u, s)    if (++testsrun, (u) == upath(s) && equal(u, s)) \
        ++passed; else test_failed(cstring(u), cstring(s), __LINE__)

#define sequal(u, s)    if (++testsrun, equal(u, s)) \
        ++passed; else test_failed(cstring(u), cstring(s), __LINE__)

static int testsrun, passed, failed;
static const char *prog;

class strtest {
    const char *name;
public:
    strtest(const char *s) : name(s) {
        failed = passed = testsrun = 0;
    }
    ~strtest() {
        if (failed || passed != testsrun) {
            printf("%s: %7s: %d tests failed, %d tests passed of %d total\n",
                    prog, name, failed, passed, testsrun);
        }
        else {
            printf("%s: %7s: %2d/%d tests passed\n",
                    prog, name, passed, testsrun);
        }
    }
};

static void test_failed(const char *u, const char *s, int l)
{
    printf("%s: Test failed in %s:%d: u = \"%s\", s = \"%s\"\n",
            prog, source, l, u, s);
    ++failed;
}

static void test_mstring()
{
    strtest tester("mstring");

    mstring x("foo");
    expect(x, "foo");
    assert(x, x.length() == 3);

    mstring b("bar", 2);
    expect(b, "ba");
    assert(b, b.length() == 2);

    mstring e(null);
    expect(e, "");
    assert(e, e.length() == 0);
    assert(e, e == null);
    assert(e, e.indexOf(' ') == -1);

    mstring m("abc", 0);
    expect(m, "");
    assert(m, m.length() == 0);

    mstring n(0, 0);
    expect(n, "");
    assert(n, n.length() == 0);

    mstring y = x;
    expect(y, "foo");
    assert(y, y.length() == 3);

    mstring z = y.remove(1, 1);
    expect(z, "fo");
    assert(z, z.length() == 2);

    e += b;
    expect(e, "ba");
    assert(e, e.length() == 2);

    mstring c = y + e;
    expect(c, "fooba");
    assert(c, c.length() == 5);
    assert(c, c != y);
    assert(y, y != c);
    assert(c, c != e);
    assert(e, e != c);
    assert(c, c != y);
    assert(e, e != y);
    assert(x, x == y);

    mstring s = c.substring(2);
    expect(s, "oba");
    s = c.substring(2, 2);
    expect(s, "ob");

    assert(s, s.charAt(1) == 'b');
    assert(s, s.charAt(2) == -1);
    assert(s, s.indexOf('b') == 1);
    assert(s, s.indexOf('c') == -1);

    char buf[10];
    c.copyTo(buf, sizeof buf);
    expect(c, buf);

    for (int i = 0; i < 10000; i++) {
        z = x.replace(x.length(), 0, y);
    }
    expect(z, "foofoo");
    assert(z, z.length() == 6);

    assert(z, z.startsWith("foof"));
    assert(z, !z.startsWith("goof"));
    assert(z, !z.startsWith("foofoof"));
    assert(z, z.startsWith(""));
    assert(z, z.endsWith("foo"));
    assert(z, !z.endsWith("foof"));
    assert(z, !z.endsWith("foofoof"));
    assert(z, z.endsWith(""));

    mstring l(null), r(null);
    e = null;
    assert(e, e.split(0, &l, &r) == false);
    assert(z, z.split('o', &l, &r));
    expect(z, "foofoo");
    expect(l, "f");
    expect(r, "ofoo");
    assert(l, l.splitall('o', &l, &r));
    expect(l, "f");
    expect(r, "");

    mstring w = mstring(" \t\r\nabc\n\r\t ");
    mstring t = w.trim();
    expect(t, "abc");
    expect(w, " \t\r\nabc\n\r\t ");
    w = w.replace(0, 4, "_");
    w = w.replace(4, 4, ".");
    expect(w, "_abc.");
    t = w.remove(1, 3);
    expect(t, "_.");
    mstring k = t.insert(1, "xyz");
    expect(k, "_xyz.");
    mstring q = t.append("!?");
    expect(q, "_.!?");

    mstring ht = "http://www.icewm.org/";
    mstring hs = "https://www.icewm.org/";
    assert(ht, ht.find("://") == 4);
    assert(ht, ht.find(":///") == -1);
    assert(hs, ht.substring(ht.find("www")) == hs.substring(hs.find("www")));

    mstring sl = "/././././././././././";
    q = sl.searchAndReplaceAll("/./", "/");
    expect(q, "/");
    q = sl.searchAndReplaceAll("/./", "@");
    expect(q, "@.@.@.@.@./");
    q = sl.searchAndReplaceAll("/./", "/./");
    expect(q, sl);

    mstring ul = "aBcD.";
    q = ul.lower();
    expect(q, "abcd.");
    q = ul.upper();
    expect(q, "ABCD.");
}

static void test_upath()
{
    strtest tester("upath");

    upath r = upath::root();
    upath s = upath::sep();
    ispath(r, "/");
    ispath(s, "/");
    ispath(r + s, "/");
    assert(r, r != null);
    upath t = null;
    assert(t, t == null);
    upath u = null;
    assert(u, t == u);
    assert(r, r == s);
    assert(r, r != u);
    assert(r, u != r);

    upath e = "etc";
    ispath(e, "etc");
    upath re = r;
    re += e;
    ispath(re, "/etc");
    upath res = re;
    res += s;
    ispath(res, "/etc/");

    upath p = "passwd";
    ispath(p, "passwd");
    upath ep = r + e + p;
    ispath(ep, "/etc/passwd");
    ispath(ep.parent(), "/etc");
    expect(ep.name(), "passwd");
    ispath(ep, r + ep.parent().name() + ep.name());
    ispath(ep, r + ep.parent().name() + s + ep.name());

    assert(re, re.isAbsolute());
    assert(re, !re.isRelative());
    assert(re, re.dirExists());
    assert(re, re.isReadable());
    assert(re, !re.isWritable() || getuid() == 0);
    assert(re, re.isExecutable());
    assert(re, !re.fileExists());

    assert(ep, ep.isAbsolute());
    assert(ep, !ep.isRelative());
    assert(ep, ep.fileExists());
    assert(ep, ep.isReadable());
    assert(ep, !ep.isWritable() || getuid() == 0);
    assert(ep, !ep.isExecutable());
    assert(ep, !ep.dirExists());

    upath eps = ep + s;
    expect(eps, "/etc/passwd/");
    upath neps = eps.name();
    expect(neps, "");
    upath peps = eps.parent();
    expect(peps, "/etc");

    upath fi = "file:///etc/passwd";
    upath ft = "ftp://www.icewm.org/";
    upath ht = "http://www.icewm.org/";
    upath hs = "https://www.icewm.org";
    upath np = "https:/www.icewm.org";
    upath nq = "https//www.icewm.org";
    assert(fi, fi.hasProtocol());
    assert(ft, ft.hasProtocol());
    assert(ht, ht.hasProtocol());
    assert(hs, hs.hasProtocol());
    assert(np, !np.hasProtocol());
    assert(nq, !nq.hasProtocol());
    assert(fi, !fi.isHttp());
    assert(ft, !ft.isHttp());
    assert(ht, ht.isHttp());
    assert(hs, hs.isHttp());
    assert(np, !np.isHttp());
    assert(nq, !nq.isHttp());
}

static void test_strlc()
{
    strtest tester("strlc");
    char d[10] = "@";
    size_t n;
    n = strlcpy(d, "", 0);
    sequal(d, "@");
    assert(d, n == 0);

    n = strlcpy(d, "a", 0);
    sequal(d, "@");
    assert(d, n == 1);

    n = strlcpy(d, "", 1);
    sequal(d, "");
    assert(d, n == 0);

    n = strlcpy(d, "a", 1);
    sequal(d, "");
    assert(d, n == 1);

    n = strlcpy(d, "a", 2);
    sequal(d, "a");
    assert(d, n == 1);

    n = strlcpy(d, "ab", 2);
    sequal(d, "a");
    assert(d, n == 2);

    n = strlcpy(d, "ab", 3);
    sequal(d, "ab");
    assert(d, n == 2);

    n = strlcpy(d, "abc", sizeof d);
    sequal(d, "abc");
    assert(d, n == 3);

    n = strlcat(d, "def", 4);
    sequal(d, "abc");
    assert(d, n == 6);

    n = strlcat(d, "def", sizeof d);
    sequal(d, "abcdef");
    assert(d, n == 6);

    n = strlcat(d, "ghijkl", sizeof d);
    sequal(d, "abcdefghi");
    assert(d, n == 12);
}

int main(int argc, char **argv)
{
    prog = basename(argv[0]);

    test_mstring();
    test_upath();
    test_strlc();

    return 0;
}
<commit_msg>test strlcpy with operlap of source/destination.<commit_after>#include "config.h"
#include "mstring.h"
#include "upath.h"
#include <stdio.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>

char const *ApplicationName = "strtest";
static const char source[] = __FILE__;

#define equal(p, s)     (0 == strcmp(cstring(p), cstring(s)))

#define expect(u, s)    if (++testsrun, (u) == mstring(s) && equal(u, s)) \
        ++passed; else test_failed(cstring(u), cstring(s), __LINE__)

#define assert(u, b)    if (++testsrun, (b)) ++passed; else \
        test_failed(cstring(u), #b, __LINE__)

#define ispath(u, s)    if (++testsrun, (u) == upath(s) && equal(u, s)) \
        ++passed; else test_failed(cstring(u), cstring(s), __LINE__)

#define sequal(u, s)    if (++testsrun, equal(u, s)) \
        ++passed; else test_failed(cstring(u), cstring(s), __LINE__)

static int testsrun, passed, failed;
static const char *prog;

class strtest {
    const char *name;
public:
    strtest(const char *s) : name(s) {
        failed = passed = testsrun = 0;
    }
    ~strtest() {
        if (failed || passed != testsrun) {
            printf("%s: %7s: %d tests failed, %d tests passed of %d total\n",
                    prog, name, failed, passed, testsrun);
        }
        else {
            printf("%s: %7s: %2d/%d tests passed\n",
                    prog, name, passed, testsrun);
        }
    }
};

static void test_failed(const char *u, const char *s, int l)
{
    printf("%s: Test failed in %s:%d: u = \"%s\", s = \"%s\"\n",
            prog, source, l, u, s);
    ++failed;
}

static void test_mstring()
{
    strtest tester("mstring");

    mstring x("foo");
    expect(x, "foo");
    assert(x, x.length() == 3);

    mstring b("bar", 2);
    expect(b, "ba");
    assert(b, b.length() == 2);

    mstring e(null);
    expect(e, "");
    assert(e, e.length() == 0);
    assert(e, e == null);
    assert(e, e.indexOf(' ') == -1);

    mstring m("abc", 0);
    expect(m, "");
    assert(m, m.length() == 0);

    mstring n(0, 0);
    expect(n, "");
    assert(n, n.length() == 0);

    mstring y = x;
    expect(y, "foo");
    assert(y, y.length() == 3);

    mstring z = y.remove(1, 1);
    expect(z, "fo");
    assert(z, z.length() == 2);

    e += b;
    expect(e, "ba");
    assert(e, e.length() == 2);

    mstring c = y + e;
    expect(c, "fooba");
    assert(c, c.length() == 5);
    assert(c, c != y);
    assert(y, y != c);
    assert(c, c != e);
    assert(e, e != c);
    assert(c, c != y);
    assert(e, e != y);
    assert(x, x == y);

    mstring s = c.substring(2);
    expect(s, "oba");
    s = c.substring(2, 2);
    expect(s, "ob");

    assert(s, s.charAt(1) == 'b');
    assert(s, s.charAt(2) == -1);
    assert(s, s.indexOf('b') == 1);
    assert(s, s.indexOf('c') == -1);

    char buf[10];
    c.copyTo(buf, sizeof buf);
    expect(c, buf);

    for (int i = 0; i < 10000; i++) {
        z = x.replace(x.length(), 0, y);
    }
    expect(z, "foofoo");
    assert(z, z.length() == 6);

    assert(z, z.startsWith("foof"));
    assert(z, !z.startsWith("goof"));
    assert(z, !z.startsWith("foofoof"));
    assert(z, z.startsWith(""));
    assert(z, z.endsWith("foo"));
    assert(z, !z.endsWith("foof"));
    assert(z, !z.endsWith("foofoof"));
    assert(z, z.endsWith(""));

    mstring l(null), r(null);
    e = null;
    assert(e, e.split(0, &l, &r) == false);
    assert(z, z.split('o', &l, &r));
    expect(z, "foofoo");
    expect(l, "f");
    expect(r, "ofoo");
    assert(l, l.splitall('o', &l, &r));
    expect(l, "f");
    expect(r, "");

    mstring w = mstring(" \t\r\nabc\n\r\t ");
    mstring t = w.trim();
    expect(t, "abc");
    expect(w, " \t\r\nabc\n\r\t ");
    w = w.replace(0, 4, "_");
    w = w.replace(4, 4, ".");
    expect(w, "_abc.");
    t = w.remove(1, 3);
    expect(t, "_.");
    mstring k = t.insert(1, "xyz");
    expect(k, "_xyz.");
    mstring q = t.append("!?");
    expect(q, "_.!?");

    mstring ht = "http://www.icewm.org/";
    mstring hs = "https://www.icewm.org/";
    assert(ht, ht.find("://") == 4);
    assert(ht, ht.find(":///") == -1);
    assert(hs, ht.substring(ht.find("www")) == hs.substring(hs.find("www")));

    mstring sl = "/././././././././././";
    q = sl.searchAndReplaceAll("/./", "/");
    expect(q, "/");
    q = sl.searchAndReplaceAll("/./", "@");
    expect(q, "@.@.@.@.@./");
    q = sl.searchAndReplaceAll("/./", "/./");
    expect(q, sl);

    mstring ul = "aBcD.";
    q = ul.lower();
    expect(q, "abcd.");
    q = ul.upper();
    expect(q, "ABCD.");
}

static void test_upath()
{
    strtest tester("upath");

    upath r = upath::root();
    upath s = upath::sep();
    ispath(r, "/");
    ispath(s, "/");
    ispath(r + s, "/");
    assert(r, r != null);
    upath t = null;
    assert(t, t == null);
    upath u = null;
    assert(u, t == u);
    assert(r, r == s);
    assert(r, r != u);
    assert(r, u != r);

    upath e = "etc";
    ispath(e, "etc");
    upath re = r;
    re += e;
    ispath(re, "/etc");
    upath res = re;
    res += s;
    ispath(res, "/etc/");

    upath p = "passwd";
    ispath(p, "passwd");
    upath ep = r + e + p;
    ispath(ep, "/etc/passwd");
    ispath(ep.parent(), "/etc");
    expect(ep.name(), "passwd");
    ispath(ep, r + ep.parent().name() + ep.name());
    ispath(ep, r + ep.parent().name() + s + ep.name());

    assert(re, re.isAbsolute());
    assert(re, !re.isRelative());
    assert(re, re.dirExists());
    assert(re, re.isReadable());
    assert(re, !re.isWritable() || getuid() == 0);
    assert(re, re.isExecutable());
    assert(re, !re.fileExists());

    assert(ep, ep.isAbsolute());
    assert(ep, !ep.isRelative());
    assert(ep, ep.fileExists());
    assert(ep, ep.isReadable());
    assert(ep, !ep.isWritable() || getuid() == 0);
    assert(ep, !ep.isExecutable());
    assert(ep, !ep.dirExists());

    upath eps = ep + s;
    expect(eps, "/etc/passwd/");
    upath neps = eps.name();
    expect(neps, "");
    upath peps = eps.parent();
    expect(peps, "/etc");

    upath fi = "file:///etc/passwd";
    upath ft = "ftp://www.icewm.org/";
    upath ht = "http://www.icewm.org/";
    upath hs = "https://www.icewm.org";
    upath np = "https:/www.icewm.org";
    upath nq = "https//www.icewm.org";
    assert(fi, fi.hasProtocol());
    assert(ft, ft.hasProtocol());
    assert(ht, ht.hasProtocol());
    assert(hs, hs.hasProtocol());
    assert(np, !np.hasProtocol());
    assert(nq, !nq.hasProtocol());
    assert(fi, !fi.isHttp());
    assert(ft, !ft.isHttp());
    assert(ht, ht.isHttp());
    assert(hs, hs.isHttp());
    assert(np, !np.isHttp());
    assert(nq, !nq.isHttp());
}

static void test_strlc()
{
    strtest tester("strlc");
    char d[10] = "@";
    size_t n;
    n = strlcpy(d, "", 0);
    sequal(d, "@");
    assert(d, n == 0);

    n = strlcpy(d, "a", 0);
    sequal(d, "@");
    assert(d, n == 1);

    n = strlcpy(d, "", 1);
    sequal(d, "");
    assert(d, n == 0);

    n = strlcpy(d, "a", 1);
    sequal(d, "");
    assert(d, n == 1);

    n = strlcpy(d, "a", 2);
    sequal(d, "a");
    assert(d, n == 1);

    n = strlcpy(d, "ab", 2);
    sequal(d, "a");
    assert(d, n == 2);

    n = strlcpy(d, "ab", 3);
    sequal(d, "ab");
    assert(d, n == 2);

    n = strlcpy(d, "abc", sizeof d);
    sequal(d, "abc");
    assert(d, n == 3);

    n = strlcat(d, "def", 4);
    sequal(d, "abc");
    assert(d, n == 6);

    n = strlcat(d, "def", sizeof d);
    sequal(d, "abcdef");
    assert(d, n == 6);

    n = strlcat(d, "ghijkl", sizeof d);
    sequal(d, "abcdefghi");
    assert(d, n == 12);

    n = strlcpy(d, "123", sizeof d);
    sequal(d, "123");
    assert(d, n == 3);

    n = strlcpy(d, d + 1, sizeof d);
    sequal(d, "23");
    assert(d, n == 2);

    n = strlcpy(d, d + 1, sizeof d);
    sequal(d, "3");
    assert(d, n == 1);

    n = strlcpy(d, d + 1, sizeof d);
    sequal(d, "");
    assert(d, n == 0);
}

int main(int argc, char **argv)
{
    prog = basename(argv[0]);

    test_mstring();
    test_upath();
    test_strlc();

    return 0;
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *                                                                         *
 *   This program 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 program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library 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.         *
 ***************************************************************************/

#include "handlerhelper.h"
#include "imapstreamparser.h"
#include "storage/countquerybuilder.h"
#include "storage/datastore.h"
#include "storage/selectquerybuilder.h"
#include "libs/imapparser_p.h"
#include "libs/protocol_p.h"
#include "handler.h"

#include <QtSql/QSqlError>

using namespace Akonadi;

Collection HandlerHelper::collectionFromIdOrName(const QByteArray & id)
{
  // id is a number
  bool ok = false;
  qint64 collectionId = id.toLongLong( &ok );
  if ( ok )
    return Collection::retrieveById( collectionId );

  // id is a path
  QString path = QString::fromUtf8( id ); // ### should be UTF-7 for real IMAP compatibility

  const QStringList pathParts = path.split( QLatin1Char('/'), QString::SkipEmptyParts );
  Collection col;
  Q_FOREACH ( const QString &part, pathParts ) {
    SelectQueryBuilder<Collection> qb;
    qb.addValueCondition( Collection::nameColumn(), Query::Equals, part );
    if ( col.isValid() )
      qb.addValueCondition( Collection::parentIdColumn(), Query::Equals, col.id() );
    else
      qb.addValueCondition( Collection::parentIdColumn(), Query::Is, QVariant() );
    if ( !qb.exec() )
      return Collection();
    Collection::List list = qb.result();
    if ( list.count() != 1 )
      return Collection();
    col = list.first();
  }
  return col;
}

QString HandlerHelper::pathForCollection(const Collection & col)
{
  QStringList parts;
  Collection current = col;
  while ( current.isValid() ) {
    parts.prepend( current.name() );
    current = current.parent();
  }
  return parts.join( QLatin1String("/") );
}

bool HandlerHelper::itemStatistics(const Akonadi::Collection& col, qint64& count, qint64& size)
{
  QueryBuilder qb( PimItem::tableName() );
  qb.addAggregation( PimItem::idColumn(), QLatin1String("count") );
  qb.addAggregation( PimItem::sizeColumn(), QLatin1String("sum") );

  if ( col.resource().isVirtual() ) {
    qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
                CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
    qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
  } else {
    qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
  }

  if ( !qb.exec() )
    return false;
  if ( !qb.query().next() ) {
    akError() << "Error during retrieving result of statistics query:" << qb.query().lastError().text();
    return false;
  }
  count = qb.query().value( 0 ).toLongLong();
  size = qb.query().value( 1 ).toLongLong();
  return true;
}

int HandlerHelper::itemWithFlagsCount(const Akonadi::Collection& col, const QStringList& flags)
{
  CountQueryBuilder qb( PimItem::tableName(), PimItem::idFullColumnName(), CountQueryBuilder::Distinct );
  qb.addJoin( QueryBuilder::InnerJoin, PimItemFlagRelation::tableName(),
              PimItem::idFullColumnName(), PimItemFlagRelation::leftFullColumnName() );
  qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, col.id() );
  Query::Condition cond( Query::Or );
  // We use the below instead of an inner join in the query above because postgres seems
  // to struggle to optimize the two inner joins, despite having indeces that should
  // facilitate that. This exploits the fact that the Flag::retrieveByName is fast because
  // it hits an in-memory cache.
  Q_FOREACH ( const QString &flag, flags ) {
    const Flag f = Flag::retrieveByName( flag );
    cond.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, f.id() );
  }
  qb.addCondition( cond );
  if ( !qb.exec() )
    return -1;
  return qb.result();
}

int HandlerHelper::itemCount(const Collection& col)
{
  CountQueryBuilder qb( PimItem::tableName() );
  qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
  if ( !qb.exec() )
    return -1;
  return qb.result();
}

int HandlerHelper::parseCachePolicy(const QByteArray & data, Collection & col, int start, bool *changed )
{
  bool inheritChanged = false;
  bool somethingElseChanged = false;

  QList<QByteArray> params;
  int end = Akonadi::ImapParser::parseParenthesizedList( data, params, start );
  for ( int i = 0; i < params.count() - 1; i += 2 ) {
    const QByteArray key = params[i];
    const QByteArray value = params[i + 1];

    if ( key == "INHERIT" ) {
      const bool inherit = value == "true";
      inheritChanged = col.cachePolicyInherit() != inherit;
      col.setCachePolicyInherit( inherit );
    } else if ( key == "INTERVAL" ) {
      const int interval = value.toInt();
      somethingElseChanged = somethingElseChanged || interval != col.cachePolicyCheckInterval();
      col.setCachePolicyCheckInterval( interval );
    } else if ( key == "CACHETIMEOUT" ) {
      const int timeout = value.toInt();
      somethingElseChanged = somethingElseChanged || timeout != col.cachePolicyCacheTimeout();
      col.setCachePolicyCacheTimeout( timeout );
    } else if ( key == "SYNCONDEMAND" ) {
      const bool syncOnDemand = value == "true";
      somethingElseChanged = somethingElseChanged || syncOnDemand != col.cachePolicySyncOnDemand();
      col.setCachePolicySyncOnDemand( syncOnDemand );
    } else if ( key == "LOCALPARTS" ) {
      QList<QByteArray> tmp;
      QStringList partsList;
      Akonadi::ImapParser::parseParenthesizedList( value, tmp );
      Q_FOREACH ( const QByteArray &ba, tmp )
        partsList << QString::fromLatin1( ba );
      const QString parts = partsList.join( QLatin1String( " " ) );
      somethingElseChanged = somethingElseChanged || col.cachePolicyLocalParts() != parts;
      col.setCachePolicyLocalParts( parts );
    }
  }

  if ( changed && (inheritChanged || (!col.cachePolicyInherit() && somethingElseChanged)) )
    *changed = true;

  return end;
}

QByteArray HandlerHelper::cachePolicyToByteArray(const Collection & col)
{
  QByteArray rv = "CACHEPOLICY (";
  rv += "INHERIT " + ( col.cachePolicyInherit() ? QByteArray("true") : QByteArray("false") );
  rv += " INTERVAL " + QByteArray::number( col.cachePolicyCheckInterval() );
  rv += " CACHETIMEOUT " + QByteArray::number( col.cachePolicyCacheTimeout() );
  rv += " SYNCONDEMAND " + ( col.cachePolicySyncOnDemand() ? QByteArray("true") : QByteArray("false") );
  rv += " LOCALPARTS (" + col.cachePolicyLocalParts().toLatin1() + ')';
  rv += ')';
  return rv;
}

QByteArray HandlerHelper::collectionToByteArray( const Collection & col, bool hidden, bool includeStatistics,
                                                 int ancestorDepth, const QStack<Collection> &ancestors )
{
  QByteArray b = QByteArray::number( col.id() ) + ' '
               + QByteArray::number( col.parentId() ) + " (";

  b += "NAME "+ ImapParser::quote( col.name().toUtf8() ) + ' ';
  if ( hidden )
    b+= "MIMETYPE () ";
  else
    b += "MIMETYPE (" + MimeType::joinByName( col.mimeTypes(), QLatin1String( " " ) ).toLatin1() + ") ";
  b += "REMOTEID " + ImapParser::quote( col.remoteId().toUtf8() );
  b += " REMOTEREVISION " + ImapParser::quote( col.remoteRevision().toUtf8() );
  b += " RESOURCE " + ImapParser::quote( col.resource().name().toUtf8() );
  b += " VIRTUAL " + QByteArray::number( col.isVirtual() ) + ' ';

  if ( includeStatistics ) {
    qint64 itemCount, itemSize;
    if ( itemStatistics( col, itemCount, itemSize ) ) {
      b += "MESSAGES " + QByteArray::number( itemCount ) + ' ';
      // itemWithFlagCount is twice as fast as itemWithoutFlagCount, so emulated that...
      b += "UNSEEN " + QByteArray::number( itemCount - itemWithFlagsCount( col, QStringList() << QLatin1String( "\\SEEN" ) << QLatin1String( "$IGNORED" ) ) ) + ' ';
      b += "SIZE " + QByteArray::number( itemSize ) + ' ';
    }
  }

  if ( !col.queryLanguage().isEmpty() ) {
    b += AKONADI_PARAM_PERSISTENTSEARCH " ";
    QList<QByteArray> args;
    args.append( AKONADI_PARAM_PERSISTENTSEARCH_QUERYLANG );
    args.append( col.queryLanguage().toLatin1() );
    args.append( AKONADI_PARAM_PERSISTENTSEARCH_QUERYSTRING );
    args.append( ImapParser::quote( col.queryString().toUtf8() ) );
    b += ImapParser::quote( "(" + ImapParser::join( args, " " ) + ")" );
    b += ' ';
  }

  b += HandlerHelper::cachePolicyToByteArray( col ) + ' ';
  if ( ancestorDepth > 0 )
    b += HandlerHelper::ancestorsToByteArray( ancestorDepth, ancestors ) + ' ';

  const CollectionAttribute::List attrs = col.attributes();
  for ( int i = 0; i < attrs.size(); ++i ) {
    const CollectionAttribute &attr = attrs[i];
    b += attr.type() + ' ' + ImapParser::quote( attr.value() );
    if ( i != attrs.size() - 1 )
      b += ' ';
  }
  b+= ')';

  return b;
}

QByteArray HandlerHelper::ancestorsToByteArray( int ancestorDepth, const QStack<Collection> &_ancestors )
{
  QByteArray b;
  if ( ancestorDepth > 0 ) {
    b += AKONADI_PARAM_ANCESTORS " (";
    QStack<Collection> ancestors( _ancestors );
    for ( int i = 0; i < ancestorDepth; ++i ) {
      if ( ancestors.isEmpty() ) {
        b += "(0 \"\")";
        break;
      }
      b += '(';
      const Collection c = ancestors.pop();
      b += QByteArray::number( c.id() ) + " ";
      b += ImapParser::quote( c.remoteId().toUtf8() );
      b += ")";
      if ( i != ancestorDepth - 1 )
        b += ' ';
    }
    b += ')';
  }
  return b;
}

int HandlerHelper::parseDepth( const QByteArray &depth )
{
  if ( depth.isEmpty() )
    throw ImapParserException( "No depth specified" );
  if ( depth == "INF" )
    return INT_MAX;
  bool ok = false;
  int result = depth.toInt( &ok );
  if ( !ok )
    throw ImapParserException( "Invalid depth argument" );
  return result;
}

Akonadi::Flag::List Akonadi::HandlerHelper::resolveFlags(const QList< QByteArray >& flagNames)
{
  Flag::List flagList;
  Q_FOREACH ( const QByteArray &flagName, flagNames ) {
    Flag flag = Flag::retrieveByName( QString::fromUtf8( flagName ) );
    if ( !flag.isValid() ) {
      flag = Flag( QString::fromUtf8( flagName ) );
      if ( !flag.insert() )
        throw HandlerException( "Unable to create flag" );
    }
    flagList.append( flag );
  }
  return flagList;
}
<commit_msg>Correctly count items flags in virtual collections<commit_after>/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *                                                                         *
 *   This program 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 program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library 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.         *
 ***************************************************************************/

#include "handlerhelper.h"
#include "imapstreamparser.h"
#include "storage/countquerybuilder.h"
#include "storage/datastore.h"
#include "storage/selectquerybuilder.h"
#include "libs/imapparser_p.h"
#include "libs/protocol_p.h"
#include "handler.h"

#include <QtSql/QSqlError>

using namespace Akonadi;

Collection HandlerHelper::collectionFromIdOrName(const QByteArray & id)
{
  // id is a number
  bool ok = false;
  qint64 collectionId = id.toLongLong( &ok );
  if ( ok )
    return Collection::retrieveById( collectionId );

  // id is a path
  QString path = QString::fromUtf8( id ); // ### should be UTF-7 for real IMAP compatibility

  const QStringList pathParts = path.split( QLatin1Char('/'), QString::SkipEmptyParts );
  Collection col;
  Q_FOREACH ( const QString &part, pathParts ) {
    SelectQueryBuilder<Collection> qb;
    qb.addValueCondition( Collection::nameColumn(), Query::Equals, part );
    if ( col.isValid() )
      qb.addValueCondition( Collection::parentIdColumn(), Query::Equals, col.id() );
    else
      qb.addValueCondition( Collection::parentIdColumn(), Query::Is, QVariant() );
    if ( !qb.exec() )
      return Collection();
    Collection::List list = qb.result();
    if ( list.count() != 1 )
      return Collection();
    col = list.first();
  }
  return col;
}

QString HandlerHelper::pathForCollection(const Collection & col)
{
  QStringList parts;
  Collection current = col;
  while ( current.isValid() ) {
    parts.prepend( current.name() );
    current = current.parent();
  }
  return parts.join( QLatin1String("/") );
}

bool HandlerHelper::itemStatistics(const Akonadi::Collection& col, qint64& count, qint64& size)
{
  QueryBuilder qb( PimItem::tableName() );
  qb.addAggregation( PimItem::idColumn(), QLatin1String("count") );
  qb.addAggregation( PimItem::sizeColumn(), QLatin1String("sum") );

  if ( col.resource().isVirtual() ) {
    qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
                CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
    qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
  } else {
    qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
  }

  if ( !qb.exec() )
    return false;
  if ( !qb.query().next() ) {
    akError() << "Error during retrieving result of statistics query:" << qb.query().lastError().text();
    return false;
  }
  count = qb.query().value( 0 ).toLongLong();
  size = qb.query().value( 1 ).toLongLong();
  return true;
}

int HandlerHelper::itemWithFlagsCount(const Akonadi::Collection& col, const QStringList& flags)
{
  CountQueryBuilder qb( PimItem::tableName(), PimItem::idFullColumnName(), CountQueryBuilder::Distinct );
  qb.addJoin( QueryBuilder::InnerJoin, PimItemFlagRelation::tableName(),
              PimItem::idFullColumnName(), PimItemFlagRelation::leftFullColumnName() );
  if ( col.isVirtual() ) {
    qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
                CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
    qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
  } else {
    qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, col.id() );
  }
  Query::Condition cond( Query::Or );
  // We use the below instead of an inner join in the query above because postgres seems
  // to struggle to optimize the two inner joins, despite having indeces that should
  // facilitate that. This exploits the fact that the Flag::retrieveByName is fast because
  // it hits an in-memory cache.
  Q_FOREACH ( const QString &flag, flags ) {
    const Flag f = Flag::retrieveByName( flag );
    cond.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, f.id() );
  }
  qb.addCondition( cond );
  if ( !qb.exec() )
    return -1;
  return qb.result();
}

int HandlerHelper::itemCount(const Collection& col)
{
  CountQueryBuilder qb( PimItem::tableName() );
  qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
  if ( !qb.exec() )
    return -1;
  return qb.result();
}

int HandlerHelper::parseCachePolicy(const QByteArray & data, Collection & col, int start, bool *changed )
{
  bool inheritChanged = false;
  bool somethingElseChanged = false;

  QList<QByteArray> params;
  int end = Akonadi::ImapParser::parseParenthesizedList( data, params, start );
  for ( int i = 0; i < params.count() - 1; i += 2 ) {
    const QByteArray key = params[i];
    const QByteArray value = params[i + 1];

    if ( key == "INHERIT" ) {
      const bool inherit = value == "true";
      inheritChanged = col.cachePolicyInherit() != inherit;
      col.setCachePolicyInherit( inherit );
    } else if ( key == "INTERVAL" ) {
      const int interval = value.toInt();
      somethingElseChanged = somethingElseChanged || interval != col.cachePolicyCheckInterval();
      col.setCachePolicyCheckInterval( interval );
    } else if ( key == "CACHETIMEOUT" ) {
      const int timeout = value.toInt();
      somethingElseChanged = somethingElseChanged || timeout != col.cachePolicyCacheTimeout();
      col.setCachePolicyCacheTimeout( timeout );
    } else if ( key == "SYNCONDEMAND" ) {
      const bool syncOnDemand = value == "true";
      somethingElseChanged = somethingElseChanged || syncOnDemand != col.cachePolicySyncOnDemand();
      col.setCachePolicySyncOnDemand( syncOnDemand );
    } else if ( key == "LOCALPARTS" ) {
      QList<QByteArray> tmp;
      QStringList partsList;
      Akonadi::ImapParser::parseParenthesizedList( value, tmp );
      Q_FOREACH ( const QByteArray &ba, tmp )
        partsList << QString::fromLatin1( ba );
      const QString parts = partsList.join( QLatin1String( " " ) );
      somethingElseChanged = somethingElseChanged || col.cachePolicyLocalParts() != parts;
      col.setCachePolicyLocalParts( parts );
    }
  }

  if ( changed && (inheritChanged || (!col.cachePolicyInherit() && somethingElseChanged)) )
    *changed = true;

  return end;
}

QByteArray HandlerHelper::cachePolicyToByteArray(const Collection & col)
{
  QByteArray rv = "CACHEPOLICY (";
  rv += "INHERIT " + ( col.cachePolicyInherit() ? QByteArray("true") : QByteArray("false") );
  rv += " INTERVAL " + QByteArray::number( col.cachePolicyCheckInterval() );
  rv += " CACHETIMEOUT " + QByteArray::number( col.cachePolicyCacheTimeout() );
  rv += " SYNCONDEMAND " + ( col.cachePolicySyncOnDemand() ? QByteArray("true") : QByteArray("false") );
  rv += " LOCALPARTS (" + col.cachePolicyLocalParts().toLatin1() + ')';
  rv += ')';
  return rv;
}

QByteArray HandlerHelper::collectionToByteArray( const Collection & col, bool hidden, bool includeStatistics,
                                                 int ancestorDepth, const QStack<Collection> &ancestors )
{
  QByteArray b = QByteArray::number( col.id() ) + ' '
               + QByteArray::number( col.parentId() ) + " (";

  b += "NAME "+ ImapParser::quote( col.name().toUtf8() ) + ' ';
  if ( hidden )
    b+= "MIMETYPE () ";
  else
    b += "MIMETYPE (" + MimeType::joinByName( col.mimeTypes(), QLatin1String( " " ) ).toLatin1() + ") ";
  b += "REMOTEID " + ImapParser::quote( col.remoteId().toUtf8() );
  b += " REMOTEREVISION " + ImapParser::quote( col.remoteRevision().toUtf8() );
  b += " RESOURCE " + ImapParser::quote( col.resource().name().toUtf8() );
  b += " VIRTUAL " + QByteArray::number( col.isVirtual() ) + ' ';

  if ( includeStatistics ) {
    qint64 itemCount, itemSize;
    if ( itemStatistics( col, itemCount, itemSize ) ) {
      b += "MESSAGES " + QByteArray::number( itemCount ) + ' ';
      // itemWithFlagCount is twice as fast as itemWithoutFlagCount, so emulated that...
      b += "UNSEEN " + QByteArray::number( itemCount - itemWithFlagsCount( col, QStringList() << QLatin1String( "\\SEEN" ) << QLatin1String( "$IGNORED" ) ) ) + ' ';
      b += "SIZE " + QByteArray::number( itemSize ) + ' ';
    }
  }

  if ( !col.queryLanguage().isEmpty() ) {
    b += AKONADI_PARAM_PERSISTENTSEARCH " ";
    QList<QByteArray> args;
    args.append( AKONADI_PARAM_PERSISTENTSEARCH_QUERYLANG );
    args.append( col.queryLanguage().toLatin1() );
    args.append( AKONADI_PARAM_PERSISTENTSEARCH_QUERYSTRING );
    args.append( ImapParser::quote( col.queryString().toUtf8() ) );
    b += ImapParser::quote( "(" + ImapParser::join( args, " " ) + ")" );
    b += ' ';
  }

  b += HandlerHelper::cachePolicyToByteArray( col ) + ' ';
  if ( ancestorDepth > 0 )
    b += HandlerHelper::ancestorsToByteArray( ancestorDepth, ancestors ) + ' ';

  const CollectionAttribute::List attrs = col.attributes();
  for ( int i = 0; i < attrs.size(); ++i ) {
    const CollectionAttribute &attr = attrs[i];
    b += attr.type() + ' ' + ImapParser::quote( attr.value() );
    if ( i != attrs.size() - 1 )
      b += ' ';
  }
  b+= ')';

  return b;
}

QByteArray HandlerHelper::ancestorsToByteArray( int ancestorDepth, const QStack<Collection> &_ancestors )
{
  QByteArray b;
  if ( ancestorDepth > 0 ) {
    b += AKONADI_PARAM_ANCESTORS " (";
    QStack<Collection> ancestors( _ancestors );
    for ( int i = 0; i < ancestorDepth; ++i ) {
      if ( ancestors.isEmpty() ) {
        b += "(0 \"\")";
        break;
      }
      b += '(';
      const Collection c = ancestors.pop();
      b += QByteArray::number( c.id() ) + " ";
      b += ImapParser::quote( c.remoteId().toUtf8() );
      b += ")";
      if ( i != ancestorDepth - 1 )
        b += ' ';
    }
    b += ')';
  }
  return b;
}

int HandlerHelper::parseDepth( const QByteArray &depth )
{
  if ( depth.isEmpty() )
    throw ImapParserException( "No depth specified" );
  if ( depth == "INF" )
    return INT_MAX;
  bool ok = false;
  int result = depth.toInt( &ok );
  if ( !ok )
    throw ImapParserException( "Invalid depth argument" );
  return result;
}

Akonadi::Flag::List Akonadi::HandlerHelper::resolveFlags(const QList< QByteArray >& flagNames)
{
  Flag::List flagList;
  Q_FOREACH ( const QByteArray &flagName, flagNames ) {
    Flag flag = Flag::retrieveByName( QString::fromUtf8( flagName ) );
    if ( !flag.isValid() ) {
      flag = Flag( QString::fromUtf8( flagName ) );
      if ( !flag.insert() )
        throw HandlerException( "Unable to create flag" );
    }
    flagList.append( flag );
  }
  return flagList;
}
<|endoftext|>


<commit_before>#include <tiled-reader/tileset.h>

#include <tiled-reader/exceptions.h>
#include <libjson.h>

namespace tiled {

Tileset::Tileset(const JSONNode& json_node) {
	image_ = json_node["image"].as_string();
	tile_width_ = json_node["tilewidth"].as_int();
	tile_height_ = json_node["tileheight"].as_int();
	image_width_ = json_node["imagewidth"].as_int();
	image_height_ = json_node["imageheight"].as_int();
	margin_ = json_node["margin"].as_int();
	spacing_ = json_node["spacing"].as_int();

	try {
		for (const auto& property_node : json_node.at("properties")) {
			properties_[property_node.name()] = property_node.as_string();
		}
	}
	catch (std::out_of_range) {}
}

std::unique_ptr<Tileset> Tileset::ReadFromFile(const std::string& filepath, const FileLoader& loader) {
	auto json_file = loader.OpenFile(filepath);
	if (!json_file)
		throw tiled::BaseException("File not found: %s\n", filepath.c_str());

	auto contents = loader.GetContents(json_file);
	if (!libjson::is_valid(contents))
		throw tiled::BaseException("Invalid json: %s\n", filepath.c_str());

	auto json_root = libjson::parse(contents);
	return std::make_unique<Tileset>(json_root);
}

TileInfo Tileset::tileinfo_for(int tile) const
{
	int col = tile % columns();
	int row = tile / rows();
	TileInfo result;
	result.asset_name = image_;
	result.tile_width = tile_width_;
	result.tile_height = tile_height_;
	result.p1_u = double(margin_ + col * (tile_width_ + spacing_)) / image_width_;
	result.p1_v = double(margin_ + row * (tile_height_ + spacing_)) / image_height_;
	result.p2_u = result.p1_u + double(tile_width_) / image_width_;
	result.p2_v = result.p1_v + double(tile_height_) / image_height_;
	return result;
}

int Tileset::columns() const
{
	return (image_width_ - margin_ + spacing_) / (tile_width_ + spacing_);
}

int Tileset::rows() const
{
	return (image_height_ - margin_ + spacing_) / (tile_height_ + spacing_);
}

} // namespace tiled<commit_msg>Fix Tileset::tileinfo_for.<commit_after>#include <tiled-reader/tileset.h>

#include <tiled-reader/exceptions.h>
#include <libjson.h>

namespace tiled {

Tileset::Tileset(const JSONNode& json_node) {
	image_ = json_node["image"].as_string();
	tile_width_ = json_node["tilewidth"].as_int();
	tile_height_ = json_node["tileheight"].as_int();
	image_width_ = json_node["imagewidth"].as_int();
	image_height_ = json_node["imageheight"].as_int();
	margin_ = json_node["margin"].as_int();
	spacing_ = json_node["spacing"].as_int();

	try {
		for (const auto& property_node : json_node.at("properties")) {
			properties_[property_node.name()] = property_node.as_string();
		}
	}
	catch (std::out_of_range) {}
}

std::unique_ptr<Tileset> Tileset::ReadFromFile(const std::string& filepath, const FileLoader& loader) {
	auto json_file = loader.OpenFile(filepath);
	if (!json_file)
		throw tiled::BaseException("File not found: %s\n", filepath.c_str());

	auto contents = loader.GetContents(json_file);
	if (!libjson::is_valid(contents))
		throw tiled::BaseException("Invalid json: %s\n", filepath.c_str());

	auto json_root = libjson::parse(contents);
	return std::make_unique<Tileset>(json_root);
}

TileInfo Tileset::tileinfo_for(int tile) const
{
	int num_cols = columns();
	int col = tile % num_cols;
	int row = tile / num_cols;
	TileInfo result;
	result.asset_name = image_;
	result.tile_width = tile_width_;
	result.tile_height = tile_height_;
	result.p1_u = double(margin_ + col * (tile_width_ + spacing_)) / image_width_;
	result.p1_v = double(margin_ + row * (tile_height_ + spacing_)) / image_height_;
	result.p2_u = result.p1_u + double(tile_width_) / image_width_;
	result.p2_v = result.p1_v + double(tile_height_) / image_height_;
	return result;
}

int Tileset::columns() const
{
	return (image_width_ - margin_ + spacing_) / (tile_width_ + spacing_);
}

int Tileset::rows() const
{
	return (image_height_ - margin_ + spacing_) / (tile_height_ + spacing_);
}

} // namespace tiled<|endoftext|>


<commit_before>#include "ghost/timing.h"
#include "ghost/util.h"

#include <map>
#include <vector>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <numeric>
#include <algorithm>

using namespace std;

typedef struct
{
    /**
     * @brief User-defined callback function to compute a region's performance.
     */
    ghost_compute_performance_func perfFunc;
    /**
     * @brief Argument to perfFunc.
     */
    void *perfFuncArg;
    /**
     * @brief The unit of performance.
     */
    const char *perfUnit;
}
ghost_timing_perfFunc;

/**
 * @brief Region timing accumulator
 */
typedef struct
{
    /**
     * @brief The runtimes of this region.
     */
    vector<double> times;
    /**
     * @brief The last start time of this region.
     */
    double start;

    vector<ghost_timing_perfFunc> perfFuncs;
}
ghost_timing_region_accu;

static map<string,ghost_timing_region_accu> timings;
static pthread_mutex_t timingsMutex = PTHREAD_MUTEX_INITIALIZER;

void ghost_timing_tick(const char *tag)
{
    pthread_mutex_lock(&timingsMutex);

    double start = 0.;
    ghost_timing_wc(&start);
    timings[tag].start = start;

    pthread_mutex_unlock(&timingsMutex);
}

void ghost_timing_tock(const char *tag)
{
    pthread_mutex_lock(&timingsMutex);
    double end;
    ghost_timing_wc(&end);
    ghost_timing_region_accu *ti = &timings[string(tag)];
    ti->times.push_back(end-ti->start);
    pthread_mutex_unlock(&timingsMutex);
}

void ghost_timing_set_perfFunc(const char *prefix, const char *tag, ghost_compute_performance_func func, void *arg, size_t sizeofarg, const char *unit)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_UTIL);

    if (!tag) {
        WARNING_LOG("Empty tag! This should not have happened... Prefix is %s",prefix);
        return;
    }

    ghost_timing_perfFunc pf;
    pf.perfFunc = func;
    pf.perfUnit = unit;

    ghost_timing_region_accu *regionaccu;
    if (prefix) {
        string fulltag = (string(prefix)+"->"+string(tag));
        regionaccu = &timings[fulltag];
    } else {
        regionaccu = &timings[string(tag)];
    }

    for (std::vector<ghost_timing_perfFunc>::iterator it = regionaccu->perfFuncs.begin();
            it !=regionaccu->perfFuncs.end(); ++it) {
        if (it->perfFunc == func && !strcmp(it->perfUnit,unit)) {
            return;
        }
    }

    ghost_malloc((void **)&(pf.perfFuncArg),sizeofarg);
    memcpy(pf.perfFuncArg,arg,sizeofarg);
    regionaccu->perfFuncs.push_back(pf);

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_UTIL);
}

ghost_error ghost_timing_region_create(ghost_timing_region ** ri, const char *tag)
{
    ghost_timing_region_accu ti = timings[string(tag)];
    if (!ti.times.size()) {
        *ri = NULL;
        return GHOST_SUCCESS;
    }

    ghost_error ret = GHOST_SUCCESS;
    GHOST_CALL_GOTO(ghost_malloc((void **)ri,sizeof(ghost_timing_region)),err,ret);
    (*ri)->nCalls =  ti.times.size();
    (*ri)->minTime = *min_element(ti.times.begin(),ti.times.end());
    (*ri)->maxTime = *max_element(ti.times.begin(),ti.times.end());
    (*ri)->accTime = accumulate(ti.times.begin(),ti.times.end(),0.);
    (*ri)->avgTime = (*ri)->accTime/(*ri)->nCalls;
    if ((*ri)->nCalls > 10) {
        (*ri)->skip10avgTime = accumulate(ti.times.begin()+10,ti.times.end(),0.)/((*ri)->nCalls-10);
    } else {
        (*ri)->skip10avgTime = 0.;
    }

    GHOST_CALL_GOTO(ghost_malloc((void **)(&((*ri)->times)),sizeof(double)*(*ri)->nCalls),err,ret);
    memcpy((*ri)->times,&ti.times[0],(*ri)->nCalls*sizeof(double));

    goto out;

err:
    ERROR_LOG("Freeing region info");
    if (*ri) {
        free((*ri)->times); (*ri)->times = NULL;
    }
    free(*ri); (*ri) = NULL;
out:

    return ret;
}

void ghost_timing_region_destroy(ghost_timing_region * ri)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_UTIL);

    if (ri) {
        free(ri->times); ri->times = NULL;
    }
    free(ri);

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_UTIL);
}

ghost_error ghost_timing_summarystring(char **str)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_UTIL);

    stringstream buffer;
    map<string,ghost_timing_region_accu>::iterator iter;
    vector<ghost_timing_perfFunc>::iterator pf_iter;

    size_t maxRegionLen = 0;
    size_t maxCallsLen = 0;
    size_t maxUnitLen = 0;

    stringstream tmp;
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        int regLen = 0;
        for (pf_iter = iter->second.perfFuncs.begin(); pf_iter != iter->second.perfFuncs.end(); ++pf_iter) {
            if (pf_iter->perfUnit) {
                tmp << iter->first.length();
                maxUnitLen = max(strlen(pf_iter->perfUnit),maxUnitLen);
                regLen = strlen(pf_iter->perfUnit);
                tmp.str("");
            }

        }
        tmp << regLen+iter->first.length();
        maxRegionLen = max(regLen+iter->first.length(),maxRegionLen);
        tmp.str("");

        tmp << iter->second.times.size();
        maxCallsLen = max(maxCallsLen,tmp.str().length());
        tmp.str("");

    }
    if (maxCallsLen < 5) {
        maxCallsLen = 5;
    }

    buffer << left << setw(maxRegionLen+4) << "Region" << right << " | ";
    buffer << setw(maxCallsLen+3) << "Calls | ";
    buffer << "   t_min | ";
    buffer << "   t_max | ";
    buffer << "   t_avg | ";
    buffer << "   t_s10 | ";
    buffer << "   t_acc" << endl;
    buffer << string(maxRegionLen+maxCallsLen+7+5*11,'-') << endl;

    buffer.precision(2);
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        ghost_timing_region *region = NULL;
        ghost_timing_region_create(&region,iter->first.c_str());

        if (region) {
            buffer << scientific << left << setw(maxRegionLen+4) << iter->first << " | " << right << setw(maxCallsLen) <<
                region->nCalls << " | " <<
                region->minTime << " | " <<
                region->maxTime << " | " <<
                region->avgTime << " | " <<
                region->skip10avgTime << " | " <<
                region->accTime << endl;

            ghost_timing_region_destroy(region);
        }
    }

    int printed = 0;
    buffer.precision(2);
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        if (!printed) {
            buffer << endl << endl << left << setw(maxRegionLen+4) << "Region" << right << " | ";
            buffer << setw(maxCallsLen+3) << "Calls | ";
            buffer << "   P_max | ";
            buffer << "   P_min | ";
            buffer << "   P_avg | ";
            buffer << "P_skip10" << endl;;
            buffer << string(maxRegionLen+maxCallsLen+7+4*11,'-') << endl;
        }
        printed = 1;

        ghost_timing_region *region = NULL;
        ghost_timing_region_create(&region,iter->first.c_str());

        if (region) {
            for (pf_iter = iter->second.perfFuncs.begin(); pf_iter != iter->second.perfFuncs.end(); ++pf_iter) {
                ghost_compute_performance_func pf = pf_iter->perfFunc;
                void *pfa = pf_iter->perfFuncArg;

                double P_min = 0., P_max = 0., P_avg = 0., P_skip10 = 0.;
                int err = pf(&P_min,region->maxTime,pfa);
                if (err) {
                    ERROR_LOG("Error in calling performance computation callback!");
                }
                err = pf(&P_max,region->minTime,pfa);
                if (err) {
                    ERROR_LOG("Error in calling performance computation callback!");
                }
                err = pf(&P_avg,region->avgTime,pfa);
                if (err) {
                    ERROR_LOG("Error in calling performance computation callback!");
                }
                if (region->nCalls > 10) {
                    err = pf(&P_skip10,accumulate(iter->second.times.begin()+10,iter->second.times.end(),0.)/(region->nCalls-10),pfa);
                    if (err) {
                        ERROR_LOG("Error in calling performance computation callback!");
                    }
                }

                buffer << scientific << left << setw(maxRegionLen-maxUnitLen+2) << iter->first <<
                    right << "(" << setw(maxUnitLen) << pf_iter->perfUnit << ")" << " | " << setw(maxCallsLen) <<
                    region->nCalls << " | " <<
                    P_max << " | " <<
                    P_min << " | " <<
                    P_avg << " | " <<
                    P_skip10 << endl;
            }
            ghost_timing_region_destroy(region);
        }
    }

    // clear all timings to prevent memory leaks
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        for (pf_iter = iter->second.perfFuncs.begin(); pf_iter != iter->second.perfFuncs.end(); ++pf_iter) {
            if( pf_iter->perfFuncArg != NULL )
                free(pf_iter->perfFuncArg);
            pf_iter->perfFuncArg = NULL;
        }
    }
    timings.clear();

    GHOST_CALL_RETURN(ghost_malloc((void **)str,buffer.str().length()+1));
    strcpy(*str,buffer.str().c_str());

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_UTIL);
    return GHOST_SUCCESS;
}
<commit_msg>fixed memory leak<commit_after>#include "ghost/timing.h"
#include "ghost/util.h"

#include <map>
#include <vector>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <numeric>
#include <algorithm>

using namespace std;

typedef struct
{
    /**
     * @brief User-defined callback function to compute a region's performance.
     */
    ghost_compute_performance_func perfFunc;
    /**
     * @brief Argument to perfFunc.
     */
    void *perfFuncArg;
    /**
     * @brief The unit of performance.
     */
    const char *perfUnit;
}
ghost_timing_perfFunc;

/**
 * @brief Region timing accumulator
 */
typedef struct
{
    /**
     * @brief The runtimes of this region.
     */
    vector<double> times;
    /**
     * @brief The last start time of this region.
     */
    double start;

    vector<ghost_timing_perfFunc> perfFuncs;
}
ghost_timing_region_accu;

static map<string,ghost_timing_region_accu> timings;
static pthread_mutex_t timingsMutex = PTHREAD_MUTEX_INITIALIZER;

void ghost_timing_tick(const char *tag)
{
    pthread_mutex_lock(&timingsMutex);

    double start = 0.;
    ghost_timing_wc(&start);
    timings[tag].start = start;

    pthread_mutex_unlock(&timingsMutex);
}

void ghost_timing_tock(const char *tag)
{
    pthread_mutex_lock(&timingsMutex);
    double end;
    ghost_timing_wc(&end);
    ghost_timing_region_accu *ti = &timings[string(tag)];
    ti->times.push_back(end-ti->start);
    pthread_mutex_unlock(&timingsMutex);
}

void ghost_timing_set_perfFunc(const char *prefix, const char *tag, ghost_compute_performance_func func, void *arg, size_t sizeofarg, const char *unit)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_UTIL);

    if (!tag) {
        WARNING_LOG("Empty tag! This should not have happened... Prefix is %s",prefix);
        goto out;
    }

    ghost_timing_perfFunc pf;
    pf.perfFunc = func;
    pf.perfUnit = unit;

    ghost_timing_region_accu *regionaccu;
    if (prefix) {
        string fulltag = (string(prefix)+"->"+string(tag));
        regionaccu = &timings[fulltag];
    } else {
        regionaccu = &timings[string(tag)];
    }

    for (std::vector<ghost_timing_perfFunc>::iterator it = regionaccu->perfFuncs.begin();
            it !=regionaccu->perfFuncs.end(); ++it) {
        if (it->perfFunc == func && !strcmp(it->perfUnit,unit)) {
            goto out;
        }
    }

    ghost_malloc((void **)&(pf.perfFuncArg),sizeofarg);
    memcpy(pf.perfFuncArg,arg,sizeofarg);
    regionaccu->perfFuncs.push_back(pf);

out:
    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_UTIL);
}

ghost_error ghost_timing_region_create(ghost_timing_region ** ri, const char *tag)
{
    ghost_timing_region_accu ti = timings[string(tag)];
    if (!ti.times.size()) {
        *ri = NULL;
        return GHOST_SUCCESS;
    }

    ghost_error ret = GHOST_SUCCESS;
    GHOST_CALL_GOTO(ghost_malloc((void **)ri,sizeof(ghost_timing_region)),err,ret);
    (*ri)->nCalls =  ti.times.size();
    (*ri)->minTime = *min_element(ti.times.begin(),ti.times.end());
    (*ri)->maxTime = *max_element(ti.times.begin(),ti.times.end());
    (*ri)->accTime = accumulate(ti.times.begin(),ti.times.end(),0.);
    (*ri)->avgTime = (*ri)->accTime/(*ri)->nCalls;
    if ((*ri)->nCalls > 10) {
        (*ri)->skip10avgTime = accumulate(ti.times.begin()+10,ti.times.end(),0.)/((*ri)->nCalls-10);
    } else {
        (*ri)->skip10avgTime = 0.;
    }

    GHOST_CALL_GOTO(ghost_malloc((void **)(&((*ri)->times)),sizeof(double)*(*ri)->nCalls),err,ret);
    memcpy((*ri)->times,&ti.times[0],(*ri)->nCalls*sizeof(double));

    goto out;

err:
    ERROR_LOG("Freeing region info");
    if (*ri) {
        free((*ri)->times); (*ri)->times = NULL;
    }
    free(*ri); (*ri) = NULL;
out:

    return ret;
}

void ghost_timing_region_destroy(ghost_timing_region * ri)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_UTIL);

    if (ri) {
        free(ri->times); ri->times = NULL;
    }
    free(ri);

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_UTIL);
}

ghost_error ghost_timing_summarystring(char **str)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_UTIL);

    stringstream buffer;
    map<string,ghost_timing_region_accu>::iterator iter;
    vector<ghost_timing_perfFunc>::iterator pf_iter;

    size_t maxRegionLen = 0;
    size_t maxCallsLen = 0;
    size_t maxUnitLen = 0;

    stringstream tmp;
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        int regLen = 0;
        for (pf_iter = iter->second.perfFuncs.begin(); pf_iter != iter->second.perfFuncs.end(); ++pf_iter) {
            if (pf_iter->perfUnit) {
                tmp << iter->first.length();
                maxUnitLen = max(strlen(pf_iter->perfUnit),maxUnitLen);
                regLen = strlen(pf_iter->perfUnit);
                tmp.str("");
            }

        }
        tmp << regLen+iter->first.length();
        maxRegionLen = max(regLen+iter->first.length(),maxRegionLen);
        tmp.str("");

        tmp << iter->second.times.size();
        maxCallsLen = max(maxCallsLen,tmp.str().length());
        tmp.str("");

    }
    if (maxCallsLen < 5) {
        maxCallsLen = 5;
    }

    buffer << left << setw(maxRegionLen+4) << "Region" << right << " | ";
    buffer << setw(maxCallsLen+3) << "Calls | ";
    buffer << "   t_min | ";
    buffer << "   t_max | ";
    buffer << "   t_avg | ";
    buffer << "   t_s10 | ";
    buffer << "   t_acc" << endl;
    buffer << string(maxRegionLen+maxCallsLen+7+5*11,'-') << endl;

    buffer.precision(2);
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        ghost_timing_region *region = NULL;
        ghost_timing_region_create(&region,iter->first.c_str());

        if (region) {
            buffer << scientific << left << setw(maxRegionLen+4) << iter->first << " | " << right << setw(maxCallsLen) <<
                region->nCalls << " | " <<
                region->minTime << " | " <<
                region->maxTime << " | " <<
                region->avgTime << " | " <<
                region->skip10avgTime << " | " <<
                region->accTime << endl;

            ghost_timing_region_destroy(region);
        }
    }

    int printed = 0;
    buffer.precision(2);
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        if (!printed) {
            buffer << endl << endl << left << setw(maxRegionLen+4) << "Region" << right << " | ";
            buffer << setw(maxCallsLen+3) << "Calls | ";
            buffer << "   P_max | ";
            buffer << "   P_min | ";
            buffer << "   P_avg | ";
            buffer << "P_skip10" << endl;;
            buffer << string(maxRegionLen+maxCallsLen+7+4*11,'-') << endl;
        }
        printed = 1;

        ghost_timing_region *region = NULL;
        ghost_timing_region_create(&region,iter->first.c_str());

        if (region) {
            for (pf_iter = iter->second.perfFuncs.begin(); pf_iter != iter->second.perfFuncs.end(); ++pf_iter) {
                ghost_compute_performance_func pf = pf_iter->perfFunc;
                void *pfa = pf_iter->perfFuncArg;

                double P_min = 0., P_max = 0., P_avg = 0., P_skip10 = 0.;
                int err = pf(&P_min,region->maxTime,pfa);
                if (err) {
                    ERROR_LOG("Error in calling performance computation callback!");
                }
                err = pf(&P_max,region->minTime,pfa);
                if (err) {
                    ERROR_LOG("Error in calling performance computation callback!");
                }
                err = pf(&P_avg,region->avgTime,pfa);
                if (err) {
                    ERROR_LOG("Error in calling performance computation callback!");
                }
                if (region->nCalls > 10) {
                    err = pf(&P_skip10,accumulate(iter->second.times.begin()+10,iter->second.times.end(),0.)/(region->nCalls-10),pfa);
                    if (err) {
                        ERROR_LOG("Error in calling performance computation callback!");
                    }
                }

                buffer << scientific << left << setw(maxRegionLen-maxUnitLen+2) << iter->first <<
                    right << "(" << setw(maxUnitLen) << pf_iter->perfUnit << ")" << " | " << setw(maxCallsLen) <<
                    region->nCalls << " | " <<
                    P_max << " | " <<
                    P_min << " | " <<
                    P_avg << " | " <<
                    P_skip10 << endl;
            }
            ghost_timing_region_destroy(region);
        }
    }

    // clear all timings to prevent memory leaks
    for (iter = timings.begin(); iter != timings.end(); ++iter) {
        for (pf_iter = iter->second.perfFuncs.begin(); pf_iter != iter->second.perfFuncs.end(); ++pf_iter) {
            if( pf_iter->perfFuncArg != NULL )
                free(pf_iter->perfFuncArg);
            pf_iter->perfFuncArg = NULL;
        }
    }
    timings.clear();

    GHOST_CALL_RETURN(ghost_malloc((void **)str,buffer.str().length()+1));
    strcpy(*str,buffer.str().c_str());

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_UTIL);
    return GHOST_SUCCESS;
}
<|endoftext|>


<commit_before>// localconsts.hxx -- various constant that are shared
//
// Written by Curtis Olson, started September 2000.
//
// Copyright (C) 2000  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 Library 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.
//
// $Id$

#ifndef _SG_LOCAL_CONSTS_HXX
#define _SG_LOCAL_CONSTS_HXX

// Value of earth flattening parameter from ref [8]
//
//      Note: FP = f
//            E  = 1-f
//            EPS = sqrt(1-(1-f)^2)
//

static const double FP =   0.003352813178;
static const double E  =   0.996647186;
static const double EPS =  0.081819221;
static const double INVG = 0.031080997;

#endif // _SG_LOCAL_CONSTS_HXX
<commit_msg>Remove unused files.<commit_after><|endoftext|>


<commit_before># include "valves.h"
# include <application.h>

// set pins for valve control
int relayPower = D0; // used to allow valves to move, controls power to valve relays
int relayFertilizer = D1; // high = RO water, low = tap water
int relayRO = D2; // high = fertilizer on, low = fertilizer off
int input_ferlizier_on = D3; // valve 1 open when low
int input_ferlizier_off = D4; // valve 1 closed when low
int input_RO_on = D5; // valve 2 open when low
int input_RO_off = D6; // valve 1 closed when low

// initialize variables for
int fertilizerStatus = 0; // 0 = on, 1 = off
int ROStatus = 0; // 0 = on, 1 = off
int fertilizerReply = 0;
int ROReply = 0;

Timer relayTimer(15000, relaysOff);

//setup for program input pins to check status of valves
void setupPinsValves()
{
  pinMode(relayPower, OUTPUT);
  pinMode(relayFertilizer, OUTPUT);
  pinMode(relayRO, OUTPUT);
  pinMode(input_ferlizier_on, INPUT_PULLUP);
  pinMode(input_ferlizier_off, INPUT_PULLUP);
  pinMode(input_RO_on, INPUT_PULLUP);
  pinMode(input_RO_off, INPUT_PULLUP);
  digitalWrite(relayFertilizer, HIGH);
  digitalWrite(relayRO, HIGH);
  digitalWrite(relayPower, HIGH);
  // set up function called by Indigo to control valves
  Particle.function("valves", messageValves);
  Particle.variable("fertPosit", fertilizerReply);
  Particle.variable("ROPosit", ROReply);
  valves(fertilizerStatus, ROStatus); // call function to set valves to startup position
}

int messageValves(String command)
{

  if ((command.indexOf("RO") != -1) || (command.indexOf("Fertilizer") != -1))
  {
    if (command.indexOf("RO") != -1)
    {
      int index = command.indexOf("RO");
      Particle.publish("RO valve", command.substring(index+3,index+6));
      if (command.substring(index+3,index+5) == "on")
      {
         ROStatus = 0;
      }
      else if (command.substring(index+3,index+6) == "off")
      {
        ROStatus = 1;
      }
      else return -1;
    }
    if (command.indexOf("Fertilizer") != -1)
    {
      int index = command.indexOf("Fertilizer");
      Particle.publish("Fert valve", command.substring(index+11,index+14));
      if (command.substring(index+11,index+13) == "on")
      {
        fertilizerStatus = 0;
      }
      else if (command.substring(index+11,index+14) == "off")
      {
        fertilizerStatus = 1;
      }
      else return -1;
    }
    Particle.publish("valves was called");
    valves(fertilizerStatus, ROStatus);
    return 1;
  }
  else return -1;
}

void valves(int fertilizer,int RO)
{
    if (fertilizer == 0)
    {
      digitalWrite(relayFertilizer, LOW);
    }
    else if (fertilizer == 1)
    {
      digitalWrite(relayFertilizer, HIGH);
    }
    else
    {
        //log error TBD
    }
    if (RO == 0)
    {
      digitalWrite(relayRO, LOW);
    }
    else if (RO == 1)
    {
      digitalWrite(relayRO, HIGH);
    }
    else
    {
        //log error TBD
    }
    digitalWrite(relayPower, LOW);
    relayTimer.start();

}

void relaysOff()
{
    digitalWrite(relayPower, HIGH);
    digitalWrite(relayFertilizer, HIGH);
    digitalWrite(relayRO, HIGH);
    int _fertilizer_on = digitalRead(input_ferlizier_on);
    int _fertilizer_off = digitalRead(input_ferlizier_off);
    int _RO_on = digitalRead(input_RO_on);
    int _RO_off = digitalRead(input_RO_off);
    if (_fertilizer_on == LOW && _fertilizer_off == HIGH)
    {
        Particle.publish("message", "Fertilizer on");
        fertilizerReply = 1;
    }
    else if (_fertilizer_on == HIGH && _fertilizer_off == LOW)
    {
        Particle.publish("message", "Fertilizer off");
        fertilizerReply = 0;
    }
    else
    {
        Particle.publish("message", "failed");
    }
    if (_RO_on == LOW && _RO_off == HIGH)
    {
        Particle.publish("message", "RO on");
        ROReply = 1;
    }
    else if (_RO_on == HIGH && _RO_off == LOW)
    {
        Particle.publish("message", "RO off");
        ROReply = 0;
    }
    else
    {
        Particle.publish("message", "failed");
    }
    relayTimer.reset();
    relayTimer.stop();
}
<commit_msg>revered valve inputs to pulldown<commit_after># include "valves.h"
# include <application.h>

// set pins for valve control
int relayPower = D0; // used to allow valves to move, controls power to valve relays
int relayFertilizer = D1; // high = RO water, low = tap water
int relayRO = D2; // high = fertilizer on, low = fertilizer off
int input_ferlizier_on = D3; // valve 1 open when low
int input_ferlizier_off = D4; // valve 1 closed when low
int input_RO_on = D5; // valve 2 open when low
int input_RO_off = D6; // valve 1 closed when low

// initialize variables for
int fertilizerStatus = 0; // 0 = on, 1 = off
int ROStatus = 0; // 0 = on, 1 = off
int fertilizerReply = 0;
int ROReply = 0;

Timer relayTimer(15000, relaysOff);

//setup for program input pins to check status of valves
void setupPinsValves()
{
  pinMode(relayPower, OUTPUT);
  pinMode(relayFertilizer, OUTPUT);
  pinMode(relayRO, OUTPUT);
  pinMode(input_ferlizier_on, INPUT_PULLDOWN);
  pinMode(input_ferlizier_off, INPUT_PULLDOWN);
  pinMode(input_RO_on, INPUT_PULLDOWN);
  pinMode(input_RO_off, INPUT_PULLDown);
  digitalWrite(relayFertilizer, HIGH);
  digitalWrite(relayRO, HIGH);
  digitalWrite(relayPower, HIGH);
  // set up function called by Indigo to control valves
  Particle.function("valves", messageValves);
  Particle.variable("fertPosit", fertilizerReply);
  Particle.variable("ROPosit", ROReply);
  valves(fertilizerStatus, ROStatus); // call function to set valves to startup position
}

int messageValves(String command)
{

  if ((command.indexOf("RO") != -1) || (command.indexOf("Fertilizer") != -1))
  {
    if (command.indexOf("RO") != -1)
    {
      int index = command.indexOf("RO");
      Particle.publish("RO valve", command.substring(index+3,index+6));
      if (command.substring(index+3,index+5) == "on")
      {
         ROStatus = 0;
      }
      else if (command.substring(index+3,index+6) == "off")
      {
        ROStatus = 1;
      }
      else return -1;
    }
    if (command.indexOf("Fertilizer") != -1)
    {
      int index = command.indexOf("Fertilizer");
      Particle.publish("Fert valve", command.substring(index+11,index+14));
      if (command.substring(index+11,index+13) == "on")
      {
        fertilizerStatus = 0;
      }
      else if (command.substring(index+11,index+14) == "off")
      {
        fertilizerStatus = 1;
      }
      else return -1;
    }
    Particle.publish("valves was called");
    valves(fertilizerStatus, ROStatus);
    return 1;
  }
  else return -1;
}

void valves(int fertilizer,int RO)
{
    if (fertilizer == 0)
    {
      digitalWrite(relayFertilizer, LOW);
    }
    else if (fertilizer == 1)
    {
      digitalWrite(relayFertilizer, HIGH);
    }
    else
    {
        //log error TBD
    }
    if (RO == 0)
    {
      digitalWrite(relayRO, LOW);
    }
    else if (RO == 1)
    {
      digitalWrite(relayRO, HIGH);
    }
    else
    {
        //log error TBD
    }
    digitalWrite(relayPower, LOW);
    relayTimer.start();

}

void relaysOff()
{
    digitalWrite(relayPower, HIGH);
    digitalWrite(relayFertilizer, HIGH);
    digitalWrite(relayRO, HIGH);
    int _fertilizer_on = digitalRead(input_ferlizier_on);
    int _fertilizer_off = digitalRead(input_ferlizier_off);
    int _RO_on = digitalRead(input_RO_on);
    int _RO_off = digitalRead(input_RO_off);
    if (_fertilizer_on == HIGH && _fertilizer_off == LOW)
    {
        Particle.publish("message", "Fertilizer on");
        fertilizerReply = 1;
    }
    else if (_fertilizer_on == LOW && _fertilizer_off == HIGH)
    {
        Particle.publish("message", "Fertilizer off");
        fertilizerReply = 0;
    }
    else
    {
        Particle.publish("message", "failed");
    }
    if (_RO_on == HIGH && _RO_off == LOW)
    {
        Particle.publish("message", "RO on");
        ROReply = 1;
    }
    else if (_RO_on == LOW && _RO_off == HIGH)
    {
        Particle.publish("message", "RO off");
        ROReply = 0;
    }
    else
    {
        Particle.publish("message", "failed");
    }
    relayTimer.reset();
    relayTimer.stop();
}
<|endoftext|>