<commit_before>#ifndef INCLUDE_AL_HASHSPACE_HPP
#define INCLUDE_AL_HASHSPACE_HPP

#include "allocore/math/al_Vec.hpp"
#include "allocore/types/al_Array.hpp"

#include <vector>

/*	Allocore --
	Multimedia / virtual environment application class library

	Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
	Copyright (C) 2006-2008. The Regents of the University of California (REGENTS).
	All Rights Reserved.

	Permission to use, copy, modify, distribute, and distribute modified versions
	of this software and its documentation without fee and without a signed
	licensing agreement, is hereby granted, provided that the above copyright
	notice, the list of contributors, this paragraph and the following two paragraphs
	appear in all copies, modifications, and distributions.

	IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
	SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
	OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
	BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
	PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
	HEREUNDER IS PROVIDED "AS IS". REGENTS HAS  NO OBLIGATION TO PROVIDE
	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

	File description:
	HashSpace is a way to detect object collisions using a voxel grid
	The grid has a given resolution (no. voxel cells per side)

	It is optimized for densely packed points and querying for nearest neighbors
	within given radii (results will be roughly sorted by distance).

	TODO: non-toroidal version.

	File author(s):
	Graham Wakefield, 2011, grrrwaaa@gmail.com
	Wesley Smith, 2010, wesley.hoke@gmail.com

	Inspired by this paper:
	http://nicolas.brodu.numerimoire.net/common/recherche/publications/QuerySphereIndexing.pdf
*/

namespace al {

class HashSpace {
public:

	// container for registered spatial elements
	struct Object {
		Object() : hash(invalidHash()), next(NULL), prev(NULL), userdata(0) {}

		Vec3d pos;
		uint32_t hash;	///< which voxel ID it belongs to (or invalidHash())
		Object * next, * prev;	///< neighbors in the same voxel
		union {			// a way to attach user-defined payloads:
			uint32_t id;
			void * userdata;
		};
	};

	struct Voxel {
		Voxel() : mObjects(NULL) {}
		Voxel(const Voxel& cpy) : mObjects(NULL) {}

		// this is definitely not thread-safe.
		inline void add(Object * o);

		// this is definitely not thread-safe.
		inline void remove(Object * o);

		Object * mObjects;
	};

	// create and re-use a query object to find neighbors:
	struct Query {

		typedef std::vector<HashSpace::Object *> Vector;
		typedef Vector::iterator Iterator;

		Query(uint32_t maxResults=128)
		:	mMaxResults(maxResults)
		{
			mObjects.reserve(maxResults);
		}

		/// clear is separated from the main query operation,
		/// to support aggregating queries in series
		/// typically however you would want to clear() and then query.
		Query& clear() { mObjects.clear(); return *this; }

		/// set/get the maximum number of desired results
		Query& maxResults(uint32_t i) { mMaxResults = i; return *this; }
		uint32_t maxResults() const { return mMaxResults; }

		/// get the neighbors within maxRadius (and beyond minRadius if given)
		// the maximum permissible value of radius is space.maxRadius()
		int operator()(const HashSpace& space, const Vec3d center, double maxRadius, double minRadius=0.);

		/// just get the nearest neighbors
		int operator()(const HashSpace& space, Vec3d center);

		/// get number of results:
		unsigned size() const { return mObjects.size(); }
		/// get each result:
		Object * operator[](unsigned i) const { return mObjects[i]; }

		// std::vector interface:
		Iterator begin() { return mObjects.begin(); }
		Iterator end() { return mObjects.end(); }
		Vector& vector() { return mObjects; }

	protected:
		uint32_t mMaxResults;
		std::vector<Object *> mObjects;
	};

	/// resolution defines the size of the space (2^resolution per axis)
	/// locations will range from [0..2^resolution)
	HashSpace(uint32_t resolution=5, uint32_t numObjects=0);
	~HashSpace();

	/// the dimension of the space per axis:
	uint32_t dim() const { return mDim; }
	/// the maximum valid radius to query (half the dimension):
	uint32_t maxRadius() const { return mDimHalf; }

	/// get/set the number of objects:
	void numObjects(int numObjects);
	uint32_t numObjects() const { return mObjects.size(); }

	/// get the object at a given index:
	Object& object(uint32_t i) { return mObjects[i]; }

	/// set the position of an object:
	HashSpace& move(uint32_t objectId, double x, double y, double z) { return move(objectId, Vec3d(x,y,z)); }
	HashSpace& move(uint32_t objectId, Vec3d pos);

	/// this removes the object from voxels/queries, but does not destroy it
	/// the objectId can be reused later via move()
	HashSpace& remove(uint32_t objectId);

	/// wrap an absolute position within the space:
	double wrap(double x) const { return wrap(x, dim()); }
	Vec3d wrap(Vec3d v) const {
		return Vec3d( wrap(v.x), wrap(v.y), wrap(v.z) );
	}

	/// wrap a relative vector within the space:
	/// use this when computing the vector between objects
	/// to properly take into account toroidal wrapping
	double wrapRelative(double x) const { return wrap(x, maxRadius()); }
	Vec3d wrapRelative(Vec3d v) const {
		return wrap(v + maxRadius()) - maxRadius();
	}

	/// an invalid voxel index used to indicate non-membership
	static uint32_t invalidHash() { return UINT_MAX; }

protected:

	// integer distance squared
	uint32_t distanceSquared(double a1, double a2, double a3) const;

	// convert x,y,z in range [0..DIM) to unsigned hash:
	// this is also the valid mVoxels index for the corresponding voxel:
	template<typename T>
	uint32_t hash(Vec<3,T> v) const { return hash(v[0], v[1], v[2]); }
	uint32_t hash(unsigned x, unsigned y, unsigned z) const {
		return hashx(x)+hashy(y)+hashz(z);
	}

	inline uint32_t hashx(uint32_t v) const { return v & mWrap; }
	inline uint32_t hashy(uint32_t v) const { return (v & mWrap)<<mShift; }
	inline uint32_t hashz(uint32_t v) const { return (v & mWrap)<<mShift2; }

	// convert unsigned hash to x,y,z in range [0..mDim):
	Vec3i unhash(uint32_t h) const { return Vec3i(unhashx(h), unhashy(h), unhashz(h)); }

	inline uint32_t unhashx(uint32_t h) const { return (h) & mWrap; }
	inline uint32_t unhashy(uint32_t h) const { return (h>>mShift) & mWrap; }
	inline uint32_t unhashz(uint32_t h) const { return (h>>mShift2) & mWrap; }

	// safe floating-point wrapping
	static double wrap(double x, double mod);
	static double wrap(double x, double lo, double hi);

	uint32_t mShift, mShift2, mDim, mDim2, mDim3, mWrap, mWrap3;
	int mDimHalf;	// the valid maximum radius for queries
	uint32_t mMaxD2, mMaxHalfD2;

	/// the array of objects
	std::vector<Object> mObjects;

	/// the array of voxels (indexed by hashed location)
	std::vector<Voxel> mVoxels;

	/// a baked array of voxel indices sorted by distance
	std::vector<uint32_t> mVoxelIndices;
	/// a baked array mapping distance to mVoxelIndices offsets
	std::vector<uint32_t> mDistanceToVoxelIndices;
};

// this is definitely not thread-safe.
inline void HashSpace::Voxel :: add(Object * o) {
	if (mObjects) {
		// add to tail:
		Object * last = mObjects->prev;
		last->next = o;
		o->prev = last;
		o->next = mObjects;
		mObjects->prev = o;
	} else {
		// unique:
		mObjects = o->prev = o->next = o;
	}
}

// this is definitely not thread-safe.
inline void HashSpace::Voxel :: remove(Object * o) {
	if (o == o->prev) {	// voxel only has 1 item
		mObjects = NULL;
	} else {
		Object * prev = o->prev;
		Object * next = o->next;
		prev->next = next;
		next->prev = prev;
		// update head pointer?
		if (mObjects == o) { mObjects = next; }
	}
	// leave the object clean:
	o->prev = o->next = NULL;
}

inline int HashSpace::Query :: operator()(const HashSpace& space, Vec3d center) {
	return (*this)(space, center, space.maxRadius());
}

// the maximum permissible value of radius is mDimHalf
// if int(inner^2) == int(outer^2), only 1 shell will be queried.
// TODO: non-toroidal version.
inline int HashSpace::Query :: operator()(const HashSpace& space, Vec3d center, double maxRadius, double minRadius) {
	unsigned nres = 0;
	uint32_t offset = space.hash(center);
	double minr2 = minRadius*minRadius;
	double maxr2 = maxRadius*maxRadius;
	uint32_t iminr2 = al::max(uint32_t(0), uint32_t((minRadius-1)*(minRadius-1)));
	uint32_t imaxr2 = al::min(space.mMaxHalfD2, uint32_t((maxRadius+1)*(maxRadius+1)));
	if (iminr2 < imaxr2) {
		uint32_t cellstart = space.mDistanceToVoxelIndices[iminr2];
		uint32_t cellend = space.mDistanceToVoxelIndices[imaxr2];

		for (uint32_t i = cellstart; i < cellend; i++) {
			// offset current index by voxel grid:
			uint32_t index = (offset + space.mVoxelIndices[i]) & space.mWrap3;
			const Voxel& cell = space.mVoxels[index];
			// now add any objects in this voxel to the result...
			Object * head = cell.mObjects;
			if (head) {
				Object * o = head;
				do {
					// final check - float version:
					Vec3d rel = space.wrapRelative(o->pos - center);
					double d2 = rel.magSqr();
					if (d2 >= minr2 && d2 <= maxr2) {
						mObjects.push_back(o);
						nres++;
					}
					o = o->next;
				} while (o != head && nres < mMaxResults);
			}
			if(nres == mMaxResults) break;
		}
	}
	return nres;
}

inline void HashSpace :: numObjects(int numObjects) {
	mObjects.clear();
	mObjects.resize(numObjects);
	// clear all voxels:
	for (unsigned i=0; i<mVoxels.size(); i++) {
		mVoxels[i].mObjects = 0;
	}
}

inline HashSpace& HashSpace :: move(uint32_t objectId, Vec3d pos) {
	Object& o = mObjects[objectId];
	o.pos.set(wrap(pos));
	uint32_t newhash = hash(o.pos);
	if (newhash != o.hash) {
		if (o.hash != invalidHash()) mVoxels[o.hash].remove(&o);
		o.hash = newhash;
		mVoxels[newhash].add(&o);
	}
	return *this;
}

inline HashSpace& HashSpace :: remove(uint32_t objectId) {
	Object& o = mObjects[objectId];
	if (o.hash != invalidHash()) mVoxels[o.hash].remove(&o);
	o.hash = invalidHash();
	return *this;
}

// integer distance squared
inline uint32_t HashSpace :: distanceSquared(double x, double y, double z) const {
	return x*x+y*y+z*z;
}

// wrapping floating point numbers is surprisingly complex
// because of rounding errors, behavior when near zero
// and other oddities
inline double HashSpace :: wrap(double x, double mod) {
	if (mod) {
		if (x > mod) {
			// shift down
			if (x > (mod*2.)) {
				// multiple wraps:
				double div = x / mod;
				// get fract:
				double divl = (long)div;
				double fract = div - (double)divl;
				return fract * mod;
			} else {
				// single wrap:
				return x - mod;
			}
		} else if (x < 0.) {
			// negative x, shift up
			if (x < -mod) {
				// multiple wraps:
				double div = x / mod;
				// get fract:
				double divl = (long)div;
				double fract = div - (double)divl;
				double x1 = fract * mod;
				return (x1 < 0.) ? x1 + mod : x1;
			} else {
				// single wrap:
				return x + mod;
			}
		} else {
			return x;
		}
	} else {
		return 0.;	// avoid divide by zero
	}
}

inline double HashSpace :: wrap(double x, double lo, double hi) {
	return lo + wrap(x-lo, hi-lo);
}

} // al::

