<commit_before>//-----------------------------------------------------------------------------
// All code is property of Matthew Loesby
// Contact at Loesby.dev@gmail.com for permission to use
// Or to discuss ideas
// (c) 2017

// Systems/WorldTile.cpp
// Creates and updates all tiles in the world
// When interactions extend to a new set of tiles, creates those and starts updating them

#include "../Core/typedef.h"

#include "Systems.h"

#include "../ECS/System.h"
#include "../ECS/ECS.h"

#include "../Util/Pathing.h"

// Terms:
// * Tile: Unit of the world terrain
//         It has a position in a sector
//         At standard zoom, it's 5px square
// * Sector: A 20 x 20 grid of Tiles.
// * Quadrant: A 2x2 Grid of Sectors
// A world position is a combination of Quadrant X, Y
//    Sector X, Y
//    Tile X, Y
//  Printed as QX.QY:SX.SY:TX.TY
// The world starts with 1 quadrant, player spawns in the center of it
//    So spawn position is 0.0:1.1:0.0
// When a position beyond the current spawned quadrants becomes relevant,
// new quadrants are spawned from the current world out to that quadrant
// A thickness of up to 3 quadrants may be spawned to try to create pathing
// to the target quadrant

namespace TileConstants
{
	constexpr int TILE_SIDE_LENGTH = 5;
	constexpr int SECTOR_SIDE_LENGTH = 100;
	constexpr int QUADRANT_SIDE_LENGTH = 4;

	constexpr int BASE_QUADRANT_ORIGIN_COORDINATE =
		-TILE_SIDE_LENGTH *
		SECTOR_SIDE_LENGTH *
		QUADRANT_SIDE_LENGTH / 2;

	// Will later be configuration data
	constexpr int TILE_TYPE_COUNT = 8;
}

// Non-entity data the tile system needs.
namespace TileNED
{
	using QuadrantId = std::pair<int, int>;
	using namespace TileConstants;

	struct Tile
	{
		int m_tileType;
		std::optional<int> m_movementCost; // If notset, unpathable

		// Each 1 pixel is 4 components: RGBA
		sf::Uint32 m_tilePixels[TILE_SIDE_LENGTH * TILE_SIDE_LENGTH];
	};

	using Pathability = bool[Pathing::PathingSide::_COUNT][Pathing::PathingSide::_COUNT];

	struct Sector
	{
		Pathability m_pathability;
		Tile m_tiles
			[TileConstants::SECTOR_SIDE_LENGTH]
			[TileConstants::SECTOR_SIDE_LENGTH];

	};
	struct Quadrant
	{
		Pathability m_pathability;
		Sector m_sectors
			[TileConstants::QUADRANT_SIDE_LENGTH]
			[TileConstants::QUADRANT_SIDE_LENGTH];

		sf::Texture m_texture;
	};
	using SpawnedQuadrantMap = std::map<QuadrantId, Quadrant>;
	SpawnedQuadrantMap s_spawnedQuadrants;
	bool baseQuadrantSpawned{ false };

	struct WorldCoordinates
	{
		WorldCoordinates() { }
		WorldCoordinates(CartesianVector2&& quad, CartesianVector2&& sec, CartesianVector2&& tile)
			: m_quadrant(quad)
			, m_sector(sec)
			, m_tile(tile)
		{}
		CartesianVector2 m_quadrant;
		CartesianVector2 m_sector;
		CartesianVector2 m_tile;
	};
}

void SpawnQuadrant(int X, int Y, ECS_Core::Manager& manager)
{
	using namespace TileConstants;
	if (TileNED::s_spawnedQuadrants.find({ X,Y })
		!= TileNED::s_spawnedQuadrants.end())
	{
		// Quadrant is already here
		return;
	}
	auto index = manager.createIndex();
	auto quadrantSideLength = QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH;
	manager.addComponent<ECS_Core::Components::C_QuadrantPosition>(
		index,
		X, Y);
	manager.addComponent<ECS_Core::Components::C_PositionCartesian>(
		index,
		BASE_QUADRANT_ORIGIN_COORDINATE +
		(quadrantSideLength * X),
		BASE_QUADRANT_ORIGIN_COORDINATE +
		(quadrantSideLength * Y),
		0);

	auto rect = std::make_unique<sf::RectangleShape>(sf::Vector2f(
		static_cast<float>(quadrantSideLength),
		static_cast<float>(quadrantSideLength)));
	auto& quadrant = TileNED::s_spawnedQuadrants[{X, Y}];
	quadrant.m_texture.create(quadrantSideLength, quadrantSideLength);
	for (auto secX = 0; secX < TileConstants::QUADRANT_SIDE_LENGTH; ++secX)
	{
		for (auto secY = 0; secY < TileConstants::QUADRANT_SIDE_LENGTH; ++secY)
		{
			auto& sector = quadrant.m_sectors[secX][secY];
			for (auto tileX = 0; tileX < TileConstants::SECTOR_SIDE_LENGTH; ++tileX)
			{
				for (auto tileY = 0; tileY < TileConstants::SECTOR_SIDE_LENGTH; ++tileY)
				{
					auto& tile = sector.m_tiles[tileX][tileY];
					// Later this will be based on all sorts of fun terrain generation
					// But that's later
					tile.m_tileType = rand() % TileConstants::TILE_TYPE_COUNT;
					if (tile.m_tileType) // Make type 0 unpathable for testing
						tile.m_movementCost = rand() % 6;
					for (auto& pixel : tile.m_tilePixels)
					{
						pixel =
							(((tile.m_tileType & 1) ? 255 : 0) << 0) + // R
							(((tile.m_tileType & 2) ? 255 : 0) << 8) + // G
							(((tile.m_tileType & 4) ? 255 : 0) << 16)  + // B
							+ (0xFF << 24); // A
					}
					quadrant.m_texture.update(
						reinterpret_cast<const sf::Uint8*>(tile.m_tilePixels),
						TILE_SIDE_LENGTH,
						TILE_SIDE_LENGTH,
						((secX * SECTOR_SIDE_LENGTH) + tileX) * TILE_SIDE_LENGTH,
						((secY * SECTOR_SIDE_LENGTH) + tileY) * TILE_SIDE_LENGTH);
				}
			}

			using namespace Pathing::PathingSide;
			bool nwPathable = false;
			if (sector.m_tiles[0][0].m_movementCost)
			{
				nwPathable = true;
			}
			else
			{
				//  Start at top and left edge, other than corner, start trying to find paths
				auto isPathable = [](const TileNED::Tile& tile) -> bool {
					return (bool)tile.m_movementCost;
				};
				for (auto&& tileRow : sector.m_tiles)
				{
					for (auto&& tile : tileRow)
					{
						bool pathable = isPathable(tile);
					}
				}
			}
			sector.m_pathability[NORTH][WEST] = nwPathable;
			sector.m_pathability[WEST][NORTH] = nwPathable;

			bool nePathable = false;
			if (sector.m_tiles[TILE_SIDE_LENGTH - 1][0].m_movementCost)
			{
				nePathable = true;
			}
			else
			{
			}
			sector.m_pathability[NORTH][EAST] = nePathable;
			sector.m_pathability[EAST][NORTH] = nePathable;

			bool sePathable = false;
			if (sector.m_tiles[TILE_SIDE_LENGTH - 1][TILE_SIDE_LENGTH - 1].m_movementCost)
			{
				sePathable = true;
				sector.m_pathability[SOUTH][EAST] = true;
				sector.m_pathability[EAST][SOUTH] = true;
			}

			bool swPathable = false;
			if (sector.m_tiles[0][TILE_SIDE_LENGTH - 1].m_movementCost)
			{
				swPathable = true;
			}
			else
			{

			}
			sector.m_pathability[SOUTH][WEST] = swPathable;
			sector.m_pathability[WEST][SOUTH] = swPathable;
		}
	}
	rect->setTexture(&quadrant.m_texture);
	manager.addComponent<ECS_Core::Components::C_SFMLDrawable>(
		index,
		std::move(rect),
		ECS_Core::Components::DrawLayer::TERRAIN,
		0);
}

template<class T>
int sign(T x)
{
	return x < 0 ? -1 : 1;
}

template<class T, class U>
int min(T&& a, U&& b)
{
	return a < b ? a : b;
}

TileNED::WorldCoordinates WorldPositionToCoordinates(const CartesianVector2& worldPos)
{
	using namespace TileConstants;
	auto offsetFromQuadrantOrigin = worldPos - CartesianVector2(
		BASE_QUADRANT_ORIGIN_COORDINATE,
		BASE_QUADRANT_ORIGIN_COORDINATE);
	return {
		{ min(0, sign(offsetFromQuadrantOrigin.m_x)) + sign(offsetFromQuadrantOrigin.m_x) * (int)(abs(offsetFromQuadrantOrigin.m_x) / (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)),
		  min(0, sign(offsetFromQuadrantOrigin.m_y)) + sign(offsetFromQuadrantOrigin.m_y) * (int)(abs(offsetFromQuadrantOrigin.m_y) / (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH))},

		{ min(0, sign(offsetFromQuadrantOrigin.m_x)) + sign(offsetFromQuadrantOrigin.m_x) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH),
		  min(0, sign(offsetFromQuadrantOrigin.m_y)) + sign(offsetFromQuadrantOrigin.m_y) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)},

		{ min(0, sign(offsetFromQuadrantOrigin.m_x)) + sign(offsetFromQuadrantOrigin.m_x) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / TILE_SIDE_LENGTH,
		  min(0, sign(offsetFromQuadrantOrigin.m_y)) + sign(offsetFromQuadrantOrigin.m_y) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / TILE_SIDE_LENGTH}
	};
}

CartesianVector2 CoordinatesToWorldPosition(const TileNED::WorldCoordinates& worldCoords)
{
	using namespace TileConstants;
	return {
		BASE_QUADRANT_ORIGIN_COORDINATE +
		(((((QUADRANT_SIDE_LENGTH * worldCoords.m_quadrant.m_x)
			+ worldCoords.m_sector.m_x) * SECTOR_SIDE_LENGTH)
			+ worldCoords.m_tile.m_x) * TILE_SIDE_LENGTH),

		BASE_QUADRANT_ORIGIN_COORDINATE +
		(((((QUADRANT_SIDE_LENGTH * worldCoords.m_quadrant.m_y)
			+ worldCoords.m_sector.m_y) * SECTOR_SIDE_LENGTH)
			+ worldCoords.m_tile.m_y) * TILE_SIDE_LENGTH)
	};
}

