<commit_before>
// BoundingBox.cpp

// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords

#include "Globals.h"
#include "BoundingBox.h"
#include "Defines.h"

#if SELF_TEST

/// A simple self-test that is executed on program start, used to verify bbox functionality
class SelfTest
{
public:
	SelfTest(void)
	{
		Vector3d Min(1, 1, 1);
		Vector3d Max(2, 2, 2);
		Vector3d LineDefs[] =
		{
			Vector3d(1.5,   4, 1.5), Vector3d(1.5,   3, 1.5),  // Should intersect at 2, face 1 (YP)
			Vector3d(1.5,   0, 1.5), Vector3d(1.5,   4, 1.5),  // Should intersect at 0.25, face 0 (YM)
			Vector3d(0,     0,   0), Vector3d(2,     2,   2),  // Should intersect at 0.5,  face 0, 3 or 5 (anyM)
			Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5),  // Should not intersect
			Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5),  // Should intersect at 0.25, face 0 (YM)
			Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5),  // Should not intersect
		} ;
		bool Results[] = {true,true,true,false,true,false};
		double LineCoeffs[] = {2,0.25,0.5,0,0.25,0};

		for (size_t i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++)
		{
			double LineCoeff;
			eBlockFace Face;
			Vector3d Line1 = LineDefs[2 * i];
			Vector3d Line2 = LineDefs[2 * i + 1];
			bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face);
			if (res != Results[i])
			{
				printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
					Line1.x, Line1.y, Line1.z,
					Line2.x, Line2.y, Line2.z,
					res ? 1 : 0, LineCoeff, Face
				);
				abort();
			}
			if (res)
			{
				if (LineCoeff != LineCoeffs[i])
				{
					printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
						Line1.x, Line1.y, Line1.z,
						Line2.x, Line2.y, Line2.z,
						res ? 1 : 0, LineCoeff, Face
					);
					abort();
				}
			}
		}  // for i - LineDefs[]
		printf("BoundingBox selftest complete.");
	}
} Test;

#endif

cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) :
	m_Min(a_MinX, a_MinY, a_MinZ),
	m_Max(a_MaxX, a_MaxY, a_MaxZ)
{
}

cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) :
	m_Min(a_Min),
	m_Max(a_Max)
{
}

cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) :
	m_Min(a_Pos.x - a_Radius, a_Pos.y,            a_Pos.z - a_Radius),
	m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius)
{
}

cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) :
	m_Min(a_Orig.m_Min),
	m_Max(a_Orig.m_Max)
{
}

void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ)
{
	m_Min.x += a_OffX;
	m_Min.y += a_OffY;
	m_Min.z += a_OffZ;
	m_Max.x += a_OffX;
	m_Max.y += a_OffY;
	m_Max.z += a_OffZ;
}

void cBoundingBox::Move(const Vector3d & a_Off)
{
	m_Min.x += a_Off.x;
	m_Min.y += a_Off.y;
	m_Min.z += a_Off.z;
	m_Max.x += a_Off.x;
	m_Max.y += a_Off.y;
	m_Max.z += a_Off.z;
}

void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ)
{
	m_Min.x -= a_ExpandX;
	m_Min.y -= a_ExpandY;
	m_Min.z -= a_ExpandZ;
	m_Max.x += a_ExpandX;
	m_Max.y += a_ExpandY;
	m_Max.z += a_ExpandZ;
}

bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other)
{
	return (
		((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) &&  // X coords intersect
		((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) &&  // Y coords intersect
		((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z))     // Z coords intersect
	);
}

cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other)
{
	return cBoundingBox(
		std::min(m_Min.x, a_Other.m_Min.x),
		std::min(m_Min.y, a_Other.m_Min.y),
		std::min(m_Min.z, a_Other.m_Min.z),
		std::max(m_Max.x, a_Other.m_Max.x),
		std::max(m_Max.y, a_Other.m_Max.y),
		std::max(m_Max.z, a_Other.m_Max.z)
	);
}

bool cBoundingBox::IsInside(const Vector3d & a_Point)
{
	return IsInside(m_Min, m_Max, a_Point);
}

bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z)
{
	return IsInside(m_Min, m_Max, a_X, a_Y, a_Z);
}

bool cBoundingBox::IsInside(cBoundingBox & a_Other)
{
	// If both a_Other's coords are inside this, then the entire a_Other is inside
	return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max));
}

bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max)
{
	// If both coords are inside this, then the entire a_Other is inside
	return (IsInside(a_Min) && IsInside(a_Max));
}

bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point)
{
	return (
		((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) &&
		((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) &&
		((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z))
	);
}

bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z)
{
	return (
		((a_X >= a_Min.x) && (a_X <= a_Max.x)) &&
		((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) &&
		((a_Z >= a_Min.z) && (a_Z <= a_Max.z))
	);
}

bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, eBlockFace & a_Face)
{
	return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face);
}

bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, eBlockFace & a_Face)
{
	if (IsInside(a_Min, a_Max, a_Line1))
	{
		// The starting point is inside the bounding box.
		a_LineCoeff = 0;
		a_Face = BLOCK_FACE_NONE;  // No faces hit
		return true;
	}

	eBlockFace Face = BLOCK_FACE_NONE;
	double Coeff = Vector3d::NO_INTERSECTION;

	// Check each individual bbox face for intersection with the line, remember the one with the lowest coeff
	double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y);
	if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
		Coeff = c;
	}

	if (Coeff >= Vector3d::NO_INTERSECTION)
	{
		// There has been no intersection
		return false;
	}

	a_LineCoeff = Coeff;
	a_Face = Face;
	return true;
}

bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection)
{
	a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x);
	a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x);
	if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x)
	{
		return false;
	}
	a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y);
	a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y);
	if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y)
	{
		return false;
	}
	a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z);
	a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z);
	if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z)
	{
		return false;
	}
	return true;
}

<commit_msg>Change Output to stderr<commit_after>// BoundingBox.cpp

// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords

#include "Globals.h"
#include "BoundingBox.h"
#include "Defines.h"

#if SELF_TEST

/// A simple self-test that is executed on program start, used to verify bbox functionality
class SelfTest
{
public:
	SelfTest(void)
	{
		Vector3d Min(1, 1, 1);
		Vector3d Max(2, 2, 2);
		Vector3d LineDefs[] =
		{
			Vector3d(1.5,   4, 1.5), Vector3d(1.5,   3, 1.5),  // Should intersect at 2, face 1 (YP)
			Vector3d(1.5,   0, 1.5), Vector3d(1.5,   4, 1.5),  // Should intersect at 0.25, face 0 (YM)
			Vector3d(0,     0,   0), Vector3d(2,     2,   2),  // Should intersect at 0.5,  face 0, 3 or 5 (anyM)
			Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5),  // Should not intersect
			Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5),  // Should intersect at 0.25, face 0 (YM)
			Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5),  // Should not intersect
		} ;
		bool Results[] = {true,true,true,false,true,false};
		double LineCoeffs[] = {2,0.25,0.5,0,0.25,0};

		for (size_t i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++)
		{
			double LineCoeff;
			eBlockFace Face;
			Vector3d Line1 = LineDefs[2 * i];
			Vector3d Line2 = LineDefs[2 * i + 1];
			bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face);
			if (res != Results[i])
			{
				fprintf(stderr,"LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
					Line1.x, Line1.y, Line1.z,
					Line2.x, Line2.y, Line2.z,
					res ? 1 : 0, LineCoeff, Face
				);
				abort();
			}
			if (res)
			{
				if (LineCoeff != LineCoeffs[i])
				{
					fprintf(stderr,"LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
						Line1.x, Line1.y, Line1.z,
						Line2.x, Line2.y, Line2.z,
						res ? 1 : 0, LineCoeff, Face
					);
					abort();
				}
			}
		}  // for i - LineDefs[]
		fprintf(stderr,"BoundingBox selftest complete.");
	}
} Test;

#endif

cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) :
	m_Min(a_MinX, a_MinY, a_MinZ),
	m_Max(a_MaxX, a_MaxY, a_MaxZ)
{
}

cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) :
	m_Min(a_Min),
	m_Max(a_Max)
{
}

cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) :
	m_Min(a_Pos.x - a_Radius, a_Pos.y,            a_Pos.z - a_Radius),
	m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius)
{
}

cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) :
	m_Min(a_Orig.m_Min),
	m_Max(a_Orig.m_Max)
{
}

void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ)
{
	m_Min.x += a_OffX;
	m_Min.y += a_OffY;
	m_Min.z += a_OffZ;
	m_Max.x += a_OffX;
	m_Max.y += a_OffY;
	m_Max.z += a_OffZ;
}

void cBoundingBox::Move(const Vector3d & a_Off)
{
	m_Min.x += a_Off.x;
	m_Min.y += a_Off.y;
	m_Min.z += a_Off.z;
	m_Max.x += a_Off.x;
	m_Max.y += a_Off.y;
	m_Max.z += a_Off.z;
}

void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ)
{
	m_Min.x -= a_ExpandX;
	m_Min.y -= a_ExpandY;
	m_Min.z -= a_ExpandZ;
	m_Max.x += a_ExpandX;
	m_Max.y += a_ExpandY;
	m_Max.z += a_ExpandZ;
}

bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other)
{
	return (
		((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) &&  // X coords intersect
		((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) &&  // Y coords intersect
		((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z))     // Z coords intersect
	);
}

cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other)
{
	return cBoundingBox(
		std::min(m_Min.x, a_Other.m_Min.x),
		std::min(m_Min.y, a_Other.m_Min.y),
		std::min(m_Min.z, a_Other.m_Min.z),
		std::max(m_Max.x, a_Other.m_Max.x),
		std::max(m_Max.y, a_Other.m_Max.y),
		std::max(m_Max.z, a_Other.m_Max.z)
	);
}

bool cBoundingBox::IsInside(const Vector3d & a_Point)
{
	return IsInside(m_Min, m_Max, a_Point);
}

bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z)
{
	return IsInside(m_Min, m_Max, a_X, a_Y, a_Z);
}

bool cBoundingBox::IsInside(cBoundingBox & a_Other)
{
	// If both a_Other's coords are inside this, then the entire a_Other is inside
	return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max));
}

bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max)
{
	// If both coords are inside this, then the entire a_Other is inside
	return (IsInside(a_Min) && IsInside(a_Max));
}

bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point)
{
	return (
		((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) &&
		((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) &&
		((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z))
	);
}

bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z)
{
	return (
		((a_X >= a_Min.x) && (a_X <= a_Max.x)) &&
		((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) &&
		((a_Z >= a_Min.z) && (a_Z <= a_Max.z))
	);
}

bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, eBlockFace & a_Face)
{
	return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face);
}

bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, eBlockFace & a_Face)
{
	if (IsInside(a_Min, a_Max, a_Line1))
	{
		// The starting point is inside the bounding box.
		a_LineCoeff = 0;
		a_Face = BLOCK_FACE_NONE;  // No faces hit
		return true;
	}

	eBlockFace Face = BLOCK_FACE_NONE;
	double Coeff = Vector3d::NO_INTERSECTION;

	// Check each individual bbox face for intersection with the line, remember the one with the lowest coeff
	double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y);
	if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
		Coeff = c;
	}
	c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x);
	if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
	{
		Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
		Coeff = c;
	}

	if (Coeff >= Vector3d::NO_INTERSECTION)
	{
		// There has been no intersection
		return false;
	}

	a_LineCoeff = Coeff;
	a_Face = Face;
	return true;
}

bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection)
{
	a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x);
	a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x);
	if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x)
	{
		return false;
	}
	a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y);
	a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y);
	if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y)
	{
		return false;
	}
	a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z);
	a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z);
	if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z)
	{
		return false;
	}
	return true;
}

<|endoftext|>


<commit_before>
#include "AdsLib.h"
#include "AdsNotificationOOI.h"
#include "AdsVariable.h"

#include <array>
#include <iostream>
#include <iomanip>

static void NotifyCallback(const AmsAddr* pAddr, const AdsNotificationHeader* pNotification, uint32_t hUser)
{
    const uint8_t* data = reinterpret_cast<const uint8_t*>(pNotification + 1);
    std::cout << std::setfill('0') <<
        "NetId: " << pAddr->netId <<
        " hUser 0x" << std::hex << hUser <<
        " sample time: " << std::dec << pNotification->nTimeStamp <<
        " sample size: " << std::dec << pNotification->cbSampleSize <<
        " value:";
    for (size_t i = 0; i < pNotification->cbSampleSize; ++i) {
        std::cout << " 0x" << std::hex << (int)data[i];
    }
    std::cout << '\n';
}

static void notificationExample(std::ostream& out, const AdsDevice& route)
{
    const AdsNotificationAttrib attrib = {
        1,
        ADSTRANS_SERVERCYCLE,
        0,
        {4000000}
    };
    AdsNotification notification { route, 0x4020, 4, attrib, &NotifyCallback, 0xDEADBEEF };

    out << "Hit ENTER to stop notifications\n";
    std::cin.ignore();
}

static void notificationByNameExample(std::ostream& out, const AdsDevice& route)
{
    const AdsNotificationAttrib attrib = {
        1,
        ADSTRANS_SERVERCYCLE,
        0,
        {4000000}
    };

    out << __FUNCTION__ << "():\n";
    AdsNotification notification { route, "MAIN.byByte[4]", attrib, &NotifyCallback, 0xBEEFDEAD };

    out << "Hit ENTER to stop by name notifications\n";
    std::cin.ignore();
}

static void readExample(std::ostream& out, const AdsDevice& route)
{
    AdsVariable<uint8_t> readVar {route, 0x4020, 0};

    out << __FUNCTION__ << "():\n";
    for (size_t i = 0; i < 8; ++i) {
        out << "ADS read " << std::hex << (uint32_t)readVar << '\n';
    }
}

static void readByNameExample(std::ostream& out, const AdsDevice& route)
{
    AdsVariable<uint8_t> readVar {route, "MAIN.byByte[4]"};

    out << __FUNCTION__ << "():\n";
    for (size_t i = 0; i < 8; ++i) {
        out << "ADS read " << std::hex << (uint32_t)readVar << '\n';
    }
}

static void readWriteExample(std::ostream& out, const AdsDevice& route)
{
    AdsVariable<uint8_t> simpleVar {route, "MAIN.byByte[0]"};
    AdsVariable<uint8_t> validation {route, "MAIN.byByte[0]"};

    out << __FUNCTION__ << "():\n";
    simpleVar = 0xA5;
    out << "Wrote " << 0xA5 << " to MAIN.byByte and read " << (uint32_t)validation << " back\n";
    simpleVar = 0x5A;
    out << "Wrote " << (uint32_t)simpleVar << " to MAIN.byByte and read " << (uint32_t)validation << " back\n";
}

static void readWriteArrayExample(std::ostream& out, const AdsDevice& route)
{
    static const std::array<uint8_t, 4> arrayToWrite = { 1, 2, 3, 4 };
    AdsVariable<std::array<uint8_t, 4> > arrayVar {route, "MAIN.byByte"};
    arrayVar = arrayToWrite;
    std::array<uint8_t, 4> readArray = arrayVar;
    out << "Wrote array with first value " << (uint32_t)arrayToWrite[0] << " and last value " <<
    (uint32_t)arrayToWrite[3] << "\n";
    out << "Read back array with first value " << (uint32_t)readArray[0] << " and last value " <<
    (uint32_t)readArray[3] << "\n";
}

static void readStateExample(std::ostream& out, const AdsDevice& route)
{
    const auto state = route.GetState();

    out << "ADS state: " << std::dec << (uint16_t)state.ads << " devState: " << std::dec << (uint16_t)state.device <<
        '\n';
}