#endif
<commit_msg>better doxy<commit_after>#ifndef INCLUDE_AL_HASHSPACE_HPP
#define INCLUDE_AL_HASHSPACE_HPP

#include "allocore/math/al_Vec.hpp"
#include "allocore/types/al_Array.hpp"

#include <vector>

/*	Allocore --
	Multimedia / virtual environment application class library

	Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
	Copyright (C) 2006-2008. The Regents of the University of California (REGENTS).
	All Rights Reserved.

	Permission to use, copy, modify, distribute, and distribute modified versions
	of this software and its documentation without fee and without a signed
	licensing agreement, is hereby granted, provided that the above copyright
	notice, the list of contributors, this paragraph and the following two paragraphs
	appear in all copies, modifications, and distributions.

	IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
	SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
	OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
	BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
	PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
	HEREUNDER IS PROVIDED "AS IS". REGENTS HAS  NO OBLIGATION TO PROVIDE
	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

	File description:
	HashSpace is a way to detect object collisions using a voxel grid
	The grid has a given resolution (no. voxel cells per side)

	It is optimized for densely packed points and querying for nearest neighbors
	within given radii (results will be roughly sorted by distance).

	TODO: non-toroidal version.

	File author(s):
	Wesley Smith, 2010, wesley.hoke@gmail.com
	Graham Wakefield, 2011, grrrwaaa@gmail.com

	Inspired by this paper:
	http://nicolas.brodu.numerimoire.net/common/recherche/publications/QuerySphereIndexing.pdf
*/

namespace al {

/**
	HashSpace is a way to detect object collisions using a voxel grid
	The grid has a given resolution (no. voxel cells per side)

	It is optimized for densely packed points and querying for nearest neighbors
	within given radii (results will be roughly sorted by distance).
*/
class HashSpace {
public:

	/// container for registered spatial elements
	struct Object {
		Object() : hash(invalidHash()), next(NULL), prev(NULL), userdata(0) {}

		Vec3d pos;
		uint32_t hash;	///< which voxel ID it belongs to (or invalidHash())
		Object * next, * prev;	///< neighbors in the same voxel
		union {			///< a way to attach user-defined payloads:
			uint32_t id;
			void * userdata;
		};
	};

	/// each Voxel contains a linked list of Objects
	struct Voxel {
		Voxel() : mObjects(NULL) {}
		Voxel(const Voxel& cpy) : mObjects(NULL) {}

		/// definitely not thread-safe.
		inline void add(Object * o);

		/// definitely not thread-safe.
		inline void remove(Object * o);

		/// the linked list of objects in this voxel
		Object * mObjects;
	};

	/**
		Query functor
		create and re-use a query functor to find neighbors

		Query query;

		query.clear(); // do this if you want to re-use the query object
		query(space, Vec3d(0, 0, 0), 10);
		for (int i=0; i<query.size(); i++) {
			Object * o = query[i];
			...
		}
	*/
	struct Query {

		typedef std::vector<HashSpace::Object *> Vector;
		typedef Vector::iterator Iterator;

		/**
			Constructor
			@param maxResults the maximum number of results to find
		*/
		Query(uint32_t maxResults=128)
		:	mMaxResults(maxResults)
		{
			mObjects.reserve(maxResults);
		}

		/**
			The main method of the query object
			finds the neighbors of a given point, within given distances
			the matches will be roughly (not exactly) sorted by distance

			@param space the HashSpace object to search in
			@param center finds objects near to this point
			@param maxRadius finds objects if they are nearer this distance
				the maximum permissible value of radius is space.maxRadius()
			@param minRadius finds objects if they are beyond this distance
			@return the number of results found
		*/
		int operator()(const HashSpace& space, const Vec3d center, double maxRadius, double minRadius=0.);

		/**
			finds all the neighbors of the given point, up to maxResults()
			the matches will be roughly (not exactly) sorted by distance

			@param space the HashSpace object to search in
			@param center finds objects near to this point
		*/
		int operator()(const HashSpace& space, Vec3d center);

		/// get number of results:
		unsigned size() const { return mObjects.size(); }
		/// get each result:
		Object * operator[](unsigned i) const { return mObjects[i]; }

		/**
			clear is separated from the main query operation,
			to support aggregating queries in series
			typically however you would want to clear() and then call the query.
		*/
		Query& clear() { mObjects.clear(); return *this; }

		/// set the maximum number of desired results
		Query& maxResults(uint32_t i) { mMaxResults = i; return *this; }
		/// get the maximum number of desired results
		uint32_t maxResults() const { return mMaxResults; }

		/// std::vector interface:
		Iterator begin() { return mObjects.begin(); }
		Iterator end() { return mObjects.end(); }
		Vector& vector() { return mObjects; }

	protected:
		uint32_t mMaxResults;
		std::vector<Object *> mObjects;
	};

	/**
		Construct a HashSpace
		locations will range from [0..2^resolution)

		@param resolution determines the number of voxels as 2^resolution per axis
		@param numObjects set how many Object slots to initally allocate
	*/
	HashSpace(uint32_t resolution=5, uint32_t numObjects=0);

	~HashSpace();

	/// the dimension of the space per axis:
	uint32_t dim() const { return mDim; }
	/// the maximum valid radius to query (half the dimension):
	uint32_t maxRadius() const { return mDimHalf; }

	/// get/set the number of objects:
	void numObjects(int numObjects);
	uint32_t numObjects() const { return mObjects.size(); }

	/// get the object at a given index:
	Object& object(uint32_t i) { return mObjects[i]; }

	/// set the position of an object:
	HashSpace& move(uint32_t objectId, double x, double y, double z) { return move(objectId, Vec3d(x,y,z)); }
	HashSpace& move(uint32_t objectId, Vec3d pos);

	/// this removes the object from voxels/queries, but does not destroy it
	/// the objectId can be reused later via move()
	HashSpace& remove(uint32_t objectId);

	/// wrap an absolute position within the space:
	double wrap(double x) const { return wrap(x, dim()); }
	Vec3d wrap(Vec3d v) const {
		return Vec3d( wrap(v.x), wrap(v.y), wrap(v.z) );
	}

	/// wrap a relative vector within the space:
	/// use this when computing the vector between objects
	/// to properly take into account toroidal wrapping
	double wrapRelative(double x) const { return wrap(x, maxRadius()); }
	Vec3d wrapRelative(Vec3d v) const {
		return wrap(v + maxRadius()) - maxRadius();
	}

	/// an invalid voxel index used to indicate non-membership
	static uint32_t invalidHash() { return UINT_MAX; }

protected:

	// integer distance squared
	uint32_t distanceSquared(double a1, double a2, double a3) const;

	// convert x,y,z in range [0..DIM) to unsigned hash:
	// this is also the valid mVoxels index for the corresponding voxel:
	template<typename T>
	uint32_t hash(Vec<3,T> v) const { return hash(v[0], v[1], v[2]); }
	uint32_t hash(unsigned x, unsigned y, unsigned z) const {
		return hashx(x)+hashy(y)+hashz(z);
	}

	inline uint32_t hashx(uint32_t v) const { return v & mWrap; }
	inline uint32_t hashy(uint32_t v) const { return (v & mWrap)<<mShift; }
	inline uint32_t hashz(uint32_t v) const { return (v & mWrap)<<mShift2; }

	// convert unsigned hash to x,y,z in range [0..mDim):
	Vec3i unhash(uint32_t h) const { return Vec3i(unhashx(h), unhashy(h), unhashz(h)); }

	inline uint32_t unhashx(uint32_t h) const { return (h) & mWrap; }
	inline uint32_t unhashy(uint32_t h) const { return (h>>mShift) & mWrap; }
	inline uint32_t unhashz(uint32_t h) const { return (h>>mShift2) & mWrap; }

	// safe floating-point wrapping
	static double wrap(double x, double mod);
	static double wrap(double x, double lo, double hi);