void CheckWorldClick(ECS_Core::Manager& manager)
{
	auto inputEntities = manager.entitiesMatching<ECS_Core::Signatures::S_Input>();
	if (inputEntities.size() == 0) return;
	ECS_Core::Components::C_UserInputs& inputComponent = manager.getComponent<ECS_Core::Components::C_UserInputs>(inputEntities.front());
	if (inputComponent.m_unprocessedThisFrameDownMouseButtonFlags & (u8)ECS_Core::Components::MouseButtons::LEFT)
	{
		auto mouseWorldCoords = WorldPositionToCoordinates(inputComponent.m_currentMousePosition.m_worldPosition);

		if (TileNED::s_spawnedQuadrants.find({ (int)mouseWorldCoords.m_quadrant.m_x, (int)mouseWorldCoords.m_quadrant.m_y }) == TileNED::s_spawnedQuadrants.end())
		{
			SpawnQuadrant((int)mouseWorldCoords.m_quadrant.m_x, (int)mouseWorldCoords.m_quadrant.m_y, manager);
		}

		inputComponent.ProcessMouseDown(ECS_Core::Components::MouseButtons::LEFT);
	}
}

void WorldTile::Operate(GameLoopPhase phase, const timeuS& frameDuration)
{
	switch (phase)
	{
	case GameLoopPhase::PREPARATION:
		if (!TileNED::baseQuadrantSpawned)
		{
			SpawnQuadrant(0, 0, m_managerRef);
			TileNED::baseQuadrantSpawned = true;
		}
		break;
	case GameLoopPhase::ACTION:
		CheckWorldClick(m_managerRef);
		break;

	case GameLoopPhase::INPUT:
	case GameLoopPhase::ACTION_RESPONSE:
	case GameLoopPhase::RENDER:
	case GameLoopPhase::CLEANUP:
		return;
	}
}

bool WorldTile::ShouldExit()
{
	return false;
}

DEFINE_SYSTEM_INSTANTIATION(WorldTile);<commit_msg>Spawning tiles between origin and relevant tile<commit_after>//-----------------------------------------------------------------------------
// All code is property of Matthew Loesby
// Contact at Loesby.dev@gmail.com for permission to use
// Or to discuss ideas
// (c) 2017

// Systems/WorldTile.cpp
// Creates and updates all tiles in the world
// When interactions extend to a new set of tiles, creates those and starts updating them

#include "../Core/typedef.h"

#include "Systems.h"

#include "../ECS/System.h"
#include "../ECS/ECS.h"

#include "../Util/Pathing.h"

#include <limits>

// Terms:
// * Tile: Unit of the world terrain
//         It has a position in a sector
//         At standard zoom, it's 5px square
// * Sector: A 20 x 20 grid of Tiles.
// * Quadrant: A 2x2 Grid of Sectors
// A world position is a combination of Quadrant X, Y
//    Sector X, Y
//    Tile X, Y
//  Printed as QX.QY:SX.SY:TX.TY
// The world starts with 1 quadrant, player spawns in the center of it
//    So spawn position is 0.0:1.1:0.0
// When a position beyond the current spawned quadrants becomes relevant,
// new quadrants are spawned from the current world out to that quadrant
// A thickness of up to 3 quadrants may be spawned to try to create pathing
// to the target quadrant

namespace TileConstants
{
	constexpr int TILE_SIDE_LENGTH = 5;
	constexpr int SECTOR_SIDE_LENGTH = 100;
	constexpr int QUADRANT_SIDE_LENGTH = 4;

	constexpr int BASE_QUADRANT_ORIGIN_COORDINATE =
		-TILE_SIDE_LENGTH *
		SECTOR_SIDE_LENGTH *
		QUADRANT_SIDE_LENGTH / 2;

	// Will later be configuration data
	constexpr int TILE_TYPE_COUNT = 8;
}

// Non-entity data the tile system needs.
namespace TileNED
{
	using QuadrantId = std::pair<int, int>;
	using namespace TileConstants;

	struct Tile
	{
		int m_tileType;
		std::optional<int> m_movementCost; // If notset, unpathable

		// Each 1 pixel is 4 components: RGBA
		sf::Uint32 m_tilePixels[TILE_SIDE_LENGTH * TILE_SIDE_LENGTH];
	};

	using Pathability = bool[Pathing::PathingSide::_COUNT][Pathing::PathingSide::_COUNT];

	struct Sector
	{
		Pathability m_pathability;
		Tile m_tiles
			[TileConstants::SECTOR_SIDE_LENGTH]
			[TileConstants::SECTOR_SIDE_LENGTH];

	};
	struct Quadrant
	{
		Pathability m_pathability;
		Sector m_sectors
			[TileConstants::QUADRANT_SIDE_LENGTH]
			[TileConstants::QUADRANT_SIDE_LENGTH];

		sf::Texture m_texture;
	};
	using SpawnedQuadrantMap = std::map<QuadrantId, Quadrant>;
	SpawnedQuadrantMap s_spawnedQuadrants;
	bool baseQuadrantSpawned{ false };

	struct WorldCoordinates
	{
		WorldCoordinates() { }
		WorldCoordinates(CartesianVector2&& quad, CartesianVector2&& sec, CartesianVector2&& tile)
			: m_quadrant(quad)
			, m_sector(sec)
			, m_tile(tile)
		{}
		CartesianVector2 m_quadrant;
		CartesianVector2 m_sector;
		CartesianVector2 m_tile;
	};

	void CheckWorldClick(ECS_Core::Manager& manager);
	void SpawnBetween(
		int originX,
		int originY,
		int targetX,
		int targetY,
		ECS_Core::Manager& manager);
}

void SpawnQuadrant(int X, int Y, ECS_Core::Manager& manager)
{
	using namespace TileConstants;
	if (TileNED::s_spawnedQuadrants.find({ X,Y })
		!= TileNED::s_spawnedQuadrants.end())
	{
		// Quadrant is already here
		return;
	}
	auto index = manager.createIndex();
	auto quadrantSideLength = QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH;
	manager.addComponent<ECS_Core::Components::C_QuadrantPosition>(
		index,
		X, Y);
	manager.addComponent<ECS_Core::Components::C_PositionCartesian>(
		index,
		BASE_QUADRANT_ORIGIN_COORDINATE +
		(quadrantSideLength * X),
		BASE_QUADRANT_ORIGIN_COORDINATE +
		(quadrantSideLength * Y),
		0);

	auto rect = std::make_unique<sf::RectangleShape>(sf::Vector2f(
		static_cast<float>(quadrantSideLength),
		static_cast<float>(quadrantSideLength)));
	auto& quadrant = TileNED::s_spawnedQuadrants[{X, Y}];
	quadrant.m_texture.create(quadrantSideLength, quadrantSideLength);
	for (auto secX = 0; secX < TileConstants::QUADRANT_SIDE_LENGTH; ++secX)
	{
		for (auto secY = 0; secY < TileConstants::QUADRANT_SIDE_LENGTH; ++secY)
		{
			auto& sector = quadrant.m_sectors[secX][secY];
			for (auto tileX = 0; tileX < TileConstants::SECTOR_SIDE_LENGTH; ++tileX)
			{
				for (auto tileY = 0; tileY < TileConstants::SECTOR_SIDE_LENGTH; ++tileY)
				{
					auto& tile = sector.m_tiles[tileX][tileY];
					// Later this will be based on all sorts of fun terrain generation
					// But that's later
					tile.m_tileType = rand() % TileConstants::TILE_TYPE_COUNT;
					if (tile.m_tileType) // Make type 0 unpathable for testing
						tile.m_movementCost = rand() % 6;
					for (auto& pixel : tile.m_tilePixels)
					{
						pixel =
							(((tile.m_tileType & 1) ? 255 : 0) << 0) + // R
							(((tile.m_tileType & 2) ? 255 : 0) << 8) + // G
							(((tile.m_tileType & 4) ? 255 : 0) << 16)  + // B
							+ (0xFF << 24); // A
					}
					quadrant.m_texture.update(
						reinterpret_cast<const sf::Uint8*>(tile.m_tilePixels),
						TILE_SIDE_LENGTH,
						TILE_SIDE_LENGTH,
						((secX * SECTOR_SIDE_LENGTH) + tileX) * TILE_SIDE_LENGTH,
						((secY * SECTOR_SIDE_LENGTH) + tileY) * TILE_SIDE_LENGTH);
				}
			}

			using namespace Pathing::PathingSide;
			bool nwPathable = false;
			if (sector.m_tiles[0][0].m_movementCost)
			{
				nwPathable = true;
			}
			else
			{
				//  Start at top and left edge, other than corner, start trying to find paths
				auto isPathable = [](const TileNED::Tile& tile) -> bool {
					return (bool)tile.m_movementCost;
				};
				for (auto&& tileRow : sector.m_tiles)
				{
					for (auto&& tile : tileRow)
					{
						bool pathable = isPathable(tile);
					}
				}
			}
			sector.m_pathability[NORTH][WEST] = nwPathable;
			sector.m_pathability[WEST][NORTH] = nwPathable;

			bool nePathable = false;
			if (sector.m_tiles[TILE_SIDE_LENGTH - 1][0].m_movementCost)
			{
				nePathable = true;
			}
			else
			{
			}
			sector.m_pathability[NORTH][EAST] = nePathable;
			sector.m_pathability[EAST][NORTH] = nePathable;

			bool sePathable = false;
			if (sector.m_tiles[TILE_SIDE_LENGTH - 1][TILE_SIDE_LENGTH - 1].m_movementCost)
			{
				sePathable = true;
				sector.m_pathability[SOUTH][EAST] = true;
				sector.m_pathability[EAST][SOUTH] = true;
			}

			bool swPathable = false;
			if (sector.m_tiles[0][TILE_SIDE_LENGTH - 1].m_movementCost)
			{
				swPathable = true;
			}
			else
			{

			}
			sector.m_pathability[SOUTH][WEST] = swPathable;
			sector.m_pathability[WEST][SOUTH] = swPathable;
		}
	}
	rect->setTexture(&quadrant.m_texture);
	manager.addComponent<ECS_Core::Components::C_SFMLDrawable>(
		index,
		std::move(rect),
		ECS_Core::Components::DrawLayer::TERRAIN,
		0);
}

