<commit_before>#pragma once
#include "common.hpp"
#include <algorithm> // for std::min, max

namespace rack {
namespace math {

////////////////////
// basic integer functions
////////////////////

/** Returns true if x is odd */
inline bool isEven(int x) {
	return x % 2 == 0;
}

/** Returns true if x is odd */
inline bool isOdd(int x) {
	return x % 2 != 0;
}

/** Limits `x` between `a` and `b`
Assumes a <= b
*/
inline int clamp(int x, int a, int b) {
	return std::min(std::max(x, a), b);
}

/** Limits `x` between `a` and `b`
If a > b, switches the two values
*/
inline int clampBetween(int x, int a, int b) {
	return clamp(x, std::min(a, b), std::max(a, b));
}

/** Euclidean modulus. Always returns 0 <= mod < b.
b must be positive.
*/
inline int eucMod(int a, int b) {
	int mod = a % b;
	if (mod < 0) {
		mod += b;
	}
	return mod;
}

/** Euclidean division.
b must be positive.
*/
inline int eucDiv(int a, int b) {
	int div = a / b;
	int mod = a % b;
	if (mod < 0) {
		div -= 1;
	}
	return div;
}

inline void eucDivMod(int a, int b, int *div, int *mod) {
	*div = a / b;
	*mod = a % b;
	if (*mod < 0) {
		*div -= 1;
		*mod += b;
	}
}

/** Returns floor(log_2(n)), or 0 if n == 1.
*/
inline int log2(int n) {
	int i = 0;
	while (n >>= 1) {
		i++;
	}
	return i;
}

inline bool isPow2(int n) {
	return n > 0 && (n & (n - 1)) == 0;
}

////////////////////
// basic float functions
////////////////////

/** Limits `x` between `a` and `b`
Assumes a <= b
*/
inline float clamp(float x, float a, float b) {
	return std::min(std::max(x, a), b);
}

/** Limits `x` between `a` and `b`
If a > b, switches the two values
*/
inline float clampBetween(float x, float a, float b) {
	return clamp(x, std::min(a, b), std::max(a, b));
}

/** Returns 1 for positive numbers, -1 for negative numbers, and 0 for zero */
inline float sgn(float x) {
	return x > 0.f ? 1.f : x < 0.f ? -1.f : 0.f;
}

inline float eucMod(float a, float base) {
	float mod = std::fmod(a, base);
	return (mod >= 0.0f) ? mod : mod + base;
}

inline bool isNear(float a, float b, float epsilon = 1.0e-6f) {
	return std::abs(a - b) <= epsilon;
}

/** If the magnitude of x if less than epsilon, return 0 */
inline float chop(float x, float epsilon = 1.0e-6f) {
	return isNear(x, 0.f, epsilon) ? 0.f : x;
}

inline float rescale(float x, float a, float b, float yMin, float yMax) {
	return yMin + (x - a) / (b - a) * (yMax - yMin);
}

inline float crossfade(float a, float b, float frac) {
	return a + frac * (b - a);
}

/** Linearly interpolate an array `p` with index `x`
Assumes that the array at `p` is of length at least floor(x)+1.
*/
inline float interpolateLinear(const float *p, float x) {
	int xi = x;
	float xf = x - xi;
	return crossfade(p[xi], p[xi+1], xf);
}

/** Complex multiply c = a * b
Arguments may be the same pointers
i.e. cmultf(&ar, &ai, ar, ai, br, bi)
*/
inline void cmult(float *cr, float *ci, float ar, float ai, float br, float bi) {
	*cr = ar * br - ai * bi;
	*ci = ar * bi + ai * br;
}

////////////////////
// 2D vector and rectangle
////////////////////

struct Rect;

struct Vec {
	float x = 0.f;
	float y = 0.f;

	Vec() {}
	Vec(float x, float y) : x(x), y(y) {}

	Vec neg() const {
		return Vec(-x, -y);
	}
	Vec plus(Vec b) const {
		return Vec(x + b.x, y + b.y);
	}
	Vec minus(Vec b) const {
		return Vec(x - b.x, y - b.y);
	}
	Vec mult(float s) const {
		return Vec(x * s, y * s);
	}
	Vec mult(Vec b) const {
		return Vec(x * b.x, y * b.y);
	}
	Vec div(float s) const {
		return Vec(x / s, y / s);
	}
	Vec div(Vec b) const {
		return Vec(x / b.x, y / b.y);
	}
	float dot(Vec b) const {
		return x * b.x + y * b.y;
	}
	float norm() const {
		return std::hypotf(x, y);
	}
	Vec flip() const {
		return Vec(y, x);
	}
	Vec min(Vec b) const {
		return Vec(std::min(x, b.x), std::min(y, b.y));
	}
	Vec max(Vec b) const {
		return Vec(std::max(x, b.x), std::max(y, b.y));
	}
	Vec round() const {
		return Vec(std::round(x), std::round(y));
	}
	Vec floor() const {
		return Vec(std::floor(x), std::floor(y));
	}
	Vec ceil() const {
		return Vec(std::ceil(x), std::ceil(y));
	}
	bool isEqual(Vec b) const {
		return x == b.x && y == b.y;
	}
	bool isZero() const {
		return x == 0.0f && y == 0.0f;
	}
	bool isFinite() const {
		return std::isfinite(x) && std::isfinite(y);
	}
	Vec clamp(Rect bound) const;
	Vec clampBetween(Rect bound) const;
	DEPRECATED Vec clamp2(Rect bound) const;
};

struct Rect {
	Vec pos;
	Vec size;

	Rect() {}
	Rect(Vec pos, Vec size) : pos(pos), size(size) {}
	/** Constructs a Rect from the upper-left position `a` and lower-right pos `b` */
	static Rect fromMinMax(Vec a, Vec b) {
		return Rect(a, b.minus(a));
	}

	/** Returns whether this Rect contains an entire point, inclusive on the top/left, non-inclusive on the bottom/right */
	bool contains(Vec v) const {
		return pos.x <= v.x && v.x < pos.x + size.x
			&& pos.y <= v.y && v.y < pos.y + size.y;
	}
	/** Returns whether this Rect contains an entire Rect */
	bool contains(Rect r) const {
		return pos.x <= r.pos.x && r.pos.x + r.size.x <= pos.x + size.x
			&& pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y;
	}
	/** Returns whether this Rect overlaps with another Rect */
	bool intersects(Rect r) const {
		return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x)
			&& (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y);
	}
	bool isEqual(Rect r) const {
		return pos.isEqual(r.pos) && size.isEqual(r.size);
	}
	float getLeft() const {
		return pos.x + size.x;
	}
	float getBottom() const {
		return pos.y + size.y;
	}
	Vec getCenter() const {
		return pos.plus(size.mult(0.5f));
	}
	Vec getTopLeft() const {
		return pos;
	}
	Vec getTopRight() const {
		return pos.plus(Vec(size.x, 0.f));
	}
	Vec getBottomLeft() const {
		return pos.plus(Vec(0.f, size.y));
	}
	Vec getBottomRight() const {
		return pos.plus(size);
	}
	/** Clamps the edges of the rectangle to fit within a bound */
	Rect clamp(Rect bound) const {
		Rect r;
		r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x);
		r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y);
		r.size.x = math::clamp(pos.x + size.x, bound.pos.x, bound.pos.x + bound.size.x) - r.pos.x;
		r.size.y = math::clamp(pos.y + size.y, bound.pos.y, bound.pos.y + bound.size.y) - r.pos.y;
		return r;
	}
	/** Nudges the position to fix inside a bounding box */
	Rect nudge(Rect bound) const {
		Rect r;
		r.size = size;
		r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x - size.x);
		r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y);
		return r;
	}
	/** Expands this Rect to contain `other` */
	Rect expand(Rect other) const {
		Rect r;
		r.pos.x = std::min(pos.x, other.pos.x);
		r.pos.y = std::min(pos.y, other.pos.y);
		r.size.x = std::max(pos.x + size.x, other.pos.x + other.size.x) - r.pos.x;
		r.size.y = std::max(pos.y + size.y, other.pos.y + other.size.y) - r.pos.y;
		return r;
	}
	/** Returns a Rect with its position set to zero */
	Rect zeroPos() const {
		return Rect(Vec(), size);
	}
	/** Expands each corner
	Use a negative delta to shrink.
	*/
	Rect grow(Vec delta) const {
		Rect r;
		r.pos = pos.minus(delta);
		r.size = size.plus(delta.mult(2.f));
		return r;
	}
};

inline Vec Vec::clamp(Rect bound) const {
	return Vec(
		math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x),
		math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y));
}

inline Vec Vec::clampBetween(Rect bound) const {
	return Vec(
		math::clampBetween(x, bound.pos.x, bound.pos.x + bound.size.x),
		math::clampBetween(y, bound.pos.y, bound.pos.y + bound.size.y));
}

inline Vec Vec::clamp2(Rect bound) const {return clampBetween(bound);}

} // namespace math
} // namespace rack
<commit_msg>Add Vec::rotate()<commit_after>#pragma once
#include "common.hpp"
#include <algorithm> // for std::min, max