	uint32_t mShift, mShift2, mDim, mDim2, mDim3, mWrap, mWrap3;
	int mDimHalf;	// the valid maximum radius for queries
	uint32_t mMaxD2, mMaxHalfD2;

	/// the array of objects
	std::vector<Object> mObjects;

	/// the array of voxels (indexed by hashed location)
	std::vector<Voxel> mVoxels;

	/// a baked array of voxel indices sorted by distance
	std::vector<uint32_t> mVoxelIndices;
	/// a baked array mapping distance to mVoxelIndices offsets
	std::vector<uint32_t> mDistanceToVoxelIndices;
};

// this is definitely not thread-safe.
inline void HashSpace::Voxel :: add(Object * o) {
	if (mObjects) {
		// add to tail:
		Object * last = mObjects->prev;
		last->next = o;
		o->prev = last;
		o->next = mObjects;
		mObjects->prev = o;
	} else {
		// unique:
		mObjects = o->prev = o->next = o;
	}
}

// this is definitely not thread-safe.
inline void HashSpace::Voxel :: remove(Object * o) {
	if (o == o->prev) {	// voxel only has 1 item
		mObjects = NULL;
	} else {
		Object * prev = o->prev;
		Object * next = o->next;
		prev->next = next;
		next->prev = prev;
		// update head pointer?
		if (mObjects == o) { mObjects = next; }
	}
	// leave the object clean:
	o->prev = o->next = NULL;
}

inline int HashSpace::Query :: operator()(const HashSpace& space, Vec3d center) {
	return (*this)(space, center, space.maxRadius());
}

// the maximum permissible value of radius is mDimHalf
// if int(inner^2) == int(outer^2), only 1 shell will be queried.
// TODO: non-toroidal version.
inline int HashSpace::Query :: operator()(const HashSpace& space, Vec3d center, double maxRadius, double minRadius) {
	unsigned nres = 0;
	uint32_t offset = space.hash(center);
	double minr2 = minRadius*minRadius;
	double maxr2 = maxRadius*maxRadius;
	uint32_t iminr2 = al::max(uint32_t(0), uint32_t((minRadius-1)*(minRadius-1)));
	uint32_t imaxr2 = al::min(space.mMaxHalfD2, uint32_t((maxRadius+1)*(maxRadius+1)));
	if (iminr2 < imaxr2) {
		uint32_t cellstart = space.mDistanceToVoxelIndices[iminr2];
		uint32_t cellend = space.mDistanceToVoxelIndices[imaxr2];

		for (uint32_t i = cellstart; i < cellend; i++) {
			// offset current index by voxel grid:
			uint32_t index = (offset + space.mVoxelIndices[i]) & space.mWrap3;
			const Voxel& cell = space.mVoxels[index];
			// now add any objects in this voxel to the result...
			Object * head = cell.mObjects;
			if (head) {
				Object * o = head;
				do {
					// final check - float version:
					Vec3d rel = space.wrapRelative(o->pos - center);
					double d2 = rel.magSqr();
					if (d2 >= minr2 && d2 <= maxr2) {
						mObjects.push_back(o);
						nres++;
					}
					o = o->next;
				} while (o != head && nres < mMaxResults);
			}
			if(nres == mMaxResults) break;
		}
	}
	return nres;
}

inline void HashSpace :: numObjects(int numObjects) {
	mObjects.clear();
	mObjects.resize(numObjects);
	// clear all voxels:
	for (unsigned i=0; i<mVoxels.size(); i++) {
		mVoxels[i].mObjects = 0;
	}
}

inline HashSpace& HashSpace :: move(uint32_t objectId, Vec3d pos) {
	Object& o = mObjects[objectId];
	o.pos.set(wrap(pos));
	uint32_t newhash = hash(o.pos);
	if (newhash != o.hash) {
		if (o.hash != invalidHash()) mVoxels[o.hash].remove(&o);
		o.hash = newhash;
		mVoxels[newhash].add(&o);
	}
	return *this;
}

inline HashSpace& HashSpace :: remove(uint32_t objectId) {
	Object& o = mObjects[objectId];
	if (o.hash != invalidHash()) mVoxels[o.hash].remove(&o);
	o.hash = invalidHash();
	return *this;
}

// integer distance squared
inline uint32_t HashSpace :: distanceSquared(double x, double y, double z) const {
	return x*x+y*y+z*z;
}

// wrapping floating point numbers is surprisingly complex
// because of rounding errors, behavior when near zero
// and other oddities
inline double HashSpace :: wrap(double x, double mod) {
	if (mod) {
		if (x > mod) {
			// shift down
			if (x > (mod*2.)) {
				// multiple wraps:
				double div = x / mod;
				// get fract:
				double divl = (long)div;
				double fract = div - (double)divl;
				return fract * mod;
			} else {
				// single wrap:
				return x - mod;
			}
		} else if (x < 0.) {
			// negative x, shift up
			if (x < -mod) {
				// multiple wraps:
				double div = x / mod;
				// get fract:
				double divl = (long)div;
				double fract = div - (double)divl;
				double x1 = fract * mod;
				return (x1 < 0.) ? x1 + mod : x1;
			} else {
				// single wrap:
				return x + mod;
			}
		} else {
			return x;
		}
	} else {
		return 0.;	// avoid divide by zero
	}
}

inline double HashSpace :: wrap(double x, double lo, double hi) {
	return lo + wrap(x-lo, hi-lo);
}

} // al::

#endif
<|endoftext|>


<commit_before>//******************************************************************************************************
//  InstanceSubscribe.cpp - Gbtc
//
//  Copyright  2010, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.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.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  03/27/2018 - J. Ritchie Carroll
//       Generated original version of source code.
//
//******************************************************************************************************

#include <iostream>

#include "SubscriberHandler.h"

#define TotalInstances 3

SubscriberHandler* Subscriber[TotalInstances];

int main(int argc, char* argv[])
{
    string hostname;
    uint16_t port;

    // Ensure that the necessary
    // command line arguments are given.
    if (argc < 3)
    {
        cout << "Usage:" << endl;
        cout << "    InstanceSubscribe HOSTNAME PORT" << endl;
        return 0;
    }

    // Get hostname and port.
    hostname = argv[1];
    stringstream(argv[2]) >> port;

    // Initialize the subscribers.
    for (size_t i = 0; i < TotalInstances; i++)
    {
        stringstream name;

        name << "Subscriber" << (i + 1);

        SubscriberHandler* subscriber = new SubscriberHandler(name.str());

        subscriber->Initialize(hostname, port);

        switch (i)
        {
            case 1:
                subscriber->SetFilterExpression("FILTER TOP 5 ActiveMeasurements WHERE SignalType = 'FREQ'");
                break;
            case 2:
                subscriber->SetFilterExpression("FILTER TOP 10 ActiveMeasurements WHERE SignalType LIKE '%PHA'");
                break;
            default:
                subscriber->SetFilterExpression("FILTER TOP 10 ActiveMeasurements WHERE SignalType LIKE '%PHM'");
                break;
        }

        subscriber->Connect();

        Subscriber[i] = subscriber;
    }

    // Wait until the user presses enter before quitting.
    string line;
    getline(cin, line);

    // Shutdown subscriber instances
    for (size_t i = 0; i < TotalInstances; i++)
    {
        Subscriber[i]->Disconnect();
        delete Subscriber[i];
    }

    // Disconnect the subscriber to stop background threads.
    cout << "Disconnected." << endl;

    return 0;
}<commit_msg>TimeSeriesPlatformLibrary: Updated instance subscribe to include a default option to subscribe to all non-statistic measurements.<commit_after>//******************************************************************************************************
//  InstanceSubscribe.cpp - Gbtc
//
//  Copyright  2010, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.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.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  03/27/2018 - J. Ritchie Carroll
//       Generated original version of source code.
//
//******************************************************************************************************

#include <iostream>

#include "SubscriberHandler.h"

#define TotalInstances 3

SubscriberHandler* Subscriber[TotalInstances];

int main(int argc, char* argv[])
{
    string hostname;
    uint16_t port;

    // Ensure that the necessary
    // command line arguments are given.
    if (argc < 3)
    {
        cout << "Usage:" << endl;
        cout << "    InstanceSubscribe HOSTNAME PORT" << endl;
        return 0;
    }

    // Get hostname and port.
    hostname = argv[1];
    stringstream(argv[2]) >> port;

    // Initialize the subscribers.
    for (size_t i = 0; i < TotalInstances; i++)
    {
        stringstream name;

        name << "Subscriber" << (i + 1);

        SubscriberHandler* subscriber = new SubscriberHandler(name.str());

        subscriber->Initialize(hostname, port);

        switch (i)
        {
            case 0:
                subscriber->SetFilterExpression("FILTER TOP 10 ActiveMeasurements WHERE SignalType = 'FREQ'");
                break;
            case 1:
                subscriber->SetFilterExpression("FILTER TOP 10 ActiveMeasurements WHERE SignalType LIKE '%PHA'");
                break;
            case 2:
                subscriber->SetFilterExpression("FILTER TOP 10 ActiveMeasurements WHERE SignalType LIKE '%PHM'");
                break;
            default:
                subscriber->SetFilterExpression(SubscriberInstance::SubscribeAllNoStatsExpression);
                break;
        }

        subscriber->Connect();

        Subscriber[i] = subscriber;
    }

    // Wait until the user presses enter before quitting.
    string line;
    getline(cin, line);

    // Shutdown subscriber instances
    for (size_t i = 0; i < TotalInstances; i++)
    {
        Subscriber[i]->Disconnect();
        delete Subscriber[i];
    }

    // Disconnect the subscriber to stop background threads.
    cout << "Disconnected." << endl;

    return 0;
}<|endoftext|>


<commit_before>#include <sys/time.h>
#include <sys/types.h>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)

#include "thread_private.h"

#define BULK_SIZE 1000

#define NUM_PAGES (40960 * nthreads)

extern bool verify_read_content;