static void runExample(std::ostream& out)
{
    static const AmsNetId remoteNetId { 192, 168, 0, 231, 1, 1 };
    static const char remoteIpV4[] = "ads-server";

    // uncomment and adjust if automatic AmsNetId deduction is not working as expected
    //AdsSetLocalAddress({192, 168, 0, 1, 1, 1});

    AdsDevice route {remoteIpV4, remoteNetId, AMSPORT_R0_PLC_TC3};
    notificationExample(out, route);
    notificationByNameExample(out, route);
    readExample(out, route);
    readByNameExample(out, route);
    readWriteExample(out, route);
    readWriteArrayExample(out, route);
    readStateExample(out, route);
}

int main()
{
    try {
        runExample(std::cout);
    } catch (const std::runtime_error& ex) {
        std::cout << ex.what() << '\n';
    }
}
<commit_msg>example: improve error reporting<commit_after>
#include "AdsLib.h"
#include "AdsNotificationOOI.h"
#include "AdsVariable.h"

#include <array>
#include <iostream>
#include <iomanip>

static void NotifyCallback(const AmsAddr* pAddr, const AdsNotificationHeader* pNotification, uint32_t hUser)
{
    const uint8_t* data = reinterpret_cast<const uint8_t*>(pNotification + 1);
    std::cout << std::setfill('0') <<
        "NetId: " << pAddr->netId <<
        " hUser 0x" << std::hex << hUser <<
        " sample time: " << std::dec << pNotification->nTimeStamp <<
        " sample size: " << std::dec << pNotification->cbSampleSize <<
        " value:";
    for (size_t i = 0; i < pNotification->cbSampleSize; ++i) {
        std::cout << " 0x" << std::hex << (int)data[i];
    }
    std::cout << '\n';
}

static void notificationExample(std::ostream& out, const AdsDevice& route)
{
    const AdsNotificationAttrib attrib = {
        1,
        ADSTRANS_SERVERCYCLE,
        0,
        {4000000}
    };
    AdsNotification notification { route, 0x4020, 4, attrib, &NotifyCallback, 0xDEADBEEF };

    out << "Hit ENTER to stop notifications\n";
    std::cin.ignore();
}

static void notificationByNameExample(std::ostream& out, const AdsDevice& route)
{
    const AdsNotificationAttrib attrib = {
        1,
        ADSTRANS_SERVERCYCLE,
        0,
        {4000000}
    };

    out << __FUNCTION__ << "():\n";
    AdsNotification notification { route, "MAIN.byByte[4]", attrib, &NotifyCallback, 0xBEEFDEAD };

    out << "Hit ENTER to stop by name notifications\n";
    std::cin.ignore();
}

static void readExample(std::ostream& out, const AdsDevice& route)
{
    AdsVariable<uint8_t> readVar {route, 0x4020, 0};

    out << __FUNCTION__ << "():\n";
    for (size_t i = 0; i < 8; ++i) {
        out << "ADS read " << std::hex << (uint32_t)readVar << '\n';
    }
}

static void readByNameExample(std::ostream& out, const AdsDevice& route)
{
    AdsVariable<uint8_t> readVar {route, "MAIN.byByte[4]"};

    out << __FUNCTION__ << "():\n";
    for (size_t i = 0; i < 8; ++i) {
        out << "ADS read " << std::hex << (uint32_t)readVar << '\n';
    }
}

static void readWriteExample(std::ostream& out, const AdsDevice& route)
{
    AdsVariable<uint8_t> simpleVar {route, "MAIN.byByte[0]"};
    AdsVariable<uint8_t> validation {route, "MAIN.byByte[0]"};

    out << __FUNCTION__ << "():\n";
    simpleVar = 0xA5;
    out << "Wrote " << 0xA5 << " to MAIN.byByte and read " << (uint32_t)validation << " back\n";
    simpleVar = 0x5A;
    out << "Wrote " << (uint32_t)simpleVar << " to MAIN.byByte and read " << (uint32_t)validation << " back\n";
}

static void readWriteArrayExample(std::ostream& out, const AdsDevice& route)
{
    static const std::array<uint8_t, 4> arrayToWrite = { 1, 2, 3, 4 };
    AdsVariable<std::array<uint8_t, 4> > arrayVar {route, "MAIN.byByte"};
    arrayVar = arrayToWrite;
    std::array<uint8_t, 4> readArray = arrayVar;
    out << "Wrote array with first value " << (uint32_t)arrayToWrite[0] << " and last value " <<
    (uint32_t)arrayToWrite[3] << "\n";
    out << "Read back array with first value " << (uint32_t)readArray[0] << " and last value " <<
    (uint32_t)readArray[3] << "\n";
}

static void readStateExample(std::ostream& out, const AdsDevice& route)
{
    const auto state = route.GetState();

    out << "ADS state: " << std::dec << (uint16_t)state.ads << " devState: " << std::dec << (uint16_t)state.device <<
        '\n';
}

static void runExample(std::ostream& out)
{
    static const AmsNetId remoteNetId { 192, 168, 0, 231, 1, 1 };
    static const char remoteIpV4[] = "ads-server";

    // uncomment and adjust if automatic AmsNetId deduction is not working as expected
    //AdsSetLocalAddress({192, 168, 0, 1, 1, 1});

    AdsDevice route {remoteIpV4, remoteNetId, AMSPORT_R0_PLC_TC3};
    notificationExample(out, route);
    notificationByNameExample(out, route);
    readExample(out, route);
    readByNameExample(out, route);
    readWriteExample(out, route);
    readWriteArrayExample(out, route);
    readStateExample(out, route);
}

int main()
{
    try {
        runExample(std::cout);
    } catch (const AdsException& ex) {
        std::cout << "Error: " << ex.errorCode << "\n";
        std::cout << "AdsException message: " << ex.what() << "\n";
    } catch (const std::runtime_error& ex) {
        std::cout << ex.what() << '\n';
    }
    std::cout << "Hit ENTER to continue\n";
    std::cin.ignore();
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/utf_string_conversions.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/shell/shell.h"
#include "content/test/content_browser_test.h"
#include "content/test/content_browser_test_utils.h"
#include "net/test/test_server.h"

#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

namespace content {

class WebrtcBrowserTest: public ContentBrowserTest {
 public:
  WebrtcBrowserTest() {}
  virtual ~WebrtcBrowserTest() {}

  virtual void SetUpOnMainThread() OVERRIDE {
    // We need fake devices in this test since we want to run on naked VMs. We
    // assume this switch is set by default in content_browsertests.
    ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
        switches::kUseFakeDeviceForMediaStream));

    ASSERT_TRUE(test_server()->Start());
  }
 protected:
  bool ExecuteJavascript(const std::string& javascript) {
    return ExecuteScript(shell()->web_contents(), javascript);
  }

  void ExpectTitle(const std::string& expected_title) const {
    string16 expected_title16(ASCIIToUTF16(expected_title));
    TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
    EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
  }
};

// These tests will all make a getUserMedia call with different constraints and
// see that the success callback is called. If the error callback is called or
// none of the callbacks are called the tests will simply time out and fail.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetVideoStreamAndStop) {
  GURL url(test_server()->GetURL("files/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("getUserMedia({video: true});"));

  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndStop) {
  GURL url(test_server()->GetURL("files/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("getUserMedia({video: true, audio: true});"));

  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndClone) {
  GURL url(test_server()->GetURL("files/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("getUserMediaAndClone();"));

  ExpectTitle("OK");
}

// These tests will make a complete PeerConnection-based call and verify that
// video is playing for the call.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CanSetupVideoCall) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("call({video: true});"));
  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CanSetupAudioAndVideoCall) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("call({video: true, audio: true});"));
  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(
      ExecuteJavascript("callAndSendDtmf('123,abc');"));
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                       CanMakeEmptyCallThenAddStreamsAndRenegotiate) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  const char* kJavascript =
      "makeEmptyCallThenAddOneStreamAndRenegotiate("
      "{video: true, audio: true});";
  EXPECT_TRUE(ExecuteJavascript(kJavascript));
  ExpectTitle("OK");
}

// This test will make a complete PeerConnection-based call but remove the
// MSID and bundle attribute from the initial offer to verify that
// video is playing for the call even if the initiating client don't support
// MSID. http://tools.ietf.org/html/draft-alvestrand-rtcweb-msid-02
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                       CanSetupAudioAndVideoCallWithoutMsidAndBundle) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithoutMsidAndBundle();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataOnly) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithDataOnly();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel and audio and video tracks.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataAndMedia) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithDataAndMedia();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel and later add an audio and video track.
// TODO(perkj): Fix flakiness. See: http://crbug.com/227409
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                       DISABLED_CallWithDataAndLaterAddMedia) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithDataAndLaterAddMedia();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and send a new Video
// MediaStream that has been created based on a MediaStream created with
// getUserMedia.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithNewVideoMediaStream) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithNewVideoMediaStream();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and send a new Video
// MediaStream that has been created based on a MediaStream created with
// getUserMedia. When video is flowing, the VideoTrack is removed and an
// AudioTrack is added instead.
// TODO(phoglund): This test is manual since not all buildbots has an audio
// input.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CallAndModifyStream) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(
      ExecuteJavascript("callWithNewVideoMediaStreamLaterSwitchToAudio();"));
  ExpectTitle("OK");
}

}  // namespace content