namespace rack {
namespace math {

////////////////////
// basic integer functions
////////////////////

/** Returns true if x is odd */
inline bool isEven(int x) {
	return x % 2 == 0;
}

/** Returns true if x is odd */
inline bool isOdd(int x) {
	return x % 2 != 0;
}

/** Limits `x` between `a` and `b`
Assumes a <= b
*/
inline int clamp(int x, int a, int b) {
	return std::min(std::max(x, a), b);
}

/** Limits `x` between `a` and `b`
If a > b, switches the two values
*/
inline int clampBetween(int x, int a, int b) {
	return clamp(x, std::min(a, b), std::max(a, b));
}

/** Euclidean modulus. Always returns 0 <= mod < b.
b must be positive.
*/
inline int eucMod(int a, int b) {
	int mod = a % b;
	if (mod < 0) {
		mod += b;
	}
	return mod;
}

/** Euclidean division.
b must be positive.
*/
inline int eucDiv(int a, int b) {
	int div = a / b;
	int mod = a % b;
	if (mod < 0) {
		div -= 1;
	}
	return div;
}

inline void eucDivMod(int a, int b, int *div, int *mod) {
	*div = a / b;
	*mod = a % b;
	if (*mod < 0) {
		*div -= 1;
		*mod += b;
	}
}

/** Returns floor(log_2(n)), or 0 if n == 1.
*/
inline int log2(int n) {
	int i = 0;
	while (n >>= 1) {
		i++;
	}
	return i;
}

inline bool isPow2(int n) {
	return n > 0 && (n & (n - 1)) == 0;
}

////////////////////
// basic float functions
////////////////////

/** Limits `x` between `a` and `b`
Assumes a <= b
*/
inline float clamp(float x, float a, float b) {
	return std::min(std::max(x, a), b);
}

/** Limits `x` between `a` and `b`
If a > b, switches the two values
*/
inline float clampBetween(float x, float a, float b) {
	return clamp(x, std::min(a, b), std::max(a, b));
}

/** Returns 1 for positive numbers, -1 for negative numbers, and 0 for zero */
inline float sgn(float x) {
	return x > 0.f ? 1.f : x < 0.f ? -1.f : 0.f;
}

inline float eucMod(float a, float base) {
	float mod = std::fmod(a, base);
	return (mod >= 0.0f) ? mod : mod + base;
}

inline bool isNear(float a, float b, float epsilon = 1.0e-6f) {
	return std::abs(a - b) <= epsilon;
}

/** If the magnitude of x if less than epsilon, return 0 */
inline float chop(float x, float epsilon = 1.0e-6f) {
	return isNear(x, 0.f, epsilon) ? 0.f : x;
}

inline float rescale(float x, float a, float b, float yMin, float yMax) {
	return yMin + (x - a) / (b - a) * (yMax - yMin);
}

inline float crossfade(float a, float b, float frac) {
	return a + frac * (b - a);
}

/** Linearly interpolate an array `p` with index `x`
Assumes that the array at `p` is of length at least floor(x)+1.
*/
inline float interpolateLinear(const float *p, float x) {
	int xi = x;
	float xf = x - xi;
	return crossfade(p[xi], p[xi+1], xf);
}

/** Complex multiply c = a * b
Arguments may be the same pointers
i.e. cmultf(&ar, &ai, ar, ai, br, bi)
*/
inline void cmult(float *cr, float *ci, float ar, float ai, float br, float bi) {
	*cr = ar * br - ai * bi;
	*ci = ar * bi + ai * br;
}

////////////////////
// 2D vector and rectangle
////////////////////

struct Rect;

struct Vec {
	float x = 0.f;
	float y = 0.f;

	Vec() {}
	Vec(float x, float y) : x(x), y(y) {}

	/** Negates the vector
	Equivalent to a reflection across the y=-x line.
	*/
	Vec neg() const {
		return Vec(-x, -y);
	}
	Vec plus(Vec b) const {
		return Vec(x + b.x, y + b.y);
	}
	Vec minus(Vec b) const {
		return Vec(x - b.x, y - b.y);
	}
	Vec mult(float s) const {
		return Vec(x * s, y * s);
	}
	Vec mult(Vec b) const {
		return Vec(x * b.x, y * b.y);
	}
	Vec div(float s) const {
		return Vec(x / s, y / s);
	}
	Vec div(Vec b) const {
		return Vec(x / b.x, y / b.y);
	}
	float dot(Vec b) const {
		return x * b.x + y * b.y;
	}
	float norm() const {
		return std::hypotf(x, y);
	}
	/** Rotates counterclockwise in radians */
	Vec rotate(float angle) {
		float sin = std::sin(angle);
		float cos = std::cos(angle);
		return Vec(x * cos - y * sin, x * sin + y * cos);
	}
	/** Swaps the coordinates
	Equivalent to a reflection across the y=x line.
	*/
	Vec flip() const {
		return Vec(y, x);
	}
	Vec min(Vec b) const {
		return Vec(std::min(x, b.x), std::min(y, b.y));
	}
	Vec max(Vec b) const {
		return Vec(std::max(x, b.x), std::max(y, b.y));
	}
	Vec round() const {
		return Vec(std::round(x), std::round(y));
	}
	Vec floor() const {
		return Vec(std::floor(x), std::floor(y));
	}
	Vec ceil() const {
		return Vec(std::ceil(x), std::ceil(y));
	}
	bool isEqual(Vec b) const {
		return x == b.x && y == b.y;
	}
	bool isZero() const {
		return x == 0.0f && y == 0.0f;
	}
	bool isFinite() const {
		return std::isfinite(x) && std::isfinite(y);
	}
	Vec clamp(Rect bound) const;
	Vec clampBetween(Rect bound) const;
	DEPRECATED Vec clamp2(Rect bound) const;
};

struct Rect {
	Vec pos;
	Vec size;

	Rect() {}
	Rect(Vec pos, Vec size) : pos(pos), size(size) {}
	/** Constructs a Rect from the upper-left position `a` and lower-right pos `b` */
	static Rect fromMinMax(Vec a, Vec b) {
		return Rect(a, b.minus(a));
	}

	/** Returns whether this Rect contains an entire point, inclusive on the top/left, non-inclusive on the bottom/right */
	bool contains(Vec v) const {
		return pos.x <= v.x && v.x < pos.x + size.x
			&& pos.y <= v.y && v.y < pos.y + size.y;
	}
	/** Returns whether this Rect contains an entire Rect */
	bool contains(Rect r) const {
		return pos.x <= r.pos.x && r.pos.x + r.size.x <= pos.x + size.x
			&& pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y;
	}
	/** Returns whether this Rect overlaps with another Rect */
	bool intersects(Rect r) const {
		return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x)
			&& (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y);
	}
	bool isEqual(Rect r) const {
		return pos.isEqual(r.pos) && size.isEqual(r.size);
	}
	float getLeft() const {
		return pos.x + size.x;
	}
	float getBottom() const {
		return pos.y + size.y;
	}
	Vec getCenter() const {
		return pos.plus(size.mult(0.5f));
	}
	Vec getTopLeft() const {
		return pos;
	}
	Vec getTopRight() const {
		return pos.plus(Vec(size.x, 0.f));
	}
	Vec getBottomLeft() const {
		return pos.plus(Vec(0.f, size.y));
	}
	Vec getBottomRight() const {
		return pos.plus(size);
	}
	/** Clamps the edges of the rectangle to fit within a bound */
	Rect clamp(Rect bound) const {
		Rect r;
		r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x);
		r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y);
		r.size.x = math::clamp(pos.x + size.x, bound.pos.x, bound.pos.x + bound.size.x) - r.pos.x;
		r.size.y = math::clamp(pos.y + size.y, bound.pos.y, bound.pos.y + bound.size.y) - r.pos.y;
		return r;
	}
	/** Nudges the position to fix inside a bounding box */
	Rect nudge(Rect bound) const {
		Rect r;
		r.size = size;
		r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x - size.x);
		r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y);
		return r;
	}
	/** Expands this Rect to contain `other` */
	Rect expand(Rect other) const {
		Rect r;
		r.pos.x = std::min(pos.x, other.pos.x);
		r.pos.y = std::min(pos.y, other.pos.y);
		r.size.x = std::max(pos.x + size.x, other.pos.x + other.size.x) - r.pos.x;
		r.size.y = std::max(pos.y + size.y, other.pos.y + other.size.y) - r.pos.y;
		return r;
	}
	/** Returns a Rect with its position set to zero */
	Rect zeroPos() const {
		return Rect(Vec(), size);
	}
	/** Expands each corner
	Use a negative delta to shrink.
	*/
	Rect grow(Vec delta) const {
		Rect r;
		r.pos = pos.minus(delta);
		r.size = size.plus(delta.mult(2.f));
		return r;
	}
};

inline Vec Vec::clamp(Rect bound) const {
	return Vec(
		math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x),
		math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y));
}

inline Vec Vec::clampBetween(Rect bound) const {
	return Vec(
		math::clampBetween(x, bound.pos.x, bound.pos.x + bound.size.x),
		math::clampBetween(y, bound.pos.y, bound.pos.y + bound.size.y));
}

inline Vec Vec::clamp2(Rect bound) const {return clampBetween(bound);}

} // namespace math
} // namespace rack
<|endoftext|>


<commit_before>/*
    Copyright 2002-2013 CEA LIST

    This file is part of LIMA.

    LIMA is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    LIMA 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with LIMA.  If not, see <http://www.gnu.org/licenses/>
*/
/**
  * @file       configurationStructure.cpp
  * @brief      originally detectConfigurationStructure.cpp in detectlibraries
  * @date       begin Mon Oct, 13 2003 (ven oct 18 2002)
  * @author     Gael de Chalendar <Gael.de-Chalendar@cea.fr>

  *             copyright (C) 2002-2003 by CEA
  */

#include "common/LimaCommon.h"
#include "xmlConfigurationFileExceptions.h"
#include "configurationStructure.h"

#include <iostream>

using namespace std;