class cleanup_callback: public callback
{
	rand_buf *buf;
	ssize_t read_bytes;
	int thread_id;
public:
	cleanup_callback(rand_buf *buf, int idx) {
		this->buf = buf;
		read_bytes = 0;
		this->thread_id = idx;
	}

	int invoke(io_request *rq) {
		extern bool verify_read_content;
		if (rq->get_access_method() == READ && verify_read_content) {
			if(*(unsigned long *) rq->get_buf() != rq->get_offset() / sizeof(long))
				printf("%ld %ld\n", *(unsigned long *) rq->get_buf(),
						rq->get_offset() / sizeof(long));
			assert(*(unsigned long *) rq->get_buf()
					== rq->get_offset() / sizeof(long));
		}
		buf->free_entry(rq->get_buf());
		read_bytes += rq->get_size();
		return 0;
	}

	ssize_t get_size() {
		return read_bytes;
	}
};

ssize_t thread_private::get_read_bytes() {
	if (cb)
		return cb->get_size();
	else
		return read_bytes;
}

int thread_private::thread_init() {
	attach2cpu();
	io->init();

	rand_buf *buf = new rand_buf(NUM_PAGES / (nthreads
				// TODO maybe I should set the right entry size for a buffer.
				// If each access size is irregular, I'll break each access
				// into pages so each access is no larger than a page, so it
				// should workl fine.
				/ NUM_NODES) * PAGE_SIZE, PAGE_SIZE);
	this->buf = buf;
	if (io->support_aio()) {
		cb = new cleanup_callback(buf, idx);
		io->set_callback(cb);
	}
	return 0;
}

int thread_private::run()
{
	ssize_t ret = -1;
	gettimeofday(&start_time, NULL);
	int reqs_capacity = BULK_SIZE * 2;
	io_request *reqs = (io_request *) malloc(sizeof(io_request) * reqs_capacity);
	while (gen->has_next()) {
		if (io->support_aio()) {
			int i;
//			io_request *reqs = gc->allocate_obj(BULK_SIZE);
			for (i = 0; i < BULK_SIZE && gen->has_next(); ) {
//				printf("thread %d: allocate %p\n", idx, p);
				// TODO right now it only support read.
				workload_t workload = gen->next();
				// TODO let's read data first;
				int access_method = READ;
				off_t off = workload.off;
				int size = workload.size;
				while (size > 0) {
					off_t next_off = ROUNDUP_PAGE(off + 1);
					if (next_off > off + size)
						next_off = off + size;
					// This is a very hacking way to handle the case that one access
					// is broken into multiple requests. It works because the array for
					// storing requests are twice as large as needed.
					assert (i < reqs_capacity);
					char *p = buf->next_entry();
					reqs[i].init(p, off, next_off - off, access_method, io);
					size -= next_off - off;
					off = next_off;
					i++;
				}
			}
			ret = io->access(reqs, i);
			if (ret < 0) {
				perror("access_vector");
				exit(1);
			}
		}
		else {
			workload_t workload = gen->next();
			off_t off = workload.off;
			// TODO let's just read data first.
			int access_method = READ;
			int entry_size = workload.size;

			while (entry_size > 0) {
				char *entry = buf->next_entry();
				/*
				 * generate the data for writing the file,
				 * so the data in the file isn't changed.
				 */
				if (access_method == WRITE) {
					unsigned long *p = (unsigned long *) entry;
					long start = off / sizeof(long);
					for (unsigned int i = 0; i < entry_size / sizeof(*p); i++)
						p[i] = start++;
				}
				// There is at least one byte we need to access in the page.
				// By adding 1 and rounding up the offset, we'll get the next page
				// behind the current offset.
				off_t next_off = ROUNDUP_PAGE(off + 1);
				if (next_off > off + entry_size)
					next_off = off + entry_size;
				ret = io->access(entry, off, next_off - off, access_method);
				if (ret > 0) {
					if (access_method == READ && verify_read_content) {
						if (*(unsigned long *) entry != off / sizeof(long))
							printf("entry: %ld, off: %ld\n", *(unsigned long *) entry, off / sizeof(long));
						assert(*(unsigned long *) entry == off / sizeof(long));
					}
					read_bytes += ret;
				}
				buf->free_entry(entry);
				if (ret < 0) {
					perror("access");
					exit(1);
				}
				entry_size -= next_off - off;
				off = next_off;
			}
		}
	}
	io->cleanup();
	printf("thread %d exits\n", idx);
	gettimeofday(&end_time, NULL);
	free(reqs);
	return 0;
}

int thread_private::attach2cpu()
{
#if NCPUS > 0
	cpu_set_t cpuset;
	pthread_t thread = pthread_self();
	CPU_ZERO(&cpuset);
	int cpu_num = idx % NCPUS;
	CPU_SET(cpu_num, &cpuset);
	int ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
	if (ret != 0) {
		perror("pthread_setaffinity_np");
		exit(1);
	}
	printf("attach thread %d to CPU %d\n", idx, cpu_num);
	return ret;
#else
	return -1;
#endif
}

static void *rand_read(void *arg)
{
	thread_private *priv = (thread_private *) arg;

	printf("rand_read: pid: %d, tid: %ld\n", getpid(), gettid());
	priv->thread_init();
	priv->run();
	return NULL;
}

#ifdef USE_PROCESS
static int process_create(pid_t *pid, void (*func)(void *), void *priv)
{
	pid_t id = fork();

	if (id < 0)
		return -1;

	if (id == 0) {	// child
		func(priv);
		exit(0);
	}

	if (id > 0)
		*pid = id;
	return 0;
}

static int process_join(pid_t pid)
{
	int status;
	pid_t ret = waitpid(pid, &status, 0);
	return ret < 0 ? ret : 0;
}
#endif

int thread_private::start_thread()
{
	int ret;
#ifdef USE_PROCESS
	ret = process_create(&id, rand_read, (void *) this);
#else
	ret = pthread_create(&id, NULL, rand_read, (void *) this);
#endif
	return ret;
}

int thread_private::wait_thread_end()
{
	int ret;
#ifdef USE_PROCESS
	ret = process_join(id);
#else
	ret = pthread_join(id, NULL);
#endif
	return ret;
}
<commit_msg>Fix a bug in data checking for unaligned read.<commit_after>#include <sys/time.h>
#include <sys/types.h>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)

#include "thread_private.h"

#define BULK_SIZE 1000

#define NUM_PAGES (40960 * nthreads)

extern bool verify_read_content;

void check_read_content(char *buf, int size, off_t off)
{
	// I assume the space in the buffer is larger than 8 bytes.
	off_t aligned_off = off & (~(sizeof(off_t) - 1));
	long data[2];
	data[0] = aligned_off / sizeof(off_t);
	data[1] = aligned_off / sizeof(off_t) + 1;
	long expected = 0;
	int copy_size = size < (int) sizeof(off_t) ? size : (int) sizeof(off_t);
	memcpy(&expected, ((char *) data) + (off - aligned_off), copy_size);
	long read_value = 0;
	memcpy(&read_value, buf, copy_size);
	if(read_value != expected)
		printf("%ld %ld\n", read_value, expected);
	assert(read_value == expected);
}

class cleanup_callback: public callback
{
	rand_buf *buf;
	ssize_t read_bytes;
	int thread_id;
public:
	cleanup_callback(rand_buf *buf, int idx) {
		this->buf = buf;
		read_bytes = 0;
		this->thread_id = idx;
	}

	int invoke(io_request *rq) {
		extern bool verify_read_content;
		if (rq->get_access_method() == READ && verify_read_content) {
			check_read_content(rq->get_buf(), rq->get_size(), rq->get_offset());
		}
		buf->free_entry(rq->get_buf());
		read_bytes += rq->get_size();
		return 0;
	}

	ssize_t get_size() {
		return read_bytes;
	}
};

ssize_t thread_private::get_read_bytes() {
	if (cb)
		return cb->get_size();
	else
		return read_bytes;
}

int thread_private::thread_init() {
	attach2cpu();
	io->init();

	rand_buf *buf = new rand_buf(NUM_PAGES / (nthreads
				// TODO maybe I should set the right entry size for a buffer.
				// If each access size is irregular, I'll break each access
				// into pages so each access is no larger than a page, so it
				// should workl fine.
				/ NUM_NODES) * PAGE_SIZE, PAGE_SIZE);
	this->buf = buf;
	if (io->support_aio()) {
		cb = new cleanup_callback(buf, idx);
		io->set_callback(cb);
	}
	return 0;
}

int thread_private::run()
{
	ssize_t ret = -1;
	gettimeofday(&start_time, NULL);
	int reqs_capacity = BULK_SIZE * 2;
	io_request *reqs = (io_request *) malloc(sizeof(io_request) * reqs_capacity);
	while (gen->has_next()) {
		if (io->support_aio()) {
			int i;
//			io_request *reqs = gc->allocate_obj(BULK_SIZE);
			for (i = 0; i < BULK_SIZE && gen->has_next(); ) {
//				printf("thread %d: allocate %p\n", idx, p);
				// TODO right now it only support read.
				workload_t workload = gen->next();
				// TODO let's read data first;
				int access_method = READ;
				off_t off = workload.off;
				int size = workload.size;
				while (size > 0) {
					off_t next_off = ROUNDUP_PAGE(off + 1);
					if (next_off > off + size)
						next_off = off + size;
					// This is a very hacking way to handle the case that one access
					// is broken into multiple requests. It works because the array for
					// storing requests are twice as large as needed.
					assert (i < reqs_capacity);
					char *p = buf->next_entry();
					reqs[i].init(p, off, next_off - off, access_method, io);
					size -= next_off - off;
					off = next_off;
					i++;
				}
			}
			ret = io->access(reqs, i);
			if (ret < 0) {
				perror("access_vector");
				exit(1);
			}
		}
		else {
			workload_t workload = gen->next();
			off_t off = workload.off;
			// TODO let's just read data first.
			int access_method = READ;
			int entry_size = workload.size;

			while (entry_size > 0) {
				char *entry = buf->next_entry();
				/*
				 * generate the data for writing the file,
				 * so the data in the file isn't changed.
				 */
				if (access_method == WRITE) {
					unsigned long *p = (unsigned long *) entry;
					long start = off / sizeof(long);
					for (unsigned int i = 0; i < entry_size / sizeof(*p); i++)
						p[i] = start++;
				}
				// There is at least one byte we need to access in the page.
				// By adding 1 and rounding up the offset, we'll get the next page
				// behind the current offset.
				off_t next_off = ROUNDUP_PAGE(off + 1);
				if (next_off > off + entry_size)
					next_off = off + entry_size;
				ret = io->access(entry, off, next_off - off, access_method);
				if (ret > 0) {
					if (access_method == READ && verify_read_content) {
						check_read_content(entry, next_off - off, off);
					}
					read_bytes += ret;
				}
				buf->free_entry(entry);
				if (ret < 0) {
					perror("access");
					exit(1);
				}
				entry_size -= next_off - off;
				off = next_off;
			}
		}
	}
	io->cleanup();
	printf("thread %d exits\n", idx);
	gettimeofday(&end_time, NULL);
	free(reqs);
	return 0;
}