<commit_msg>Disable CanSetupAudioAndVideoCallWithoutMsidAndBundle.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/utf_string_conversions.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/shell/shell.h"
#include "content/test/content_browser_test.h"
#include "content/test/content_browser_test_utils.h"
#include "net/test/test_server.h"

#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

namespace content {

class WebrtcBrowserTest: public ContentBrowserTest {
 public:
  WebrtcBrowserTest() {}
  virtual ~WebrtcBrowserTest() {}

  virtual void SetUpOnMainThread() OVERRIDE {
    // We need fake devices in this test since we want to run on naked VMs. We
    // assume this switch is set by default in content_browsertests.
    ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
        switches::kUseFakeDeviceForMediaStream));

    ASSERT_TRUE(test_server()->Start());
  }
 protected:
  bool ExecuteJavascript(const std::string& javascript) {
    return ExecuteScript(shell()->web_contents(), javascript);
  }

  void ExpectTitle(const std::string& expected_title) const {
    string16 expected_title16(ASCIIToUTF16(expected_title));
    TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
    EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
  }
};

// These tests will all make a getUserMedia call with different constraints and
// see that the success callback is called. If the error callback is called or
// none of the callbacks are called the tests will simply time out and fail.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetVideoStreamAndStop) {
  GURL url(test_server()->GetURL("files/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("getUserMedia({video: true});"));

  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndStop) {
  GURL url(test_server()->GetURL("files/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("getUserMedia({video: true, audio: true});"));

  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndClone) {
  GURL url(test_server()->GetURL("files/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("getUserMediaAndClone();"));

  ExpectTitle("OK");
}

// These tests will make a complete PeerConnection-based call and verify that
// video is playing for the call.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CanSetupVideoCall) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("call({video: true});"));
  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CanSetupAudioAndVideoCall) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("call({video: true, audio: true});"));
  ExpectTitle("OK");
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(
      ExecuteJavascript("callAndSendDtmf('123,abc');"));
}

IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                       CanMakeEmptyCallThenAddStreamsAndRenegotiate) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  const char* kJavascript =
      "makeEmptyCallThenAddOneStreamAndRenegotiate("
      "{video: true, audio: true});";
  EXPECT_TRUE(ExecuteJavascript(kJavascript));
  ExpectTitle("OK");
}

// This test will make a complete PeerConnection-based call but remove the
// MSID and bundle attribute from the initial offer to verify that
// video is playing for the call even if the initiating client don't support
// MSID. http://tools.ietf.org/html/draft-alvestrand-rtcweb-msid-02
// Disabled for win7_aura, see http://crbug.com/235089.
#if defined(OS_WIN) && defined(USE_AURA)
#define MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle\
        DISABLED_CanSetupAudioAndVideoCallWithoutMsidAndBundle
#else
#define MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle\
        CanSetupAudioAndVideoCallWithoutMsidAndBundle
#endif
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                       MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithoutMsidAndBundle();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataOnly) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithDataOnly();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel and audio and video tracks.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataAndMedia) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithDataAndMedia();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel and later add an audio and video track.
// TODO(perkj): Fix flakiness. See: http://crbug.com/227409
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                       DISABLED_CallWithDataAndLaterAddMedia) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithDataAndLaterAddMedia();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and send a new Video
// MediaStream that has been created based on a MediaStream created with
// getUserMedia.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithNewVideoMediaStream) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(ExecuteJavascript("callWithNewVideoMediaStream();"));
  ExpectTitle("OK");
}

// This test will make a PeerConnection-based call and send a new Video
// MediaStream that has been created based on a MediaStream created with
// getUserMedia. When video is flowing, the VideoTrack is removed and an
// AudioTrack is added instead.
// TODO(phoglund): This test is manual since not all buildbots has an audio
// input.
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CallAndModifyStream) {
  GURL url(test_server()->GetURL("files/media/peerconnection-call.html"));
  NavigateToURL(shell(), url);

  EXPECT_TRUE(
      ExecuteJavascript("callWithNewVideoMediaStreamLaterSwitchToAudio();"));
  ExpectTitle("OK");
}

}  // namespace content

<|endoftext|>


<commit_before>/// @file
/// @version 3.3
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#if defined(_ANDROID) || defined(__APPLE__)
#include <unistd.h>
#endif

#include <hltypes/harray.h>
#include <hltypes/hmap.h>
#include <hltypes/hmutex.h>
#include <hltypes/hstream.h>
#include <hltypes/hstring.h>

#include "xal.h"
#include "Buffer.h"
#include "BufferAsync.h"

namespace xal
{
	harray<Buffer*> BufferAsync::buffers;
	int BufferAsync::loaded = 0;
	hmutex BufferAsync::queueMutex;

	hthread BufferAsync::readerThread(&BufferAsync::_read, "XAL async loader");
	bool BufferAsync::readerRunning = false;

	harray<hthread*> BufferAsync::decoderThreads;

	static int cpus = 0; // needed, because certain calls are made when fetching SystemInfo that are not allowed to be made in secondary threads on some platforms

	void BufferAsync::update()
	{
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		if (BufferAsync::readerRunning && !BufferAsync::readerThread.isRunning())
		{
			BufferAsync::readerThread.join();
			BufferAsync::readerRunning = false;
		}
		if (!BufferAsync::readerRunning && BufferAsync::buffers.size() > 0) // new textures got queued in the meantime
		{
			BufferAsync::readerRunning = true;
			BufferAsync::readerThread.start();
		}
	}

	bool BufferAsync::queueLoad(Buffer* buffer)
	{
		if (cpus == 0)
		{
#ifdef _WIN32
			SYSTEM_INFO w32info;
			GetNativeSystemInfo(&w32info);
			cpus = w32info.dwNumberOfProcessors;
#elif defined(__APPLE__) // not sure why, but for Apple hardware the ONLN one is used (possibly some hardware may have locked available cores)
			cpus = sysconf(_SC_NPROCESSORS_ONLN);
#else // on Android and other Unix it's better to use CONF since they are more flexible than Apple's hardware and might switch cores on/off dynamically
			cpus = sysconf(_SC_NPROCESSORS_CONF);
#endif
		}
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		if (BufferAsync::buffers.contains(buffer))
		{
			return false;
		}
		BufferAsync::buffers += buffer;
		if (!BufferAsync::readerRunning)
		{
			BufferAsync::readerRunning = true;
			BufferAsync::readerThread.start();
		}
		return true;
	}

	bool BufferAsync::prioritizeLoad(Buffer* buffer)
	{
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		if (!BufferAsync::buffers.contains(buffer))
		{
			return false;
		}
		int index = BufferAsync::buffers.index_of(buffer);
		if (index >= BufferAsync::loaded) // if not loaded from disk yet
		{
			if (index > BufferAsync::loaded) // if not already at the front
			{
				BufferAsync::buffers.remove_at(index);
				BufferAsync::buffers.insert_at(loaded, buffer);
			}
		}
		else if (index > 0) // if data was already loaded in RAM, but not decoded and not already at the front
		{
			BufferAsync::buffers.remove_at(index);
			BufferAsync::buffers.push_first(buffer);
		}
		return true;
	}