namespace Lima
{
namespace Common
{
namespace XMLConfigurationFiles
{

ConfigurationStructure::ConfigurationStructure() : std::map< std::string, ModuleConfigurationStructure >()
{}

ConfigurationStructure::~ConfigurationStructure()
{}

void ConfigurationStructure::addParamValuePairForModuleAndGroup(const string &param,
    const string &value, const string &moduleName, const string & group)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding param '" << moduleName.c_str() << "': no such module !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addParamValuePairForGroup(param, value, group);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addListNamedForModuleAndGroup(const string &listName,
    const string &moduleName, const string & groupName)

{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding param '" << moduleName.c_str() << "': no such module !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addListNamedForGroup(listName, groupName);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addItemInListNamedForModuleAndGroup(const string& item,
    const string &listName, const string &moduleName, const string & groupName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding param '" << moduleName.c_str() << "': no such module !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addItemInListNamedForGroup(item, listName, groupName);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addGroupNamedForModuleNamed(const string& groupName,
    const string& moduleName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addGroupNamed(groupName);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addMapNamedForModuleAndGroup(const std::string &mapName, const std::string& moduleName, const std::string& groupName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding map '" << mapName.c_str() << "' in group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  itC->second.addMapInGroup(mapName,groupName);
}

void ConfigurationStructure::addEntryInMapNamedForModuleAndGroup(const std::string& entryKey,const std::string& entryValue,const std::string& mapName,const std::string& moduleName,const std::string& groupName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding mapEntry in '" << mapName.c_str() << "' in group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  itC->second.addEntryInMapInGroup(entryKey,entryValue,mapName,groupName);
}

void ConfigurationStructure::addAttributeForGroupInModule(const std::string& attKey,const std::string& attValue,const std::string& groupName,const std::string& moduleName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding mapattribute in group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  LDEBUG << "add attribute: module='"<<moduleName.c_str()<<"' group='"<<groupName.c_str()<<"' attribute='"<<attKey.c_str()<<"' value='"<<attValue.c_str()<<"'" ;
  itC->second.addAttributeInGroup(attKey,attValue,groupName);
}

GroupConfigurationStructure& ConfigurationStructure::
getGroupConf(const std::string& moduleName,
             const std::string& group)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "'No such module '" << moduleName.c_str() << "'" ;
    throw NoSuchModule(moduleName);
  }
  return (*itC).second.getGroupNamed(group) ;
}

void ConfigurationStructure::
addListOfItemsForModuleAndGroup(const std::string &listName,
                                const std::string& moduleName,
                                const std::string& group)
{
  getGroupConf(moduleName,group).addListOfItems(listName);
}

void ConfigurationStructure::
addItemInListOfItemsForModuleAndGroup(const ItemWithAttributes& item,
                                      const std::string &listName,
                                      const std::string& moduleName,
                                      const std::string& group)
{
  getGroupConf(moduleName,group).addItemInListOfItems(listName,item);
}

void ConfigurationStructure::
addMapOfItemsForModuleAndGroup(const std::string &mapName,
                               const std::string& moduleName,
                               const std::string& group)
{
  getGroupConf(moduleName,group).addMapOfItems(mapName);
}

void ConfigurationStructure::
addEntryInMapOfItemsForModuleAndGroup(const std::string& key,
                                      const ItemWithAttributes& item,
                                      const std::string& mapName,
                                      const std::string& moduleName,
                                      const std::string& group)
{
  getGroupConf(moduleName,group).addEntryInMapOfItems(mapName,key,item);
}

std::ostream& operator<<(std::ostream &os, const ConfigurationStructure& conf)
{
  for (ConfigurationStructure::const_iterator it = conf.begin(); it != conf.end(); it++)
  {
    const ModuleConfigurationStructure& dmcs((*it).second);
    os << (*it).first << std::endl << dmcs;
  }
  return os;
}

void ConfigurationStructure::
changeListToListOfItems(const std::string &listName,
                        const std::string& moduleName,
                        const std::string& group)
{
  getGroupConf(moduleName,group).changeListToListOfItems(listName);
}

void ConfigurationStructure::
changeMapToMapOfItems(const std::string &mapName,
                      const std::string& moduleName,
                      const std::string& group)
{
  getGroupConf(moduleName,group).changeMapToMapOfItems(mapName);
}

} //closing namespace XMLConfigurationFiles
} //closing namespace Common
} //closing namespace Lima
<commit_msg>Move a logger init near its use<commit_after>/*
    Copyright 2002-2013 CEA LIST

    This file is part of LIMA.

    LIMA is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    LIMA 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with LIMA.  If not, see <http://www.gnu.org/licenses/>
*/
/**
  * @file       configurationStructure.cpp
  * @brief      originally detectConfigurationStructure.cpp in detectlibraries
  * @date       begin Mon Oct, 13 2003 (ven oct 18 2002)
  * @author     Gael de Chalendar <Gael.de-Chalendar@cea.fr>

  *             copyright (C) 2002-2003 by CEA
  */

#include "common/LimaCommon.h"
#include "xmlConfigurationFileExceptions.h"
#include "configurationStructure.h"

#include <iostream>

using namespace std;

namespace Lima
{
namespace Common
{
namespace XMLConfigurationFiles
{

ConfigurationStructure::ConfigurationStructure() : std::map< std::string, ModuleConfigurationStructure >()
{}

ConfigurationStructure::~ConfigurationStructure()
{}

void ConfigurationStructure::addParamValuePairForModuleAndGroup(const string &param,
    const string &value, const string &moduleName, const string & group)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding param '" << moduleName.c_str() << "': no such module !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addParamValuePairForGroup(param, value, group);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addListNamedForModuleAndGroup(const string &listName,
    const string &moduleName, const string & groupName)

{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding param '" << moduleName.c_str() << "': no such module !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addListNamedForGroup(listName, groupName);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addItemInListNamedForModuleAndGroup(const string& item,
    const string &listName, const string &moduleName, const string & groupName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding param '" << moduleName.c_str() << "': no such module !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addItemInListNamedForGroup(item, listName, groupName);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addGroupNamedForModuleNamed(const string& groupName,
    const string& moduleName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  ModuleConfigurationStructure& moduleGroups = (*itC).second;
  moduleGroups.addGroupNamed(groupName);
//   erase(moduleName);
//   insert(make_pair(moduleName, moduleGroups));
}

void ConfigurationStructure::addMapNamedForModuleAndGroup(const std::string &mapName, const std::string& moduleName, const std::string& groupName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding map '" << mapName.c_str() << "' in group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  itC->second.addMapInGroup(mapName,groupName);
}

void ConfigurationStructure::addEntryInMapNamedForModuleAndGroup(const std::string& entryKey,const std::string& entryValue,const std::string& mapName,const std::string& moduleName,const std::string& groupName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding mapEntry in '" << mapName.c_str() << "' in group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  itC->second.addEntryInMapInGroup(entryKey,entryValue,mapName,groupName);
}

void ConfigurationStructure::addAttributeForGroupInModule(const std::string& attKey,const std::string& attValue,const std::string& groupName,const std::string& moduleName)
{
  XMLCFGLOGINIT;
  iterator itC = find(moduleName);
  if (itC == end())
  {
    LERROR << "Error adding mapattribute in group '" << groupName.c_str() << "': no such module '" << moduleName.c_str() << "' !" ;
    throw NoSuchModule(moduleName);
  }
  LDEBUG << "add attribute: module='"<<moduleName.c_str()<<"' group='"<<groupName.c_str()<<"' attribute='"<<attKey.c_str()<<"' value='"<<attValue.c_str()<<"'" ;
  itC->second.addAttributeInGroup(attKey,attValue,groupName);
}

GroupConfigurationStructure& ConfigurationStructure::
getGroupConf(const std::string& moduleName,
             const std::string& group)
{
  iterator itC = find(moduleName);
  if (itC == end())
  {
    XMLCFGLOGINIT;
    LERROR << "'No such module '" << moduleName.c_str() << "'" ;
    throw NoSuchModule(moduleName);
  }
  return (*itC).second.getGroupNamed(group) ;
}

void ConfigurationStructure::
addListOfItemsForModuleAndGroup(const std::string &listName,
                                const std::string& moduleName,
                                const std::string& group)
{
  getGroupConf(moduleName,group).addListOfItems(listName);
}

void ConfigurationStructure::
addItemInListOfItemsForModuleAndGroup(const ItemWithAttributes& item,
                                      const std::string &listName,
                                      const std::string& moduleName,
                                      const std::string& group)
{
  getGroupConf(moduleName,group).addItemInListOfItems(listName,item);
}

void ConfigurationStructure::
addMapOfItemsForModuleAndGroup(const std::string &mapName,
                               const std::string& moduleName,
                               const std::string& group)
{
  getGroupConf(moduleName,group).addMapOfItems(mapName);
}

void ConfigurationStructure::
addEntryInMapOfItemsForModuleAndGroup(const std::string& key,
                                      const ItemWithAttributes& item,
                                      const std::string& mapName,
                                      const std::string& moduleName,
                                      const std::string& group)
{
  getGroupConf(moduleName,group).addEntryInMapOfItems(mapName,key,item);
}

std::ostream& operator<<(std::ostream &os, const ConfigurationStructure& conf)
{
  for (ConfigurationStructure::const_iterator it = conf.begin(); it != conf.end(); it++)
  {
    const ModuleConfigurationStructure& dmcs((*it).second);
    os << (*it).first << std::endl << dmcs;
  }
  return os;
}

void ConfigurationStructure::
changeListToListOfItems(const std::string &listName,
                        const std::string& moduleName,
                        const std::string& group)
{
  getGroupConf(moduleName,group).changeListToListOfItems(listName);
}

void ConfigurationStructure::
changeMapToMapOfItems(const std::string &mapName,
                      const std::string& moduleName,
                      const std::string& group)
{
  getGroupConf(moduleName,group).changeMapToMapOfItems(mapName);
}

} //closing namespace XMLConfigurationFiles
} //closing namespace Common
} //closing namespace Lima
<|endoftext|>


<commit_before>//===--- LoadCopyToLoadBorrowOpt.cpp --------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Defines the main optimization that converts load [copy] -> load_borrow if we
/// can prove that the +1 is not actually needed and the memory loaded from is
/// never written to while the load [copy]'s object value is being used.
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-semantic-arc-opts"

#include "OwnershipLiveRange.h"
#include "SemanticARCOptVisitor.h"
#include "swift/SIL/LinearLifetimeChecker.h"
#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/OwnershipUtils.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
#include "llvm/Support/CommandLine.h"

using namespace swift;
using namespace swift::semanticarc;

//===----------------------------------------------------------------------===//
//                              Memory Analysis
//===----------------------------------------------------------------------===//

namespace {

/// A class that computes in a flow insensitive way if we can prove that our
/// storage is either never written to, or is initialized exactly once and never
/// written to again. In both cases, we can convert load [copy] -> load_borrow
/// safely.
class StorageGuaranteesLoadVisitor
    : public AccessUseDefChainVisitor<StorageGuaranteesLoadVisitor> {
  // The context that contains global state used across all semantic arc
  // optimizations.
  Context &ctx;

  // The live range of the original load.
  const OwnershipLiveRange &liveRange;

  // The current address being visited.
  SILValue currentAddress;

  Optional<bool> isWritten;

public:
  StorageGuaranteesLoadVisitor(Context &context, LoadInst *load,
                               const OwnershipLiveRange &liveRange)
      : ctx(context), liveRange(liveRange), currentAddress(load->getOperand()) {
  }

  void answer(bool written) {
    currentAddress = nullptr;
    isWritten = written;
  }

  void next(SILValue address) { currentAddress = address; }

  void visitNestedAccess(BeginAccessInst *access) {
    // First see if we have read/modify. If we do not, just look through the
    // nested access.
    switch (access->getAccessKind()) {
    case SILAccessKind::Init:
    case SILAccessKind::Deinit:
      return next(access->getOperand());
    case SILAccessKind::Read:
    case SILAccessKind::Modify:
      break;
    }

    // Next check if our live range is completely in the begin/end access
    // scope. If so, we may be able to use a load_borrow here!
    SmallVector<Operand *, 8> endScopeUses;
    transform(access->getEndAccesses(), std::back_inserter(endScopeUses),
              [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });
    LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
    if (!checker.validateLifetime(access, endScopeUses,
                                  liveRange.getAllConsumingUses())) {
      // If we fail the linear lifetime check, then just recur:
      return next(access->getOperand());
    }

    // Otherwise, if we have read, then we are done!
    if (access->getAccessKind() == SILAccessKind::Read) {
      return answer(false);
    }

    // If we have a modify, check if our value is /ever/ written to. If it is
    // never actually written to, then we convert to a load_borrow.
    auto result = ctx.addressToExhaustiveWriteListCache.get(access);
    if (!result.hasValue()) {
      return answer(true);
    }

    if (result.getValue().empty()) {
      return answer(false);
    }

    return answer(true);
  }

  void visitArgumentAccess(SILFunctionArgument *arg) {
    // If this load_copy is from an indirect in_guaranteed argument, then we
    // know for sure that it will never be written to.
    if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) {
      return answer(false);
    }

    // If we have an inout parameter that isn't ever actually written to, return
    // false.
    if (arg->getKnownParameterInfo().isIndirectMutating()) {
      auto wellBehavedWrites = ctx.addressToExhaustiveWriteListCache.get(arg);
      if (!wellBehavedWrites.hasValue()) {
        return answer(true);
      }

      // No writes.
      if (wellBehavedWrites->empty()) {
        return answer(false);
      }

      // Ok, we have some writes. See if any of them are within our live
      // range. If any are, we definitely can not promote to load_borrow.
      SmallVector<BeginAccessInst *, 16> foundBeginAccess;
      LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
      SILValue introducerValue = liveRange.getIntroducer().value;
      if (!checker.usesNotContainedWithinLifetime(introducerValue,
                                                  liveRange.getDestroyingUses(),
                                                  *wellBehavedWrites)) {
        return answer(true);
      }

      // Finally, check if our live range is strictly contained within any of
      // our scoped writes.
      SmallVector<Operand *, 16> endAccessList;
      for (Operand *use : *wellBehavedWrites) {
        auto *bai = dyn_cast<BeginAccessInst>(use->getUser());
        if (!bai) {
          continue;
        }

        endAccessList.clear();
        llvm::transform(
            bai->getUsersOfType<EndAccessInst>(),
            std::back_inserter(endAccessList),
            [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });

        // We know that our live range is based on a load [copy], so we know
        // that our value must have a defining inst.
        auto *definingInst =
            cast<LoadInst>(introducerValue->getDefiningInstruction());

        // Then if our defining inst is not in our bai, endAccessList region, we
        // know that the two ranges must be disjoint, so continue.
        if (!checker.validateLifetime(bai, endAccessList,
                                      &definingInst->getAllOperands()[0])) {
          continue;
        }

        // Otherwise, we do have an overlap, return true.
        return answer(true);
      }

      // Otherwise, there isn't an overlap, so we don't write to it.
      return answer(false);
    }

    // TODO: This should be extended:
    //
    // 1. We should be able to analyze in arguments and see if they are only
    //    ever destroyed at the end of the function. In such a case, we may be
    //    able to also to promote load [copy] from such args to load_borrow.
    return answer(true);
  }