int thread_private::attach2cpu()
{
#if NCPUS > 0
	cpu_set_t cpuset;
	pthread_t thread = pthread_self();
	CPU_ZERO(&cpuset);
	int cpu_num = idx % NCPUS;
	CPU_SET(cpu_num, &cpuset);
	int ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
	if (ret != 0) {
		perror("pthread_setaffinity_np");
		exit(1);
	}
	printf("attach thread %d to CPU %d\n", idx, cpu_num);
	return ret;
#else
	return -1;
#endif
}

static void *rand_read(void *arg)
{
	thread_private *priv = (thread_private *) arg;

	printf("rand_read: pid: %d, tid: %ld\n", getpid(), gettid());
	priv->thread_init();
	priv->run();
	return NULL;
}

#ifdef USE_PROCESS
static int process_create(pid_t *pid, void (*func)(void *), void *priv)
{
	pid_t id = fork();

	if (id < 0)
		return -1;

	if (id == 0) {	// child
		func(priv);
		exit(0);
	}

	if (id > 0)
		*pid = id;
	return 0;
}

static int process_join(pid_t pid)
{
	int status;
	pid_t ret = waitpid(pid, &status, 0);
	return ret < 0 ? ret : 0;
}
#endif

int thread_private::start_thread()
{
	int ret;
#ifdef USE_PROCESS
	ret = process_create(&id, rand_read, (void *) this);
#else
	ret = pthread_create(&id, NULL, rand_read, (void *) this);
#endif
	return ret;
}

int thread_private::wait_thread_end()
{
	int ret;
#ifdef USE_PROCESS
	ret = process_join(id);
#else
	ret = pthread_join(id, NULL);
#endif
	return ret;
}
<|endoftext|>


<commit_before>#include "ex14_16_StrVec.h"
#include <iostream>
#include <vector>

int main()
{
    StrVec vec;
    vec.reserve(6);
    std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl;

    vec.reserve(4);
    std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl;

    vec.push_back("hello");
    vec.push_back("world");

    vec.resize(4);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        std::cout << *i << std::endl;
    std::cout << "-EOF-" << std::endl;

    vec.resize(1);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        std::cout << *i << std::endl;
    std::cout << "-EOF-" << std::endl;

    StrVec vec_list{"hello", "world", "pezy"};

    for (auto i = vec_list.begin(); i != vec_list.end(); ++i)
        std::cout << *i << " ";
    std::cout << std::endl;

    // Test operator==

    const StrVec const_vec_list{"hello", "world", "pezy"};
    if (vec_list == const_vec_list)
        for (const auto& str : const_vec_list) std::cout << str << " ";
    std::cout << std::endl;
}
<commit_msg>Update ex14_16_StrVecMain.cpp<commit_after>#include <iostream>
#include <vector>
#include "ex14_16_StrVec.h"
using namespace std;

int main()
{
    StrVec vec;
    vec.reserve(6);
    cout << "capacity(reserve to 6): " << vec.capacity() << endl;

    vec.reserve(4);
    cout << "capacity(reserve to 4): " << vec.capacity() << endl;

    vec.push_back("hello");
    vec.push_back("world");

    vec.resize(4);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        cout << *i << endl;
    cout << "-EOF-" << endl;

    vec.resize(1);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        cout << *i << endl;
    cout << "-EOF-" << endl;

    StrVec vec_list{"hello", "world", "pezy"};

    for (auto i = vec_list.begin(); i != vec_list.end(); ++i)
        cout << *i << " ";
    cout << endl;

    // Test operator==
    const StrVec const_vec_list{"hello", "world", "pezy"};
    if (vec_list == const_vec_list)
        for (const auto& str : const_vec_list)
            cout << str << " ";
    cout << endl;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Stephan Vedder <stefano.pigozzi@gmail.com>
 *
 * This file is part of libass.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "config.h"

#ifdef CONFIG_DIRECTWRITE

#include <dwrite.h>

extern "C"
{
#include "ass_directwrite.h"
#include "ass_utils.h"
}

typedef struct {
	IDWriteFont *font;
	IDWriteFontFileStream *stream;
} FontPrivate;

static bool init_font_private(FontPrivate *priv)
{
	HRESULT hr = S_OK;
	IDWriteFont* font = priv->font;
	IDWriteFontFace* face = NULL;
	IDWriteFontFile* file = NULL;
	IDWriteFontFileStream* stream = NULL;
	IDWriteFontFileLoader* loader = NULL;
	UINT32 n_files = 1;
	const void* refKey = NULL;
	UINT32 keySize = 0;

	if (priv->stream != NULL)
		return true;

	hr = font->CreateFontFace(&face);
	if (FAILED(hr) || !face)
		return false;

	hr = face->GetFiles(&n_files, &file);
	if (FAILED(hr) || !file) {
		face->Release();
		return false;
	}

	hr = file->GetReferenceKey(&refKey, &keySize);
	if (FAILED(hr)) {
		file->Release();
		face->Release();
		return false;
	}

	hr = file->GetLoader(&loader);
	if (FAILED(hr) || !loader) {
		file->Release();
		face->Release();
		return false;
	}

	hr = loader->CreateStreamFromKey(refKey, keySize, &stream);
	if (FAILED(hr) || !stream) {
		file->Release();
		face->Release();
		return false;
	}

	priv->stream = stream;
	file->Release();
	face->Release();

	return true;
}

static size_t get_data(void *data, unsigned char* buf, size_t offset, size_t length)
{
	HRESULT hr = S_OK;
	FontPrivate *priv = (FontPrivate *)data;
	const void *fileBuf = NULL;
	void *fragContext = NULL;

	if (!init_font_private(priv))
		return 0;

	if (buf == NULL) {
		UINT64 fileSize;
		hr = priv->stream->GetFileSize(&fileSize);
		if (FAILED(hr))
			return 0;

		return fileSize;
	}

	hr = priv->stream->ReadFileFragment(&fileBuf, offset, length, &fragContext);
	if (FAILED(hr) || !fileBuf)
		return 0;

	memcpy(buf, fileBuf, length);

	priv->stream->ReleaseFileFragment(fragContext);

	return length;
}

static int check_glyph(void *data, uint32_t code)
{
	HRESULT hr = S_OK;
	FontPrivate *priv = (FontPrivate *)data;
	BOOL exists = FALSE;

	if (code == 0)
		return 1;

	priv->font->HasCharacter(code, &exists);
	if (FAILED(hr))
		return 0;

	return exists;
}

static void destroy(void* priv)
{
	((IDWriteFactory*)priv)->Release();
}

static void destroy_font(void *data)
{
	FontPrivate *priv = (FontPrivate *)data;

	priv->font->Release();
	if (priv->stream != NULL)
		priv->stream->Release();

	free(priv);
}

static void scan_fonts(IDWriteFactory *factory, ASS_FontProvider *provider)
{
	HRESULT hr = S_OK;
	IDWriteFontCollection* fontCollection = NULL;
	IDWriteFont* font = NULL;
	DWRITE_FONT_METRICS metrics;
	DWRITE_FONT_STYLE style;
	ASS_FontProviderMetaData meta = ASS_FontProviderMetaData();
	hr = factory->GetSystemFontCollection(&fontCollection,FALSE);
	wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
	int size_needed = 0;

	if(FAILED(hr)||!fontCollection)
		return;

	UINT32 familyCount = fontCollection->GetFontFamilyCount();

	for (UINT32 i = 0; i < familyCount; ++i)
    {
		IDWriteFontFamily* fontFamily = NULL;
		IDWriteLocalizedStrings* familyNames = NULL;
		IDWriteLocalizedStrings* fontNames = NULL;
		IDWriteLocalizedStrings* psNames = NULL;
		BOOL exists = FALSE;
		char* psName = NULL;

		// Get the font family.
		hr = fontCollection->GetFontFamily(i, &fontFamily);
		if (FAILED(hr))
			return;

		UINT32 fontCount = fontFamily->GetFontCount();
		for (UINT32 j = 0; j < fontCount; ++j)
		{
			hr = fontFamily->GetFont(j, &font);
			if (FAILED(hr))
				return;

			meta.weight = font->GetWeight();
			font->GetMetrics(&metrics);
			style = font->GetStyle();
			meta.slant =	(style==DWRITE_FONT_STYLE_NORMAL)? FONT_SLANT_NONE:
							(style==DWRITE_FONT_STYLE_OBLIQUE)? FONT_SLANT_OBLIQUE:
							(style==DWRITE_FONT_STYLE_ITALIC)? FONT_SLANT_ITALIC : FONT_SLANT_NONE;

			hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &psNames, &exists);
			if (FAILED(hr))
				return;

			if (exists)
			{
				hr = psNames->GetString(0, localeName, LOCALE_NAME_MAX_LENGTH + 1);
				if (FAILED(hr))
					return;

				size_needed = WideCharToMultiByte(CP_UTF8, 0, localeName, -1, NULL, 0, NULL, NULL);
				psName = (char*)ass_aligned_alloc(32, size_needed);
				WideCharToMultiByte(CP_UTF8, 0, localeName, -1, psName, size_needed, NULL, NULL);
			}

			hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_FULL_NAME, &fontNames, &exists);
			if (FAILED(hr))
				return;

			meta.n_fullname = fontNames->GetCount();
			meta.fullnames = (char **)calloc(meta.n_fullname, sizeof(char *));
			for (UINT32 k = 0; k < meta.n_fullname; ++k)
			{
				hr = fontNames->GetString(k,localeName, LOCALE_NAME_MAX_LENGTH + 1);
				if (FAILED(hr))
					return;

				size_needed = WideCharToMultiByte(CP_UTF8, 0, localeName, -1, NULL, 0, NULL, NULL);
				char* mbName = (char *)malloc(size_needed);
				WideCharToMultiByte(CP_UTF8, 0, localeName, -1, mbName, size_needed, NULL, NULL);
				meta.fullnames[k] = mbName;
			}

			hr = fontFamily->GetFamilyNames(&familyNames);
			if (FAILED(hr))
				return;

			meta.n_family = familyNames->GetCount();
			meta.families = (char **)calloc(meta.n_family, sizeof(char *));
			for (UINT32 k = 0; k < meta.n_family; ++k)
			{
				hr = familyNames->GetString(k, localeName, LOCALE_NAME_MAX_LENGTH + 1);
				if (FAILED(hr))
					return;

				size_needed = WideCharToMultiByte(CP_UTF8, 0, localeName, -1, NULL, 0, NULL, NULL);
				char* mbName = (char *)malloc(size_needed);
				WideCharToMultiByte(CP_UTF8, 0, localeName, -1, mbName, size_needed, NULL, NULL);
				meta.families[k] = mbName;
			}

			FontPrivate *font_priv = (FontPrivate *)calloc(1, sizeof(*font_priv));
			font_priv->font = font;

			ass_font_provider_add_font(provider, &meta, NULL, 0, psName, font_priv);

			for (UINT32 k = 0; k < meta.n_family; ++k)
				free(meta.families[k]);
			for (UINT32 k = 0; k < meta.n_fullname; ++k)
				free(meta.fullnames[k]);
			free(meta.fullnames);
			free(meta.families);
			free(psName);
		}
    }
}