template<class T>
inline int sign(T x)
{
	return (x < 0) ? -1 : ((x > 0) ? 1 : 0);
}

template<class T, class U>
int min(T&& a, U&& b)
{
	return a < b ? a : b;
}

TileNED::WorldCoordinates WorldPositionToCoordinates(const CartesianVector2& worldPos)
{
	using namespace TileConstants;
	auto offsetFromQuadrantOrigin = worldPos - CartesianVector2(
		BASE_QUADRANT_ORIGIN_COORDINATE,
		BASE_QUADRANT_ORIGIN_COORDINATE);
	return {
		{ min(0, sign(offsetFromQuadrantOrigin.m_x)) + sign(offsetFromQuadrantOrigin.m_x) * (int)(abs(offsetFromQuadrantOrigin.m_x) / (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)),
		  min(0, sign(offsetFromQuadrantOrigin.m_y)) + sign(offsetFromQuadrantOrigin.m_y) * (int)(abs(offsetFromQuadrantOrigin.m_y) / (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH))},

		{ min(0, sign(offsetFromQuadrantOrigin.m_x)) + sign(offsetFromQuadrantOrigin.m_x) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH),
		  min(0, sign(offsetFromQuadrantOrigin.m_y)) + sign(offsetFromQuadrantOrigin.m_y) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (QUADRANT_SIDE_LENGTH * SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)},

		{ min(0, sign(offsetFromQuadrantOrigin.m_x)) + sign(offsetFromQuadrantOrigin.m_x) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / TILE_SIDE_LENGTH,
		  min(0, sign(offsetFromQuadrantOrigin.m_y)) + sign(offsetFromQuadrantOrigin.m_y) * ((int)abs(offsetFromQuadrantOrigin.m_x) % (SECTOR_SIDE_LENGTH * TILE_SIDE_LENGTH)) / TILE_SIDE_LENGTH}
	};
}

CartesianVector2 CoordinatesToWorldPosition(const TileNED::WorldCoordinates& worldCoords)
{
	using namespace TileConstants;
	return {
		BASE_QUADRANT_ORIGIN_COORDINATE +
		(((((QUADRANT_SIDE_LENGTH * worldCoords.m_quadrant.m_x)
			+ worldCoords.m_sector.m_x) * SECTOR_SIDE_LENGTH)
			+ worldCoords.m_tile.m_x) * TILE_SIDE_LENGTH),

		BASE_QUADRANT_ORIGIN_COORDINATE +
		(((((QUADRANT_SIDE_LENGTH * worldCoords.m_quadrant.m_y)
			+ worldCoords.m_sector.m_y) * SECTOR_SIDE_LENGTH)
			+ worldCoords.m_tile.m_y) * TILE_SIDE_LENGTH)
	};
}

void TileNED::SpawnBetween(
	int originX,
	int originY,
	int targetX,
	int targetY,
	ECS_Core::Manager& manager)
{
	// Base case: Spawn between a place and itself
	if (targetX == originX && targetY == originY)
	{
		// Spawn just in case, will return immediately in most cases.
		SpawnQuadrant(targetX, targetY, manager);
		return;
	}

	SpawnQuadrant(targetX, targetY, manager);
	SpawnQuadrant(originX, originY, manager);

	auto midX = (targetX + originX) / 2;
	auto midY = (targetY + originY) / 2;
	SpawnQuadrant(midX, midY, manager);

	if ((midX == targetX && midY == targetY)
		|| midX == originX && midY == originY)
	{
		// We're all filled in on this segment.
		return;
	}

	SpawnBetween(originX, originY, midX, midY, manager);
	SpawnBetween(midX + sign(targetX - originX), midY + sign(targetY - originY), targetX, targetY, manager);
}

void TileNED::CheckWorldClick(ECS_Core::Manager& manager)
{
	auto inputEntities = manager.entitiesMatching<ECS_Core::Signatures::S_Input>();
	if (inputEntities.size() == 0) return;
	ECS_Core::Components::C_UserInputs& inputComponent = manager.getComponent<ECS_Core::Components::C_UserInputs>(inputEntities.front());
	if (inputComponent.m_unprocessedThisFrameDownMouseButtonFlags & (u8)ECS_Core::Components::MouseButtons::LEFT)
	{
		auto mouseWorldCoords = WorldPositionToCoordinates(inputComponent.m_currentMousePosition.m_worldPosition);
		auto&& quadrantCoords = mouseWorldCoords.m_quadrant;

		if (TileNED::s_spawnedQuadrants.find({ (int)quadrantCoords.m_x, (int)quadrantCoords.m_y }) == TileNED::s_spawnedQuadrants.end())
		{
			// We're going to need to spawn world up to that point.
			// first: find the closest available world tile
			int closestX = 0;
			int closestY = 0;
			int smallestDistance = std::numeric_limits<int>::max();
			// Assume the initial tile is the closest for a start
			for (auto&& quadrant : s_spawnedQuadrants)
			{
				auto&& quadX = quadrant.first.first;
				auto&& quadY = quadrant.first.second;
				auto&& distX = quadX - static_cast<int>(quadrantCoords.m_x);
				auto&& distY = quadY - static_cast<int>(quadrantCoords.m_y);
				auto&& distanceSq = distX * distX + distY * distY;
				if (distanceSq < smallestDistance)
				{
					closestX = quadX;
					closestY = quadY;
					smallestDistance = distanceSq;
				}
			}

			SpawnBetween(
				closestX,
				closestY,
				static_cast<int>(quadrantCoords.m_x),
				static_cast<int>(quadrantCoords.m_y),
				manager);
		}

		inputComponent.ProcessMouseDown(ECS_Core::Components::MouseButtons::LEFT);
	}
}

void WorldTile::Operate(GameLoopPhase phase, const timeuS& frameDuration)
{
	switch (phase)
	{
	case GameLoopPhase::PREPARATION:
		if (!TileNED::baseQuadrantSpawned)
		{
			SpawnQuadrant(0, 0, m_managerRef);
			TileNED::baseQuadrantSpawned = true;
		}
		break;
	case GameLoopPhase::ACTION:
		TileNED::CheckWorldClick(m_managerRef);
		break;

	case GameLoopPhase::INPUT:
	case GameLoopPhase::ACTION_RESPONSE:
	case GameLoopPhase::RENDER:
	case GameLoopPhase::CLEANUP:
		return;
	}
}

bool WorldTile::ShouldExit()
{
	return false;
}

DEFINE_SYSTEM_INSTANTIATION(WorldTile);<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  emscURLLoader.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "emscURLLoader.h"
#include <emscripten/emscripten.h>

namespace Oryol {
namespace _priv {

//------------------------------------------------------------------------------
bool
emscURLLoader::doRequest(const Ptr<IORead>& req) {
    if (baseURLLoader::doRequest(req)) {
        this->startRequest(req);
        return true;
    }
    else {
        // request was cancelled
        return false;
    }
}

//------------------------------------------------------------------------------
void
emscURLLoader::startRequest(const Ptr<IORead>& req) {
    o_assert(req.isValid() && !req->Handled());

    // bump the requests refcount and get a raw pointer
    IORead* reqPtr = req.get();
    reqPtr->addRef();

    // start the asynchronous XHR
    // NOTE: we can only load from our own HTTP server, so the host part of
    // the URL is completely irrelevant...
    String urlPath = req->Url.PathToEnd();
    emscripten_async_wget_data(urlPath.AsCStr(), (void*) reqPtr, emscURLLoader::onLoaded, emscURLLoader::onFailed);
}

//------------------------------------------------------------------------------
void
emscURLLoader::onLoaded(void* userData, void* buffer, int size) {
    o_assert(0 != userData);
    o_assert(0 != buffer);
    o_assert(size > 0);

    Ptr<IORead> req = userData;
    req->release();
    req->Status = IOStatus::OK;
    req->Data.Add((const uint8_t*)buffer, size);
    req->Handled = true;
}

//------------------------------------------------------------------------------
void
emscURLLoader::onFailed(void* userData) {
    o_assert(0 != userData);

    // user data is a HTTPRequest ptr, put it back into a smart pointer
    Ptr<IORead> req = userData;
    req->release();
    Log::Dbg("emscURLLoader::onFailed(url=%s)\n", req->Url.AsCStr());

    // hmm we don't know why it failed, so make something up, we should definitely
    // fix this somehow (looks like the wget2 functions also pass a HTTP status code)
    const IOStatus::Code ioStatus = IOStatus::NotFound;
    req->Status = ioStatus;
    req->Handled = true;
}

} // namespace _priv
} // naespace Oryol

<commit_msg>Fixed debug compile bug in emscURLLoader<commit_after>//------------------------------------------------------------------------------
//  emscURLLoader.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "emscURLLoader.h"
#include <emscripten/emscripten.h>

namespace Oryol {
namespace _priv {

//------------------------------------------------------------------------------
bool
emscURLLoader::doRequest(const Ptr<IORead>& req) {
    if (baseURLLoader::doRequest(req)) {
        this->startRequest(req);
        return true;
    }
    else {
        // request was cancelled
        return false;
    }
}

//------------------------------------------------------------------------------
void
emscURLLoader::startRequest(const Ptr<IORead>& req) {
    o_assert(req.isValid() && !req->Handled);

    // bump the requests refcount and get a raw pointer
    IORead* reqPtr = req.get();
    reqPtr->addRef();

    // start the asynchronous XHR
    // NOTE: we can only load from our own HTTP server, so the host part of
    // the URL is completely irrelevant...
    String urlPath = req->Url.PathToEnd();
    emscripten_async_wget_data(urlPath.AsCStr(), (void*) reqPtr, emscURLLoader::onLoaded, emscURLLoader::onFailed);
}

//------------------------------------------------------------------------------
void
emscURLLoader::onLoaded(void* userData, void* buffer, int size) {
    o_assert(0 != userData);
    o_assert(0 != buffer);
    o_assert(size > 0);

    Ptr<IORead> req = userData;
    req->release();
    req->Status = IOStatus::OK;
    req->Data.Add((const uint8_t*)buffer, size);
    req->Handled = true;
}

//------------------------------------------------------------------------------
void
emscURLLoader::onFailed(void* userData) {
    o_assert(0 != userData);

    // user data is a HTTPRequest ptr, put it back into a smart pointer
    Ptr<IORead> req = userData;
    req->release();
    Log::Dbg("emscURLLoader::onFailed(url=%s)\n", req->Url.AsCStr());

    // hmm we don't know why it failed, so make something up, we should definitely
    // fix this somehow (looks like the wget2 functions also pass a HTTP status code)
    const IOStatus::Code ioStatus = IOStatus::NotFound;
    req->Status = ioStatus;
    req->Handled = true;
}

} // namespace _priv
} // naespace Oryol