  void visitGlobalAccess(SILValue global) {
    return answer(
        !AccessedStorage(global, AccessedStorage::Global).isLetAccess());
  }

  void visitClassAccess(RefElementAddrInst *field) {
    currentAddress = nullptr;

    // We know a let property won't be written to if the base object is
    // guaranteed for the duration of the access.
    // For non-let properties conservatively assume they may be written to.
    if (!field->getField()->isLet()) {
      return answer(true);
    }

    // The lifetime of the `let` is guaranteed if it's dominated by the
    // guarantee on the base. See if we can find a single borrow introducer for
    // this object. If we could not find a single such borrow introducer, assume
    // that our property is conservatively written to.
    SILValue baseObject = field->getOperand();
    auto value = getSingleBorrowIntroducingValue(baseObject);
    if (!value) {
      return answer(true);
    }

    // Ok, we have a single borrow introducing value. First do a quick check if
    // we have a non-local scope that is a function argument. In such a case, we
    // know statically that our let can not be written to in the current
    // function. To be conservative, assume that all other non-local scopes
    // write to memory.
    if (!value.isLocalScope()) {
      if (value.kind == BorrowedValueKind::SILFunctionArgument) {
        return answer(false);
      }

      // TODO: Once we model Coroutine results as non-local scopes, we should be
      // able to return false here for them as well.
      return answer(true);
    }

    // TODO: This is disabled temporarily for guaranteed phi args just for
    // staging purposes. Thus be conservative and assume true in these cases.
    if (value.kind == BorrowedValueKind::Phi) {
      return answer(true);
    }

    // Ok, we now know that we have a local scope whose lifetime we need to
    // analyze. With that in mind, gather up the lifetime ending uses of our
    // borrow scope introducing value and then use the linear lifetime checker
    // to check whether the copied value is dominated by the lifetime of the
    // borrow it's based on.
    SmallVector<Operand *, 4> endScopeInsts;
    value.visitLocalScopeEndingUses(
      [&](Operand *use) { endScopeInsts.push_back(use); return true; });

    LinearLifetimeChecker checker(ctx.getDeadEndBlocks());

    // Returns true on success. So we invert.
    bool foundError = !checker.validateLifetime(
        baseObject, endScopeInsts, liveRange.getAllConsumingUses());
    return answer(foundError);
  }

  // TODO: Handle other access kinds?
  void visitBase(SILValue base, AccessedStorage::Kind kind) {
    return answer(true);
  }

  void visitNonAccess(SILValue addr) { return answer(true); }

  void visitCast(SingleValueInstruction *cast, Operand *parentAddr) {
    return next(parentAddr->get());
  }

  void visitStorageCast(SingleValueInstruction *projectedAddr,
                        Operand *parentAddr) {
    return next(parentAddr->get());
  }

  void visitAccessProjection(SingleValueInstruction *projectedAddr,
                             Operand *parentAddr) {
    return next(parentAddr->get());
  }

  void visitPhi(SILPhiArgument *phi) {
    // We shouldn't have address phis in OSSA SIL, so we don't need to recur
    // through the predecessors here.
    return answer(true);
  }

  /// See if we have an alloc_stack that is only written to once by an
  /// initializing instruction.
  void visitStackAccess(AllocStackInst *stack) {
    SmallVector<Operand *, 8> destroyAddrOperands;
    bool initialAnswer = isSingleInitAllocStack(stack, destroyAddrOperands);
    if (!initialAnswer)
      return answer(true);

    // Then make sure that all of our load [copy] uses are within the
    // destroy_addr.
    LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
    // Returns true on success. So we invert.
    bool foundError = !checker.validateLifetime(
        stack, destroyAddrOperands /*consuming users*/,
        liveRange.getAllConsumingUses() /*non consuming users*/);
    return answer(foundError);
  }

  bool doIt() {
    while (currentAddress) {
      visit(currentAddress);
    }
    return *isWritten;
  }
};

} // namespace

static bool isWrittenTo(Context &ctx, LoadInst *load,
                        const OwnershipLiveRange &lr) {
  StorageGuaranteesLoadVisitor visitor(ctx, load, lr);
  return visitor.doIt();
}

//===----------------------------------------------------------------------===//
//                            Top Level Entrypoint
//===----------------------------------------------------------------------===//

// Convert a load [copy] from unique storage [read] that has all uses that can
// accept a guaranteed parameter to a load_borrow.
bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
  // This optimization can use more complex analysis. We should do some
  // experiments before enabling this by default as a guaranteed optimization.
  if (ctx.onlyMandatoryOpts)
    return false;

  // If we are not supposed to perform this transform, bail.
  if (!ctx.shouldPerform(ARCTransformKind::LoadCopyToLoadBorrowPeephole))
    return false;

  if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
    return false;

  // Ok, we have our load [copy]. Make sure its value is truly a dead live range
  // implying it is only ever consumed by destroy_value instructions. If it is
  // consumed, we need to pass off a +1 value, so bail.
  //
  // FIXME: We should consider if it is worth promoting a load [copy]
  // -> load_borrow if we can put a copy_value on a cold path and thus
  // eliminate RR traffic on a hot path.
  OwnershipLiveRange lr(li);
  if (bool(lr.hasUnknownConsumingUse()))
    return false;

  // Then check if our address is ever written to. If it is, then we cannot use
  // the load_borrow because the stored value may be released during the loaded
  // value's live range.
  if (isWrittenTo(ctx, li, lr))
    return false;

  // Ok, we can perform our optimization. Convert the load [copy] into a
  // load_borrow.
  auto *lbi =
      SILBuilderWithScope(li).createLoadBorrow(li->getLoc(), li->getOperand());