static ASS_FontProviderFuncs directwrite_callbacks = {
    get_data,
	check_glyph,
    NULL,
    destroy,
    NULL
};

ASS_FontProvider *
ass_directwrite_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
                          const char *config)
{
	HRESULT hr = S_OK;
	IDWriteFactory* dwFactory = NULL;
	ASS_FontProvider *provider = NULL;

	hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            (IUnknown**)(&dwFactory));

	if(FAILED(hr))
	{
		ass_msg(lib, MSGL_WARN, "Failed to initialize directwrite.");
		goto exit;
	}

    provider = ass_font_provider_new(selector, &directwrite_callbacks, dwFactory);

    scan_fonts(dwFactory,provider);
exit:
    return provider;
}

#endif
<commit_msg>directwrite: wire up destroy callbacks<commit_after>/*
 * Copyright (C) 2015 Stephan Vedder <stefano.pigozzi@gmail.com>
 *
 * This file is part of libass.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "config.h"

#ifdef CONFIG_DIRECTWRITE

#include <dwrite.h>

extern "C"
{
#include "ass_directwrite.h"
#include "ass_utils.h"
}

typedef struct {
	IDWriteFont *font;
	IDWriteFontFileStream *stream;
} FontPrivate;

static bool init_font_private(FontPrivate *priv)
{
	HRESULT hr = S_OK;
	IDWriteFont* font = priv->font;
	IDWriteFontFace* face = NULL;
	IDWriteFontFile* file = NULL;
	IDWriteFontFileStream* stream = NULL;
	IDWriteFontFileLoader* loader = NULL;
	UINT32 n_files = 1;
	const void* refKey = NULL;
	UINT32 keySize = 0;

	if (priv->stream != NULL)
		return true;

	hr = font->CreateFontFace(&face);
	if (FAILED(hr) || !face)
		return false;

	hr = face->GetFiles(&n_files, &file);
	if (FAILED(hr) || !file) {
		face->Release();
		return false;
	}

	hr = file->GetReferenceKey(&refKey, &keySize);
	if (FAILED(hr)) {
		file->Release();
		face->Release();
		return false;
	}

	hr = file->GetLoader(&loader);
	if (FAILED(hr) || !loader) {
		file->Release();
		face->Release();
		return false;
	}

	hr = loader->CreateStreamFromKey(refKey, keySize, &stream);
	if (FAILED(hr) || !stream) {
		file->Release();
		face->Release();
		return false;
	}

	priv->stream = stream;
	file->Release();
	face->Release();

	return true;
}

static size_t get_data(void *data, unsigned char* buf, size_t offset, size_t length)
{
	HRESULT hr = S_OK;
	FontPrivate *priv = (FontPrivate *)data;
	const void *fileBuf = NULL;
	void *fragContext = NULL;

	if (!init_font_private(priv))
		return 0;

	if (buf == NULL) {
		UINT64 fileSize;
		hr = priv->stream->GetFileSize(&fileSize);
		if (FAILED(hr))
			return 0;

		return fileSize;
	}

	hr = priv->stream->ReadFileFragment(&fileBuf, offset, length, &fragContext);
	if (FAILED(hr) || !fileBuf)
		return 0;

	memcpy(buf, fileBuf, length);

	priv->stream->ReleaseFileFragment(fragContext);

	return length;
}

static int check_glyph(void *data, uint32_t code)
{
	HRESULT hr = S_OK;
	FontPrivate *priv = (FontPrivate *)data;
	BOOL exists = FALSE;

	if (code == 0)
		return 1;

	priv->font->HasCharacter(code, &exists);
	if (FAILED(hr))
		return 0;

	return exists;
}

static void destroy_provider(void *priv)
{
	((IDWriteFactory*)priv)->Release();
}

static void destroy_font(void *data)
{
	FontPrivate *priv = (FontPrivate *)data;

	priv->font->Release();
	if (priv->stream != NULL)
		priv->stream->Release();

	free(priv);
}

static void scan_fonts(IDWriteFactory *factory, ASS_FontProvider *provider)
{
	HRESULT hr = S_OK;
	IDWriteFontCollection* fontCollection = NULL;
	IDWriteFont* font = NULL;
	DWRITE_FONT_METRICS metrics;
	DWRITE_FONT_STYLE style;
	ASS_FontProviderMetaData meta = ASS_FontProviderMetaData();
	hr = factory->GetSystemFontCollection(&fontCollection,FALSE);
	wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
	int size_needed = 0;

	if(FAILED(hr)||!fontCollection)
		return;

	UINT32 familyCount = fontCollection->GetFontFamilyCount();

	for (UINT32 i = 0; i < familyCount; ++i)
    {
		IDWriteFontFamily* fontFamily = NULL;
		IDWriteLocalizedStrings* familyNames = NULL;
		IDWriteLocalizedStrings* fontNames = NULL;
		IDWriteLocalizedStrings* psNames = NULL;
		BOOL exists = FALSE;
		char* psName = NULL;

		// Get the font family.
		hr = fontCollection->GetFontFamily(i, &fontFamily);
		if (FAILED(hr))
			return;

		UINT32 fontCount = fontFamily->GetFontCount();
		for (UINT32 j = 0; j < fontCount; ++j)
		{
			hr = fontFamily->GetFont(j, &font);
			if (FAILED(hr))
				return;

			meta.weight = font->GetWeight();
			font->GetMetrics(&metrics);
			style = font->GetStyle();
			meta.slant =	(style==DWRITE_FONT_STYLE_NORMAL)? FONT_SLANT_NONE:
							(style==DWRITE_FONT_STYLE_OBLIQUE)? FONT_SLANT_OBLIQUE:
							(style==DWRITE_FONT_STYLE_ITALIC)? FONT_SLANT_ITALIC : FONT_SLANT_NONE;

			hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &psNames, &exists);
			if (FAILED(hr))
				return;

			if (exists)
			{
				hr = psNames->GetString(0, localeName, LOCALE_NAME_MAX_LENGTH + 1);
				if (FAILED(hr))
					return;

				size_needed = WideCharToMultiByte(CP_UTF8, 0, localeName, -1, NULL, 0, NULL, NULL);
				psName = (char*)ass_aligned_alloc(32, size_needed);
				WideCharToMultiByte(CP_UTF8, 0, localeName, -1, psName, size_needed, NULL, NULL);
			}

			hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_FULL_NAME, &fontNames, &exists);
			if (FAILED(hr))
				return;

			meta.n_fullname = fontNames->GetCount();
			meta.fullnames = (char **)calloc(meta.n_fullname, sizeof(char *));
			for (UINT32 k = 0; k < meta.n_fullname; ++k)
			{
				hr = fontNames->GetString(k,localeName, LOCALE_NAME_MAX_LENGTH + 1);
				if (FAILED(hr))
					return;

				size_needed = WideCharToMultiByte(CP_UTF8, 0, localeName, -1, NULL, 0, NULL, NULL);
				char* mbName = (char *)malloc(size_needed);
				WideCharToMultiByte(CP_UTF8, 0, localeName, -1, mbName, size_needed, NULL, NULL);
				meta.fullnames[k] = mbName;
			}

			hr = fontFamily->GetFamilyNames(&familyNames);
			if (FAILED(hr))
				return;

			meta.n_family = familyNames->GetCount();
			meta.families = (char **)calloc(meta.n_family, sizeof(char *));
			for (UINT32 k = 0; k < meta.n_family; ++k)
			{
				hr = familyNames->GetString(k, localeName, LOCALE_NAME_MAX_LENGTH + 1);
				if (FAILED(hr))
					return;

				size_needed = WideCharToMultiByte(CP_UTF8, 0, localeName, -1, NULL, 0, NULL, NULL);
				char* mbName = (char *)malloc(size_needed);
				WideCharToMultiByte(CP_UTF8, 0, localeName, -1, mbName, size_needed, NULL, NULL);
				meta.families[k] = mbName;
			}

			FontPrivate *font_priv = (FontPrivate *)calloc(1, sizeof(*font_priv));
			font_priv->font = font;

			ass_font_provider_add_font(provider, &meta, NULL, 0, psName, font_priv);

			for (UINT32 k = 0; k < meta.n_family; ++k)
				free(meta.families[k]);
			for (UINT32 k = 0; k < meta.n_fullname; ++k)
				free(meta.fullnames[k]);
			free(meta.fullnames);
			free(meta.families);
			free(psName);
		}
    }
}

static ASS_FontProviderFuncs directwrite_callbacks = {
    get_data,
	check_glyph,
    destroy_font,
    destroy_provider,
    NULL,
};

ASS_FontProvider *
ass_directwrite_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
                          const char *config)
{
	HRESULT hr = S_OK;
	IDWriteFactory* dwFactory = NULL;
	ASS_FontProvider *provider = NULL;

	hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            (IUnknown**)(&dwFactory));

	if(FAILED(hr))
	{
		ass_msg(lib, MSGL_WARN, "Failed to initialize directwrite.");
		goto exit;
	}

    provider = ass_font_provider_new(selector, &directwrite_callbacks, dwFactory);

    scan_fonts(dwFactory,provider);
exit:
    return provider;
}

#endif
<|endoftext|>


<commit_before>/* Transactor framework, a wrapper for safely retryable transactions.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transactor instead.
 *
 * Copyright (c) 2001-2017, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#ifndef PQXX_H_TRANSACTOR
#define PQXX_H_TRANSACTOR

#include "pqxx/compiler-public.hxx"
#include "pqxx/compiler-internal-pre.hxx"

#include "pqxx/connection_base"
#include "pqxx/transaction"

// Methods tested in eg. test module test01 are marked with "//[t01]".

/**
 * @defgroup transactor Transactor framework
 *
 * Sometimes your application needs to execute a transaction that should be
 * retried if it fails.  For example, your REST API might be handling an HTTP
 * request in its own database transaction, and if it fails for transient
 * reasons, you simply want to "replay" the whole request from the start, in a
 * fresh transaction.
 *
 * One of those transient reasons might be a deadlock during a SERIALIZABLE or
 * REPEATABLE READ transaction.  Another reason might be that your network
 * connection to the database fails, and perhaps you don't just want to give up
 * when that happens.
 *
 * In situations like these, the right thing to do is often to restart your
 * transaction from scratch.  You won't necessarily want to execute the exact
 * same SQL commands with the exact same data, but you'll want to re-run the
 * same application code that produced those SQL commands.
 *
 * The transactor framework makes it a little easier for you to do this safely,
 * and avoid typical pitfalls.  You encapsulate the work that you want to do in
 * a transaction into something that you pass to @c perform.
 *
 * Transactors come in two flavours.
 *
 * The old, pre-C++11 way is to derive a class from the @c transactor template,
 * and pass an instance of it to your connection's @c connection_base::perform
 * member function.  That function will create a transaction object and pass
 * it to your @c transactor, handle any exceptions, commit or abort, and
 * repeat as appropriate.
 *
 * The new, simpler C++11-based way is to write your transaction code as a
 * lambda (or other callable), which creates its own transaction object, does
 * its work, and commits at the end.  You pass that callback to pqxx::perform.
 * If any given attempt fails, its transaction object goes out of scope and
 * gets destroyed, so that it aborts implicitly.  Your callback can return its
 * results to the calling code.
 */