	bool BufferAsync::isRunning()
	{
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		return BufferAsync::readerRunning;
	}

	void BufferAsync::_read(hthread* thread)
	{
		Buffer* buffer = NULL;
		bool streamLoaded = true;
		hthread* decoderThread = NULL;
		int index = 0;
		int size = 0;
		bool running = true;
		hmutex::ScopeLock lock;
		while (running)
		{
			running = false;
			// check for new queued textures
			lock.acquire(&BufferAsync::queueMutex);
			if (BufferAsync::buffers.size() > BufferAsync::loaded)
			{
				running = true;
				buffer = BufferAsync::buffers[BufferAsync::loaded];
				lock.release();
				streamLoaded = buffer->_prepareAsyncStream();
				lock.acquire(&BufferAsync::queueMutex);
				index = BufferAsync::buffers.index_of(buffer); // it's possible that the queue was rearranged in the meantime
				if (streamLoaded)
				{
					if (index >= BufferAsync::loaded)
					{
						if (index > BufferAsync::loaded) // if texture was moved towards the back of the queue
						{
							// put it back to the current decoder position
							BufferAsync::buffers.remove_at(index);
							BufferAsync::buffers.insert_at(BufferAsync::loaded, buffer);
						}
					}
					++BufferAsync::loaded;
				}
				else // it was canceled
				{
					BufferAsync::buffers.remove_at(index);
				}
			}
			size = BufferAsync::loaded;
			lock.release();
			// create new worker threads if needed
			if (size > 0)
			{
				running = true;
				size = hmin(size, cpus) - BufferAsync::decoderThreads.size();
				for_iter (i, 0, size)
				{
					decoderThread = new hthread(&BufferAsync::_decode, "XAL async decoder");
					BufferAsync::decoderThreads += decoderThread;
					decoderThread->start();
				}
			}
			// check current worker threads' status
			if (BufferAsync::decoderThreads.size() > 0)
			{
				running = true;
				for_iter (i, 0, BufferAsync::decoderThreads.size())
				{
					if (!BufferAsync::decoderThreads[i]->isRunning())
					{
						decoderThread = BufferAsync::decoderThreads.remove_at(i);
						decoderThread->join();
						delete decoderThread;
						--i;
					}
				}
			}
		}
	}

	void BufferAsync::_decode(hthread* thread)
	{
		Buffer* buffer = NULL;
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		while (BufferAsync::loaded > 0)
		{
			buffer = BufferAsync::buffers.remove_first();
			--BufferAsync::loaded;
			lock.release();
			buffer->_decodeFromAsyncStream();
			lock.acquire(&BufferAsync::queueMutex);
		}
	}

}
<commit_msg>- small update<commit_after>/// @file
/// @version 3.3
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#if defined(_ANDROID) || defined(__APPLE__)
#include <unistd.h>
#endif

#include <hltypes/harray.h>
#include <hltypes/hmap.h>
#include <hltypes/hmutex.h>
#include <hltypes/hstream.h>
#include <hltypes/hstring.h>

#include "xal.h"
#include "Buffer.h"
#include "BufferAsync.h"

namespace xal
{
	harray<Buffer*> BufferAsync::buffers;
	int BufferAsync::loaded = 0;
	hmutex BufferAsync::queueMutex;

	hthread BufferAsync::readerThread(&BufferAsync::_read, "XAL async loader");
	bool BufferAsync::readerRunning = false;

	harray<hthread*> BufferAsync::decoderThreads;

	static int cpus = 0; // needed, because certain calls are made when fetching SystemInfo that are not allowed to be made in secondary threads on some platforms

	void BufferAsync::update()
	{
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		if (BufferAsync::readerRunning && !BufferAsync::readerThread.isRunning())
		{
			BufferAsync::readerThread.join();
			BufferAsync::readerRunning = false;
		}
		if (!BufferAsync::readerRunning && BufferAsync::buffers.size() > 0) // new textures got queued in the meantime
		{
			BufferAsync::readerRunning = true;
			BufferAsync::readerThread.start();
		}
	}

	bool BufferAsync::queueLoad(Buffer* buffer)
	{
		if (cpus == 0)
		{
#ifdef _WIN32
			SYSTEM_INFO w32info;
			GetNativeSystemInfo(&w32info);
			cpus = w32info.dwNumberOfProcessors;
#elif defined(__APPLE__) // not sure why, but for Apple hardware the ONLN one is used (possibly some hardware may have locked available cores)
			cpus = sysconf(_SC_NPROCESSORS_ONLN);
#else // on Android and other Unix it's better to use CONF since they are more flexible than Apple's hardware and might switch cores on/off dynamically
			cpus = sysconf(_SC_NPROCESSORS_CONF);
#endif
		}
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		if (BufferAsync::buffers.contains(buffer))
		{
			return false;
		}
		BufferAsync::buffers += buffer;
		if (!BufferAsync::readerRunning)
		{
			BufferAsync::readerRunning = true;
			BufferAsync::readerThread.start();
		}
		return true;
	}

	bool BufferAsync::prioritizeLoad(Buffer* buffer)
	{
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		if (!BufferAsync::buffers.contains(buffer))
		{
			return false;
		}
		int index = BufferAsync::buffers.index_of(buffer);
		if (index >= BufferAsync::loaded) // if not loaded from disk yet
		{
			if (index > BufferAsync::loaded) // if not already at the front
			{
				BufferAsync::buffers.remove_at(index);
				BufferAsync::buffers.insert_at(BufferAsync::loaded, buffer);
			}
		}
		else if (index > 0) // if data was already loaded in RAM, but not decoded and not already at the front
		{
			BufferAsync::buffers.remove_at(index);
			BufferAsync::buffers.push_first(buffer);
		}
		return true;
	}

	bool BufferAsync::isRunning()
	{
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		return BufferAsync::readerRunning;
	}

	void BufferAsync::_read(hthread* thread)
	{
		Buffer* buffer = NULL;
		bool streamLoaded = true;
		hthread* decoderThread = NULL;
		int index = 0;
		int size = 0;
		bool running = true;
		hmutex::ScopeLock lock;
		while (running)
		{
			running = false;
			// check for new queued textures
			lock.acquire(&BufferAsync::queueMutex);
			if (BufferAsync::buffers.size() > BufferAsync::loaded)
			{
				running = true;
				buffer = BufferAsync::buffers[BufferAsync::loaded];
				lock.release();
				streamLoaded = buffer->_prepareAsyncStream();
				lock.acquire(&BufferAsync::queueMutex);
				index = BufferAsync::buffers.index_of(buffer); // it's possible that the queue was rearranged in the meantime
				if (streamLoaded)
				{
					if (index >= BufferAsync::loaded)
					{
						if (index > BufferAsync::loaded) // if texture was moved towards the back of the queue
						{
							// put it back to the current decoder position
							BufferAsync::buffers.remove_at(index);
							BufferAsync::buffers.insert_at(BufferAsync::loaded, buffer);
						}
					}
					++BufferAsync::loaded;
				}
				else // it was canceled
				{
					BufferAsync::buffers.remove_at(index);
				}
			}
			size = BufferAsync::loaded;
			lock.release();
			// create new worker threads if needed
			if (size > 0)
			{
				running = true;
				size = hmin(size, cpus) - BufferAsync::decoderThreads.size();
				for_iter (i, 0, size)
				{
					decoderThread = new hthread(&BufferAsync::_decode, "XAL async decoder");
					BufferAsync::decoderThreads += decoderThread;
					decoderThread->start();
				}
			}
			// check current worker threads' status
			if (BufferAsync::decoderThreads.size() > 0)
			{
				running = true;
				for_iter (i, 0, BufferAsync::decoderThreads.size())
				{
					if (!BufferAsync::decoderThreads[i]->isRunning())
					{
						decoderThread = BufferAsync::decoderThreads.remove_at(i);
						decoderThread->join();
						delete decoderThread;
						--i;
					}
				}
			}
		}
	}