  lr.insertEndBorrowsAtDestroys(lbi, getDeadEndBlocks(), ctx.lifetimeFrontier);
  std::move(lr).convertToGuaranteedAndRAUW(lbi, getCallbacks());
  return true;
}
<commit_msg>[semantic-arc] Add a comment dexplaining that isSingleInitAllocStack does not necessarily contain operands of destroy_addr now that it handles checked_cast_addr_br, unchecked_checked_cast_addr.<commit_after>//===--- LoadCopyToLoadBorrowOpt.cpp --------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Defines the main optimization that converts load [copy] -> load_borrow if we
/// can prove that the +1 is not actually needed and the memory loaded from is
/// never written to while the load [copy]'s object value is being used.
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-semantic-arc-opts"

#include "OwnershipLiveRange.h"
#include "SemanticARCOptVisitor.h"
#include "swift/SIL/LinearLifetimeChecker.h"
#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/OwnershipUtils.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
#include "llvm/Support/CommandLine.h"

using namespace swift;
using namespace swift::semanticarc;

//===----------------------------------------------------------------------===//
//                              Memory Analysis
//===----------------------------------------------------------------------===//

namespace {

/// A class that computes in a flow insensitive way if we can prove that our
/// storage is either never written to, or is initialized exactly once and never
/// written to again. In both cases, we can convert load [copy] -> load_borrow
/// safely.
class StorageGuaranteesLoadVisitor
    : public AccessUseDefChainVisitor<StorageGuaranteesLoadVisitor> {
  // The context that contains global state used across all semantic arc
  // optimizations.
  Context &ctx;

  // The live range of the original load.
  const OwnershipLiveRange &liveRange;

  // The current address being visited.
  SILValue currentAddress;

  Optional<bool> isWritten;

public:
  StorageGuaranteesLoadVisitor(Context &context, LoadInst *load,
                               const OwnershipLiveRange &liveRange)
      : ctx(context), liveRange(liveRange), currentAddress(load->getOperand()) {
  }

  void answer(bool written) {
    currentAddress = nullptr;
    isWritten = written;
  }

  void next(SILValue address) { currentAddress = address; }

  void visitNestedAccess(BeginAccessInst *access) {
    // First see if we have read/modify. If we do not, just look through the
    // nested access.
    switch (access->getAccessKind()) {
    case SILAccessKind::Init:
    case SILAccessKind::Deinit:
      return next(access->getOperand());
    case SILAccessKind::Read:
    case SILAccessKind::Modify:
      break;
    }

    // Next check if our live range is completely in the begin/end access
    // scope. If so, we may be able to use a load_borrow here!
    SmallVector<Operand *, 8> endScopeUses;
    transform(access->getEndAccesses(), std::back_inserter(endScopeUses),
              [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });
    LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
    if (!checker.validateLifetime(access, endScopeUses,
                                  liveRange.getAllConsumingUses())) {
      // If we fail the linear lifetime check, then just recur:
      return next(access->getOperand());
    }

    // Otherwise, if we have read, then we are done!
    if (access->getAccessKind() == SILAccessKind::Read) {
      return answer(false);
    }

    // If we have a modify, check if our value is /ever/ written to. If it is
    // never actually written to, then we convert to a load_borrow.
    auto result = ctx.addressToExhaustiveWriteListCache.get(access);
    if (!result.hasValue()) {
      return answer(true);
    }

    if (result.getValue().empty()) {
      return answer(false);
    }

    return answer(true);
  }

  void visitArgumentAccess(SILFunctionArgument *arg) {
    // If this load_copy is from an indirect in_guaranteed argument, then we
    // know for sure that it will never be written to.
    if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) {
      return answer(false);
    }

    // If we have an inout parameter that isn't ever actually written to, return
    // false.
    if (arg->getKnownParameterInfo().isIndirectMutating()) {
      auto wellBehavedWrites = ctx.addressToExhaustiveWriteListCache.get(arg);
      if (!wellBehavedWrites.hasValue()) {
        return answer(true);
      }

      // No writes.
      if (wellBehavedWrites->empty()) {
        return answer(false);
      }

      // Ok, we have some writes. See if any of them are within our live
      // range. If any are, we definitely can not promote to load_borrow.
      SmallVector<BeginAccessInst *, 16> foundBeginAccess;
      LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
      SILValue introducerValue = liveRange.getIntroducer().value;
      if (!checker.usesNotContainedWithinLifetime(introducerValue,
                                                  liveRange.getDestroyingUses(),
                                                  *wellBehavedWrites)) {
        return answer(true);
      }

      // Finally, check if our live range is strictly contained within any of
      // our scoped writes.
      SmallVector<Operand *, 16> endAccessList;
      for (Operand *use : *wellBehavedWrites) {
        auto *bai = dyn_cast<BeginAccessInst>(use->getUser());
        if (!bai) {
          continue;
        }

        endAccessList.clear();
        llvm::transform(
            bai->getUsersOfType<EndAccessInst>(),
            std::back_inserter(endAccessList),
            [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });

        // We know that our live range is based on a load [copy], so we know
        // that our value must have a defining inst.
        auto *definingInst =
            cast<LoadInst>(introducerValue->getDefiningInstruction());

        // Then if our defining inst is not in our bai, endAccessList region, we
        // know that the two ranges must be disjoint, so continue.
        if (!checker.validateLifetime(bai, endAccessList,
                                      &definingInst->getAllOperands()[0])) {
          continue;
        }

        // Otherwise, we do have an overlap, return true.
        return answer(true);
      }

      // Otherwise, there isn't an overlap, so we don't write to it.
      return answer(false);
    }

    // TODO: This should be extended:
    //
    // 1. We should be able to analyze in arguments and see if they are only
    //    ever destroyed at the end of the function. In such a case, we may be
    //    able to also to promote load [copy] from such args to load_borrow.
    return answer(true);
  }

  void visitGlobalAccess(SILValue global) {
    return answer(
        !AccessedStorage(global, AccessedStorage::Global).isLetAccess());
  }

  void visitClassAccess(RefElementAddrInst *field) {
    currentAddress = nullptr;

    // We know a let property won't be written to if the base object is
    // guaranteed for the duration of the access.
    // For non-let properties conservatively assume they may be written to.
    if (!field->getField()->isLet()) {
      return answer(true);
    }

    // The lifetime of the `let` is guaranteed if it's dominated by the
    // guarantee on the base. See if we can find a single borrow introducer for
    // this object. If we could not find a single such borrow introducer, assume
    // that our property is conservatively written to.
    SILValue baseObject = field->getOperand();
    auto value = getSingleBorrowIntroducingValue(baseObject);
    if (!value) {
      return answer(true);
    }

    // Ok, we have a single borrow introducing value. First do a quick check if
    // we have a non-local scope that is a function argument. In such a case, we
    // know statically that our let can not be written to in the current
    // function. To be conservative, assume that all other non-local scopes
    // write to memory.
    if (!value.isLocalScope()) {
      if (value.kind == BorrowedValueKind::SILFunctionArgument) {
        return answer(false);
      }

      // TODO: Once we model Coroutine results as non-local scopes, we should be
      // able to return false here for them as well.
      return answer(true);
    }

    // TODO: This is disabled temporarily for guaranteed phi args just for
    // staging purposes. Thus be conservative and assume true in these cases.
    if (value.kind == BorrowedValueKind::Phi) {
      return answer(true);
    }

    // Ok, we now know that we have a local scope whose lifetime we need to
    // analyze. With that in mind, gather up the lifetime ending uses of our
    // borrow scope introducing value and then use the linear lifetime checker
    // to check whether the copied value is dominated by the lifetime of the
    // borrow it's based on.
    SmallVector<Operand *, 4> endScopeInsts;
    value.visitLocalScopeEndingUses(
      [&](Operand *use) { endScopeInsts.push_back(use); return true; });

    LinearLifetimeChecker checker(ctx.getDeadEndBlocks());

    // Returns true on success. So we invert.
    bool foundError = !checker.validateLifetime(
        baseObject, endScopeInsts, liveRange.getAllConsumingUses());
    return answer(foundError);
  }

  // TODO: Handle other access kinds?
  void visitBase(SILValue base, AccessedStorage::Kind kind) {
    return answer(true);
  }

  void visitNonAccess(SILValue addr) { return answer(true); }

  void visitCast(SingleValueInstruction *cast, Operand *parentAddr) {
    return next(parentAddr->get());
  }

  void visitStorageCast(SingleValueInstruction *projectedAddr,
                        Operand *parentAddr) {
    return next(parentAddr->get());
  }

  void visitAccessProjection(SingleValueInstruction *projectedAddr,
                             Operand *parentAddr) {
    return next(parentAddr->get());
  }

  void visitPhi(SILPhiArgument *phi) {
    // We shouldn't have address phis in OSSA SIL, so we don't need to recur
    // through the predecessors here.
    return answer(true);
  }

  /// See if we have an alloc_stack that is only written to once by an
  /// initializing instruction.
  void visitStackAccess(AllocStackInst *stack) {
    // These will contain all of the address destroying operands that form the
    // lifetime of the object. They may not be destroy_addr!
    SmallVector<Operand *, 8> addrDestroyingOperands;
    bool initialAnswer = isSingleInitAllocStack(stack, addrDestroyingOperands);
    if (!initialAnswer)
      return answer(true);

    // Then make sure that all of our load [copy] uses are within the
    // destroy_addr.
    LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
    // Returns true on success. So we invert.
    bool foundError = !checker.validateLifetime(
        stack, addrDestroyingOperands /*consuming users*/,
        liveRange.getAllConsumingUses() /*non consuming users*/);
    return answer(foundError);
  }

  bool doIt() {
    while (currentAddress) {
      visit(currentAddress);
    }
    return *isWritten;
  }
};

} // namespace

static bool isWrittenTo(Context &ctx, LoadInst *load,
                        const OwnershipLiveRange &lr) {
  StorageGuaranteesLoadVisitor visitor(ctx, load, lr);
  return visitor.doIt();
}

//===----------------------------------------------------------------------===//
//                            Top Level Entrypoint
//===----------------------------------------------------------------------===//

// Convert a load [copy] from unique storage [read] that has all uses that can
// accept a guaranteed parameter to a load_borrow.
bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
  // This optimization can use more complex analysis. We should do some
  // experiments before enabling this by default as a guaranteed optimization.
  if (ctx.onlyMandatoryOpts)
    return false;

  // If we are not supposed to perform this transform, bail.
  if (!ctx.shouldPerform(ARCTransformKind::LoadCopyToLoadBorrowPeephole))
    return false;

  if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
    return false;

  // Ok, we have our load [copy]. Make sure its value is truly a dead live range
  // implying it is only ever consumed by destroy_value instructions. If it is
  // consumed, we need to pass off a +1 value, so bail.
  //
  // FIXME: We should consider if it is worth promoting a load [copy]
  // -> load_borrow if we can put a copy_value on a cold path and thus
  // eliminate RR traffic on a hot path.
  OwnershipLiveRange lr(li);
  if (bool(lr.hasUnknownConsumingUse()))
    return false;

  // Then check if our address is ever written to. If it is, then we cannot use
  // the load_borrow because the stored value may be released during the loaded
  // value's live range.
  if (isWrittenTo(ctx, li, lr))
    return false;

  // Ok, we can perform our optimization. Convert the load [copy] into a
  // load_borrow.
  auto *lbi =
      SILBuilderWithScope(li).createLoadBorrow(li->getLoc(), li->getOperand());

  lr.insertEndBorrowsAtDestroys(lbi, getDeadEndBlocks(), ctx.lifetimeFrontier);
  std::move(lr).convertToGuaranteedAndRAUW(lbi, getCallbacks());
  return true;
}
<|endoftext|>


<commit_before>#include "game.hpp"
#include "paddle.hpp"

void PaddleBase::draw() const
{
	arduboy.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT, WHITE);
}