//@{

namespace pqxx
{
/// Simple way to execute a transaction with automatic retry.
/**
 * Executes your transaction code as a callback.  Repeats it until it completes
 * normally, or it throws an error other than the few libpqxx-generated
 * exceptions that the framework understands, or after a given number of failed
 * attempts, or if the transaction ends in an "in-doubt" state.
 *
 * (An in-doubt state is one where libpqxx cannot determine whether the server
 * finally committed a transaction or not.  This can happen if the network
 * connection to the server is lost just while we're waiting for its reply to
 * a "commit" statement.  The server may have completed the commit, or not, but
 * it can't tell you because there's no longer a connection.
 *
 * Using this still takes a bit of care.  If your callback makes use of data
 * from the database, you'll probably have to query that data within your
 * callback.  If the attempt to perform your callback fails, and the framework
 * tries again, you'll be in a new transaction and the data in the database may
 * have changed under your feet.
 *
 * Also be careful about changing variables or data structures from within
 * your callback.  The run may still fail, and perhaps get run again.  The
 * ideal way to do it (in most cases) is to return your result from your
 * callback, and change your program's data after @c perform completes
 * successfully.
 *
 * This function replaces an older, more complicated transactor framework.
 * The new function is a simpler, more lambda-friendly way of doing the same
 * thing.
 *
 * @param callback Transaction code that can be called with no arguments.
 * @param attempts Maximum number of times to attempt performing callback.
 *	Must be greater than zero.
 * @return Whatever your callback returns.
 */
template<typename TRANSACTION_CALLBACK>
inline auto perform(const TRANSACTION_CALLBACK &callback, int attempts=3)
  -> decltype(callback())
{
  if (attempts <= 0)
    throw std::invalid_argument(
	"Zero or negative number of attempts passed to pqxx::perform().");

  for (; attempts > 0; --attempts)
  {
    try
    {
      return callback();
    }
    catch (const in_doubt_error &)
    {
      // Not sure whether transaction went through or not.  The last thing in
      // the world that we should do now is try again!
      throw;
    }
    catch (const statement_completion_unknown &)
    {
      // Not sure whether our last statement succeeded.  Don't risk running it
      // again.
      throw;
    }
    catch (const broken_connection &)
    {
      // Connection failed.  Definitely worth retrying.
      if (attempts <= 1) throw;
      continue;
    }
    catch (const transaction_rollback &)
    {
      // Some error that may well be transient, such as serialization failure
      // or deadlock.  Worth retrying.
      if (attempts <= 1) throw;
      continue;
    }
  }
  throw pqxx::internal_error("No outcome reached on perform().");
}

/// @deprecated Pre-C++11 wrapper for automatically retrying transactions.
/**
 * Pass an object of your transactor-based class to connection_base::perform()
 * to execute the transaction code embedded in it.
 *
 * connection_base::perform() is actually a template, specializing itself to any
 * transactor type you pass to it.  This means you will have to pass it a
 * reference of your object's ultimate static type; runtime polymorphism is
 * not allowed.  Hence the absence of virtual methods in transactor.  The
 * exact methods to be called at runtime *must* be resolved at compile time.
 *
 * Your transactor-derived class must define a copy constructor.  This will be
 * used to create a "clean" copy of your transactor for every attempt that
 * perform() makes to run it.
 */
template<typename TRANSACTION=transaction<read_committed>> class transactor
{
public:
  using argument_type = TRANSACTION;
  explicit transactor(const std::string &TName="transactor") :		//[t04]
    m_name(TName) { }

  /// Overridable transaction definition; insert your database code here
  /** The operation will be retried if the connection to the backend is lost or
   * the operation fails, but not if the connection is broken in such a way as
   * to leave the library in doubt as to whether the operation succeeded.  In
   * that case, an in_doubt_error will be thrown.
   *
   * Recommended practice is to allow this operator to modify only the
   * transactor itself, and the dedicated transaction object it is passed as an
   * argument.  This is what makes side effects, retrying etc. controllable in
   * the transactor framework.
   * @param T Dedicated transaction context created to perform this operation.
   */
  void operator()(TRANSACTION &T);					//[t04]

  // Overridable member functions, called by connection_base::perform() if an
  // attempt to run transaction fails/succeeds, respectively, or if the
  // connection is lost at just the wrong moment, goes into an indeterminate
  // state.  Use these to patch up runtime state to match events, if needed, or
  // to report failure conditions.

  /// Optional overridable function to be called if transaction is aborted
  /** This need not imply complete failure; the transactor will automatically
   * retry the operation a number of times before giving up.  on_abort() will be
   * called for each of the failed attempts.
   *
   * One parameter is passed in by the framework: an error string describing why
   * the transaction failed.  This will also be logged to the connection's
   * notice processor.
   */
  void on_abort(const char[]) noexcept {}				//[t13]

  /// Optional overridable function to be called after successful commit
  /** If your on_commit() throws an exception, the actual back-end transaction
   * will remain committed, so any changes in the database remain regardless of
   * how this function terminates.
   */
  void on_commit() {}							//[t07]

  /// Overridable function to be called when "in doubt" about outcome
  /** This may happen if the connection to the backend is lost while attempting
   * to commit.  In that case, the backend may have committed the transaction
   * but is unable to confirm this to the frontend; or the transaction may have
   * failed, causing it to be rolled back, but again without acknowledgement to
   * the client program.  The best way to deal with this situation is typically
   * to wave red flags in the user's face and ask him to investigate.
   *
   * The robusttransaction class is intended to reduce the chances of this
   * error occurring, at a certain cost in performance.
   * @see robusttransaction
   */
  void on_doubt() noexcept {}						//[t13]

  /// The transactor's name.
  std::string name() const { return m_name; }				//[t13]

private:
  std::string m_name;
};

template<typename TRANSACTOR>
inline void connection_base::perform(
	const TRANSACTOR &T,
        int Attempts)
{
  if (Attempts <= 0) return;

  bool Done = false;

  // Make attempts to perform T
  do
  {
    --Attempts;

    // Work on a copy of T2 so we can restore the starting situation if need be
    TRANSACTOR T2(T);
    try
    {
      typename TRANSACTOR::argument_type X(*this, T2.name());
      T2(X);
      X.commit();
      Done = true;
    }
    catch (const in_doubt_error &)
    {
      // Not sure whether transaction went through or not.  The last thing in
      // the world that we should do now is retry.
      T2.on_doubt();
      throw;
    }
    catch (const std::exception &e)
    {
      // Could be any kind of error.
      T2.on_abort(e.what());
      if (Attempts <= 0) throw;
      continue;
    }
    catch (...)
    {
      // Don't try to forge ahead if we don't even know what happened
      T2.on_abort("Unknown exception");
      throw;
    }

    T2.on_commit();
  } while (!Done);
}
//@}
} // namespace pqxx
#include "pqxx/compiler-internal-post.hxx"
#endif
<commit_msg>Move pqxx namespace out of transactor doc group.<commit_after>/* Transactor framework, a wrapper for safely retryable transactions.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transactor instead.
 *
 * Copyright (c) 2001-2017, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#ifndef PQXX_H_TRANSACTOR
#define PQXX_H_TRANSACTOR

#include "pqxx/compiler-public.hxx"
#include "pqxx/compiler-internal-pre.hxx"

#include "pqxx/connection_base"
#include "pqxx/transaction"

// Methods tested in eg. test module test01 are marked with "//[t01]".

namespace pqxx
{
/**
 * @defgroup transactor Transactor framework
 *
 * Sometimes your application needs to execute a transaction that should be
 * retried if it fails.  For example, your REST API might be handling an HTTP
 * request in its own database transaction, and if it fails for transient
 * reasons, you simply want to "replay" the whole request from the start, in a
 * fresh transaction.
 *
 * One of those transient reasons might be a deadlock during a SERIALIZABLE or
 * REPEATABLE READ transaction.  Another reason might be that your network
 * connection to the database fails, and perhaps you don't just want to give up
 * when that happens.
 *
 * In situations like these, the right thing to do is often to restart your
 * transaction from scratch.  You won't necessarily want to execute the exact
 * same SQL commands with the exact same data, but you'll want to re-run the
 * same application code that produced those SQL commands.
 *
 * The transactor framework makes it a little easier for you to do this safely,
 * and avoid typical pitfalls.  You encapsulate the work that you want to do in
 * a transaction into something that you pass to @c perform.
 *
 * Transactors come in two flavours.
 *
 * The old, pre-C++11 way is to derive a class from the @c transactor template,
 * and pass an instance of it to your connection's @c connection_base::perform
 * member function.  That function will create a transaction object and pass
 * it to your @c transactor, handle any exceptions, commit or abort, and
 * repeat as appropriate.
 *
 * The new, simpler C++11-based way is to write your transaction code as a
 * lambda (or other callable), which creates its own transaction object, does
 * its work, and commits at the end.  You pass that callback to pqxx::perform.
 * If any given attempt fails, its transaction object goes out of scope and
 * gets destroyed, so that it aborts implicitly.  Your callback can return its
 * results to the calling code.
 */