<|endoftext|>


<commit_before>#ifdef _WIN32

#include "native.h"
#include "generic.h"
#include "win.h"
#include <vector>

using namespace std;

// TODO Find the right size for this
#define EVENT_BUFFER_SIZE (16*1024)

#define CREATE_SHARE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
#define CREATE_FLAGS (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED)
#define EVENT_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE)

class WatchPoint {
public:
    WatchPoint(wchar_t *path) {
        this->path = _wcsdup(path);
    }

    ~WatchPoint() {
        free(this->path);
    }

    bool isAncestorOf(wchar_t *candidate) {
        wprintf(L"~~~~ Checking if '%ls' starts with '%ls'\n", candidate, path);
        return wcsncmp(path, candidate, wcslen(path)) == 0;
    }
private:
    wchar_t *path;
};

class Server {
public:
    Server(
        JavaVM *jvm,
        JNIEnv *env,
        jobject watcherCallback,
        wchar_t drivePath[4],
        vector<WatchPoint>* watchPoints
    ) {
        this->jvm = jvm;
        this->watcherCallback = env->NewGlobalRef(watcherCallback);
        this->stopEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
        this->watchPoints = watchPoints;
        wcscpy_s(this->drivePath, 4, drivePath);

        this->threadHandle = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            EventProcessingThread,  // thread function name
            this,                   // argument to thread function
            0,                      // use default creation flags
            NULL                    // the thread identifier
        );
        SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
    }

    void close(JNIEnv *env) {
        SetEvent(this->stopEventHandle);
        WaitForSingleObject(this->threadHandle, INFINITE);
        CloseHandle(this->threadHandle);
        CloseHandle(this->stopEventHandle);
        delete watchPoints;
        env->DeleteGlobalRef(this->watcherCallback);
    }

    static DWORD WINAPI EventProcessingThread(LPVOID data) {
        Server *server = (Server*) data;
        server->run();
        return 0;
    }