void PaddleBase::move(int16_t distance)
{
	int16_t newY = y + distance;
	if (newY <= 0)
	{
		y = 1;
	}
	else if (newY > HEIGHT - PADDLE_HEIGHT - 1)
	{
		y = HEIGHT - PADDLE_HEIGHT - 1;
	}
	else
	{
		y = newY;
	}
}

void Player::move_impl()
{
	if (arduboy.pressed(UP_BUTTON))
	{
		PaddleBase::move(-1);
	}
	if (arduboy.pressed(DOWN_BUTTON))
	{
		PaddleBase::move(1);
	}
}

void Computer::move_impl()
{
	// bother the paddle if the ball is close or a random time
	if (ball.x > 80 || !random(10))
	{
		// move up if the ball is higher
		if (ball.y <= y)
		{
			PaddleBase::move(-1);
		}
		// move down if the ball is lower
		if (ball.y + BALL_SIZE >= y + PADDLE_HEIGHT)
		{
			PaddleBase::move(1);
		}
	}
}
<commit_msg>Tweak AI again<commit_after>#include "game.hpp"
#include "paddle.hpp"

void PaddleBase::draw() const
{
	arduboy.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT, WHITE);
}

void PaddleBase::move(int16_t distance)
{
	int16_t newY = y + distance;
	if (newY <= 0)
	{
		y = 1;
	}
	else if (newY > HEIGHT - PADDLE_HEIGHT - 1)
	{
		y = HEIGHT - PADDLE_HEIGHT - 1;
	}
	else
	{
		y = newY;
	}
}

void Player::move_impl()
{
	if (arduboy.pressed(UP_BUTTON))
	{
		PaddleBase::move(-1);
	}
	if (arduboy.pressed(DOWN_BUTTON))
	{
		PaddleBase::move(1);
	}
}