	void BufferAsync::_decode(hthread* thread)
	{
		Buffer* buffer = NULL;
		hmutex::ScopeLock lock(&BufferAsync::queueMutex);
		while (BufferAsync::loaded > 0)
		{
			buffer = BufferAsync::buffers.remove_first();
			--BufferAsync::loaded;
			lock.release();
			buffer->_decodeFromAsyncStream();
			lock.acquire(&BufferAsync::queueMutex);
		}
	}

}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <iostream>

#include "ast/Printer.hpp"

#include "variant.hpp"
#include "assert.hpp"
#include "VisitorUtils.hpp"
#include "Variable.hpp"

using namespace eddic;

namespace {

std::string toStringType(ast::Type type){
    if(auto* ptr = boost::get<ast::SimpleType>(&type)){
        return ptr->type;
    } else if(auto* ptr = boost::get<ast::ArrayType>(&type)){
        return ptr->type + "[]";
    } else {
        ASSERT_PATH_NOT_TAKEN("Unhandled type");
    }
}

struct DebugVisitor : public boost::static_visitor<> {
    mutable int level = 0;

    std::string indent() const {
        std::string acc = "";
        for(int i = 0; i < level; ++i){
            acc += "    ";
        }
        return acc;
    }

    template<typename Container>
    void print_each_sub(Container& container) const {
        level++;
        visit_each(*this, container);
        level--;
    }

    template<typename Container>
    void print_each_sub(Container& container, const std::string& title) const {
        std::cout << indent() << title << std::endl;
        print_each_sub(container);
    }

    template<typename Container>
    void print_each_sub_non_variant(Container& container) const {
        level++;
        visit_each_non_variant(*this, container);
        level--;
    }

    template<typename Container>
    void print_each_sub_non_variant(Container& container, const std::string& title) const {
        std::cout << indent() << title << std::endl;
        print_each_sub_non_variant(container);
    }

    template<typename Container>
    void print_sub(Container& container) const {
        level++;
        visit(*this, container);
        level--;
    }

    template<typename Container>
    void print_sub(Container& container, const std::string& title) const {
        std::cout << indent() << title << std::endl;
        print_sub(container);
    }

    void operator()(ast::SourceFile& program) const {
        print_each_sub(program.Content->blocks, "SourceFile");
    }

    void operator()(ast::Import& import) const {
        std::cout << indent() << "include \"" << import.file << "\"" << std::endl;
    }

    void operator()(ast::StandardImport& import) const {
        std::cout << indent() << "include <" << import.header << ">" << std::endl;
    }

    void operator()(ast::FunctionDeclaration& declaration) const {
        std::cout << indent() << "Function " << declaration.Content->functionName << std::endl;

        std::cout << indent() << "Parameters:" << std::endl;
        level++;
        for(auto param : declaration.Content->parameters){
            std::cout << indent() << param.parameterName << std::endl;
        }
        level--;

        print_each_sub(declaration.Content->instructions, "Instructions:");
    }

    void operator()(ast::Constructor& declaration) const {
        std::cout << indent() << "Constructor" << std::endl;

        std::cout << indent() << "Parameters:" << std::endl;
        level++;
        for(auto param : declaration.Content->parameters){
            std::cout << indent() << param.parameterName << std::endl;
        }
        level--;

        print_each_sub(declaration.Content->instructions, "Instructions:");
    }

    void operator()(ast::Destructor& declaration) const {
        std::cout << indent() << "Destructor" << std::endl;
        print_each_sub(declaration.Content->instructions, "Instructions:");
    }

    void operator()(ast::Struct& struct_) const {
        std::cout << indent() << "Structure declaration: " << struct_.Content->name << std::endl;
        level++;

        print_each_sub_non_variant(struct_.Content->members, "Members");
        print_each_sub_non_variant(struct_.Content->constructors, "Constructors");
        print_each_sub_non_variant(struct_.Content->destructors, "Destructors");
        print_each_sub_non_variant(struct_.Content->functions, "Functions");

        level--;
    }

    void operator()(ast::MemberDeclaration& declaration) const {
        std::cout << indent() << toStringType(declaration.Content->type)  << " " << declaration.Content->name << std::endl;
    }

    void operator()(ast::GlobalVariableDeclaration&) const {
        std::cout << indent() << "Global Variable" << std::endl;
    }

    void operator()(ast::GlobalArrayDeclaration&) const {
        std::cout << indent() << "Global Array" << std::endl;
    }

    void operator()(ast::New& new_) const {
        std::cout << indent() << "New " << toStringType(new_.Content->type) << std::endl;
        print_each_sub(new_.Content->values, "Value");
    }

    void operator()(ast::Delete& delete_) const {
        std::cout << indent() << "Delete " << delete_.Content->variable_name << std::endl;
    }

    void operator()(ast::For& for_) const {
        print_each_sub(for_.Content->instructions, "For");
    }

    void operator()(ast::Foreach& for_) const {
        print_each_sub(for_.Content->instructions, "Foreach");
    }

    void operator()(ast::ForeachIn& for_) const {
        print_each_sub(for_.Content->instructions, "Foreach In");
    }

    void operator()(ast::Switch& switch_) const {
        std::cout << indent() << "Switch " << std::endl;

        ++level;

        print_sub(switch_.Content->value, "Value");

        for(auto& case_ : switch_.Content->cases){
            visit_non_variant(*this, case_);
        }

        if(switch_.Content->default_case){
            visit_non_variant(*this, *switch_.Content->default_case);
        }

        --level;
    }

    void operator()(ast::SwitchCase& switch_case) const {
        std::cout << indent() << "Case" << std::endl;

        ++level;

        print_sub(switch_case.value, "Value");
        print_each_sub(switch_case.instructions, "Instructions");

        --level;
    }

    void operator()(ast::DefaultCase& default_case) const {
        print_each_sub(default_case.instructions, "Default Case");
    }

    void operator()(ast::While& while_) const {
        std::cout << indent() << "While" << std::endl;
        print_sub(while_.Content->condition, "Condition:");
        print_each_sub(while_.Content->instructions, "Instructions:");
    }

    void operator()(ast::DoWhile& while_) const {
        std::cout << indent() << "Do while" << std::endl;
        print_sub(while_.Content->condition, "Condition:");
        print_each_sub(while_.Content->instructions, "Instructions:");
    }

    void operator()(ast::Swap&) const {
        std::cout << indent() << "Swap" << std::endl;
    }

    void operator()(ast::If& if_) const {
        std::cout << indent() << "If" << std::endl;

        print_sub(if_.Content->condition, "Condition");
        print_each_sub(if_.Content->instructions, "Instructions");

        for(auto& else_if : if_.Content->elseIfs){
            std::cout << indent() << "ElseIf" << std::endl;

            print_sub(else_if.condition, "Condition");
            print_each_sub(else_if.instructions, "Instructions");
        }

        if(if_.Content->else_){
            std::cout << indent() << "Else" << std::endl;

            print_each_sub((*if_.Content->else_).instructions, "Instructions");
        }
    }

    void operator()(ast::FunctionCall& call) const {
        std::cout << indent() << "FunctionCall " << call.Content->functionName << std::endl;
        print_each_sub(call.Content->values);
    }

    void operator()(ast::MemberFunctionCall& call) const {
        std::cout << indent() << "Member FunctionCall " << call.Content->object_name << "." << call.Content->function_name << std::endl;
        print_each_sub(call.Content->values);
    }

    void operator()(ast::BuiltinOperator& builtin) const {
        std::cout << indent() << "Builtin Operator " << (int) builtin.Content->type << std::endl;
        print_each_sub(builtin.Content->values);
    }

    void operator()(ast::StructDeclaration& declaration) const {
        std::cout << indent() << "Struct declaration [" << declaration.Content->variableName << "]" << std::endl;

        print_each_sub(declaration.Content->values, "Values");
    }