private:
    JavaVM *jvm;
    wchar_t drivePath[4];
    vector<WatchPoint>* watchPoints;
    jobject watcherCallback;

    HANDLE stopEventHandle;
    HANDLE threadHandle;

    void run() {
        printf("~~~~ Starting thread\n");

        // TODO Extract this logic to some shared function
        JNIEnv *env;
        jint statAttach = jvm->AttachCurrentThreadAsDaemon((void **) &(env), NULL);
        if (statAttach != JNI_OK) {
            printf("Failed to attach JNI to current thread: %d\n", statAttach);
            return;
        }

        OVERLAPPED overlapped;
        memset(&overlapped, 0, sizeof(overlapped));
        overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        HANDLE hDrive = CreateFileW(drivePath, GENERIC_READ, CREATE_SHARE, NULL, OPEN_EXISTING, CREATE_FLAGS, NULL);

        char buffer[EVENT_BUFFER_SIZE];
        HANDLE handles[2] = {stopEventHandle, overlapped.hEvent};
        while (true) {
            int rcDrive = ReadDirectoryChangesW(
                hDrive,
                buffer,
                sizeof(buffer),
                TRUE,
                EVENT_MASK,
                NULL,
                &overlapped,
                NULL
            );
            if (rcDrive == 0) {
                // TODO Error handling
                printf("~~~~ Couldn't read directory: %d - %d\n", rcDrive, GetLastError());
                break;
            }

            DWORD rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            if (rc == WAIT_OBJECT_0) {
                break;
            }
            if (rc == WAIT_OBJECT_0 + 1) {
                DWORD dwBytesReturned;
                if (!GetOverlappedResult(hDrive, &overlapped, &dwBytesReturned, FALSE)) {
                    // TODO Error handling
                    printf("~~~~ Failed to wait\n");
                    break;
                }

                if (dwBytesReturned == 0) {
                    // don't send dirty too much, everything is changed anyway
                    // TODO Understand what this does
                    if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
                        break;

                    // Got a buffer overflow => current changes lost => send INVALIDATE on root
                    reportEvent(env, FILE_EVENT_INVALIDATE, drivePath, 3);
                } else {
                    FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *)buffer;
                    do {
                        handlePathChanged(env, info);
                        info = (FILE_NOTIFY_INFORMATION *)((char *)info + info->NextEntryOffset);
                    } while (info->NextEntryOffset != 0);
                }
            }
        }

        printf("~~~~ Stopping thread\n");

        CloseHandle(overlapped.hEvent);
        CloseHandle(hDrive);

        // TODO Extract this logic to some shared function
        jint statDetach = jvm->DetachCurrentThread();
        if (statDetach != JNI_OK) {
            printf("Failed to detach JNI from current thread: %d\n", statAttach);
            return;
        }

        return;
    }

    void reportEvent(JNIEnv *env, jint type, wchar_t *changedPath, int changedPathLen) {
        jstring changedPathJava = wchar_to_java(env, changedPath, changedPathLen, NULL);
        jclass callback_class = env->GetObjectClass(watcherCallback);
        jmethodID methodCallback = env->GetMethodID(callback_class, "pathChanged", "(ILjava/lang/String;)V");
        // TODO Do we need to add a global reference to the string here?
        env->CallVoidMethod(watcherCallback, methodCallback, type, changedPathJava);
    }

    void handlePathChanged(JNIEnv *env, FILE_NOTIFY_INFORMATION *info) {
        int pathLen = info->FileNameLength / sizeof(wchar_t);
        wchar_t *changedPath = add_prefix(info->FileName, pathLen, drivePath);
        int changedPathLen = pathLen + 3;

        wprintf(L"~~~~ Changed: 0x%x %ls\n", info->Action, changedPath);

        jint type;
        if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            type = FILE_EVENT_CREATED;
        } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            type = FILE_EVENT_REMOVED;
        } else if (info->Action == FILE_ACTION_MODIFIED) {
            type = FILE_EVENT_MODIFIED;
        } else {
            wprintf(L"~~~~ Unknown event 0x%x for %ls\n", info->Action, changedPath);
            type = FILE_EVENT_UNKNOWN;
        }

        bool watching = false;
        for (auto watchPoint : *watchPoints) {
            if (watchPoint.isAncestorOf(changedPath)) {
                watching = true;
                break;
            }
        }
        if (!watching) {
            wprintf(L"~~~~ Ignoring %ls (root is not watched)\n", changedPath);
            return;
        }

        reportEvent(env, type, changedPath, changedPathLen);
        free(changedPath);
    }
};

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatching(JNIEnv *env, jclass target, jobjectArray paths, jobject javaCallback, jobject result) {

    printf("\n~~~~ Configuring...\n");

    JavaVM* jvm;
    int jvmStatus = env->GetJavaVM(&jvm);
    if (jvmStatus != JNI_OK) {
        mark_failed_with_errno(env, "Could not store jvm instance.", result);
        return NULL;
    }

    int watchPointCount = env->GetArrayLength(paths);
    if (watchPointCount == 0) {
        mark_failed_with_errno(env, "No paths given to watch.", result);
        return NULL;
    }

    vector<WatchPoint>* watchPoints = new vector<WatchPoint>();

    wchar_t driveLetter = L'\0';
    for (int i = 0; i < watchPointCount; i++) {
        jstring path = (jstring) env->GetObjectArrayElement(paths, i);
        wchar_t* watchPoint = java_to_wchar_path(env, path, result);
        int watchPointLen = wcslen(watchPoint);
        if (watchPointLen > 240 || watchPoint[0] == L'\\') {
            mark_failed_with_errno(env, "Cannot watch long paths for now.", result);
            return NULL;
        }
        if (driveLetter == L'\0') {
            driveLetter = watchPoint[0];
        } else if (driveLetter != watchPoint[0]) {
            mark_failed_with_errno(env, "Cannot watch multiple drives for now.", result);
            return NULL;
        }
        if (watchPoint[watchPointLen - 1] != L'\\') {
            wchar_t* oldwatchPoint = watchPoint;
            watchPoint = add_suffix(watchPoint, watchPointLen, L"\\");
            free(oldwatchPoint);
        }
        wprintf(L"~~~~ Watching %ls\n", watchPoint);
        watchPoints->push_back(WatchPoint(watchPoint));
        free(watchPoint);
    }
    wchar_t drivePath[4] = {towupper(driveLetter), L':', L'\\', L'\0'};

    Server* server = new Server(
        jvm,
        env,
        javaCallback,
        drivePath,
        watchPoints
    );

    jclass clsWatch = env->FindClass("net/rubygrapefruit/platform/internal/jni/WindowsFileEventFunctions$WatcherImpl");
    jmethodID constructor = env->GetMethodID(clsWatch, "<init>", "(Ljava/lang/Object;)V");
    return env->NewObject(clsWatch, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_stopWatching(JNIEnv *env, jclass target, jobject detailsObj, jobject result) {
    Server* server = (Server*)env->GetDirectBufferAddress(detailsObj);
    server->close(env);
    delete server;
}

#endif
<commit_msg>Somewhat better memory management of WatchPoints<commit_after>#ifdef _WIN32

#include "native.h"
#include "generic.h"
#include "win.h"
#include <vector>

using namespace std;

// TODO Find the right size for this
#define EVENT_BUFFER_SIZE (16*1024)

#define CREATE_SHARE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
#define CREATE_FLAGS (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED)
#define EVENT_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE)

class WatchPoint {
public:
    WatchPoint(wchar_t *path) {
        this->path = _wcsdup(path);
    }

    WatchPoint(const WatchPoint &orig) : WatchPoint(orig.path) {}

    ~WatchPoint() {
        free(this->path);
    }

    bool isAncestorOf(wchar_t *candidate) {
        wprintf(L"~~~~ Checking if '%ls' starts with '%ls'\n", candidate, path);
        return wcsncmp(path, candidate, wcslen(path)) == 0;
    }
private:
    wchar_t *path;
};

class Server {
public:
    Server(
        JavaVM *jvm,
        JNIEnv *env,
        jobject watcherCallback,
        wchar_t drivePath[4],
        vector<WatchPoint>* watchPoints
    ) {
        this->jvm = jvm;
        this->watcherCallback = env->NewGlobalRef(watcherCallback);
        this->stopEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
        this->watchPoints = watchPoints;
        wcscpy_s(this->drivePath, 4, drivePath);

        this->threadHandle = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            EventProcessingThread,  // thread function name
            this,                   // argument to thread function
            0,                      // use default creation flags
            NULL                    // the thread identifier
        );
        SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
    }

    void close(JNIEnv *env) {
        SetEvent(this->stopEventHandle);
        WaitForSingleObject(this->threadHandle, INFINITE);
        CloseHandle(this->threadHandle);
        CloseHandle(this->stopEventHandle);
        delete watchPoints;
        env->DeleteGlobalRef(this->watcherCallback);
    }

    static DWORD WINAPI EventProcessingThread(LPVOID data) {
        Server *server = (Server*) data;
        server->run();
        return 0;
    }

private:
    JavaVM *jvm;
    wchar_t drivePath[4];
    vector<WatchPoint>* watchPoints;
    jobject watcherCallback;

    HANDLE stopEventHandle;
    HANDLE threadHandle;

    void run() {
        printf("~~~~ Starting thread\n");

        // TODO Extract this logic to some shared function
        JNIEnv *env;
        jint statAttach = jvm->AttachCurrentThreadAsDaemon((void **) &(env), NULL);
        if (statAttach != JNI_OK) {
            printf("Failed to attach JNI to current thread: %d\n", statAttach);
            return;
        }

        OVERLAPPED overlapped;
        memset(&overlapped, 0, sizeof(overlapped));
        overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        HANDLE hDrive = CreateFileW(drivePath, GENERIC_READ, CREATE_SHARE, NULL, OPEN_EXISTING, CREATE_FLAGS, NULL);

        char buffer[EVENT_BUFFER_SIZE];
        HANDLE handles[2] = {stopEventHandle, overlapped.hEvent};
        while (true) {
            int rcDrive = ReadDirectoryChangesW(
                hDrive,
                buffer,
                sizeof(buffer),
                TRUE,
                EVENT_MASK,
                NULL,
                &overlapped,
                NULL
            );
            if (rcDrive == 0) {
                // TODO Error handling
                printf("~~~~ Couldn't read directory: %d - %d\n", rcDrive, GetLastError());
                break;
            }

            DWORD rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            if (rc == WAIT_OBJECT_0) {
                break;
            }
            if (rc == WAIT_OBJECT_0 + 1) {
                DWORD dwBytesReturned;
                if (!GetOverlappedResult(hDrive, &overlapped, &dwBytesReturned, FALSE)) {
                    // TODO Error handling
                    printf("~~~~ Failed to wait\n");
                    break;
                }

                if (dwBytesReturned == 0) {
                    // don't send dirty too much, everything is changed anyway
                    // TODO Understand what this does
                    if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
                        break;

                    // Got a buffer overflow => current changes lost => send INVALIDATE on root
                    reportEvent(env, FILE_EVENT_INVALIDATE, drivePath, 3);
                } else {
                    FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *)buffer;
                    do {
                        handlePathChanged(env, info);
                        info = (FILE_NOTIFY_INFORMATION *)((char *)info + info->NextEntryOffset);
                    } while (info->NextEntryOffset != 0);
                }
            }
        }

        printf("~~~~ Stopping thread\n");

        CloseHandle(overlapped.hEvent);
        CloseHandle(hDrive);

        // TODO Extract this logic to some shared function
        jint statDetach = jvm->DetachCurrentThread();
        if (statDetach != JNI_OK) {
            printf("Failed to detach JNI from current thread: %d\n", statAttach);
            return;
        }

        return;
    }

    void reportEvent(JNIEnv *env, jint type, wchar_t *changedPath, int changedPathLen) {
        jstring changedPathJava = wchar_to_java(env, changedPath, changedPathLen, NULL);
        jclass callback_class = env->GetObjectClass(watcherCallback);
        jmethodID methodCallback = env->GetMethodID(callback_class, "pathChanged", "(ILjava/lang/String;)V");
        // TODO Do we need to add a global reference to the string here?
        env->CallVoidMethod(watcherCallback, methodCallback, type, changedPathJava);
    }

    void handlePathChanged(JNIEnv *env, FILE_NOTIFY_INFORMATION *info) {
        int pathLen = info->FileNameLength / sizeof(wchar_t);
        wchar_t *changedPath = add_prefix(info->FileName, pathLen, drivePath);
        int changedPathLen = pathLen + 3;

        wprintf(L"~~~~ Changed: 0x%x %ls\n", info->Action, changedPath);

        jint type;
        if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            type = FILE_EVENT_CREATED;
        } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            type = FILE_EVENT_REMOVED;
        } else if (info->Action == FILE_ACTION_MODIFIED) {
            type = FILE_EVENT_MODIFIED;
        } else {
            wprintf(L"~~~~ Unknown event 0x%x for %ls\n", info->Action, changedPath);
            type = FILE_EVENT_UNKNOWN;
        }

        bool watching = false;
        for (auto &watchPoint : *watchPoints) {
            if (watchPoint.isAncestorOf(changedPath)) {
                watching = true;
                break;
            }
        }
        if (!watching) {
            wprintf(L"~~~~ Ignoring %ls (root is not watched)\n", changedPath);
            return;
        }

        reportEvent(env, type, changedPath, changedPathLen);
        free(changedPath);
    }
};

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatching(JNIEnv *env, jclass target, jobjectArray paths, jobject javaCallback, jobject result) {

    printf("\n~~~~ Configuring...\n");

    JavaVM* jvm;
    int jvmStatus = env->GetJavaVM(&jvm);
    if (jvmStatus != JNI_OK) {
        mark_failed_with_errno(env, "Could not store jvm instance.", result);
        return NULL;
    }

    int watchPointCount = env->GetArrayLength(paths);
    if (watchPointCount == 0) {
        mark_failed_with_errno(env, "No paths given to watch.", result);
        return NULL;
    }

    vector<WatchPoint>* watchPoints = new vector<WatchPoint>();

    wchar_t driveLetter = L'\0';
    for (int i = 0; i < watchPointCount; i++) {
        jstring path = (jstring) env->GetObjectArrayElement(paths, i);
        wchar_t* watchPoint = java_to_wchar_path(env, path, result);
        int watchPointLen = wcslen(watchPoint);
        if (watchPointLen > 240 || watchPoint[0] == L'\\') {
            mark_failed_with_errno(env, "Cannot watch long paths for now.", result);
            return NULL;
        }
        if (driveLetter == L'\0') {
            driveLetter = watchPoint[0];
        } else if (driveLetter != watchPoint[0]) {
            mark_failed_with_errno(env, "Cannot watch multiple drives for now.", result);
            return NULL;
        }
        if (watchPoint[watchPointLen - 1] != L'\\') {
            wchar_t* oldwatchPoint = watchPoint;
            watchPoint = add_suffix(watchPoint, watchPointLen, L"\\");
            free(oldwatchPoint);
        }
        wprintf(L"~~~~ Watching %ls\n", watchPoint);
        watchPoints->emplace_back(watchPoint);
        free(watchPoint);
    }
    wchar_t drivePath[4] = {towupper(driveLetter), L':', L'\\', L'\0'};

    Server* server = new Server(
        jvm,
        env,
        javaCallback,
        drivePath,
        watchPoints
    );

    jclass clsWatch = env->FindClass("net/rubygrapefruit/platform/internal/jni/WindowsFileEventFunctions$WatcherImpl");
    jmethodID constructor = env->GetMethodID(clsWatch, "<init>", "(Ljava/lang/Object;)V");
    return env->NewObject(clsWatch, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_stopWatching(JNIEnv *env, jclass target, jobject detailsObj, jobject result) {
    Server* server = (Server*)env->GetDirectBufferAddress(detailsObj);
    server->close(env);
    delete server;
}

#endif
<|endoftext|>


<commit_before>#include "chainerx/cuda/cuda_device.h"

#include <cstddef>
#include <cstring>
#include <memory>

#include <cuda_runtime.h>

#include "chainerx/cuda/cuda_runtime.h"
#include "chainerx/device.h"
#include "chainerx/error.h"
#include "chainerx/macro.h"
#include "chainerx/native/native_device.h"

namespace chainerx {
namespace cuda {

std::shared_ptr<void> CudaDevice::Allocate(size_t bytesize) {
    void* ptr = memory_pool_.Malloc(bytesize);
    return std::shared_ptr<void>{ptr, [this](void* ptr) { memory_pool_.Free(ptr); }};
}

std::shared_ptr<void> CudaDevice::AllocatePinnedMemory(size_t bytesize) {
    void* ptr = pinned_memory_pool_.Malloc(bytesize);
    return std::shared_ptr<void>{ptr, [this](void* ptr) { pinned_memory_pool_.Free(ptr); }};
}

std::shared_ptr<void> CudaDevice::MakeDataFromForeignPointer(const std::shared_ptr<void>& data) {
    if (data == nullptr) {
        return data;
    }

    // check memory validity
    void* ptr = data.get();
    cudaPointerAttributes attr{};
    cudaError_t status = cudaPointerGetAttributes(&attr, ptr);
    switch (status) {
        case cudaSuccess:
            if (attr.device != index()) {
                throw ChainerxError{"CUDA memory: ", ptr, " must reside on the device: ", index()};
            }
            break;
        case cudaErrorInvalidValue:
            throw ChainerxError{"Memory: ", ptr, " is not a CUDA memory"};
        default:
            Throw(status);
    }
    return data;
}

void CudaDevice::MemoryCopyFrom(void* dst, const void* src, size_t bytesize, Device& src_device) {
    CHAINERX_ASSERT(bytesize == 0 || IsPointerCudaMemory(dst));
    if (bytesize == 0) {
        return;
    }
    // TODO(niboshi): Do device management with RAII
    int old_device{};
    CheckCudaError(cudaGetDevice(&old_device));
    CheckCudaError(cudaSetDevice(index()));
    if (&src_device == this || nullptr != dynamic_cast<CudaDevice*>(&src_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&src_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy from native device
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyHostToDevice));
    }
    CheckCudaError(cudaSetDevice(old_device));
}

void CudaDevice::MemoryCopyTo(void* dst, const void* src, size_t bytesize, Device& dst_device) {
    CHAINERX_ASSERT(bytesize == 0 || src == nullptr || IsPointerCudaMemory(src));
    if (bytesize == 0) {
        return;
    }
    // TODO(niboshi): Do device management with RAII
    int old_device{};
    CheckCudaError(cudaGetDevice(&old_device));
    CheckCudaError(cudaSetDevice(index()));
    if (&dst_device == this || nullptr != dynamic_cast<CudaDevice*>(&dst_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&dst_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy to native device
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToHost));
    }
    CheckCudaError(cudaSetDevice(old_device));
}

std::shared_ptr<void> CudaDevice::TransferDataFrom(
        Device& src_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    MemoryCopyFrom(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, src_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::TransferDataTo(Device& dst_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = dst_device.Allocate(bytesize);
    MemoryCopyTo(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, dst_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::FromHostMemory(const std::shared_ptr<void>& src_ptr, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    std::shared_ptr<void> pinned_src_ptr = AllocatePinnedMemory(bytesize);
    std::memcpy(pinned_src_ptr.get(), src_ptr.get(), bytesize);
    CheckCudaError(cudaMemcpyAsync(dst_ptr.get(), pinned_src_ptr.get(), bytesize, cudaMemcpyHostToDevice));
    return dst_ptr;
}

}  // namespace cuda
}  // namespace chainerx
<commit_msg>Use cudaMemcpy instead of memcpy<commit_after>#include "chainerx/cuda/cuda_device.h"

#include <cstddef>
#include <memory>

#include <cuda_runtime.h>

#include "chainerx/cuda/cuda_runtime.h"
#include "chainerx/device.h"
#include "chainerx/error.h"
#include "chainerx/macro.h"
#include "chainerx/native/native_device.h"

namespace chainerx {
namespace cuda {

std::shared_ptr<void> CudaDevice::Allocate(size_t bytesize) {
    void* ptr = memory_pool_.Malloc(bytesize);
    return std::shared_ptr<void>{ptr, [this](void* ptr) { memory_pool_.Free(ptr); }};
}

std::shared_ptr<void> CudaDevice::AllocatePinnedMemory(size_t bytesize) {
    void* ptr = pinned_memory_pool_.Malloc(bytesize);
    return std::shared_ptr<void>{ptr, [this](void* ptr) { pinned_memory_pool_.Free(ptr); }};
}

std::shared_ptr<void> CudaDevice::MakeDataFromForeignPointer(const std::shared_ptr<void>& data) {
    if (data == nullptr) {
        return data;
    }

    // check memory validity
    void* ptr = data.get();
    cudaPointerAttributes attr{};
    cudaError_t status = cudaPointerGetAttributes(&attr, ptr);
    switch (status) {
        case cudaSuccess:
            if (attr.device != index()) {
                throw ChainerxError{"CUDA memory: ", ptr, " must reside on the device: ", index()};
            }
            break;
        case cudaErrorInvalidValue:
            throw ChainerxError{"Memory: ", ptr, " is not a CUDA memory"};
        default:
            Throw(status);
    }
    return data;
}

void CudaDevice::MemoryCopyFrom(void* dst, const void* src, size_t bytesize, Device& src_device) {
    CHAINERX_ASSERT(bytesize == 0 || IsPointerCudaMemory(dst));
    if (bytesize == 0) {
        return;
    }
    // TODO(niboshi): Do device management with RAII
    int old_device{};
    CheckCudaError(cudaGetDevice(&old_device));
    CheckCudaError(cudaSetDevice(index()));
    if (&src_device == this || nullptr != dynamic_cast<CudaDevice*>(&src_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&src_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy from native device
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyHostToDevice));
    }
    CheckCudaError(cudaSetDevice(old_device));
}

void CudaDevice::MemoryCopyTo(void* dst, const void* src, size_t bytesize, Device& dst_device) {
    CHAINERX_ASSERT(bytesize == 0 || src == nullptr || IsPointerCudaMemory(src));
    if (bytesize == 0) {
        return;
    }
    // TODO(niboshi): Do device management with RAII
    int old_device{};
    CheckCudaError(cudaGetDevice(&old_device));
    CheckCudaError(cudaSetDevice(index()));
    if (&dst_device == this || nullptr != dynamic_cast<CudaDevice*>(&dst_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&dst_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy to native device
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToHost));
    }
    CheckCudaError(cudaSetDevice(old_device));
}

std::shared_ptr<void> CudaDevice::TransferDataFrom(
        Device& src_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    MemoryCopyFrom(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, src_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::TransferDataTo(Device& dst_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = dst_device.Allocate(bytesize);
    MemoryCopyTo(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, dst_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::FromHostMemory(const std::shared_ptr<void>& src_ptr, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    std::shared_ptr<void> pinned_src_ptr = AllocatePinnedMemory(bytesize);
    CheckCudaError(cudaMemcpy(pinned_src_ptr.get(), src_ptr.get(), bytesize, cudaMemcpyHostToHost));
    CheckCudaError(cudaMemcpyAsync(dst_ptr.get(), pinned_src_ptr.get(), bytesize, cudaMemcpyHostToDevice));
    return dst_ptr;
}

}  // namespace cuda
}  // namespace chainerx
<|endoftext|>


<commit_before><commit_msg>Clean up iterating code.<commit_after><|endoftext|>


<commit_before>#include <stdarg.h>
#include "StackInterpreter.hpp"
#include "PreprocessorHacks.hpp"
#include "BytecodeSets.hpp"
#include "Common.hpp"

namespace Lodtalk
{

/**
 * The stack interpreter.
 * This stack interpreter is based in the arquitecture of squeak.
 */
class StackInterpreter
{
public:
	StackInterpreter();
	~StackInterpreter();

	Oop interpretMethod(CompiledMethod *method, Oop receiver, int argumentCount, Oop *arguments);

private:
	void error(const char *message)
	{
		fprintf(stdout, "Interpreter error: %s\n", message);
		abort();
	}

	void errorFormat(const char *format, ...)
	{
		char buffer[1024];
		va_list args;
		va_start(args, format);
		vsnprintf(buffer, 1024, format, args);
		error(buffer);
		abort();
	}

	void fetchMethodData(CompiledMethod *newMethod)
	{
		method = newMethod;
		literalCount = method->getLiteralCount();
		argumentCount = method->getArgumentCount();
		temporalCount = method->getTemporalCount();
		firstInstructionPointer = method->getFirstBCPointer();
		literalArray = method->getFirstLiteralPointer();
	}

	int fetchByte()
	{
		return *instructionPointer++;
	}

	int fetchSByte()
	{
		auto sbyte = *reinterpret_cast<int8_t*> (instructionPointer);
		instructionPointer++;
		return sbyte;
	}

	void fetchNextInstructionOpcode()
	{
		nextOpcode = fetchByte();
	}

	void push(Oop object)
	{
		stackMemory.push_back(object);
	}

	size_t getCurrentStackSize() const
	{
		return stackMemory.size();
	}

	Oop pop()
	{
		if(!getCurrentStackSize())
			error("Stack underflow");

		auto result = stackMemory.back();
		stackMemory.pop_back();
		return result;
	}

	Oop stackTop()
	{
		if(!getCurrentStackSize())
			error("Stack underflow");

		return stackMemory.back();
	}

	bool condJumpOnNotBoolean(bool jumpType)
	{
		LODTALK_UNIMPLEMENTED();
	}

private:
	// TODO: Use a proper downward growing stack
	std::vector<Oop> stackMemory;

	// Interpreter data
	CompiledMethod *method;
	uint8_t *firstInstructionPointer;
	uint8_t *instructionPointer;
	int nextOpcode;
	int currentOpcode;

	size_t argumentCount;
	size_t literalCount;
	size_t temporalCount;

	uint64_t extendA;
	int64_t extendB;

	Oop receiver;
	Oop *arguments;
	Oop *literalArray;

	bool isReturning;
	Oop methodReturnValue;

private:
	void pushLiteralVariable(int literalVarIndex)
	{
		auto literal = literalArray[literalVarIndex];

		// Cast the literal variable and push the value
		auto literalVar = reinterpret_cast<LiteralVariable*> (literal.pointer);
		push(literalVar->value);
	}

	void pushLiteral(int literalIndex)
	{
		auto literal = literalArray[literalIndex];

		// Cast the literal variable and push the value
		push(literal);
	}

	void sendLiteralIndexArgumentCount(int literalIndex, int argumentCount)
	{
		auto selector = literalArray[literalIndex];

		// TODO: Make this more efficient.
		Oop arguments[argumentCount];
		for(int i = 0; i < argumentCount; ++i)
			arguments[argumentCount - i - 1] = pop();

		// Get the receiver.
		Oop newReceiver = pop();

		// Perform the message send.
		Oop result = sendMessage(newReceiver, selector, argumentCount, arguments);
		push(result);
	}

	// Bytecode instructions
	void interpretPushReceiverVariableShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushLiteralVariableShort()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal inde
		auto literalVarIndex = currentOpcode & 0xF;
		pushLiteralVariable(literalVarIndex);
	}

	void interpretPushLiteralShort()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal inded
		auto literalIndex = currentOpcode & 0x1F;
		pushLiteral(literalIndex);
	}

	void interpretPushTempShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretSendShortArgs0()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal index
		auto literalIndex = currentOpcode & 0x0F;
		sendLiteralIndexArgumentCount(literalIndex, 0);
	}

	void interpretSendShortArgs1()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal index
		auto literalIndex = currentOpcode & 0x0F;
		sendLiteralIndexArgumentCount(literalIndex, 1);
	}

	void interpretSendShortArgs2()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal index
		auto literalIndex = currentOpcode & 0x0F;
		sendLiteralIndexArgumentCount(literalIndex, 2);
	}

	void interpretJumpShort()
	{
		auto delta = (currentOpcode & 7) + 1;
		instructionPointer += delta;
	}

	void interpretJumpOnTrueShort()
	{
		// Fetch the condition and the next instruction opcode
		fetchNextInstructionOpcode();
		auto condition = pop();

		// Perform the branch when requested.
		if(condition == trueOop())
		{
			auto delta = (currentOpcode & 7);
			instructionPointer += delta;
			fetchNextInstructionOpcode();
		}
		else if(condition != falseOop())
		{
			// If the condition is not a boolean, trap
			condJumpOnNotBoolean(true);
		}
	}

	void interpretJumpOnFalseShort()
	{
		// Fetch the condition and the next instruction opcode
		fetchNextInstructionOpcode();
		auto condition = pop();

		// Perform the branch when requested.
		if(condition == falseOop())
		{
			auto delta = (currentOpcode & 7);
			instructionPointer += delta;
			fetchNextInstructionOpcode();
		}
		else if(condition != trueOop())
		{
			// If the condition is not a boolean, trap
			condJumpOnNotBoolean(false);
		}
	}

	void interpretPopStoreReceiverVariableShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreTemporalVariableShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushReceiver()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushTrue()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushFalse()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushNil()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushZero()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushOne()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushThisContext()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretDuplicateStackTop()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretReturnReceiver()
	{
		isReturning = true;
		methodReturnValue = receiver;
	}

	void interpretReturnTrue()
	{
		isReturning = true;
		methodReturnValue = trueOop();
	}

	void interpretReturnFalse()
	{
		isReturning = true;
		methodReturnValue = falseOop();
	}

	void interpretReturnNil()
	{
		isReturning = true;
		methodReturnValue = nilOop();
	}

	void interpretReturnTop()
	{
		isReturning = true;
		methodReturnValue = pop();
	}

	void interpretBlockReturnNil()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretBlockReturnTop()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretNop()
	{
		fetchNextInstructionOpcode();

		// Do nothing
	}

	void interpretPopStackTop()
	{
		fetchNextInstructionOpcode();

		// Pop the element from the stack.
		pop();
	}

	void interpretExtendA()
	{
		extendA = extendA*256 + fetchByte();
		fetchNextInstructionOpcode();
	}

	void interpretExtendB()
	{
		extendB = extendB*256 + fetchSByte();
		fetchNextInstructionOpcode();
	}

	void interpretPushReceiverVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushLiteralVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushLiteral()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushTemporary()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushNTemps()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushInteger()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushCharacter()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushArrayWithElements()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretSend()
	{
		// Fetch the data.
		int data = fetchByte();
		fetchNextInstructionOpcode();

		// Decode the literal index and argument index
		auto argumentCount = (data & BytecodeSet::Send_ArgumentCountMask) + extendB * BytecodeSet::Send_ArgumentCountCount;
		auto literalIndex = ((data >> BytecodeSet::Send_LiteralIndexShift) & BytecodeSet::Send_LiteralIndexMask) + extendA * BytecodeSet::Send_LiteralIndexCount;

		// Clear the extension values.
		extendA = 0;
		extendB = 0;

		// Send the message
		sendLiteralIndexArgumentCount(literalIndex, argumentCount);
	}

	void interpretSuperSend()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretTrapOnBehavior()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretJump()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretJumpOnTrue()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretJumpOnFalse()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreReceiverVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreLiteralVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreTemporalVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretStoreReceiverVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretStoreLiteralVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretStoreTemporalVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

};

StackInterpreter::StackInterpreter()
{
}

StackInterpreter::~StackInterpreter()
{
}

Oop StackInterpreter::interpretMethod(CompiledMethod *newMethod, Oop receiver, int argumentCount, Oop *arguments)
{
	// Check the argument count
	if(argumentCount != (int)newMethod->getArgumentCount())
		error("invalid suplied argument count.");

	isReturning = false;

	// Store the receiver and the argument data
	this->receiver = receiver;
	this->arguments = arguments;

	// Fetch the method data
	fetchMethodData(newMethod);
	instructionPointer = firstInstructionPointer;

	// Fetch the first instruction opcode
	fetchNextInstructionOpcode();

	// Reset the extensions values
	extendA = 0;
	extendB = 0;

	while(!isReturning)
	{
		currentOpcode = nextOpcode;

		switch(currentOpcode)
		{
#define MAKE_RANGE_CASE(i, from, to, name) \
case i + from:\
	interpret ## name(); \
	break;

#define SISTAV1_INSTRUCTION_RANGE(name, rangeFirst, rangeLast) \
	LODTALK_EVAL(LODTALK_FROM_TO_INCLUSIVE(rangeFirst, rangeLast, MAKE_RANGE_CASE, name))

#define SISTAV1_INSTRUCTION(name, opcode) \
case opcode:\
	interpret ## name(); \
	break;
#include "SistaV1BytecodeSet.inc"
#undef SISTAV1_INSTRUCTION_RANGE
#undef SISTAV1_INSTRUCTION
#undef MAKE_RANGE_CASE

		default:
			errorFormat("unsupported bytecode %d", currentOpcode);
		}
	}

	return methodReturnValue;
}

Oop interpretCompiledMethod(CompiledMethod *method, Oop receiver, int argumentCount, Oop *arguments)
{
	StackInterpreter interpreter;
	return interpreter.interpretMethod(method, receiver, argumentCount, arguments);
}

} // End of namespace Lodtalk
<commit_msg>I did more work in the interpreter.<commit_after>#include <stdarg.h>
#include "StackInterpreter.hpp"
#include "PreprocessorHacks.hpp"
#include "BytecodeSets.hpp"
#include "Common.hpp"

namespace Lodtalk
{

/**
 * The stack interpreter.
 * This stack interpreter is based in the arquitecture of squeak.
 */
class StackInterpreter
{
public:
	StackInterpreter();
	~StackInterpreter();

	Oop interpretMethod(CompiledMethod *method, Oop receiver, int argumentCount, Oop *arguments);

private:
	void error(const char *message)
	{
		fprintf(stdout, "Interpreter error: %s\n", message);
		abort();
	}

	void errorFormat(const char *format, ...)
	{
		char buffer[1024];
		va_list args;
		va_start(args, format);
		vsnprintf(buffer, 1024, format, args);
		error(buffer);
		abort();
	}

	void fetchMethodData(CompiledMethod *newMethod)
	{
		method = newMethod;
		literalCount = method->getLiteralCount();
		argumentCount = method->getArgumentCount();
		temporalCount = method->getTemporalCount();
		firstInstructionPointer = method->getFirstBCPointer();
		literalArray = method->getFirstLiteralPointer();
	}

	int fetchByte()
	{
		return *instructionPointer++;
	}

	int fetchSByte()
	{
		auto sbyte = *reinterpret_cast<int8_t*> (instructionPointer);
		instructionPointer++;
		return sbyte;
	}

	void fetchNextInstructionOpcode()
	{
		nextOpcode = fetchByte();
	}

	void push(Oop object)
	{
		stackMemory.push_back(object);
	}

	size_t getCurrentStackSize() const
	{
		return stackMemory.size();
	}

	Oop currentReceiver() const
	{
		return methodReceiver;
	}

	Oop pop()
	{
		if(!getCurrentStackSize())
			error("Stack underflow");

		auto result = stackMemory.back();
		stackMemory.pop_back();
		return result;
	}

	Oop stackTop()
	{
		if(!getCurrentStackSize())
			error("Stack underflow");

		return stackMemory.back();
	}

	bool condJumpOnNotBoolean(bool jumpType)
	{
		LODTALK_UNIMPLEMENTED();
	}

private:
	// TODO: Use a proper downward growing stack
	std::vector<Oop> stackMemory;

	// Interpreter data
	CompiledMethod *method;
	uint8_t *firstInstructionPointer;
	uint8_t *instructionPointer;
	int nextOpcode;
	int currentOpcode;

	size_t argumentCount;
	size_t literalCount;
	size_t temporalCount;

	uint64_t extendA;
	int64_t extendB;

	Oop methodReceiver;
	Oop *arguments;
	Oop *literalArray;

	bool isReturning;
	Oop methodReturnValue;

private:
	void pushLiteralVariable(int literalVarIndex)
	{
		auto literal = literalArray[literalVarIndex];

		// Cast the literal variable and push the value
		auto literalVar = reinterpret_cast<LiteralVariable*> (literal.pointer);
		push(literalVar->value);
	}

	void pushLiteral(int literalIndex)
	{
		auto literal = literalArray[literalIndex];

		// Cast the literal variable and push the value
		push(literal);
	}

	void sendLiteralIndexArgumentCount(int literalIndex, int argumentCount)
	{
		auto selector = literalArray[literalIndex];

		// TODO: Make this more efficient.
		Oop arguments[argumentCount];
		for(int i = 0; i < argumentCount; ++i)
			arguments[argumentCount - i - 1] = pop();

		// Get the receiver.
		Oop newReceiver = pop();

		// Perform the message send.
		Oop result = sendMessage(newReceiver, selector, argumentCount, arguments);
		push(result);
	}

	// Bytecode instructions
	void interpretPushReceiverVariableShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushLiteralVariableShort()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal inde
		auto literalVarIndex = currentOpcode & 0xF;
		pushLiteralVariable(literalVarIndex);
	}

	void interpretPushLiteralShort()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal inded
		auto literalIndex = currentOpcode & 0x1F;
		pushLiteral(literalIndex);
	}

	void interpretPushTempShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretSendShortArgs0()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal index
		auto literalIndex = currentOpcode & 0x0F;
		sendLiteralIndexArgumentCount(literalIndex, 0);
	}

	void interpretSendShortArgs1()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal index
		auto literalIndex = currentOpcode & 0x0F;
		sendLiteralIndexArgumentCount(literalIndex, 1);
	}

	void interpretSendShortArgs2()
	{
		fetchNextInstructionOpcode();

		// Fetch the literal index
		auto literalIndex = currentOpcode & 0x0F;
		sendLiteralIndexArgumentCount(literalIndex, 2);
	}

	void interpretJumpShort()
	{
		auto delta = (currentOpcode & 7) + 1;
		instructionPointer += delta;
	}

	void interpretJumpOnTrueShort()
	{
		// Fetch the condition and the next instruction opcode
		fetchNextInstructionOpcode();
		auto condition = pop();

		// Perform the branch when requested.
		if(condition == trueOop())
		{
			auto delta = (currentOpcode & 7);
			instructionPointer += delta;
			fetchNextInstructionOpcode();
		}
		else if(condition != falseOop())
		{
			// If the condition is not a boolean, trap
			condJumpOnNotBoolean(true);
		}
	}

	void interpretJumpOnFalseShort()
	{
		// Fetch the condition and the next instruction opcode
		fetchNextInstructionOpcode();
		auto condition = pop();

		// Perform the branch when requested.
		if(condition == falseOop())
		{
			auto delta = (currentOpcode & 7);
			instructionPointer += delta;
			fetchNextInstructionOpcode();
		}
		else if(condition != trueOop())
		{
			// If the condition is not a boolean, trap
			condJumpOnNotBoolean(false);
		}
	}

	void interpretPopStoreReceiverVariableShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreTemporalVariableShort()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushReceiver()
	{
		fetchNextInstructionOpcode();
		push(currentReceiver());
	}

	void interpretPushTrue()
	{
		fetchNextInstructionOpcode();
		push(trueOop());
	}

	void interpretPushFalse()
	{
		fetchNextInstructionOpcode();
		push(falseOop());
	}

	void interpretPushNil()
	{
		fetchNextInstructionOpcode();
		push(nilOop());
	}

	void interpretPushZero()
	{
		fetchNextInstructionOpcode();
		push(Oop::encodeSmallInteger(0));
	}

	void interpretPushOne()
	{
		fetchNextInstructionOpcode();
		push(Oop::encodeSmallInteger(1));
	}

	void interpretPushThisContext()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretDuplicateStackTop()
	{
		fetchNextInstructionOpcode();
		push(stackTop());
	}

	void interpretReturnReceiver()
	{
		isReturning = true;
		methodReturnValue = currentReceiver();
	}

	void interpretReturnTrue()
	{
		isReturning = true;
		methodReturnValue = trueOop();
	}

	void interpretReturnFalse()
	{
		isReturning = true;
		methodReturnValue = falseOop();
	}

	void interpretReturnNil()
	{
		isReturning = true;
		methodReturnValue = nilOop();
	}

	void interpretReturnTop()
	{
		isReturning = true;
		methodReturnValue = pop();
	}

	void interpretBlockReturnNil()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretBlockReturnTop()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretNop()
	{
		fetchNextInstructionOpcode();

		// Do nothing
	}

	void interpretPopStackTop()
	{
		fetchNextInstructionOpcode();

		// Pop the element from the stack.
		pop();
	}

	void interpretExtendA()
	{
		extendA = extendA*256 + fetchByte();
		fetchNextInstructionOpcode();
	}

	void interpretExtendB()
	{
		extendB = extendB*256 + fetchSByte();
		fetchNextInstructionOpcode();
	}

	void interpretPushReceiverVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushLiteralVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushLiteral()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushTemporary()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushNTemps()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushInteger()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushCharacter()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPushArrayWithElements()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretSend()
	{
		// Fetch the data.
		int data = fetchByte();
		fetchNextInstructionOpcode();

		// Decode the literal index and argument index
		auto argumentCount = (data & BytecodeSet::Send_ArgumentCountMask) + extendB * BytecodeSet::Send_ArgumentCountCount;
		auto literalIndex = ((data >> BytecodeSet::Send_LiteralIndexShift) & BytecodeSet::Send_LiteralIndexMask) + extendA * BytecodeSet::Send_LiteralIndexCount;

		// Clear the extension values.
		extendA = 0;
		extendB = 0;

		// Send the message
		sendLiteralIndexArgumentCount(literalIndex, argumentCount);
	}

	void interpretSuperSend()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretTrapOnBehavior()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretJump()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretJumpOnTrue()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretJumpOnFalse()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreReceiverVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreLiteralVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretPopStoreTemporalVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretStoreReceiverVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretStoreLiteralVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

	void interpretStoreTemporalVariable()
	{
		LODTALK_UNIMPLEMENTED();
	}

};