void Computer::move_impl()
{
	// bother the paddle if the ball is close or a random time
	if (ball.x > 85 || !random(15))
	{
		// move up if the ball is higher
		if (ball.y <= y)
		{
			PaddleBase::move(-1);
		}
		// move down if the ball is lower
		if (ball.y + BALL_SIZE >= y + PADDLE_HEIGHT)
		{
			PaddleBase::move(1);
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <QtTest/QtTest>
#include <QtCore>
#include "nanoxml.h"
#include "fileutils.h"
#include "contexttypeinfo.h"
#include "contexttyperegistryinfo.h"

ContextTypeRegistryInfo* ContextTypeRegistryInfo::registryInstance = new ContextTypeRegistryInfo();

/* Mocked ContextTypeRegistryInfo */

ContextTypeRegistryInfo::ContextTypeRegistryInfo()
{
}

ContextTypeRegistryInfo* ContextTypeRegistryInfo::instance()
{
    return registryInstance;
}

NanoTree ContextTypeRegistryInfo::typeDefinitionForName(QString name)
{
    if (name == "double") {
        QVariantList tree;
        QVariantList doc;
        QVariantList name;
        name << QVariant("name");
        name << QVariant("double");
        doc << QVariant("doc");
        doc << QVariant("double doc");
        tree << QVariant("type");
        tree << QVariant(name);
        tree << QVariant(doc);
        return ContextTypeInfo(QVariant(tree));
    } else if (name == "complex") {
        QVariantList tree;
        QVariantList doc;
        QVariantList base;
        QVariantList name;
        QVariantList params;
        QVariantList p1;
        QVariantList p1Doc;

        name << QVariant("name");
        name << QVariant("complex");

        doc << QVariant("doc");
        doc << QVariant("complex doc");

        base << QVariant("base");
        base << QVariant("double");

        p1 << QVariant("p1");
        p1Doc << QVariant("doc");
        p1Doc << QVariant("p1 doc");
        p1 << QVariant(p1Doc);
        params << QVariant("params");
        params << QVariant(p1);

        tree << QVariant("type");
        tree << QVariant(name);
        tree << QVariant(params);
        tree << QVariant(doc);
        tree << QVariant(base);
        return ContextTypeInfo(QVariant(tree));
    } else
        return ContextTypeInfo();
}

class ContextTypeInfoUnitTest : public QObject
{
    Q_OBJECT

private slots:
    void name();
    void doc();
    void base();
    void definition();
    void parameterDoc();
    void ensureNewTypes();
    void parameterValue();
    void parameterNode();
};

void ContextTypeInfoUnitTest::name()
{
    QCOMPARE(ContextTypeInfo(QString("bool")).name(), QString("bool"));
    QCOMPARE(ContextTypeInfo(QString("string")).name(), QString("string"));

    QVariantList lst;
    lst << QVariant("int32");
    QCOMPARE(ContextTypeInfo(QVariant(lst)).name(), QString("int32"));
}

void ContextTypeInfoUnitTest::doc()
{
    QCOMPARE(ContextTypeInfo(QString("double")).doc(), QString("double doc"));
}

void ContextTypeInfoUnitTest::definition()
{
    NanoTree def = ContextTypeInfo(QString("double")).definition();
    QCOMPARE(def.keyValue("name").toString(), QString("double"));
    QCOMPARE(def.keyValue("doc").toString(), QString("double doc"));
}

void ContextTypeInfoUnitTest::base()
{
    ContextTypeInfo base = ContextTypeInfo(QString("complex")).base();
    QCOMPARE(base.name(), QString("double"));
    QCOMPARE(base.doc(), QString("double doc"));
}

void ContextTypeInfoUnitTest::parameterDoc()
{
    ContextTypeInfo typeInfo = ContextTypeInfo(QString("complex"));
    QCOMPARE(typeInfo.parameterDoc("p1"), QString("p1 doc"));

}

void ContextTypeInfoUnitTest::ensureNewTypes()
{
    QCOMPARE(ContextTypeInfo(QString("INTEGER")).ensureNewTypes().name(), QString("int32"));
    QCOMPARE(ContextTypeInfo(QString("INT")).ensureNewTypes().name(), QString("int32"));
    QCOMPARE(ContextTypeInfo(QString("TRUTH")).ensureNewTypes().name(), QString("bool"));
    QCOMPARE(ContextTypeInfo(QString("STRING")).ensureNewTypes().name(), QString("string"));
    QCOMPARE(ContextTypeInfo(QString("DOUBLE")).ensureNewTypes().name(), QString("double"));
    QCOMPARE(ContextTypeInfo(QString("bool")).ensureNewTypes().name(), QString("bool"));
}

void ContextTypeInfoUnitTest::parameterValue()
{
    QVariantList lst;
    QVariantList minParam;
    lst << QVariant("complex");
    minParam << QVariant("min");
    minParam << QVariant("0");
    lst << QVariant(minParam);
    QVariant tree(lst);

    ContextTypeInfo typeInfo(tree);
    QCOMPARE(typeInfo.parameterValue("min").toString(), QString("0"));
}

void ContextTypeInfoUnitTest::parameterNode()
{
    QVariantList lst;
    QVariantList minParam;
    lst << QVariant("complex");
    minParam << QVariant("min");
    minParam << QVariant("0");
    lst << QVariant(minParam);
    QVariant tree(lst);

    ContextTypeInfo typeInfo(tree);
    QCOMPARE(typeInfo.parameterNode("min").toList().at(1).toString(), QString("0"));
}

#include "contexttypeinfounittest.moc"
QTEST_MAIN(ContextTypeInfoUnitTest);
<commit_msg>Coverage improvements.<commit_after>/*
 * Copyright (C) 2009 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <QtTest/QtTest>
#include <QtCore>
#include "nanoxml.h"
#include "fileutils.h"
#include "contexttypeinfo.h"
#include "contexttyperegistryinfo.h"

ContextTypeRegistryInfo* ContextTypeRegistryInfo::registryInstance = new ContextTypeRegistryInfo();

/* Mocked ContextTypeRegistryInfo */

ContextTypeRegistryInfo::ContextTypeRegistryInfo()
{
}

ContextTypeRegistryInfo* ContextTypeRegistryInfo::instance()
{
    return registryInstance;
}

NanoTree ContextTypeRegistryInfo::typeDefinitionForName(QString name)
{
    if (name == "double") {
        QVariantList tree;
        QVariantList doc;
        QVariantList name;
        name << QVariant("name");
        name << QVariant("double");
        doc << QVariant("doc");
        doc << QVariant("double doc");
        tree << QVariant("type");
        tree << QVariant(name);
        tree << QVariant(doc);
        return ContextTypeInfo(QVariant(tree));
    } else if (name == "complex") {
        QVariantList tree;
        QVariantList doc;
        QVariantList base;
        QVariantList name;
        QVariantList params;
        QVariantList p1;
        QVariantList p1Doc;

        name << QVariant("name");
        name << QVariant("complex");

        doc << QVariant("doc");
        doc << QVariant("complex doc");

        base << QVariant("base");
        base << QVariant("double");

        p1 << QVariant("p1");
        p1Doc << QVariant("doc");
        p1Doc << QVariant("p1 doc");
        p1 << QVariant(p1Doc);
        params << QVariant("params");
        params << QVariant(p1);

        tree << QVariant("type");
        tree << QVariant(name);
        tree << QVariant(params);
        tree << QVariant(doc);
        tree << QVariant(base);
        return ContextTypeInfo(QVariant(tree));
    } else
        return ContextTypeInfo();
}

class ContextTypeInfoUnitTest : public QObject
{
    Q_OBJECT

private slots:
    void name();
    void doc();
    void base();
    void definition();
    void parameterDoc();
    void ensureNewTypes();
    void parameterValue();
    void parameterNode();
    void parameters();
};

void ContextTypeInfoUnitTest::name()
{
    QCOMPARE(ContextTypeInfo(QString("bool")).name(), QString("bool"));
    QCOMPARE(ContextTypeInfo(QString("string")).name(), QString("string"));

    QVariantList lst;
    lst << QVariant("int32");
    QCOMPARE(ContextTypeInfo(QVariant(lst)).name(), QString("int32"));
}

void ContextTypeInfoUnitTest::doc()
{
    QCOMPARE(ContextTypeInfo(QString("double")).doc(), QString("double doc"));
}

void ContextTypeInfoUnitTest::definition()
{
    NanoTree def = ContextTypeInfo(QString("double")).definition();
    QCOMPARE(def.keyValue("name").toString(), QString("double"));
    QCOMPARE(def.keyValue("doc").toString(), QString("double doc"));
}

void ContextTypeInfoUnitTest::base()
{
    ContextTypeInfo base = ContextTypeInfo(QString("complex")).base();
    QCOMPARE(base.name(), QString("double"));
    QCOMPARE(base.doc(), QString("double doc"));
}

void ContextTypeInfoUnitTest::parameterDoc()
{
    ContextTypeInfo typeInfo = ContextTypeInfo(QString("complex"));
    QCOMPARE(typeInfo.parameterDoc("p1"), QString("p1 doc"));

}

void ContextTypeInfoUnitTest::ensureNewTypes()
{
    QCOMPARE(ContextTypeInfo(QString("INTEGER")).ensureNewTypes().name(), QString("int32"));
    QCOMPARE(ContextTypeInfo(QString("INT")).ensureNewTypes().name(), QString("int32"));
    QCOMPARE(ContextTypeInfo(QString("TRUTH")).ensureNewTypes().name(), QString("bool"));
    QCOMPARE(ContextTypeInfo(QString("STRING")).ensureNewTypes().name(), QString("string"));
    QCOMPARE(ContextTypeInfo(QString("DOUBLE")).ensureNewTypes().name(), QString("double"));
    QCOMPARE(ContextTypeInfo(QString("bool")).ensureNewTypes().name(), QString("bool"));
}

void ContextTypeInfoUnitTest::parameterValue()
{
    QVariantList lst;
    QVariantList minParam;
    lst << QVariant("complex");
    minParam << QVariant("min");
    minParam << QVariant("0");
    lst << QVariant(minParam);
    QVariant tree(lst);

    ContextTypeInfo typeInfo(tree);
    QCOMPARE(typeInfo.parameterValue("min").toString(), QString("0"));
}

void ContextTypeInfoUnitTest::parameterNode()
{
    QVariantList lst;
    QVariantList minParam;
    lst << QVariant("complex");
    minParam << QVariant("min");
    minParam << QVariant("0");
    lst << QVariant(minParam);
    QVariant tree(lst);

    ContextTypeInfo typeInfo(tree);
    QCOMPARE(typeInfo.parameterNode("min").toList().at(1).toString(), QString("0"));
}

void ContextTypeInfoUnitTest::parameters()
{
    QVariantList lst;
    QVariantList minParam;
    lst << QVariant("complex");
    minParam << QVariant("min");
    minParam << QVariant("0");
    lst << QVariant(minParam);
    QVariant tree(lst);

    ContextTypeInfo typeInfo(tree);
    QVariantList params = typeInfo.parameters();
    QCOMPARE(params.size(), 1);
    QCOMPARE(params.at(0).toList().at(0).toString(), QString("min"));

    QVariant variant("double");
    QVERIFY(ContextTypeInfo(variant).parameters().size() == 0);
}

#include "contexttypeinfounittest.moc"
QTEST_MAIN(ContextTypeInfoUnitTest);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010 Matt Fichman
 *
 * 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, APEXPRESS 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 "String.hpp"

char String::escape(const std::string& str) {
    // Returns the escaped character for 'in', e.g., if '\n' is given to this
    // function, it returns the newline character.

    const char* c = &str[0];
    if (*c != '\\') {
        return *c;
    }
    c++;

    if (isdigit(c[0]) && isdigit(c[1]) && isdigit(c[2])) {
        return 64*(c[0]-'0') + 8*(c[1]-'0') + (c[2]-'0');
    }
    if (*c == 'x' && isxdigit(c[1]) && isxdigit(c[2])) {
        char ret = 0;
        if (isupper(c[1])) {
            ret += 10 + c[1] - 'A';
        } else if (islower(c[1])) {
            ret += 10 + c[1] - 'a';
        } else {
            ret += c[1] - '0';
        }
        ret = ret << 4;
        if (isupper(c[2])) {
            ret += 10 + c[2] - 'A';
        } else if (islower(c[2])) {
            ret += 10 + c[2] - 'a';
        } else {
            ret += c[2] - '0';
        }
        return ret;
    }
    switch (*c) {
    case 'a': return 0x7; // alarm
    case 'b': return 0x8; // backspace
    case 't': return 0x9; // horizontal tab
    case 'n': return 0xa; // newline
    case 'v': return 0xb; // vertical tab
    case 'f': return 0xc; // form feed
    case 'r': return 0xd; // carriage return
    case '"': return 0x22; // quote
    case '\'': return 0x27; // quote
    default: return c[0];
    }
}

std::string const String::unescaped() const {
    // Removes the escape sequences from this string and returns the actual
    // string, as intended by the programmer
    std::string const& in = string();
    std::string out;
    int length = 0;
    for (int i = 0; i < in.length(); i++) {
        char c = in[i];
        if (c == '\\') {
            // Output escape sequences.  For NASM, the actual hex codes must
            // be output for non-visible characters; there is no escape
            // character.
            c = in[++i];
            if (isdigit(c)) { // Octal code
                char c2 = in[++i];
                char c3 = in[++i];
                out += std::string("0o") + c + c2 + c3;
            } else if (c == 'x') { // Hexadecimal code
                char c1 = in[++i];
                char c2 = in[++i];
                out += std::string("0x") + c1 + c2;
            } else {
                switch (c) {
                case 'a': out += "\a"; break; // alarm
                case 'b': out += "\b"; break; // backspace
                case 't': out += "\t"; break; // horizontal tab
                case 'n': out += "\n"; break; // newline
                case 'v': out += "\v"; break; // vertical tab
                case 'f': out += "\f"; break; // form feed
                case 'r': out += "\r"; break; // carriage return
                case '"': out += "\""; break; // quote
                case '\'': out += "\'"; break; // quote
                default: out += c; break;
                }
            }
        } else {
            out += c;
        }
    }
    return out;
}
<commit_msg>Fixed string escape<commit_after>/*
 * Copyright (c) 2010 Matt Fichman
 *
 * 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, APEXPRESS 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 "String.hpp"

char String::escape(const std::string& str) {
    // Returns the escaped character for 'in', e.g., if '\n' is given to this
    // function, it returns the newline character.

    const char* c = &str[0];
    if (*c != '\\') {
        return *c;
    }
    c++;

    if (isdigit(c[0]) && isdigit(c[1]) && isdigit(c[2])) {
        return 64*(c[0]-'0') + 8*(c[1]-'0') + (c[2]-'0');
    }
    if (*c == 'x' && isxdigit(c[1]) && isxdigit(c[2])) {
        char ret = 0;
        if (isupper(c[1])) {
            ret += 10 + c[1] - 'A';
        } else if (islower(c[1])) {
            ret += 10 + c[1] - 'a';
        } else {
            ret += c[1] - '0';
        }
        ret = ret << 4;
        if (isupper(c[2])) {
            ret += 10 + c[2] - 'A';
        } else if (islower(c[2])) {
            ret += 10 + c[2] - 'a';
        } else {
            ret += c[2] - '0';
        }
        return ret;
    }
    switch (*c) {
    case 'a': return 0x7; // alarm
    case 'b': return 0x8; // backspace
    case 't': return 0x9; // horizontal tab
    case 'n': return 0xa; // newline
    case 'v': return 0xb; // vertical tab
    case 'f': return 0xc; // form feed
    case 'r': return 0xd; // carriage return
    case '"': return 0x22; // quote
    case '\'': return 0x27; // quote
    default: return c[0];
    }
}

std::string const String::unescaped() const {
    // Removes the escape sequences from this string and returns the actual
    // string, as intended by the programmer
    std::string const& in = string();
    std::string out;
    int length = 0;
    for (int i = 0; i < in.length(); i++) {
        char c = in[i];
        if (c == '\\') {
            // Output escape sequences.  For NASM, the actual hex codes must
            // be output for non-visible characters; there is no escape
            // character.
            c = in[++i];
            if (isdigit(c)) { // Octal code
                char c2 = in[++i];
                char c3 = in[++i];
                out += (c-'0')*64 + (c2-'0')*8 + (c3-'0');
            } else if (c == 'x') { // Hexadecimal code
                char c1 = in[++i];
                char c2 = in[++i];
                char byte = 0;
                if (isupper(c1)) {
                    byte += c1-'A'+10;
                } else if (islower(c1)) {
                    byte += c1-'a'+10;
                } else {
                    byte += c1-'0';
                }
                byte *= 16;
                if (isupper(c2)) {
                    byte += c2-'A'+10;
                } else if (islower(c2)) {
                    byte += c2-'a'+10;
                } else {
                    byte += c2-'0';
                }
                out += byte;
            } else {
                switch (c) {
                case 'a': out += "\a"; break; // alarm
                case 'b': out += "\b"; break; // backspace
                case 't': out += "\t"; break; // horizontal tab
                case 'n': out += "\n"; break; // newline
                case 'v': out += "\v"; break; // vertical tab
                case 'f': out += "\f"; break; // form feed
                case 'r': out += "\r"; break; // carriage return
                case '"': out += "\""; break; // quote
                case '\'': out += "\'"; break; // quote
                default: out += c; break;
                }
            }
        } else {
            out += c;
        }
    }
    return out;
}
<|endoftext|>


<commit_before>#include "CAudioSys.hpp"
#include "CSimplePool.hpp"
#include "CAudioGroupSet.hpp"

namespace urde {
namespace {
std::unordered_map<std::string, TLockedToken<CAudioGroupSet>> mpGroupSetDB;
std::unordered_map<CAssetId, std::string> mpGroupSetResNameDB;
const std::string mpDefaultInvalidString = "NULL";

float s_MasterVol = 1.f;
float s_SfxVol = 1.f;

s16 s_VolumeScale = 0x7f;
s16 s_DefaultVolumeScale = 0x7f;
} // Anonymous namespace

CAudioSys* CAudioSys::g_SharedSys = nullptr;

TLockedToken<CAudioGroupSet> CAudioSys::FindGroupSet(std::string_view name) {
  auto search = mpGroupSetDB.find(name.data());
  if (search == mpGroupSetDB.cend())
    return {};
  return search->second;
}

std::string_view CAudioSys::SysGetGroupSetName(CAssetId id) {
  auto search = mpGroupSetResNameDB.find(id);
  if (search == mpGroupSetResNameDB.cend())
    return mpDefaultInvalidString;
  return search->second;
}

bool CAudioSys::SysLoadGroupSet(CSimplePool* pool, CAssetId id) {
  if (!FindGroupSet(SysGetGroupSetName(id))) {
    TLockedToken<CAudioGroupSet> set = pool->GetObj(SObjectTag{FOURCC('AGSC'), id});
    mpGroupSetDB.emplace(std::make_pair(set->GetName(), set));
    mpGroupSetResNameDB.emplace(std::make_pair(id, set->GetName()));
    return false;
  } else {
    return true;
  }
}

bool CAudioSys::SysLoadGroupSet(const TLockedToken<CAudioGroupSet>& set, std::string_view name, CAssetId id) {
  if (!FindGroupSet(name)) {
    mpGroupSetDB.emplace(std::make_pair(set->GetName(), set));
    mpGroupSetResNameDB.emplace(std::make_pair(id, set->GetName()));
    return false;
  } else {
    return true;
  }
}

void CAudioSys::SysUnloadAudioGroupSet(std::string_view name) {
  auto set = FindGroupSet(name);
  if (!set)
    return;

  mpGroupSetDB.erase(name.data());
  mpGroupSetResNameDB.erase(set.GetObjectTag()->id);
}

bool CAudioSys::SysIsGroupSetLoaded(std::string_view name) { return FindGroupSet(name).operator bool(); }

void CAudioSys::SysAddGroupIntoAmuse(std::string_view name) {
  if (auto set = FindGroupSet(name))
    AddAudioGroup(set->GetAudioGroupData());
}

void CAudioSys::SysRemoveGroupFromAmuse(std::string_view name) {
  if (auto set = FindGroupSet(name))
    RemoveAudioGroup(set->GetAudioGroupData());
}

void CAudioSys::_UpdateVolume() { GetAmuseEngine().setVolume(s_MasterVol * s_SfxVol); }

void CAudioSys::SysSetVolume(u8 volume) {
  s_MasterVol = volume / 127.f;
  _UpdateVolume();
}

void CAudioSys::SysSetSfxVolume(u8 volume, u16 time, bool music, bool fx) {
  s_SfxVol = volume / 127.f;
  _UpdateVolume();
}

s16 CAudioSys::GetDefaultVolumeScale() { return s_DefaultVolumeScale; }

void CAudioSys::SetDefaultVolumeScale(s16 scale) { s_DefaultVolumeScale = scale; }

void CAudioSys::SetVolumeScale(s16 scale) { s_VolumeScale = scale; }

} // namespace urde
<commit_msg>CAudioSys: Remove construction of pairs in emplace calls<commit_after>#include "CAudioSys.hpp"
#include "CSimplePool.hpp"
#include "CAudioGroupSet.hpp"

namespace urde {
namespace {
std::unordered_map<std::string, TLockedToken<CAudioGroupSet>> mpGroupSetDB;
std::unordered_map<CAssetId, std::string> mpGroupSetResNameDB;
const std::string mpDefaultInvalidString = "NULL";

float s_MasterVol = 1.f;
float s_SfxVol = 1.f;

s16 s_VolumeScale = 0x7f;
s16 s_DefaultVolumeScale = 0x7f;
} // Anonymous namespace

CAudioSys* CAudioSys::g_SharedSys = nullptr;

TLockedToken<CAudioGroupSet> CAudioSys::FindGroupSet(std::string_view name) {
  auto search = mpGroupSetDB.find(name.data());
  if (search == mpGroupSetDB.cend())
    return {};
  return search->second;
}

std::string_view CAudioSys::SysGetGroupSetName(CAssetId id) {
  auto search = mpGroupSetResNameDB.find(id);
  if (search == mpGroupSetResNameDB.cend())
    return mpDefaultInvalidString;
  return search->second;
}

bool CAudioSys::SysLoadGroupSet(CSimplePool* pool, CAssetId id) {
  if (!FindGroupSet(SysGetGroupSetName(id))) {
    TLockedToken<CAudioGroupSet> set = pool->GetObj(SObjectTag{FOURCC('AGSC'), id});
    mpGroupSetDB.emplace(set->GetName(), set);
    mpGroupSetResNameDB.emplace(id, set->GetName());
    return false;
  } else {
    return true;
  }
}

bool CAudioSys::SysLoadGroupSet(const TLockedToken<CAudioGroupSet>& set, std::string_view name, CAssetId id) {
  if (!FindGroupSet(name)) {
    mpGroupSetDB.emplace(set->GetName(), set);
    mpGroupSetResNameDB.emplace(id, set->GetName());
    return false;
  } else {
    return true;
  }
}

void CAudioSys::SysUnloadAudioGroupSet(std::string_view name) {
  auto set = FindGroupSet(name);
  if (!set)
    return;

  mpGroupSetDB.erase(name.data());
  mpGroupSetResNameDB.erase(set.GetObjectTag()->id);
}

bool CAudioSys::SysIsGroupSetLoaded(std::string_view name) { return FindGroupSet(name).operator bool(); }

void CAudioSys::SysAddGroupIntoAmuse(std::string_view name) {
  if (auto set = FindGroupSet(name))
    AddAudioGroup(set->GetAudioGroupData());
}

void CAudioSys::SysRemoveGroupFromAmuse(std::string_view name) {
  if (auto set = FindGroupSet(name))
    RemoveAudioGroup(set->GetAudioGroupData());
}

void CAudioSys::_UpdateVolume() { GetAmuseEngine().setVolume(s_MasterVol * s_SfxVol); }

void CAudioSys::SysSetVolume(u8 volume) {
  s_MasterVol = volume / 127.f;
  _UpdateVolume();
}

void CAudioSys::SysSetSfxVolume(u8 volume, u16 time, bool music, bool fx) {
  s_SfxVol = volume / 127.f;
  _UpdateVolume();
}

s16 CAudioSys::GetDefaultVolumeScale() { return s_DefaultVolumeScale; }

void CAudioSys::SetDefaultVolumeScale(s16 scale) { s_DefaultVolumeScale = scale; }

void CAudioSys::SetVolumeScale(s16 scale) { s_VolumeScale = scale; }

} // namespace urde
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/dreamview/backend/util/trajectory_point_collector.h"

#include <cmath>

#include "gtest/gtest.h"
#include "modules/common/configs/vehicle_config_helper.h"

using apollo::common::TrajectoryPoint;

namespace apollo {
namespace dreamview {
namespace util {

class TrajectoryPointCollectorTest : public ::testing::Test {
 public:
  virtual void SetUp() { apollo::common::VehicleConfigHelper::Init(); }
};

TEST_F(TrajectoryPointCollectorTest, ThreePoints) {
  SimulationWorld world;
  TrajectoryPointCollector collector(&world);

  const double base_time = 1000.0;
  for (int i = 0; i < 4; ++i) {
    TrajectoryPoint point;
    point.mutable_path_point()->set_x(i * 100.0);
    point.mutable_path_point()->set_y(i * 100.0 + 100.0);
    point.set_relative_time(i * 1000.0);
    collector.Collect(point, base_time);
  }

  EXPECT_EQ(world.planning_trajectory_size(), 3);

  {
    const Object &point = world.planning_trajectory(0);
    EXPECT_DOUBLE_EQ(0.0, point.position_x());
    EXPECT_DOUBLE_EQ(100.0, point.position_y());
    EXPECT_DOUBLE_EQ(2000.0, point.timestamp_sec());
    EXPECT_DOUBLE_EQ(atan2(100.0, 100.0), point.heading());
  }

  {
    const Object &point = world.planning_trajectory(1);
    EXPECT_DOUBLE_EQ(100.0, point.position_x());
    EXPECT_DOUBLE_EQ(200.0, point.position_y());
    EXPECT_DOUBLE_EQ(3000.0, point.timestamp_sec());
    EXPECT_DOUBLE_EQ(atan2(100.0, 100.0), point.heading());
  }

  {
    const Object &point = world.planning_trajectory(2);
    EXPECT_DOUBLE_EQ(200.0, point.position_x());
    EXPECT_DOUBLE_EQ(300.0, point.position_y());
    EXPECT_DOUBLE_EQ(4000.0, point.timestamp_sec());
    EXPECT_DOUBLE_EQ(atan2(100.0, 100.0), point.heading());
  }
}

}  // namespace util
}  // namespace dreamview
}  // namespace apollo
<commit_msg>Dreamview: fixed a unit test<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/dreamview/backend/util/trajectory_point_collector.h"

#include <cmath>

#include "gtest/gtest.h"
#include "modules/common/configs/vehicle_config_helper.h"

using apollo::common::TrajectoryPoint;

namespace apollo {
namespace dreamview {
namespace util {

class TrajectoryPointCollectorTest : public ::testing::Test {
 public:
  virtual void SetUp() { apollo::common::VehicleConfigHelper::Init(); }
};

TEST_F(TrajectoryPointCollectorTest, ThreePoints) {
  SimulationWorld world;
  TrajectoryPointCollector collector(&world);

  const double base_time = 1000.0;
  for (int i = 0; i < 4; ++i) {
    TrajectoryPoint point;
    point.mutable_path_point()->set_x(i * 100.0);
    point.mutable_path_point()->set_y(i * 100.0 + 100.0);
    point.set_relative_time(i * 1000.0);
    collector.Collect(point, base_time);
  }

  EXPECT_EQ(world.planning_trajectory_size(), 3);

  {
    const Object &point = world.planning_trajectory(0);
    EXPECT_DOUBLE_EQ(0.0, point.position_x());
    EXPECT_DOUBLE_EQ(100.0, point.position_y());
    EXPECT_DOUBLE_EQ(1000.0, point.timestamp_sec());
    EXPECT_DOUBLE_EQ(atan2(100.0, 100.0), point.heading());
  }

  {
    const Object &point = world.planning_trajectory(1);
    EXPECT_DOUBLE_EQ(100.0, point.position_x());
    EXPECT_DOUBLE_EQ(200.0, point.position_y());
    EXPECT_DOUBLE_EQ(2000.0, point.timestamp_sec());
    EXPECT_DOUBLE_EQ(atan2(100.0, 100.0), point.heading());
  }

  {
    const Object &point = world.planning_trajectory(2);
    EXPECT_DOUBLE_EQ(200.0, point.position_x());
    EXPECT_DOUBLE_EQ(300.0, point.position_y());
    EXPECT_DOUBLE_EQ(3000.0, point.timestamp_sec());
    EXPECT_DOUBLE_EQ(atan2(100.0, 100.0), point.heading());
  }
}

}  // namespace util
}  // namespace dreamview
}  // namespace apollo
<|endoftext|>


<commit_before><commit_msg>Use std::initializer_list<commit_after><|endoftext|>