    void operator()(ast::VariableDeclaration& declaration) const {
        std::cout << indent() << "Variable declaration [" << declaration.Content->variableName << "]" << std::endl;

        if(declaration.Content->value){
            print_sub(*declaration.Content->value);
        }
    }

    void operator()(ast::ArrayDeclaration&) const {
        std::cout << indent() << "Array declaration" << std::endl;
    }

    void operator()(ast::SuffixOperation& op) const {
        std::cout << indent() << "(suffix)" << (int)op.Content->op << std::endl;

        print_sub(op.Content->left_value, "Left Value");
    }

    void operator()(ast::PrefixOperation& op) const {
        std::cout << indent() << (int)op.Content->op << "(prefix)" << std::endl;

        print_sub(op.Content->left_value, "Left Value");
    }

    void operator()(ast::Assignment& assign) const {
        std::cout << indent() << "Assignment [operator = " << static_cast<int>(assign.Content->op) << " ] " << std::endl;

        print_sub(assign.Content->left_value, "Left Value");
        print_sub(assign.Content->value, "Right Value");
    }

    void operator()(ast::Ternary& ternary) const {
        std::cout << indent() << "Ternary" << std::endl;

        print_sub(ternary.Content->condition, "Condition");
        print_sub(ternary.Content->true_value, "True Value");
        print_sub(ternary.Content->false_value, "False Value");
    }

    void operator()(ast::Return& return_) const {
        print_sub(return_.Content->value, "Function Return");
    }

    void operator()(ast::Literal& literal) const {
        std::cout << indent() << "Literal [" << literal.value << "]" << std::endl;
    }

    void operator()(ast::CharLiteral& literal) const {
        std::cout << indent() << "Char Literal [" << literal.value << "]" << std::endl;
    }

    void operator()(ast::Integer& integer) const {
        std::cout << indent() << "Integer [" << integer.value << "]" << std::endl;
    }

    void operator()(ast::IntegerSuffix& integer) const {
        std::cout << indent() << "Float (suffix) [" << integer.value << "f]" << std::endl;
    }

    void operator()(ast::Float& float_) const {
        std::cout << indent() << "Float [" << float_.value << "]" << std::endl;
    }

    void operator()(ast::True&) const {
        std::cout << indent() << "true" << std::endl;
    }

    void operator()(ast::Null&) const {
        std::cout << indent() << "null" << std::endl;
    }

    void operator()(ast::False&) const {
        std::cout << indent() << "false" << std::endl;
    }

    void operator()(ast::VariableValue& value) const {
        std::cout << indent() << "Variable " << value.Content->var->name();

        for(auto& member : value.Content->memberNames){
            std::cout << "." << member;
        }

        std::cout << std::endl;
    }

    void operator()(ast::MemberValue& value) const {
        std::cout << indent() << "Member Value ";

        for(auto& member : value.Content->memberNames){
            std::cout << "." << member;
        }

        std::cout << std::endl;

        print_sub(value.Content->location, "Location");
    }

    void operator()(ast::DereferenceValue& value) const {
        std::cout << indent() << "Dereference Variable Value" << std::endl;;

        print_sub(value.Content->ref, "Left Value");
    }

    void operator()(ast::ArrayValue& value) const {
        std::cout << indent() << "Array value [" << value.Content->arrayName << "]" << std::endl;
        print_sub(value.Content->indexValue);
    }

    void operator()(ast::Expression& value) const {
        std::cout << indent() << "Expression [" << value.Content->operations.size() << "]" << std::endl;
        ++level;
        visit(*this, value.Content->first);
        for(auto& operation : value.Content->operations){
            std::cout << indent() << (int) operation.get<0>() << std::endl;
            visit(*this, operation.get<1>());
        }
        --level;
    }

    void operator()(ast::Unary& value) const {
        std::cout << indent() << "Unary " << static_cast<int>(value.Content->op) << std::endl;
        print_sub(value.Content->value);
    }

    void operator()(ast::Cast& cast) const {
        std::cout << indent() << "Cast " << std::endl;
        std::cout << indent() << "\tType: " << toStringType(cast.Content->type) << std::endl;
        print_sub(cast.Content->value);
    }
};

} //end of anonymous namespace

void ast::Printer::print(SourceFile& program){
    DebugVisitor visitor;
    visit_non_variant(visitor, program);
}
<commit_msg>Fix the printer for pointer types<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <iostream>

#include "ast/Printer.hpp"

#include "variant.hpp"
#include "assert.hpp"
#include "VisitorUtils.hpp"
#include "Variable.hpp"

using namespace eddic;

namespace {

std::string toStringType(ast::Type type){
    if(auto* ptr = boost::get<ast::SimpleType>(&type)){
        return ptr->type;
    } else if(auto* ptr = boost::get<ast::ArrayType>(&type)){
        return ptr->type + "[]";
    } else if(auto* ptr = boost::get<ast::PointerType>(&type)){
        return ptr->type + "*";
    } else {
        ASSERT_PATH_NOT_TAKEN("Unhandled type");
    }
}

struct DebugVisitor : public boost::static_visitor<> {
    mutable int level = 0;

    std::string indent() const {
        std::string acc = "";
        for(int i = 0; i < level; ++i){
            acc += "    ";
        }
        return acc;
    }

    template<typename Container>
    void print_each_sub(Container& container) const {
        level++;
        visit_each(*this, container);
        level--;
    }

    template<typename Container>
    void print_each_sub(Container& container, const std::string& title) const {
        std::cout << indent() << title << std::endl;
        print_each_sub(container);
    }

    template<typename Container>
    void print_each_sub_non_variant(Container& container) const {
        level++;
        visit_each_non_variant(*this, container);
        level--;
    }

    template<typename Container>
    void print_each_sub_non_variant(Container& container, const std::string& title) const {
        std::cout << indent() << title << std::endl;
        print_each_sub_non_variant(container);
    }

    template<typename Container>
    void print_sub(Container& container) const {
        level++;
        visit(*this, container);
        level--;
    }

    template<typename Container>
    void print_sub(Container& container, const std::string& title) const {
        std::cout << indent() << title << std::endl;
        print_sub(container);
    }

    void operator()(ast::SourceFile& program) const {
        print_each_sub(program.Content->blocks, "SourceFile");
    }

    void operator()(ast::Import& import) const {
        std::cout << indent() << "include \"" << import.file << "\"" << std::endl;
    }

    void operator()(ast::StandardImport& import) const {
        std::cout << indent() << "include <" << import.header << ">" << std::endl;
    }

    void operator()(ast::FunctionDeclaration& declaration) const {
        std::cout << indent() << "Function " << declaration.Content->functionName << std::endl;

        std::cout << indent() << "Parameters:" << std::endl;
        level++;
        for(auto param : declaration.Content->parameters){
            std::cout << indent() << param.parameterName << std::endl;
        }
        level--;

        print_each_sub(declaration.Content->instructions, "Instructions:");
    }

    void operator()(ast::Constructor& declaration) const {
        std::cout << indent() << "Constructor" << std::endl;

        std::cout << indent() << "Parameters:" << std::endl;
        level++;
        for(auto param : declaration.Content->parameters){
            std::cout << indent() << param.parameterName << std::endl;
        }
        level--;

        print_each_sub(declaration.Content->instructions, "Instructions:");
    }

    void operator()(ast::Destructor& declaration) const {
        std::cout << indent() << "Destructor" << std::endl;
        print_each_sub(declaration.Content->instructions, "Instructions:");
    }

    void operator()(ast::Struct& struct_) const {
        std::cout << indent() << "Structure declaration: " << struct_.Content->name << std::endl;
        level++;

        print_each_sub_non_variant(struct_.Content->members, "Members");
        print_each_sub_non_variant(struct_.Content->constructors, "Constructors");
        print_each_sub_non_variant(struct_.Content->destructors, "Destructors");
        print_each_sub_non_variant(struct_.Content->functions, "Functions");

        level--;
    }

    void operator()(ast::MemberDeclaration& declaration) const {
        std::cout << indent() << toStringType(declaration.Content->type)  << " " << declaration.Content->name << std::endl;
    }