StackInterpreter::StackInterpreter()
{
}

StackInterpreter::~StackInterpreter()
{
}

Oop StackInterpreter::interpretMethod(CompiledMethod *newMethod, Oop receiver, int argumentCount, Oop *arguments)
{
	// Check the argument count
	if(argumentCount != (int)newMethod->getArgumentCount())
		error("invalid suplied argument count.");

	isReturning = false;

	// Store the receiver and the argument data
	this->methodReceiver = receiver;
	this->arguments = arguments;

	// Fetch the method data
	fetchMethodData(newMethod);
	instructionPointer = firstInstructionPointer;

	// Fetch the first instruction opcode
	fetchNextInstructionOpcode();

	// Reset the extensions values
	extendA = 0;
	extendB = 0;

	while(!isReturning)
	{
		currentOpcode = nextOpcode;

		switch(currentOpcode)
		{
#define MAKE_RANGE_CASE(i, from, to, name) \
case i + from:\
	interpret ## name(); \
	break;

#define SISTAV1_INSTRUCTION_RANGE(name, rangeFirst, rangeLast) \
	LODTALK_EVAL(LODTALK_FROM_TO_INCLUSIVE(rangeFirst, rangeLast, MAKE_RANGE_CASE, name))

#define SISTAV1_INSTRUCTION(name, opcode) \
case opcode:\
	interpret ## name(); \
	break;
#include "SistaV1BytecodeSet.inc"
#undef SISTAV1_INSTRUCTION_RANGE
#undef SISTAV1_INSTRUCTION
#undef MAKE_RANGE_CASE

		default:
			errorFormat("unsupported bytecode %d", currentOpcode);
		}
	}

	return methodReturnValue;
}