//@{

/// Simple way to execute a transaction with automatic retry.
/**
 * Executes your transaction code as a callback.  Repeats it until it completes
 * normally, or it throws an error other than the few libpqxx-generated
 * exceptions that the framework understands, or after a given number of failed
 * attempts, or if the transaction ends in an "in-doubt" state.
 *
 * (An in-doubt state is one where libpqxx cannot determine whether the server
 * finally committed a transaction or not.  This can happen if the network
 * connection to the server is lost just while we're waiting for its reply to
 * a "commit" statement.  The server may have completed the commit, or not, but
 * it can't tell you because there's no longer a connection.
 *
 * Using this still takes a bit of care.  If your callback makes use of data
 * from the database, you'll probably have to query that data within your
 * callback.  If the attempt to perform your callback fails, and the framework
 * tries again, you'll be in a new transaction and the data in the database may
 * have changed under your feet.
 *
 * Also be careful about changing variables or data structures from within
 * your callback.  The run may still fail, and perhaps get run again.  The
 * ideal way to do it (in most cases) is to return your result from your
 * callback, and change your program's data after @c perform completes
 * successfully.
 *
 * This function replaces an older, more complicated transactor framework.
 * The new function is a simpler, more lambda-friendly way of doing the same
 * thing.
 *
 * @param callback Transaction code that can be called with no arguments.
 * @param attempts Maximum number of times to attempt performing callback.
 *	Must be greater than zero.
 * @return Whatever your callback returns.
 */
template<typename TRANSACTION_CALLBACK>
inline auto perform(const TRANSACTION_CALLBACK &callback, int attempts=3)
  -> decltype(callback())
{
  if (attempts <= 0)
    throw std::invalid_argument(
	"Zero or negative number of attempts passed to pqxx::perform().");

  for (; attempts > 0; --attempts)
  {
    try
    {
      return callback();
    }
    catch (const in_doubt_error &)
    {
      // Not sure whether transaction went through or not.  The last thing in
      // the world that we should do now is try again!
      throw;
    }
    catch (const statement_completion_unknown &)
    {
      // Not sure whether our last statement succeeded.  Don't risk running it
      // again.
      throw;
    }
    catch (const broken_connection &)
    {
      // Connection failed.  Definitely worth retrying.
      if (attempts <= 1) throw;
      continue;
    }
    catch (const transaction_rollback &)
    {
      // Some error that may well be transient, such as serialization failure
      // or deadlock.  Worth retrying.
      if (attempts <= 1) throw;
      continue;
    }
  }
  throw pqxx::internal_error("No outcome reached on perform().");
}

/// @deprecated Pre-C++11 wrapper for automatically retrying transactions.
/**
 * Pass an object of your transactor-based class to connection_base::perform()
 * to execute the transaction code embedded in it.
 *
 * connection_base::perform() is actually a template, specializing itself to any
 * transactor type you pass to it.  This means you will have to pass it a
 * reference of your object's ultimate static type; runtime polymorphism is
 * not allowed.  Hence the absence of virtual methods in transactor.  The
 * exact methods to be called at runtime *must* be resolved at compile time.
 *
 * Your transactor-derived class must define a copy constructor.  This will be
 * used to create a "clean" copy of your transactor for every attempt that
 * perform() makes to run it.
 */
template<typename TRANSACTION=transaction<read_committed>> class transactor
{
public:
  using argument_type = TRANSACTION;
  explicit transactor(const std::string &TName="transactor") :		//[t04]
    m_name(TName) { }

  /// Overridable transaction definition; insert your database code here
  /** The operation will be retried if the connection to the backend is lost or
   * the operation fails, but not if the connection is broken in such a way as
   * to leave the library in doubt as to whether the operation succeeded.  In
   * that case, an in_doubt_error will be thrown.
   *
   * Recommended practice is to allow this operator to modify only the
   * transactor itself, and the dedicated transaction object it is passed as an
   * argument.  This is what makes side effects, retrying etc. controllable in
   * the transactor framework.
   * @param T Dedicated transaction context created to perform this operation.
   */
  void operator()(TRANSACTION &T);					//[t04]

  // Overridable member functions, called by connection_base::perform() if an
  // attempt to run transaction fails/succeeds, respectively, or if the
  // connection is lost at just the wrong moment, goes into an indeterminate
  // state.  Use these to patch up runtime state to match events, if needed, or
  // to report failure conditions.

  /// Optional overridable function to be called if transaction is aborted
  /** This need not imply complete failure; the transactor will automatically
   * retry the operation a number of times before giving up.  on_abort() will be
   * called for each of the failed attempts.
   *
   * One parameter is passed in by the framework: an error string describing why
   * the transaction failed.  This will also be logged to the connection's
   * notice processor.
   */
  void on_abort(const char[]) noexcept {}				//[t13]

  /// Optional overridable function to be called after successful commit
  /** If your on_commit() throws an exception, the actual back-end transaction
   * will remain committed, so any changes in the database remain regardless of
   * how this function terminates.
   */
  void on_commit() {}							//[t07]

  /// Overridable function to be called when "in doubt" about outcome
  /** This may happen if the connection to the backend is lost while attempting
   * to commit.  In that case, the backend may have committed the transaction
   * but is unable to confirm this to the frontend; or the transaction may have
   * failed, causing it to be rolled back, but again without acknowledgement to
   * the client program.  The best way to deal with this situation is typically
   * to wave red flags in the user's face and ask him to investigate.
   *
   * The robusttransaction class is intended to reduce the chances of this
   * error occurring, at a certain cost in performance.
   * @see robusttransaction
   */
  void on_doubt() noexcept {}						//[t13]

  /// The transactor's name.
  std::string name() const { return m_name; }				//[t13]

private:
  std::string m_name;
};

template<typename TRANSACTOR>
inline void connection_base::perform(
	const TRANSACTOR &T,
        int Attempts)
{
  if (Attempts <= 0) return;

  bool Done = false;

  // Make attempts to perform T
  do
  {
    --Attempts;

    // Work on a copy of T2 so we can restore the starting situation if need be
    TRANSACTOR T2(T);
    try
    {
      typename TRANSACTOR::argument_type X(*this, T2.name());
      T2(X);
      X.commit();
      Done = true;
    }
    catch (const in_doubt_error &)
    {
      // Not sure whether transaction went through or not.  The last thing in
      // the world that we should do now is retry.
      T2.on_doubt();
      throw;
    }
    catch (const std::exception &e)
    {
      // Could be any kind of error.
      T2.on_abort(e.what());
      if (Attempts <= 0) throw;
      continue;
    }
    catch (...)
    {
      // Don't try to forge ahead if we don't even know what happened
      T2.on_abort("Unknown exception");
      throw;
    }

    T2.on_commit();
  } while (!Done);
}
} // namespace pqxx
//@}
#include "pqxx/compiler-internal-post.hxx"
#endif
<|endoftext|>