    void operator()(ast::GlobalVariableDeclaration&) const {
        std::cout << indent() << "Global Variable" << std::endl;
    }

    void operator()(ast::GlobalArrayDeclaration&) const {
        std::cout << indent() << "Global Array" << std::endl;
    }

    void operator()(ast::New& new_) const {
        std::cout << indent() << "New " << toStringType(new_.Content->type) << std::endl;
        print_each_sub(new_.Content->values, "Value");
    }

    void operator()(ast::Delete& delete_) const {
        std::cout << indent() << "Delete " << delete_.Content->variable_name << std::endl;
    }

    void operator()(ast::For& for_) const {
        print_each_sub(for_.Content->instructions, "For");
    }

    void operator()(ast::Foreach& for_) const {
        print_each_sub(for_.Content->instructions, "Foreach");
    }

    void operator()(ast::ForeachIn& for_) const {
        print_each_sub(for_.Content->instructions, "Foreach In");
    }

    void operator()(ast::Switch& switch_) const {
        std::cout << indent() << "Switch " << std::endl;

        ++level;

        print_sub(switch_.Content->value, "Value");

        for(auto& case_ : switch_.Content->cases){
            visit_non_variant(*this, case_);
        }

        if(switch_.Content->default_case){
            visit_non_variant(*this, *switch_.Content->default_case);
        }

        --level;
    }

    void operator()(ast::SwitchCase& switch_case) const {
        std::cout << indent() << "Case" << std::endl;

        ++level;

        print_sub(switch_case.value, "Value");
        print_each_sub(switch_case.instructions, "Instructions");

        --level;
    }

    void operator()(ast::DefaultCase& default_case) const {
        print_each_sub(default_case.instructions, "Default Case");
    }

    void operator()(ast::While& while_) const {
        std::cout << indent() << "While" << std::endl;
        print_sub(while_.Content->condition, "Condition:");
        print_each_sub(while_.Content->instructions, "Instructions:");
    }

    void operator()(ast::DoWhile& while_) const {
        std::cout << indent() << "Do while" << std::endl;
        print_sub(while_.Content->condition, "Condition:");
        print_each_sub(while_.Content->instructions, "Instructions:");
    }

    void operator()(ast::Swap&) const {
        std::cout << indent() << "Swap" << std::endl;
    }

    void operator()(ast::If& if_) const {
        std::cout << indent() << "If" << std::endl;

        print_sub(if_.Content->condition, "Condition");
        print_each_sub(if_.Content->instructions, "Instructions");

        for(auto& else_if : if_.Content->elseIfs){
            std::cout << indent() << "ElseIf" << std::endl;

            print_sub(else_if.condition, "Condition");
            print_each_sub(else_if.instructions, "Instructions");
        }

        if(if_.Content->else_){
            std::cout << indent() << "Else" << std::endl;

            print_each_sub((*if_.Content->else_).instructions, "Instructions");
        }
    }

    void operator()(ast::FunctionCall& call) const {
        std::cout << indent() << "FunctionCall " << call.Content->functionName << std::endl;
        print_each_sub(call.Content->values);
    }

    void operator()(ast::MemberFunctionCall& call) const {
        std::cout << indent() << "Member FunctionCall " << call.Content->object_name << "." << call.Content->function_name << std::endl;
        print_each_sub(call.Content->values);
    }

    void operator()(ast::BuiltinOperator& builtin) const {
        std::cout << indent() << "Builtin Operator " << (int) builtin.Content->type << std::endl;
        print_each_sub(builtin.Content->values);
    }

    void operator()(ast::StructDeclaration& declaration) const {
        std::cout << indent() << "Struct declaration [" << declaration.Content->variableName << "]" << std::endl;

        print_each_sub(declaration.Content->values, "Values");
    }

    void operator()(ast::VariableDeclaration& declaration) const {
        std::cout << indent() << "Variable declaration [" << declaration.Content->variableName << "]" << std::endl;

        if(declaration.Content->value){
            print_sub(*declaration.Content->value);
        }
    }

    void operator()(ast::ArrayDeclaration&) const {
        std::cout << indent() << "Array declaration" << std::endl;
    }

    void operator()(ast::SuffixOperation& op) const {
        std::cout << indent() << "(suffix)" << (int)op.Content->op << std::endl;

        print_sub(op.Content->left_value, "Left Value");
    }

    void operator()(ast::PrefixOperation& op) const {
        std::cout << indent() << (int)op.Content->op << "(prefix)" << std::endl;

        print_sub(op.Content->left_value, "Left Value");
    }

    void operator()(ast::Assignment& assign) const {
        std::cout << indent() << "Assignment [operator = " << static_cast<int>(assign.Content->op) << " ] " << std::endl;

        print_sub(assign.Content->left_value, "Left Value");
        print_sub(assign.Content->value, "Right Value");
    }

    void operator()(ast::Ternary& ternary) const {
        std::cout << indent() << "Ternary" << std::endl;

        print_sub(ternary.Content->condition, "Condition");
        print_sub(ternary.Content->true_value, "True Value");
        print_sub(ternary.Content->false_value, "False Value");
    }

    void operator()(ast::Return& return_) const {
        print_sub(return_.Content->value, "Function Return");
    }

    void operator()(ast::Literal& literal) const {
        std::cout << indent() << "Literal [" << literal.value << "]" << std::endl;
    }

    void operator()(ast::CharLiteral& literal) const {
        std::cout << indent() << "Char Literal [" << literal.value << "]" << std::endl;
    }

    void operator()(ast::Integer& integer) const {
        std::cout << indent() << "Integer [" << integer.value << "]" << std::endl;
    }

    void operator()(ast::IntegerSuffix& integer) const {
        std::cout << indent() << "Float (suffix) [" << integer.value << "f]" << std::endl;
    }

    void operator()(ast::Float& float_) const {
        std::cout << indent() << "Float [" << float_.value << "]" << std::endl;
    }

    void operator()(ast::True&) const {
        std::cout << indent() << "true" << std::endl;
    }

    void operator()(ast::Null&) const {
        std::cout << indent() << "null" << std::endl;
    }

    void operator()(ast::False&) const {
        std::cout << indent() << "false" << std::endl;
    }

    void operator()(ast::VariableValue& value) const {
        std::cout << indent() << "Variable " << value.Content->var->name();

        for(auto& member : value.Content->memberNames){
            std::cout << "." << member;
        }

        std::cout << std::endl;
    }

    void operator()(ast::MemberValue& value) const {
        std::cout << indent() << "Member Value ";

        for(auto& member : value.Content->memberNames){
            std::cout << "." << member;
        }

        std::cout << std::endl;

        print_sub(value.Content->location, "Location");
    }

    void operator()(ast::DereferenceValue& value) const {
        std::cout << indent() << "Dereference Variable Value" << std::endl;;

        print_sub(value.Content->ref, "Left Value");
    }

    void operator()(ast::ArrayValue& value) const {
        std::cout << indent() << "Array value [" << value.Content->arrayName << "]" << std::endl;
        print_sub(value.Content->indexValue);
    }

    void operator()(ast::Expression& value) const {
        std::cout << indent() << "Expression [" << value.Content->operations.size() << "]" << std::endl;
        ++level;
        visit(*this, value.Content->first);
        for(auto& operation : value.Content->operations){
            std::cout << indent() << (int) operation.get<0>() << std::endl;
            visit(*this, operation.get<1>());
        }
        --level;
    }

    void operator()(ast::Unary& value) const {
        std::cout << indent() << "Unary " << static_cast<int>(value.Content->op) << std::endl;
        print_sub(value.Content->value);
    }

    void operator()(ast::Cast& cast) const {
        std::cout << indent() << "Cast " << std::endl;
        std::cout << indent() << "\tType: " << toStringType(cast.Content->type) << std::endl;
        print_sub(cast.Content->value);
    }
};

} //end of anonymous namespace

void ast::Printer::print(SourceFile& program){
    DebugVisitor visitor;
    visit_non_variant(visitor, program);
}
<|endoftext|>