Oop interpretCompiledMethod(CompiledMethod *method, Oop receiver, int argumentCount, Oop *arguments)
{
	StackInterpreter interpreter;
	return interpreter.interpretMethod(method, receiver, argumentCount, arguments);
}

} // End of namespace Lodtalk
<|endoftext|>


<commit_before>/* Siconos-Kernel, Copyright INRIA 2005-2010.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY, siconos-team@lists.gforge.inria.fr
*/

#include "BulletDS.hpp"

#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionShapes/btCylinderShape.h>
#include <LinearMath/btVector3.h>

BulletDS::BulletDS(const BroadphaseNativeTypes& shape_type,
                   const SP::SimpleVector& shapeParams,
                   SP::SiconosVector position,
                   SP::SiconosVector velocity,
                   const double& mass) : NewtonEulerDS()
{
  _mass = mass;

  btVector3 inertia;

  SP::SimpleMatrix inertiaMatrix(new SimpleMatrix(3, 3));
  inertiaMatrix->eye();

  internalInit(position, velocity, mass, inertiaMatrix);

  _collisionObject.reset(new btCollisionObject());
  btMatrix3x3 basis;
  basis.setIdentity();
  _collisionObject->getWorldTransform().setBasis(basis);

  _collisionObject->setUserPointer(this);
  _collisionObject->setCollisionFlags(_collisionObject->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);

  switch (shape_type)
  {
  case BOX_SHAPE_PROXYTYPE :
  {
    btVector3 params;
    params[0] = (*shapeParams)(0);
    params[1] = (*shapeParams)(1);
    params[2] = (*shapeParams)(2);
    _collisionShape.reset(new btBoxShape(params));
    _collisionShape->calculateLocalInertia(mass, inertia);
    (*_I)(0, 0) = inertia[0];
    (*_I)(1, 1) = inertia[1];
    (*_I)(2, 2) = inertia[2];

    _collisionObject->setCollisionShape(&*_collisionShape);

    break;
  };
  case CYLINDER_SHAPE_PROXYTYPE :
  {
    btVector3 params;
    params[0] = (*shapeParams)(0);
    params[1] = (*shapeParams)(1);
    params[2] = (*shapeParams)(2);
    _collisionShape.reset(new btCylinderShape(params));
    _collisionShape->calculateLocalInertia(mass, inertia);
    (*_I)(0, 0) = inertia[0];
    (*_I)(1, 1) = inertia[1];
    (*_I)(2, 2) = inertia[2];

    _collisionObject->setCollisionShape(&*_collisionShape);

    break;
  };
  // ...

  }
}

<commit_msg>capsule shape<commit_after>/* Siconos-Kernel, Copyright INRIA 2005-2010.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY, siconos-team@lists.gforge.inria.fr
*/

#include "BulletDS.hpp"

#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionShapes/btCylinderShape.h>
#include <BulletCollision/CollisionShapes/btCapsuleShape.h>
#include <LinearMath/btVector3.h>

BulletDS::BulletDS(const BroadphaseNativeTypes& shape_type,
                   const SP::SimpleVector& shapeParams,
                   SP::SiconosVector position,
                   SP::SiconosVector velocity,
                   const double& mass) : NewtonEulerDS()
{
  _mass = mass;

  btVector3 inertia;

  SP::SimpleMatrix inertiaMatrix(new SimpleMatrix(3, 3));
  inertiaMatrix->eye();

  internalInit(position, velocity, mass, inertiaMatrix);

  _collisionObject.reset(new btCollisionObject());
  btMatrix3x3 basis;
  basis.setIdentity();
  _collisionObject->getWorldTransform().setBasis(basis);

  _collisionObject->setUserPointer(this);
  _collisionObject->setCollisionFlags(_collisionObject->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);

  switch (shape_type)
  {
  case BOX_SHAPE_PROXYTYPE :
  {
    btVector3 params;
    params[0] = (*shapeParams)(0);
    params[1] = (*shapeParams)(1);
    params[2] = (*shapeParams)(2);
    _collisionShape.reset(new btBoxShape(params));
    _collisionShape->calculateLocalInertia(mass, inertia);
    (*_I)(0, 0) = inertia[0];
    (*_I)(1, 1) = inertia[1];
    (*_I)(2, 2) = inertia[2];

    _collisionObject->setCollisionShape(&*_collisionShape);

    break;
  };
  case CYLINDER_SHAPE_PROXYTYPE :
  {
    btVector3 params;
    params[0] = (*shapeParams)(0);
    params[1] = (*shapeParams)(1);
    params[2] = (*shapeParams)(2);
    _collisionShape.reset(new btCylinderShape(params));
    _collisionShape->calculateLocalInertia(mass, inertia);
    (*_I)(0, 0) = inertia[0];
    (*_I)(1, 1) = inertia[1];
    (*_I)(2, 2) = inertia[2];

    _collisionObject->setCollisionShape(&*_collisionShape);

    break;
  };

  case CAPSULE_SHAPE_PROXYTYPE :
  {
    btScalar radius;
    btScalar height;
    radius = (*shapeParams)(0);
    height = (*shapeParams)(1);
    _collisionShape.reset(new btCapsuleShape(radius, height));
    _collisionShape->calculateLocalInertia(mass, inertia);
    (*_I)(0, 0) = inertia[0];
    (*_I)(1, 1) = inertia[1];
    (*_I)(2, 2) = inertia[2];

    _collisionObject->setCollisionShape(&*_collisionShape);

    break;
  };
  // ...

  }
}

<|endoftext|>