<commit_before>#include "SpriteBatch.h"
namespace xixEngine
{
	/*
	SpriteBatch::SpriteBatch() :
		_vbo(0),
		_vao(0),
		_sortType(GlyphSortType::TEXTURE)
	{
	}

	SpriteBatch::~SpriteBatch()
	{
		for (int i = 0; i < _glyphs.size(); i++)
		{
			delete _glyphs[i];
		}
	}

	void SpriteBatch::createRenderBatches()
	{
		int cv = 0; //current vertex
		int offset = 0; //current offset
		//reserve some memory while keeping actual size:
		std::vector<VertexData> verticesDataArray;
		verticesDataArray.resize(_glyphs.size() * 6); //6 vertices per Glyph: allocate all the mem needed
		//if it's a new texture, create a new batch
		if (_glyphs.empty()) {
			return; //no batches to create
		}
		//Create the first batch for the first glyph
		//
		//RenderBatch newBatch(0,
		//	6, // just a plane
		//	_glyphs[0]->texture
		//);
		//_renderBatches.push_back(); //is worst because it does extra checkings
		//

		//Better than a copy, this method does not need a man in the middle ,less mem consumption:
		_renderBatches.emplace_back(0, 6, _glyphs[0]->texture);
		//add the 6 vertices in order:

		verticesDataArray[cv++] = _glyphs[0]->topLeft;
		verticesDataArray[cv++] = _glyphs[0]->bottomLeft;
		verticesDataArray[cv++] = _glyphs[0]->bottomRight;
		verticesDataArray[cv++] = _glyphs[0]->bottomRight; //again to connect
		verticesDataArray[cv++] = _glyphs[0]->topRight;
		verticesDataArray[cv++] = _glyphs[0]->topLeft;

		//cg = current Glyph
		for (int cg = 1; cg < _glyphs.size(); cg++)
		{
			//Check the previous texture:
			if (_glyphs[cg]->texture != _glyphs[cg - 1]->texture)
			{
				_renderBatches.emplace_back(offset, 6, _glyphs[cg]->texture);
			}
			else {
				_renderBatches.back().numVertices += 6;
			}
			//add the 6 vertices in order:
			verticesDataArray[cv++] = _glyphs[cg]->topLeft;
			verticesDataArray[cv++] = _glyphs[cg]->bottomLeft;
			verticesDataArray[cv++] = _glyphs[cg]->bottomRight;
			verticesDataArray[cv++] = _glyphs[cg]->bottomRight;
			verticesDataArray[cv++] = _glyphs[cg]->topRight;
			verticesDataArray[cv++] = _glyphs[cg]->topLeft;
			offset += 6;
		}
		//bind
		glBindBuffer(GL_ARRAY_BUFFER, _vbo);
		//upload can be done as usual:

		//glBufferData(
		//	GL_ARRAY_BUFFER,
		//	verticesDataArray.size()*sizeof(VertexData),
		//	verticesDataArray.data(), //address for the first element
		//	GL_STREAM_DRAW //binding dynamic allow changes
		//);

		//or upload the buffer with a trick make it faster, passing nullptr instead orphan the buffer first and upload later
		glBufferData(GL_ARRAY_BUFFER, verticesDataArray.size()*sizeof(VertexData), nullptr, GL_DYNAMIC_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, verticesDataArray.size()*sizeof(VertexData), verticesDataArray.data());
		//unbind
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	//smart function to avoid doing buffer assignments each draw
	void SpriteBatch::createVertexArray()
	{
		if (_vao == 0)
		{
			glGenVertexArrays(1, &_vao);
		}
		//now if anything changes inside it will be stored
		glBindVertexArray(_vao);

		if (_vbo == 0)
		{
			glGenBuffers(1, &_vbo);
		}
		//anytime we rebind vertexatt it will bind this buffer forward so we dont need to do this again in the draw function
		glBindBuffer(GL_ARRAY_BUFFER, _vbo);

		//1 array of positions in the 0 index
		glEnableVertexAttribArray(0);
		//same for others
		glEnableVertexAttribArray(1);
		glEnableVertexAttribArray(2);
		//where is the vertex positions array? maybe we only want a part of the array
		glVertexAttribPointer(
			0, //index: this is the position of the start of the struct VertexData
			2, //the entire array,of 2 floats
			GL_FLOAT,
			GL_FALSE,  //don't normalize
			sizeof(VertexData), //stride?
			(GLvoid *)offsetof(VertexData, position) //8 bytes after we have the color
													 //(GLvoid *)0 //no pointer right now
			);
		//Now the vertex color part of the array
		glVertexAttribPointer(
			1, //index: this is the position of the start of the struct VertexData
			4, //the entire array,of 4 bytes
			GL_UNSIGNED_BYTE, //type for the color
			GL_TRUE,  //normalize colors from 0-255 to 0-1
			sizeof(VertexData), //stride
			(GLvoid *)offsetof(VertexData, color)
			);
		//Finally, the UV part
		glVertexAttribPointer(
			2, //second attr
			2, // 2 floats
			GL_FLOAT, //type for the coordinates (float)
			GL_FALSE,  //no norm
			sizeof(VertexData), //stride
			(GLvoid *)offsetof(VertexData, uv)
			);
		//unbind
		glBindVertexArray(0);

	}

	//Initialize
	void SpriteBatch::init()
	{
		createVertexArray();

	}

	bool SpriteBatch::compareFrontToBack(Glyph* a, Glyph* b)
	{
		return a->depth < b->depth;
	}

	bool SpriteBatch::compareBackToFront(Glyph* a, Glyph* b)
	{
		return a->depth > b->depth;
	}

	bool SpriteBatch::compareTexture(Glyph* a, Glyph* b)
	{
		return a->texture < b->texture; //using ascii
	}

	void SpriteBatch::sortGlyphs()
	{
		//from algorithm,safer for us -> std::stable_sort
		switch (_sortType)
		{
		case GlyphSortType::BACK_TO_FRONT:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareBackToFront);
			break;
		case GlyphSortType::FRONT_TO_BACK:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareFrontToBack);
			break;
		case GlyphSortType::TEXTURE:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareTexture);
			break;
		}
	}

	//Set up: sort the glyph and generate batches
	void SpriteBatch::begin(GlyphSortType sortType //= GlyphSortType::TEXTURE default
		)
	{
		//how are we going to sort? with our enum sort op
		_sortType = sortType;
		//any time we call begin, we clean vectors (they add a lot of vertexdata!)
		_renderBatches.clear(); //just change the size but not free the mem
		for (int i = 0; i < _glyphs.size(); i++) {
			delete _glyphs[i];
		}
		_glyphs.clear();
	}

	//Add them to the batch
	void SpriteBatch::draw(const glm::vec4& destintyRectangle, const glm::vec4& uvRectangle, GLuint texture, const float depth, const Color& color)
	{
		Glyph* newGlyph = new Glyph();
		newGlyph->texture = texture;
		newGlyph->depth = depth;
		//1
		newGlyph->topLeft.color = color;
		newGlyph->topLeft.setPosition(
			destintyRectangle.x,
			destintyRectangle.y + destintyRectangle.w // or destintyRectangle[3] --> add the height
			);
		newGlyph->topLeft.setUV(uvRectangle.x, uvRectangle.y + uvRectangle.w);
		//2
		newGlyph->bottomLeft.color = color;
		newGlyph->bottomLeft.setPosition(destintyRectangle.x, destintyRectangle.y);
		newGlyph->bottomLeft.setUV(uvRectangle.x, uvRectangle.y);
		//3
		newGlyph->bottomRight.color = color;
		newGlyph->bottomRight.setPosition(destintyRectangle.x
			+ destintyRectangle.z // or [3] -> add the width
			, destintyRectangle.y
			);
		newGlyph->bottomRight.setUV(uvRectangle.x + uvRectangle.z, uvRectangle.y);
		//4
		newGlyph->topRight.color = color;
		newGlyph->topRight.setPosition(destintyRectangle.x + destintyRectangle.z, destintyRectangle.y + destintyRectangle.w);
		newGlyph->topRight.setUV(uvRectangle.x + uvRectangle.z, uvRectangle.y + uvRectangle.w);
		//add it
		_glyphs.push_back(newGlyph);
	}
	//
	void SpriteBatch::end()
	{
		sortGlyphs();
		createRenderBatches();
	}

	//Render to the screen
	void SpriteBatch::renderBatch()
	{
		glBindVertexArray(_vao);

		for (int i = 0; i < _renderBatches.size(); i++)
		{
			glBindTexture(GL_TEXTURE_2D, _renderBatches[i].texture);
			//draw
			glDrawArrays(GL_TRIANGLES, _renderBatches[i].offset, _renderBatches[i].numVertices);

		}

		glBindVertexArray(0);
	}*/
	SpriteBatch::SpriteBatch() : _vbo(0), _vao(0)
	{
	}

	SpriteBatch::~SpriteBatch()
	{
	}

	void SpriteBatch::init() {
		createVertexArray();
	}

	void SpriteBatch::begin(GlyphSortType sortType /* GlyphSortType::TEXTURE */) {
		_sortType = sortType;
		_renderBatches.clear();
		// Have to delete any glyphs that remain so we don't have memory leaks!
		for (int i = 0; i < _glyphs.size(); i++) {
			delete _glyphs[i];
		}
		_glyphs.clear();
	}

	void SpriteBatch::end() {
		sortGlyphs();
		createRenderBatches();
	}

	void SpriteBatch::draw(const glm::vec4& destRect, const glm::vec4& uvRect, GLuint texture, float depth, const Color& color) {

		Glyph* newGlyph = new Glyph;
		newGlyph->texture = texture;
		newGlyph->depth = depth;

		newGlyph->topLeft.color = color;
		newGlyph->topLeft.setPosition(destRect.x, destRect.y + destRect.w);
		newGlyph->topLeft.setUV(uvRect.x, uvRect.y + uvRect.w);

		newGlyph->bottomLeft.color = color;
		newGlyph->bottomLeft.setPosition(destRect.x, destRect.y);
		newGlyph->bottomLeft.setUV(uvRect.x, uvRect.y);

		newGlyph->bottomRight.color = color;
		newGlyph->bottomRight.setPosition(destRect.x + destRect.z, destRect.y);
		newGlyph->bottomRight.setUV(uvRect.x + uvRect.z, uvRect.y);

		newGlyph->topRight.color = color;
		newGlyph->topRight.setPosition(destRect.x + destRect.z, destRect.y + destRect.w);
		newGlyph->topRight.setUV(uvRect.x + uvRect.z, uvRect.y + uvRect.w);

		_glyphs.push_back(newGlyph);
	}

	void SpriteBatch::renderBatch() {

		// Bind our VAO. This sets up the opengl state we need, including the
		// vertex attribute pointers and it binds the VBO
		glBindVertexArray(_vao);

		for (int i = 0; i < _renderBatches.size(); i++) {
			glBindTexture(GL_TEXTURE_2D, _renderBatches[i].texture);

			glDrawArrays(GL_TRIANGLES, _renderBatches[i].offset, _renderBatches[i].numVertices);
		}

		glBindVertexArray(0);
	}

	void SpriteBatch::createRenderBatches() {
		// This will store all the vertices that we need to upload
		std::vector <VertexData> vertices;
		// Resize the buffer to the exact size we need so we can treat
		// it like an array
		vertices.resize(_glyphs.size() * 6);

		if (_glyphs.empty()) {
			return;
		}

		int offset = 0; // current offset
		int cv = 0; // current vertex

					//Add the first batch
		_renderBatches.emplace_back(offset, 6, _glyphs[0]->texture);
		vertices[cv++] = _glyphs[0]->topLeft;
		vertices[cv++] = _glyphs[0]->bottomLeft;
		vertices[cv++] = _glyphs[0]->bottomRight;
		vertices[cv++] = _glyphs[0]->bottomRight;
		vertices[cv++] = _glyphs[0]->topRight;
		vertices[cv++] = _glyphs[0]->topLeft;
		offset += 6;

		//Add all the rest of the glyphs
		for (int cg = 1; cg < _glyphs.size(); cg++) {

			// Check if this glyph can be part of the current batch
			if (_glyphs[cg]->texture != _glyphs[cg - 1]->texture) {
				// Make a new batch
				_renderBatches.emplace_back(offset, 6, _glyphs[cg]->texture);
			}
			else {
				// If its part of the current batch, just increase numVertices
				_renderBatches.back().numVertices += 6;
			}
			vertices[cv++] = _glyphs[cg]->topLeft;
			vertices[cv++] = _glyphs[cg]->bottomLeft;
			vertices[cv++] = _glyphs[cg]->bottomRight;
			vertices[cv++] = _glyphs[cg]->bottomRight;
			vertices[cv++] = _glyphs[cg]->topRight;
			vertices[cv++] = _glyphs[cg]->topLeft;
			offset += 6;
		}

		// Bind our VBO
		glBindBuffer(GL_ARRAY_BUFFER, _vbo);
		// Orphan the buffer (for speed)
		glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VertexData), nullptr, GL_DYNAMIC_DRAW);
		// Upload the data
		glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(VertexData), vertices.data());

		// Unbind the VBO
		glBindBuffer(GL_ARRAY_BUFFER, 0);

	}

	void SpriteBatch::createVertexArray() {

		// Generate the VAO if it isn't already generated
		if (_vao == 0) {
			glGenVertexArrays(1, &_vao);
		}

		// Bind the VAO. All subsequent opengl calls will modify it's state.
		glBindVertexArray(_vao);

		//G enerate the VBO if it isn't already generated
		if (_vbo == 0) {
			glGenBuffers(1, &_vbo);
		}
		glBindBuffer(GL_ARRAY_BUFFER, _vbo);

		//Tell opengl what attribute arrays we need
		glEnableVertexAttribArray(0);
		glEnableVertexAttribArray(1);
		glEnableVertexAttribArray(2);

		//This is the position attribute pointer
		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, position));
		//This is the color attribute pointer
		glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VertexData), (void*)offsetof(VertexData, color));
		//This is the UV attribute pointer
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, uv));

		glBindVertexArray(0);

	}

	void SpriteBatch::sortGlyphs() {

		switch (_sortType) {
		case GlyphSortType::BACK_TO_FRONT:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareBackToFront);
			break;
		case GlyphSortType::FRONT_TO_BACK:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareFrontToBack);
			break;
		case GlyphSortType::TEXTURE:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareTexture);
			break;
		}
	}

	bool SpriteBatch::compareFrontToBack(Glyph* a, Glyph* b) {
		return (a->depth < b->depth);
	}

	bool SpriteBatch::compareBackToFront(Glyph* a, Glyph* b) {
		return (a->depth > b->depth);
	}

	bool SpriteBatch::compareTexture(Glyph* a, Glyph* b) {
		return (a->texture < b->texture);
	}

}
<commit_msg>Fixed offset bug in spritebatch<commit_after>#include "SpriteBatch.h"
namespace xixEngine
{
	SpriteBatch::SpriteBatch() :
		_vbo(0),
		_vao(0),
		_sortType(GlyphSortType::TEXTURE)
	{
	}

	SpriteBatch::~SpriteBatch()
	{
		for (int i = 0; i < _glyphs.size(); i++)
		{
			delete _glyphs[i];
		}
	}

	void SpriteBatch::createRenderBatches()
	{
		int cv = 0; //current vertex
		int offset = 0; //current offset
						//reserve some memory while keeping actual size:
		std::vector<VertexData> verticesDataArray;
		verticesDataArray.resize(_glyphs.size() * 6); //6 vertices per Glyph: allocate all the mem needed
													  //if it's a new texture, create a new batch
		if (_glyphs.empty()) {
			return; //no batches to create
		}
		//Create the first batch for the first glyph
		//
		//RenderBatch newBatch(0,
		//	6, // just a plane
		//	_glyphs[0]->texture
		//);
		//_renderBatches.push_back(); //is worst because it does extra checkings
		//

		//Better than a copy, this method does not need a man in the middle ,less mem consumption:
		_renderBatches.emplace_back(0, 6, _glyphs[0]->texture);
		//add the 6 vertices in order:

		verticesDataArray[cv++] = _glyphs[0]->topLeft;
		verticesDataArray[cv++] = _glyphs[0]->bottomLeft;
		verticesDataArray[cv++] = _glyphs[0]->bottomRight;
		verticesDataArray[cv++] = _glyphs[0]->bottomRight; //again to connect
		verticesDataArray[cv++] = _glyphs[0]->topRight;
		verticesDataArray[cv++] = _glyphs[0]->topLeft;
		offset += 6;

		//cg = current Glyph
		for (int cg = 1; cg < _glyphs.size(); cg++)
		{
			//Check the previous texture:
			if (_glyphs[cg]->texture != _glyphs[cg - 1]->texture)
			{
				_renderBatches.emplace_back(offset, 6, _glyphs[cg]->texture);
			}
			else {
				_renderBatches.back().numVertices += 6;
			}
			//add the 6 vertices in order:
			verticesDataArray[cv++] = _glyphs[cg]->topLeft;
			verticesDataArray[cv++] = _glyphs[cg]->bottomLeft;
			verticesDataArray[cv++] = _glyphs[cg]->bottomRight;
			verticesDataArray[cv++] = _glyphs[cg]->bottomRight;
			verticesDataArray[cv++] = _glyphs[cg]->topRight;
			verticesDataArray[cv++] = _glyphs[cg]->topLeft;
			offset += 6;
		}
		//bind
		glBindBuffer(GL_ARRAY_BUFFER, _vbo);
		//upload can be done as usual:

		//glBufferData(
		//	GL_ARRAY_BUFFER,
		//	verticesDataArray.size()*sizeof(VertexData),
		//	verticesDataArray.data(), //address for the first element
		//	GL_STREAM_DRAW //binding dynamic allow changes
		//);

		//or upload the buffer with a trick make it faster, passing nullptr instead orphan the buffer first and upload later
		glBufferData(GL_ARRAY_BUFFER, verticesDataArray.size()*sizeof(VertexData), nullptr, GL_DYNAMIC_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, verticesDataArray.size()*sizeof(VertexData), verticesDataArray.data());
		//unbind
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	//smart function to avoid doing buffer assignments each draw
	void SpriteBatch::createVertexArray()
	{
		if (_vao == 0)
		{
			glGenVertexArrays(1, &_vao);
		}
		//now if anything changes inside it will be stored
		glBindVertexArray(_vao);

		if (_vbo == 0)
		{
			glGenBuffers(1, &_vbo);
		}
		//anytime we rebind vertexatt it will bind this buffer forward so we dont need to do this again in the draw function
		glBindBuffer(GL_ARRAY_BUFFER, _vbo);

		//1 array of positions in the 0 index
		glEnableVertexAttribArray(0);
		//same for others
		glEnableVertexAttribArray(1);
		glEnableVertexAttribArray(2);
		//where is the vertex positions array? maybe we only want a part of the array
		glVertexAttribPointer(
			0, //index: this is the position of the start of the struct VertexData
			2, //the entire array,of 2 floats
			GL_FLOAT,
			GL_FALSE,  //don't normalize
			sizeof(VertexData), //stride?
			(GLvoid *)offsetof(VertexData, position) //8 bytes after we have the color
													 //(GLvoid *)0 //no pointer right now
			);
		//Now the vertex color part of the array
		glVertexAttribPointer(
			1, //index: this is the position of the start of the struct VertexData
			4, //the entire array,of 4 bytes
			GL_UNSIGNED_BYTE, //type for the color
			GL_TRUE,  //normalize colors from 0-255 to 0-1
			sizeof(VertexData), //stride
			(GLvoid *)offsetof(VertexData, color)
			);
		//Finally, the UV part
		glVertexAttribPointer(
			2, //second attr
			2, // 2 floats
			GL_FLOAT, //type for the coordinates (float)
			GL_FALSE,  //no norm
			sizeof(VertexData), //stride
			(GLvoid *)offsetof(VertexData, uv)
			);
		//unbind
		glBindVertexArray(0);

	}

	//Initialize
	void SpriteBatch::init()
	{
		createVertexArray();

	}

	bool SpriteBatch::compareFrontToBack(Glyph* a, Glyph* b)
	{
		return a->depth < b->depth;
	}

	bool SpriteBatch::compareBackToFront(Glyph* a, Glyph* b)
	{
		return a->depth > b->depth;
	}

	bool SpriteBatch::compareTexture(Glyph* a, Glyph* b)
	{
		return a->texture < b->texture; //using ascii
	}

	void SpriteBatch::sortGlyphs()
	{
		//from algorithm,safer for us -> std::stable_sort
		switch (_sortType)
		{
		case GlyphSortType::BACK_TO_FRONT:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareBackToFront);
			break;
		case GlyphSortType::FRONT_TO_BACK:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareFrontToBack);
			break;
		case GlyphSortType::TEXTURE:
			std::stable_sort(_glyphs.begin(), _glyphs.end(), compareTexture);
			break;
		}
	}

	//Set up: sort the glyph and generate batches
	void SpriteBatch::begin(GlyphSortType sortType //= GlyphSortType::TEXTURE default
		)
	{
		//how are we going to sort? with our enum sort op
		_sortType = sortType;
		//any time we call begin, we clean vectors (they add a lot of vertexdata!)
		_renderBatches.clear(); //just change the size but not free the mem
		for (int i = 0; i < _glyphs.size(); i++) {
			delete _glyphs[i];
		}
		_glyphs.clear();
	}

	//Add them to the batch
	void SpriteBatch::draw(const glm::vec4& destintyRectangle, const glm::vec4& uvRectangle, GLuint texture, const float depth, const Color& color)
	{
		Glyph* newGlyph = new Glyph();
		newGlyph->texture = texture;
		newGlyph->depth = depth;
		//1
		newGlyph->topLeft.color = color;
		newGlyph->topLeft.setPosition(
			destintyRectangle.x,
			destintyRectangle.y + destintyRectangle.w // or destintyRectangle[3] --> add the height
			);
		newGlyph->topLeft.setUV(uvRectangle.x, uvRectangle.y + uvRectangle.w);
		//2
		newGlyph->bottomLeft.color = color;
		newGlyph->bottomLeft.setPosition(destintyRectangle.x, destintyRectangle.y);
		newGlyph->bottomLeft.setUV(uvRectangle.x, uvRectangle.y);
		//3
		newGlyph->bottomRight.color = color;
		newGlyph->bottomRight.setPosition(destintyRectangle.x
			+ destintyRectangle.z // or [3] -> add the width
			, destintyRectangle.y
			);
		newGlyph->bottomRight.setUV(uvRectangle.x + uvRectangle.z, uvRectangle.y);
		//4
		newGlyph->topRight.color = color;
		newGlyph->topRight.setPosition(destintyRectangle.x + destintyRectangle.z, destintyRectangle.y + destintyRectangle.w);
		newGlyph->topRight.setUV(uvRectangle.x + uvRectangle.z, uvRectangle.y + uvRectangle.w);
		//add it
		_glyphs.push_back(newGlyph);
	}
	//
	void SpriteBatch::end()
	{
		sortGlyphs();
		createRenderBatches();
	}

	//Render to the screen
	void SpriteBatch::renderBatch()
	{
		glBindVertexArray(_vao);

		for (int i = 0; i < _renderBatches.size(); i++)
		{
			glBindTexture(GL_TEXTURE_2D, _renderBatches[i].texture);
			//draw
			glDrawArrays(GL_TRIANGLES, _renderBatches[i].offset, _renderBatches[i].numVertices);

		}

		glBindVertexArray(0);
	}

}
<|endoftext|>


<commit_before>#include "Renderer.hpp"

#define QUOTE(x) #x
#define STR(x) QUOTE(x)

Renderer::Renderer(CubemapSource* cubemapSource)
    :
    al::OmniApp("AlloPlayer", false, 2048), resizeCtx(nullptr), cubemapSource(cubemapSource), newCubemap(false), currentCubemap(nullptr)
{
    nav().smooth(0.8);

    // set up cube
    cube.color(1,1,1,1);
    cube.primitive(al::Graphics::TRIANGLES);
    addCube(cube);
    for (int i = 0; i < cube.vertices().size(); ++i) {
        float f = (float)i / cube.vertices().size();
        cube.color(al::Color(al::HSV(f, 1 - f, 1), 1));
    }
    cube.generateNormals();

    // set up sphere
    sphere.primitive(al::Graphics::TRIANGLES);
    addSphere(sphere, 1.0, 32, 32);
    for (int i = 0; i < sphere.vertices().size(); ++i) {
        float f = (float)i / sphere.vertices().size();
        sphere.color(al::Color(al::HSV(f, 1 - f, 1), 1));
    }
    sphere.generateNormals();

    // set up light
    light.ambient(al::Color(0.4, 0.4, 0.4, 1.0));
    light.pos(5, 5, 5);

    for (int i = 0; i < 1; i++)
    {
        cubemapPool.push(nullptr);
    }

    std::function<StereoCubemap* (CubemapSource*, StereoCubemap*)> callback = boost::bind(&Renderer::onNextCubemap,
                                                                                this,
                                                                                _1,
                                                                                _2);
    cubemapSource->setOnNextCubemap(callback);
}

Renderer::~Renderer()
{
}

bool Renderer::onCreate()
{
    std::cout << "OpenGL version: " << glGetString(GL_VERSION) << ", GLSL version " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
    return OmniApp::onCreate();
}

bool Renderer::onFrame()
{
    now = al::MainLoop::now();

    bool result;
    {
        result = OmniApp::onFrame();
        newCubemap = false;
    }
    if (onDisplayedFrame) onDisplayedFrame(this);
    return result;
}

StereoCubemap* Renderer::onNextCubemap(CubemapSource* source, StereoCubemap* cubemap)
{
    StereoCubemap* oldCubemap;
    if (!cubemapPool.try_pop(oldCubemap))
    {
        if (cubemapPool.closed())
        {
            return nullptr;
        }
        else
        {
            return cubemap;
        }
    }

    cubemapBuffer.push(cubemap);
    return oldCubemap;
}

void Renderer::onDraw(al::Graphics& gl)
{
    int faceIndex = mOmni.face();
    int eyeIndex = (mOmni.eye() <= 0.0f) ? 0 : 1;

    {
        StereoCubemap* cubemap;
        if (!cubemapBuffer.try_pop(cubemap))
        {
            if (cubemapBuffer.closed())
            {
                return;
            }
            else
            {
                cubemap = currentCubemap;
            }
        }

        currentCubemap = cubemap;

        // render cubemap
        if (cubemap && cubemap->getEyesCount() > eyeIndex)
        {
            Cubemap* eye = cubemap->getEye(eyeIndex);
            if (eye->getFacesCount() > faceIndex)
            {
                // Choose right face for flipping
                CubemapFace* face;
                if (faceIndex == 0)
                {
                    face = eye->getFace(1);
                }
                else if (faceIndex == 1)
                {
                    face = eye->getFace(0);
                }
                else
                {
                    face = eye->getFace(faceIndex);
                }

                glUseProgram(0);
                glDepthMask(GL_FALSE);

                // flip face
                glPixelZoom(-1.0, 1.0);
                glRasterPos2d(1.0, -1.0);

                // draw the background
                glDrawPixels(face->getContent()->getWidth(),
                             face->getContent()->getHeight(),
                             GL_RGBA,
                             GL_UNSIGNED_BYTE,
                             (GLvoid*)face->getContent()->getPixels());

                glDepthMask(GL_TRUE);

                if(newCubemap)
                {
                    if (onDisplayedCubemapFace) onDisplayedCubemapFace(this, faceIndex + eyeIndex * Cubemap::MAX_FACES_COUNT);
                }
            }
        }

        cubemapPool.push(cubemap);
    }

    light();
    mShader.begin();
    mOmni.uniforms(mShader);

    gl.pushMatrix();

    //gl.draw(cube);

    gl.pushMatrix();
    // rotate over time:
    gl.rotate(now*30., 0.707, 0.707, 0.);
    gl.translate(1., 1., 1.);
    //gl.draw(sphere);
    gl.popMatrix();

    gl.popMatrix();

    mShader.end();
}

void Renderer::onAnimate(al_sec dt)
{
    pose = nav();
}

void Renderer::onMessage(al::osc::Message& m)
{
    OmniApp::onMessage(m);
}

bool Renderer::onKeyDown(const al::Keyboard& k)
{
    return true;
}

void Renderer::setOnDisplayedFrame(std::function<void (Renderer*)>& callback)
{
    onDisplayedFrame = callback;
}

void Renderer::setOnDisplayedCubemapFace(std::function<void (Renderer*, int)>& callback)
{
    onDisplayedCubemapFace = callback;
}
<commit_msg>Textures in AlloPlayer<commit_after>#include "Renderer.hpp"

Renderer::Renderer(CubemapSource* cubemapSource)
    :
    al::OmniApp("AlloPlayer", false, 2048), resizeCtx(nullptr), cubemapSource(cubemapSource), newCubemap(false), currentCubemap(nullptr)
{
    nav().smooth(0.8);

    for (int i = 0; i < 1; i++)
    {
        cubemapPool.push(nullptr);
    }

    std::function<StereoCubemap* (CubemapSource*, StereoCubemap*)> callback = boost::bind(&Renderer::onNextCubemap,
                                                                                this,
                                                                                _1,
                                                                                _2);
    cubemapSource->setOnNextCubemap(callback);
}

Renderer::~Renderer()
{
}

bool Renderer::onCreate()
{
    std::cout << "OpenGL version: " << glGetString(GL_VERSION) << ", GLSL version " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
    return OmniApp::onCreate();
}

bool Renderer::onFrame()
{
    now = al::MainLoop::now();

    bool result;
    {
        result = OmniApp::onFrame();
        newCubemap = false;
    }
    if (onDisplayedFrame) onDisplayedFrame(this);
    return result;
}

StereoCubemap* Renderer::onNextCubemap(CubemapSource* source, StereoCubemap* cubemap)
{
    StereoCubemap* oldCubemap;
    if (!cubemapPool.try_pop(oldCubemap))
    {
        if (cubemapPool.closed())
        {
            return nullptr;
        }
        else
        {
            return cubemap;
        }
    }

    cubemapBuffer.push(cubemap);
    return oldCubemap;
}

void Renderer::onDraw(al::Graphics& gl)
{
    int faceIndex = mOmni.face();
    int eyeIndex = (mOmni.eye() <= 0.0f) ? 0 : 1;

//    static GLuint* pixels = nullptr;
//    static GLuint mTextureID = 0;
//
//    int width = mOmni.resolution(), height = mOmni.resolution();
//
//    if (!pixels)
//    {
//        //Checkerboard pixels
//        const int CHECKERBOARD_PIXEL_COUNT = width * height;
//        pixels = new GLuint[CHECKERBOARD_PIXEL_COUNT];
//
//        //Go through pixels
//        for( int i = 0; i < CHECKERBOARD_PIXEL_COUNT; ++i )
//        {
//            //Get the individual color components
//            GLubyte* colors = (GLubyte*)&pixels[ i ];
//
//            //If the 5th bit of the x and y offsets of the pixel do not match
//            if( i / width & 16 ^ i % height & 16 )
//            {
//                //Set pixel to white
//                colors[ 0 ] = 0xFF;
//                colors[ 1 ] = 0x00;
//                colors[ 2 ] = 0x00;
//                colors[ 3 ] = 0xFF;
//            }
//            else
//            {
//                //Set pixel to red
//                colors[ 0 ] = 0x00;
//                colors[ 1 ] = 0x00;
//                colors[ 2 ] = 0xFF;
//                colors[ 3 ] = 0xFF;
//            }
//        }
//    }
//
//    if (mTextureID == 0)
//    {
//        //Generate texture ID
//        glGenTextures( 1, &mTextureID );
//
//        //Bind texture ID
//        glBindTexture( GL_TEXTURE_2D, mTextureID );
//
//        //Generate texture
//        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels );
//
//        //Set texture parameters
//        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
//        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
//
//        //Unbind texture
//        glBindTexture( GL_TEXTURE_2D, NULL );
//
//        //Check for error
//        GLenum error = glGetError();
//        if( error != GL_NO_ERROR )
//        {
//            printf( "Error loading texture from %p pixels! %s\n", pixels, gluErrorString( error ) );
//            abort();
//        }
//    }

    int width = 512, height = 512;

    static al::Texture tex(width, height, al::Graphics::RGBA, al::Graphics::UBYTE);

    {
        // The default magnification filter is linear
        //tex.filterMag(Texture::NEAREST);

        // Get a pointer to the (client-side) pixel buffer.
        // When we make a read access to the pixels, they are flagged as dirty
        // and get sent to the GPU the next time the texture bound.
        unsigned char * pixels = tex.data<unsigned char>();

        // Loop through the pixels to generate an image
        int Nx = tex.width();
        int Ny = tex.height();
        for(int j=0; j<Ny; ++j)
        {
            for(int i=0; i<Nx; ++i)
            {
                int idx = j*Nx + i;
                int stride = tex.numComponents();
                if ((j / (height / 4)) % 2 ^ (i / (width / 4)) % 2)
                {
                    pixels[idx*stride + 0] = 255;
                    pixels[idx*stride + 1] = 0;
                    pixels[idx*stride + 2] = 0;
                }
                else
                {
                    pixels[idx*stride + 0] = 0;
                    pixels[idx*stride + 1] = 255;
                    pixels[idx*stride + 2] = 0;
                }
            }
        }
    }

    {
        StereoCubemap* cubemap;
        if (!cubemapBuffer.try_pop(cubemap))
        {
            if (cubemapBuffer.closed())
            {
                return;
            }
            else
            {
                cubemap = currentCubemap;
            }
        }

        currentCubemap = cubemap;

        // render cubemap
        if (cubemap && cubemap->getEyesCount() > eyeIndex)
        {
            Cubemap* eye = cubemap->getEye(eyeIndex);
            if (eye->getFacesCount() > faceIndex)
            {
                // Choose right face for flipping
                CubemapFace* face;
                if (faceIndex == 0)
                {
                    face = eye->getFace(1);
                }
                else if (faceIndex == 1)
                {
                    face = eye->getFace(0);
                }
                else
                {
                    face = eye->getFace(faceIndex);
                }
//
//                glUseProgram(0);
//                /*glDepthMask(GL_FALSE);
//
//                // flip face
//                glPixelZoom(-1.0, 1.0);
//                glRasterPos2d(1.0, -1.0);
//
//                // draw the background
//                glDrawPixels(face->getContent()->getWidth(),
//                             face->getContent()->getHeight(),
//                             GL_RGBA,
//                             GL_UNSIGNED_BYTE,
//                             (GLvoid*)face->getContent()->getPixels());
//
//                glDepthMask(GL_TRUE);*/
//
//                glUseProgram(0);
//
//                //Initialize Projection Matrix
//                glMatrixMode( GL_PROJECTION );
//                glLoadIdentity();
//                glOrtho( 0.0, mOmni.resolution(), mOmni.resolution(), 0.0, 1.0, -1.0 );
//
//                //Initialize Modelview Matrix
//                glMatrixMode( GL_MODELVIEW );
//                glLoadIdentity();
//
//                //Initialize clear color
//                glClearColor( 0.f, 0.f, 0.f, 1.f );
//
//                //Enable texturing
//                glEnable( GL_TEXTURE_2D );
//
//                //Check for error
//                GLenum error = glGetError();
//                if( error != GL_NO_ERROR )
//                {
//                    printf( "Error initializing OpenGL! %s\n", gluErrorString( error ) );
//                    abort();
//                }
//
//                //Clear color buffer
//                glClear( GL_COLOR_BUFFER_BIT );
//
//                //Remove any previous transformations
//                glLoadIdentity();
//
//                //Set texture ID
//                glBindTexture( GL_TEXTURE_2D, mTextureID );
//
//                glTexSubImage2D ( GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels );
//
//                //Render textured quad
//                glBegin( GL_QUADS );
//                glTexCoord2f( 0.0f, 0.0f ); glVertex2f( -mOmni.resolution(), -mOmni.resolution() );
//                glTexCoord2f( 1.0f, 0.0f ); glVertex2f( 0.0f, -mOmni.resolution() );
//                glTexCoord2f( 1.0f, 1.0f ); glVertex2f( 0.0f, 0.0f );
//                glTexCoord2f( 0.0f, 1.0f ); glVertex2f( -mOmni.resolution(), 0.0f );
//                glEnd();
//
//                glDisable( GL_TEXTURE_2D );

                al::ShaderProgram::use(0);

                // Borrow a temporary Mesh from Graphics
                al::Mesh& m = gl.mesh();

                m.reset();

                // Generate geometry
                m.primitive(al::Graphics::TRIANGLE_STRIP);
                m.vertex(-1,  1);
                m.vertex(-1, -1);
                m.vertex( 1,  1);
                m.vertex( 1, -1);

                // Add texture coordinates
                m.texCoord(0,1);
                m.texCoord(0,0);
                m.texCoord(1,1);
                m.texCoord(1,0);

                // We must tell the GPU to use the texture when rendering primitives
                tex.bind();
                gl.draw(m);
                tex.unbind();

                if(newCubemap)
                {
                    if (onDisplayedCubemapFace) onDisplayedCubemapFace(this, faceIndex + eyeIndex * Cubemap::MAX_FACES_COUNT);
                }
            }
        }

        cubemapPool.push(cubemap);
    }

}

void Renderer::onAnimate(al_sec dt)
{
    pose = nav();
}

void Renderer::onMessage(al::osc::Message& m)
{
    OmniApp::onMessage(m);
}

bool Renderer::onKeyDown(const al::Keyboard& k)
{
    return true;
}

void Renderer::setOnDisplayedFrame(std::function<void (Renderer*)>& callback)
{
    onDisplayedFrame = callback;
}

void Renderer::setOnDisplayedCubemapFace(std::function<void (Renderer*, int)>& callback)
{
    onDisplayedCubemapFace = callback;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <gtest/gtest.h>

#include "BranchPrefixHoisting.h"
#include "ControlFlow.h"
#include "IRAssembler.h"
#include "IRCode.h"
#include "RedexTest.h"
#include "Show.h"

class BranchPrefixHoistingTest : public RedexTest {};

void test(const std::string& code_str,
          const std::string& expected_str,
          size_t expected_instructions_hoisted) {
  auto code = assembler::ircode_from_string(code_str);
  auto code_ptr = code.get();
  code_ptr->build_cfg(true);
  auto& cfg = code_ptr->cfg();
  std::cerr << "before:" << std::endl << SHOW(cfg);

  int actual_insns_hoisted = BranchPrefixHoistingPass::process_cfg(cfg);

  std::cerr << "after:" << std::endl << SHOW(cfg);
  EXPECT_EQ(expected_instructions_hoisted, actual_insns_hoisted);
  auto expected = assembler::ircode_from_string(expected_str);
  auto expected_ptr = expected.get();
  expected_ptr->build_cfg(true);
  auto& expected_cfg = expected_ptr->cfg();
  std::cerr << "expected:" << std::endl << SHOW(expected_cfg);

  code_ptr->clear_cfg();
  expected_ptr->clear_cfg();

  EXPECT_EQ(assembler::to_s_expr(code.get()),
            assembler::to_s_expr(expected.get()));
}

TEST_F(BranchPrefixHoistingTest, simple_insn_hoisting) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 6)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 7)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (if-eqz v0 :true)
      (const v6 6)
      (goto :end)
      (:true)
      (const v6 7)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 5);
}

TEST_F(BranchPrefixHoistingTest, stop_hoisting_at_side_effect) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v0 7)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v0 7)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (if-eqz v0 :true)
      (const v0 7)
      (goto :end)
      (:true)
      (const v0 7)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 3);
}

TEST_F(BranchPrefixHoistingTest, move_result_hoist_ok) {
  const auto& code_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (if-eqz v0 :true)
      (const v5 42)
      (goto :end)
      (:true)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

TEST_F(BranchPrefixHoistingTest, move_result_no_hoist_diff_dest) {
  const auto& code_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v4)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v4)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 0);
}

TEST_F(BranchPrefixHoistingTest, move_result_no_hoist_on_side_effect) {
  const auto& code_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 0);
}

TEST_F(BranchPrefixHoistingTest, one_block_becomes_empty) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (if-eqz v0 :true)
      (goto :end)
      (:true)
      (const v3 3)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

TEST_F(BranchPrefixHoistingTest, both_blocks_becomes_empty) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (if-eqz v0 :true)
      (goto :end)
      (:true)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 3);
}

TEST_F(BranchPrefixHoistingTest, move_result_wide) {
  const auto& code_str = R"(
    (
      (const-wide v1 2)
      (const-wide v2 10)
      (if-ge v3 v0 :true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (goto :end)
      (:true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const-wide v1 2)
      (const-wide v2 10)
      (if-ge v3 v0 :true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (goto :end)
      (:true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 0);
}

TEST_F(BranchPrefixHoistingTest, branch_goes_to_same_block) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 7)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 7)
      (if-eqz v0 :true)
      (:true)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 6);
}

TEST_F(BranchPrefixHoistingTest, switch_two_same_cases) {
  const auto& code_str = R"(
    (
      (load-param v0)
      (switch v0 (:case1 :case2))
      (:case1 1)
      (const v1 1)
      (const v2 2)
      (goto :end)
      (:case2 2)
      (const v1 1)
      (const v2 2)
      (goto :end)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (load-param v0)
      (const v1 1)
      (const v2 2)
      (switch v0 (:case1 :case2))
      (:case1 1)
      (goto :end)
      (:case2 2)
      (goto :end)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

TEST_F(BranchPrefixHoistingTest, switch_with_same_cases) {
  const auto& code_str = R"(
    (
      (load-param v0)
      (switch v0 (:a :b :c :d :e :f))

      (:a 0)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v2 v1)
      (goto :end)

      (:b 1)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v2 v1)
      (goto :end)

      (:c 2)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v2 v1)
      (goto :end)

      (:d 3)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v1 v1)
      (goto :end)

      (:e 4)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v1 v1)
      (goto :end)

      (:f 5)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v1 v1)
      (goto :end)

      (:end)
      (return-void)
    )
  )";

  auto expected_str = R"(
     (
      (load-param v0)
      (const v1 1)
      (add-int v2 v1 v1)
      (switch v0 (:a :b :c :d :e :f))

      (:a 0)
      (add-int v2 v2 v1)
      (goto :end)

      (:b 1)
      (add-int v2 v2 v1)
      (goto :end)

      (:c 2)
      (add-int v2 v2 v1)
      (goto :end)

      (:d 3)
      (add-int v2 v1 v1)
      (goto :end)

      (:e 4)
      (add-int v2 v1 v1)
      (goto :end)

      (:f 5)
      (add-int v2 v1 v1)
      (goto :end)

      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

<commit_msg>Daily `arc lint --take CLANGFORMAT`<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <gtest/gtest.h>

#include "BranchPrefixHoisting.h"
#include "ControlFlow.h"
#include "IRAssembler.h"
#include "IRCode.h"
#include "RedexTest.h"
#include "Show.h"

class BranchPrefixHoistingTest : public RedexTest {};

void test(const std::string& code_str,
          const std::string& expected_str,
          size_t expected_instructions_hoisted) {
  auto code = assembler::ircode_from_string(code_str);
  auto code_ptr = code.get();
  code_ptr->build_cfg(true);
  auto& cfg = code_ptr->cfg();
  std::cerr << "before:" << std::endl << SHOW(cfg);

  int actual_insns_hoisted = BranchPrefixHoistingPass::process_cfg(cfg);

  std::cerr << "after:" << std::endl << SHOW(cfg);
  EXPECT_EQ(expected_instructions_hoisted, actual_insns_hoisted);
  auto expected = assembler::ircode_from_string(expected_str);
  auto expected_ptr = expected.get();
  expected_ptr->build_cfg(true);
  auto& expected_cfg = expected_ptr->cfg();
  std::cerr << "expected:" << std::endl << SHOW(expected_cfg);

  code_ptr->clear_cfg();
  expected_ptr->clear_cfg();

  EXPECT_EQ(assembler::to_s_expr(code.get()),
            assembler::to_s_expr(expected.get()));
}

TEST_F(BranchPrefixHoistingTest, simple_insn_hoisting) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 6)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 7)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (if-eqz v0 :true)
      (const v6 6)
      (goto :end)
      (:true)
      (const v6 7)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 5);
}

TEST_F(BranchPrefixHoistingTest, stop_hoisting_at_side_effect) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v0 7)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v0 7)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (if-eqz v0 :true)
      (const v0 7)
      (goto :end)
      (:true)
      (const v0 7)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 3);
}

TEST_F(BranchPrefixHoistingTest, move_result_hoist_ok) {
  const auto& code_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (if-eqz v0 :true)
      (const v5 42)
      (goto :end)
      (:true)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

TEST_F(BranchPrefixHoistingTest, move_result_no_hoist_diff_dest) {
  const auto& code_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v4)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v4)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v3)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 0);
}

TEST_F(BranchPrefixHoistingTest, move_result_no_hoist_on_side_effect) {
  const auto& code_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 16)
      (const v2 8)
      (if-eqz v0 :true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v5 42)
      (goto :end)
      (:true)
      (div-int v1 v2)
      (move-result-pseudo v0)
      (const v6 43)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 0);
}

TEST_F(BranchPrefixHoistingTest, one_block_becomes_empty) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (if-eqz v0 :true)
      (goto :end)
      (:true)
      (const v3 3)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

TEST_F(BranchPrefixHoistingTest, both_blocks_becomes_empty) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (goto :end)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (if-eqz v0 :true)
      (goto :end)
      (:true)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 3);
}

TEST_F(BranchPrefixHoistingTest, move_result_wide) {
  const auto& code_str = R"(
    (
      (const-wide v1 2)
      (const-wide v2 10)
      (if-ge v3 v0 :true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (goto :end)
      (:true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const-wide v1 2)
      (const-wide v2 10)
      (if-ge v3 v0 :true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (goto :end)
      (:true)
      (invoke-static (v1 v2) "LCls;.max:(JJ)J")
      (move-result-wide v0)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 0);
}

TEST_F(BranchPrefixHoistingTest, branch_goes_to_same_block) {
  const auto& code_str = R"(
    (
      (if-eqz v0 :true)
      (:true)
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 7)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (const v1 1)
      (const v2 2)
      (const v3 3)
      (const v4 4)
      (const v5 5)
      (const v6 7)
      (if-eqz v0 :true)
      (:true)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 6);
}

TEST_F(BranchPrefixHoistingTest, switch_two_same_cases) {
  const auto& code_str = R"(
    (
      (load-param v0)
      (switch v0 (:case1 :case2))
      (:case1 1)
      (const v1 1)
      (const v2 2)
      (goto :end)
      (:case2 2)
      (const v1 1)
      (const v2 2)
      (goto :end)
      (:end)
      (return-void)
    )
  )";
  const auto& expected_str = R"(
    (
      (load-param v0)
      (const v1 1)
      (const v2 2)
      (switch v0 (:case1 :case2))
      (:case1 1)
      (goto :end)
      (:case2 2)
      (goto :end)
      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}

TEST_F(BranchPrefixHoistingTest, switch_with_same_cases) {
  const auto& code_str = R"(
    (
      (load-param v0)
      (switch v0 (:a :b :c :d :e :f))

      (:a 0)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v2 v1)
      (goto :end)

      (:b 1)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v2 v1)
      (goto :end)

      (:c 2)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v2 v1)
      (goto :end)

      (:d 3)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v1 v1)
      (goto :end)

      (:e 4)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v1 v1)
      (goto :end)

      (:f 5)
      (const v1 1)
      (add-int v2 v1 v1)
      (add-int v2 v1 v1)
      (goto :end)

      (:end)
      (return-void)
    )
  )";

  auto expected_str = R"(
     (
      (load-param v0)
      (const v1 1)
      (add-int v2 v1 v1)
      (switch v0 (:a :b :c :d :e :f))

      (:a 0)
      (add-int v2 v2 v1)
      (goto :end)

      (:b 1)
      (add-int v2 v2 v1)
      (goto :end)

      (:c 2)
      (add-int v2 v2 v1)
      (goto :end)

      (:d 3)
      (add-int v2 v1 v1)
      (goto :end)

      (:e 4)
      (add-int v2 v1 v1)
      (goto :end)

      (:f 5)
      (add-int v2 v1 v1)
      (goto :end)

      (:end)
      (return-void)
    )
  )";
  test(code_str, expected_str, 2);
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015-2016 The Squash Authors
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Authors:
 *   Evan Nemerson <evan@nemerson.com>
 */

#include <squash/squash.h>

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include <csc_enc.h>
#include <csc_dec.h>
#include <Types.h>

enum SquashCscOptionIndex {
  SQUASH_CSC_OPT_LEVEL = 0,
  SQUASH_CSC_OPT_DICT_SIZE,
  SQUASH_CSC_OPT_DELTA_FILTER,
  SQUASH_CSC_OPT_EXE_FILTER,
  SQUASH_CSC_OPT_TXT_FILTER,
};

/* C++ doesn't allow us to initialize this correctly here (or, at
   least, I can't figure out how to do it), so there is some extra
   code in the init_plugin func to finish it off. */
static SquashOptionInfo squash_csc_options[] = {
  { .name = (char*) "level",
    .type = SQUASH_OPTION_TYPE_RANGE_INT },
  { .name = (char*) "dict-size",
    .type = SQUASH_OPTION_TYPE_RANGE_SIZE },
  { .name = (char*) "delta-filter",
    .type = SQUASH_OPTION_TYPE_BOOL },
  { .name = (char*) "exe-filter",
    .type = SQUASH_OPTION_TYPE_BOOL },
  { .name = (char*) "txt-filter",
    .type = SQUASH_OPTION_TYPE_BOOL },
  { NULL, SQUASH_OPTION_TYPE_NONE, }
};

extern "C" SQUASH_PLUGIN_EXPORT
SquashStatus             squash_plugin_init_codec   (SquashCodec* codec, SquashCodecImpl* impl);
extern "C" SQUASH_PLUGIN_EXPORT
SquashStatus             squash_plugin_init_plugin  (SquashPlugin* plugin);

static void* squash_csc_alloc (void* p, size_t size) {
  return squash_malloc (size);
}

static void squash_csc_free (void* p, void* address) {
  squash_free (address);
}

static const ISzAlloc squash_csc_allocator = {
  squash_csc_alloc,
  squash_csc_free
};

struct SquashCscInStream {
  ISeqInStream stream;
  void* user_data;
  SquashReadFunc reader;
  SquashWriteFunc writer;
};

struct SquashCscOutStream {
  ISeqOutStream os;
  void* user_data;
  SquashReadFunc reader;
  SquashWriteFunc writer;
};

static SRes
squash_csc_reader (void* istream, void* buf, size_t* size) {
  SquashCscInStream* s = (SquashCscInStream*) istream;

  s->reader (size, (uint8_t*) buf, s->user_data);

  return 0;
}

static size_t
squash_csc_writer (void* ostream, const void* buf, size_t size) {
  SquashCscOutStream* s = (SquashCscOutStream*) ostream;

  s->writer (&size, (const uint8_t*) buf, s->user_data);

  return size;
}

static SquashStatus
squash_csc_splice (SquashCodec* codec,
                   SquashOptions* options,
                   SquashStreamType stream_type,
                   SquashReadFunc read_cb,
                   SquashWriteFunc write_cb,
                   void* user_data) {
  const struct SquashCscInStream in_stream = {
    { squash_csc_reader },
    user_data,
    read_cb,
    write_cb
  };
  const struct SquashCscOutStream out_stream = {
    { squash_csc_writer },
    user_data,
    read_cb,
    write_cb
  };

  CSCProps props;
  unsigned char props_buf[CSC_PROP_SIZE];

  if (stream_type == SQUASH_STREAM_COMPRESS) {
    CSCEncProps_Init (&props,
                      squash_codec_get_option_size_index (codec, options, SQUASH_CSC_OPT_DICT_SIZE),
                      squash_codec_get_option_int_index (codec, options, SQUASH_CSC_OPT_LEVEL));
    props.DLTFilter = squash_codec_get_option_bool_index (codec, options, SQUASH_CSC_OPT_DELTA_FILTER);
    props.EXEFilter = squash_codec_get_option_bool_index (codec, options, SQUASH_CSC_OPT_EXE_FILTER);
    props.TXTFilter = squash_codec_get_option_bool_index (codec, options, SQUASH_CSC_OPT_TXT_FILTER);

    CSCEnc_WriteProperties (&props, props_buf, 0);
    size_t bytes_written = squash_csc_writer ((void*) &out_stream, props_buf, CSC_PROP_SIZE);
    if (SQUASH_UNLIKELY(bytes_written != CSC_PROP_SIZE))
      return squash_error (SQUASH_FAILED);

    CSCEncHandle comp = CSCEnc_Create (&props, (ISeqOutStream*) &out_stream, (ISzAlloc*) &squash_csc_allocator);
    CSCEnc_Encode (comp, (ISeqInStream*) &in_stream, NULL);
    CSCEnc_Encode_Flush (comp);
  } else {
    size_t prop_l = CSC_PROP_SIZE;
    squash_csc_reader ((void*) &in_stream, props_buf, &prop_l);
    if (SQUASH_UNLIKELY(prop_l != CSC_PROP_SIZE))
      return squash_error (SQUASH_FAILED);

    CSCDec_ReadProperties (&props, props_buf);

    CSCDecHandle decomp = CSCDec_Create (&props, (ISeqInStream*) &in_stream, (ISzAlloc*) &squash_csc_allocator);
    CSCDec_Decode (decomp, (ISeqOutStream*) &out_stream, NULL);
  }

  return SQUASH_OK;
}

static size_t
squash_csc_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_size) {
  // TODO: this could probably be improved
  return
    uncompressed_size + 64 + (uncompressed_size / 128);
}

extern "C" SquashStatus
squash_plugin_init_plugin (SquashPlugin* plugin) {
  const SquashOptionInfoRangeInt level_range = { 1, 5, 0, false };
  const SquashOptionInfoRangeSize dict_size_range = { 32768, 1073741824, 0, false };

  squash_csc_options[SQUASH_CSC_OPT_LEVEL].default_value.int_value = 2;
  squash_csc_options[SQUASH_CSC_OPT_LEVEL].info.range_int = level_range;
  squash_csc_options[SQUASH_CSC_OPT_DICT_SIZE].default_value.size_value = (1024 * 1024 * 64);
  squash_csc_options[SQUASH_CSC_OPT_DICT_SIZE].info.range_size = dict_size_range;
  squash_csc_options[SQUASH_CSC_OPT_DELTA_FILTER].default_value.bool_value = false;
  squash_csc_options[SQUASH_CSC_OPT_EXE_FILTER].default_value.bool_value = true;
  squash_csc_options[SQUASH_CSC_OPT_TXT_FILTER].default_value.bool_value = true;

  return SQUASH_OK;
}

extern "C" SquashStatus
squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) {
  const char* name = squash_codec_get_name (codec);

  if (SQUASH_LIKELY(strcmp ("csc", name) == 0)) {
    impl->options = squash_csc_options;
    impl->splice = squash_csc_splice;
    impl->get_max_compressed_size = squash_csc_get_max_compressed_size;
  } else {
    return squash_error (SQUASH_UNABLE_TO_LOAD);
  }

  return SQUASH_OK;
}
<commit_msg>csc: don't explicitly specify field names when initializing struct<commit_after>/* Copyright (c) 2015-2016 The Squash Authors
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Authors:
 *   Evan Nemerson <evan@nemerson.com>
 */

#include <squash/squash.h>

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include <csc_enc.h>
#include <csc_dec.h>
#include <Types.h>

enum SquashCscOptionIndex {
  SQUASH_CSC_OPT_LEVEL = 0,
  SQUASH_CSC_OPT_DICT_SIZE,
  SQUASH_CSC_OPT_DELTA_FILTER,
  SQUASH_CSC_OPT_EXE_FILTER,
  SQUASH_CSC_OPT_TXT_FILTER,
};

/* C++ doesn't allow us to initialize this correctly here (or, at
   least, I can't figure out how to do it), so there is some extra
   code in the init_plugin func to finish it off. */
static SquashOptionInfo squash_csc_options[] = {
  { "level",
    SQUASH_OPTION_TYPE_RANGE_INT },
  { "dict-size",
    SQUASH_OPTION_TYPE_RANGE_SIZE },
  { "delta-filter",
    SQUASH_OPTION_TYPE_BOOL },
  { "exe-filter",
    SQUASH_OPTION_TYPE_BOOL },
  { "txt-filter",
    SQUASH_OPTION_TYPE_BOOL },
  { NULL, SQUASH_OPTION_TYPE_NONE, }
};

extern "C" SQUASH_PLUGIN_EXPORT
SquashStatus             squash_plugin_init_codec   (SquashCodec* codec, SquashCodecImpl* impl);
extern "C" SQUASH_PLUGIN_EXPORT
SquashStatus             squash_plugin_init_plugin  (SquashPlugin* plugin);

static void* squash_csc_alloc (void* p, size_t size) {
  return squash_malloc (size);
}

static void squash_csc_free (void* p, void* address) {
  squash_free (address);
}

static const ISzAlloc squash_csc_allocator = {
  squash_csc_alloc,
  squash_csc_free
};

struct SquashCscInStream {
  ISeqInStream stream;
  void* user_data;
  SquashReadFunc reader;
  SquashWriteFunc writer;
};

struct SquashCscOutStream {
  ISeqOutStream os;
  void* user_data;
  SquashReadFunc reader;
  SquashWriteFunc writer;
};

static SRes
squash_csc_reader (void* istream, void* buf, size_t* size) {
  SquashCscInStream* s = (SquashCscInStream*) istream;

  s->reader (size, (uint8_t*) buf, s->user_data);

  return 0;
}

static size_t
squash_csc_writer (void* ostream, const void* buf, size_t size) {
  SquashCscOutStream* s = (SquashCscOutStream*) ostream;

  s->writer (&size, (const uint8_t*) buf, s->user_data);

  return size;
}

static SquashStatus
squash_csc_splice (SquashCodec* codec,
                   SquashOptions* options,
                   SquashStreamType stream_type,
                   SquashReadFunc read_cb,
                   SquashWriteFunc write_cb,
                   void* user_data) {
  const struct SquashCscInStream in_stream = {
    { squash_csc_reader },
    user_data,
    read_cb,
    write_cb
  };
  const struct SquashCscOutStream out_stream = {
    { squash_csc_writer },
    user_data,
    read_cb,
    write_cb
  };

  CSCProps props;
  unsigned char props_buf[CSC_PROP_SIZE];

  if (stream_type == SQUASH_STREAM_COMPRESS) {
    CSCEncProps_Init (&props,
                      squash_codec_get_option_size_index (codec, options, SQUASH_CSC_OPT_DICT_SIZE),
                      squash_codec_get_option_int_index (codec, options, SQUASH_CSC_OPT_LEVEL));
    props.DLTFilter = squash_codec_get_option_bool_index (codec, options, SQUASH_CSC_OPT_DELTA_FILTER);
    props.EXEFilter = squash_codec_get_option_bool_index (codec, options, SQUASH_CSC_OPT_EXE_FILTER);
    props.TXTFilter = squash_codec_get_option_bool_index (codec, options, SQUASH_CSC_OPT_TXT_FILTER);

    CSCEnc_WriteProperties (&props, props_buf, 0);
    size_t bytes_written = squash_csc_writer ((void*) &out_stream, props_buf, CSC_PROP_SIZE);
    if (SQUASH_UNLIKELY(bytes_written != CSC_PROP_SIZE))
      return squash_error (SQUASH_FAILED);

    CSCEncHandle comp = CSCEnc_Create (&props, (ISeqOutStream*) &out_stream, (ISzAlloc*) &squash_csc_allocator);
    CSCEnc_Encode (comp, (ISeqInStream*) &in_stream, NULL);
    CSCEnc_Encode_Flush (comp);
  } else {
    size_t prop_l = CSC_PROP_SIZE;
    squash_csc_reader ((void*) &in_stream, props_buf, &prop_l);
    if (SQUASH_UNLIKELY(prop_l != CSC_PROP_SIZE))
      return squash_error (SQUASH_FAILED);

    CSCDec_ReadProperties (&props, props_buf);

    CSCDecHandle decomp = CSCDec_Create (&props, (ISeqInStream*) &in_stream, (ISzAlloc*) &squash_csc_allocator);
    CSCDec_Decode (decomp, (ISeqOutStream*) &out_stream, NULL);
  }

  return SQUASH_OK;
}

static size_t
squash_csc_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_size) {
  // TODO: this could probably be improved
  return
    uncompressed_size + 64 + (uncompressed_size / 128);
}

extern "C" SquashStatus
squash_plugin_init_plugin (SquashPlugin* plugin) {
  const SquashOptionInfoRangeInt level_range = { 1, 5, 0, false };
  const SquashOptionInfoRangeSize dict_size_range = { 32768, 1073741824, 0, false };

  squash_csc_options[SQUASH_CSC_OPT_LEVEL].default_value.int_value = 2;
  squash_csc_options[SQUASH_CSC_OPT_LEVEL].info.range_int = level_range;
  squash_csc_options[SQUASH_CSC_OPT_DICT_SIZE].default_value.size_value = (1024 * 1024 * 64);
  squash_csc_options[SQUASH_CSC_OPT_DICT_SIZE].info.range_size = dict_size_range;
  squash_csc_options[SQUASH_CSC_OPT_DELTA_FILTER].default_value.bool_value = false;
  squash_csc_options[SQUASH_CSC_OPT_EXE_FILTER].default_value.bool_value = true;
  squash_csc_options[SQUASH_CSC_OPT_TXT_FILTER].default_value.bool_value = true;

  return SQUASH_OK;
}

extern "C" SquashStatus
squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) {
  const char* name = squash_codec_get_name (codec);

  if (SQUASH_LIKELY(strcmp ("csc", name) == 0)) {
    impl->options = squash_csc_options;
    impl->splice = squash_csc_splice;
    impl->get_max_compressed_size = squash_csc_get_max_compressed_size;
  } else {
    return squash_error (SQUASH_UNABLE_TO_LOAD);
  }

  return SQUASH_OK;
}
<|endoftext|>


<commit_before>/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2014 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gtest/gtest.h"
#include "BitMatrix.h"
#include "BitMatrixUtility.h"
#include "oned/ODCode128Writer.h"
#include "oned/ODCode128Reader.h"
#include "DecodeHints.h"
#include "Result.h"

using namespace ZXing;
using namespace ZXing::OneD;

static const std::string FNC1 = "11110101110";
static const std::string FNC2 = "11110101000";
static const std::string FNC3 = "10111100010";
static const std::string FNC4A = "11101011110";
static const std::string FNC4B = "10111101110";
static const std::string START_CODE_A = "11010000100";
static const std::string START_CODE_B = "11010010000";
static const std::string START_CODE_C = "11010011100";
static const std::string SWITCH_CODE_A = "11101011110";
static const std::string SWITCH_CODE_B = "10111101110";
static const std::string QUIET_SPACE = "00000";
static const std::string STOP = "1100011101011";
static const std::string LF = "10000110010";

static std::string LineMatrixToString(const BitMatrix& matrix)
{
	auto result = Utility::ToString(matrix, '1', '0', false);
	return result.substr(0, result.size() - 1);
}

static ZXing::Result Decode(const BitMatrix &matrix)
{
	BitArray row;
	matrix.getRow(0, row);
	std::unique_ptr<RowReader::DecodingState> state;
	return Code128Reader(DecodeHints()).decodeRow(0, row, state);
}

TEST(ODCode128Writer, EncodeWithFunc1)
{
	auto toEncode = L"\xf1""123";
    //                                                       "12"                           "3"          check digit 92
	auto expected = QUIET_SPACE + START_CODE_C + FNC1 + "10110011100" + SWITCH_CODE_B + "11001011100" + "10101111000" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFunc2)
{
	auto toEncode = L"\xf2""123";
	//                                                       "1"            "2"             "3"          check digit 56
	auto expected = QUIET_SPACE + START_CODE_B + FNC2 + "10011100110" + "11001110010" + "11001011100" + "11100010110" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFunc3)
{
	auto toEncode = L"\xf3""123";
	//                                                       "1"            "2"             "3"          check digit 51
	auto expected = QUIET_SPACE + START_CODE_B + FNC3 + "10011100110" + "11001110010" + "11001011100" + "11101000110" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFunc4)
{
	auto toEncode = L"\xf4""123";
	//                                                       "1"            "2"             "3"          check digit 59
	auto expected = QUIET_SPACE + START_CODE_B + FNC4B + "10011100110" + "11001110010" + "11001011100" + "11100011010" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFncsAndNumberInCodesetA)
{
	auto toEncode = L"\n" "\xf1" "\xf4" "1" "\n";
	auto expected = QUIET_SPACE + START_CODE_A + LF + FNC1 + FNC4A + "10011100110" + LF + "10101111000" + STOP + QUIET_SPACE;
	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, Roundtrip)
{
	auto toEncode = L"\xf1" "10958" "\xf1" "17160526";
	auto expected = L"1095817160526";

    auto encResult = Code128Writer().encode(toEncode, 0, 0);
	auto actual = Decode(encResult).text();
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeSwitchCodesetFromAToB)
{
	// start with A switch to B and back to A
	auto toEncode = std::wstring(L"\0ABab\u0010", 6);
	//                                           "\0"            "A"             "B"             Switch to B     "a"             "b"             Switch to A     "\u0010"        check digit
	auto expected = QUIET_SPACE + START_CODE_A + "10100001100" + "10100011000" + "10001011000" + SWITCH_CODE_B + "10010110000" + "10010000110" + SWITCH_CODE_A + "10100111100" + "11001110100" + STOP + QUIET_SPACE;

	auto encoded = Code128Writer().encode(toEncode, 0, 0);
	auto actual = LineMatrixToString(encoded);
	EXPECT_EQ(actual, expected);

	auto actualRoundTrip = Decode(encoded).text();
	EXPECT_EQ(actualRoundTrip, toEncode);
}

TEST(ODCode128Writer, EncodeSwitchCodesetFromBToA)
{
	// start with B switch to A and back to B
	auto toEncode = std::wstring(L"ab\0ab", 5);
	//                                           "a"             "b"             Switch to A     "\0             "Switch to B"   "a"             "b"             check digit
	auto expected = QUIET_SPACE + START_CODE_B + "10010110000" + "10010000110" + SWITCH_CODE_A + "10100001100" + SWITCH_CODE_B + "10010110000" + "10010000110" + "11010001110" + STOP + QUIET_SPACE;

	auto encoded = Code128Writer().encode(toEncode, 0, 0);
	auto actual = LineMatrixToString(encoded);
	EXPECT_EQ(actual, expected);

	auto actualRoundTrip = Decode(encoded).text();
	EXPECT_EQ(actualRoundTrip, toEncode);
}
<commit_msg>cleanup: consistently use decodeSingleRow in tests<commit_after>/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2014 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gtest/gtest.h"
#include "BitMatrix.h"
#include "BitMatrixUtility.h"
#include "oned/ODCode128Writer.h"
#include "oned/ODCode128Reader.h"
#include "DecodeHints.h"
#include "Result.h"

using namespace ZXing;
using namespace ZXing::OneD;

static const std::string FNC1 = "11110101110";
static const std::string FNC2 = "11110101000";
static const std::string FNC3 = "10111100010";
static const std::string FNC4A = "11101011110";
static const std::string FNC4B = "10111101110";
static const std::string START_CODE_A = "11010000100";
static const std::string START_CODE_B = "11010010000";
static const std::string START_CODE_C = "11010011100";
static const std::string SWITCH_CODE_A = "11101011110";
static const std::string SWITCH_CODE_B = "10111101110";
static const std::string QUIET_SPACE = "00000";
static const std::string STOP = "1100011101011";
static const std::string LF = "10000110010";

static std::string LineMatrixToString(const BitMatrix& matrix)
{
	auto result = Utility::ToString(matrix, '1', '0', false);
	return result.substr(0, result.size() - 1);
}

static ZXing::Result Decode(const BitMatrix &matrix)
{
	BitArray row;
	matrix.getRow(0, row);
	return Code128Reader(DecodeHints()).decodeSingleRow(0, row);
}

TEST(ODCode128Writer, EncodeWithFunc1)
{
	auto toEncode = L"\xf1""123";
    //                                                       "12"                           "3"          check digit 92
	auto expected = QUIET_SPACE + START_CODE_C + FNC1 + "10110011100" + SWITCH_CODE_B + "11001011100" + "10101111000" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFunc2)
{
	auto toEncode = L"\xf2""123";
	//                                                       "1"            "2"             "3"          check digit 56
	auto expected = QUIET_SPACE + START_CODE_B + FNC2 + "10011100110" + "11001110010" + "11001011100" + "11100010110" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFunc3)
{
	auto toEncode = L"\xf3""123";
	//                                                       "1"            "2"             "3"          check digit 51
	auto expected = QUIET_SPACE + START_CODE_B + FNC3 + "10011100110" + "11001110010" + "11001011100" + "11101000110" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFunc4)
{
	auto toEncode = L"\xf4""123";
	//                                                       "1"            "2"             "3"          check digit 59
	auto expected = QUIET_SPACE + START_CODE_B + FNC4B + "10011100110" + "11001110010" + "11001011100" + "11100011010" + STOP + QUIET_SPACE;

	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeWithFncsAndNumberInCodesetA)
{
	auto toEncode = L"\n" "\xf1" "\xf4" "1" "\n";
	auto expected = QUIET_SPACE + START_CODE_A + LF + FNC1 + FNC4A + "10011100110" + LF + "10101111000" + STOP + QUIET_SPACE;
	auto actual = LineMatrixToString(Code128Writer().encode(toEncode, 0, 0));
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, Roundtrip)
{
	auto toEncode = L"\xf1" "10958" "\xf1" "17160526";
	auto expected = L"1095817160526";

    auto encResult = Code128Writer().encode(toEncode, 0, 0);
	auto actual = Decode(encResult).text();
	EXPECT_EQ(actual, expected);
}

TEST(ODCode128Writer, EncodeSwitchCodesetFromAToB)
{
	// start with A switch to B and back to A
	auto toEncode = std::wstring(L"\0ABab\u0010", 6);
	//                                           "\0"            "A"             "B"             Switch to B     "a"             "b"             Switch to A     "\u0010"        check digit
	auto expected = QUIET_SPACE + START_CODE_A + "10100001100" + "10100011000" + "10001011000" + SWITCH_CODE_B + "10010110000" + "10010000110" + SWITCH_CODE_A + "10100111100" + "11001110100" + STOP + QUIET_SPACE;

	auto encoded = Code128Writer().encode(toEncode, 0, 0);
	auto actual = LineMatrixToString(encoded);
	EXPECT_EQ(actual, expected);

	auto actualRoundTrip = Decode(encoded).text();
	EXPECT_EQ(actualRoundTrip, toEncode);
}

TEST(ODCode128Writer, EncodeSwitchCodesetFromBToA)
{
	// start with B switch to A and back to B
	auto toEncode = std::wstring(L"ab\0ab", 5);
	//                                           "a"             "b"             Switch to A     "\0             "Switch to B"   "a"             "b"             check digit
	auto expected = QUIET_SPACE + START_CODE_B + "10010110000" + "10010000110" + SWITCH_CODE_A + "10100001100" + SWITCH_CODE_B + "10010110000" + "10010000110" + "11010001110" + STOP + QUIET_SPACE;

	auto encoded = Code128Writer().encode(toEncode, 0, 0);
	auto actual = LineMatrixToString(encoded);
	EXPECT_EQ(actual, expected);

	auto actualRoundTrip = Decode(encoded).text();
	EXPECT_EQ(actualRoundTrip, toEncode);
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include <iostream>

#include "verilated_toplevel.h"
#include "verilator_memutil.h"
#include "verilator_sim_ctrl.h"

int main(int argc, char **argv) {
  top_earlgrey_verilator top;
  VerilatorMemUtil memutil;
  VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
  simctrl.SetTop(&top, &top.clk_i, &top.rst_ni,
                 VerilatorSimCtrlFlags::ResetPolarityNegative);

  memutil.RegisterMemoryArea(
      "rom",
      "TOP.top_earlgrey_verilator.top_earlgrey.u_rom_rom.gen_generic."
      "u_impl_generic");
  memutil.RegisterMemoryArea(
      "ram",
      "TOP.top_earlgrey_verilator.top_earlgrey.u_ram1p_ram_main.u_mem."
      "gen_generic.u_impl_generic");
  memutil.RegisterMemoryArea(
      "flash",
      "TOP.top_earlgrey_verilator.top_earlgrey.u_flash_eflash."
      "gen_flash_banks[0].i_core.i_flash.gen_generic.u_impl_generic.u_mem.gen_"
      "generic."
      "u_impl_generic");
  simctrl.RegisterExtension(&memutil);
  simctrl.SetInitialResetDelay(100);

  std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
            << "=================================" << std::endl
            << std::endl;

  return simctrl.Exec(argc, argv);
}
<commit_msg>[top] - Update verilator configuration for 64b<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include <iostream>

#include "verilated_toplevel.h"
#include "verilator_memutil.h"
#include "verilator_sim_ctrl.h"

int main(int argc, char **argv) {
  top_earlgrey_verilator top;
  VerilatorMemUtil memutil;
  VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
  simctrl.SetTop(&top, &top.clk_i, &top.rst_ni,
                 VerilatorSimCtrlFlags::ResetPolarityNegative);

  memutil.RegisterMemoryArea(
      "rom",
      "TOP.top_earlgrey_verilator.top_earlgrey.u_rom_rom.gen_generic."
      "u_impl_generic");
  memutil.RegisterMemoryArea(
      "ram",
      "TOP.top_earlgrey_verilator.top_earlgrey.u_ram1p_ram_main.u_mem."
      "gen_generic.u_impl_generic");
  memutil.RegisterMemoryArea(
      "flash",
      "TOP.top_earlgrey_verilator.top_earlgrey.u_flash_eflash."
      "gen_flash_banks[0].i_core.i_flash.gen_generic.u_impl_generic.u_mem.gen_"
      "generic."
      "u_impl_generic",
      64);
  simctrl.RegisterExtension(&memutil);
  simctrl.SetInitialResetDelay(100);

  std::cout << "Simulation of OpenTitan Earl Grey" << std::endl
            << "=================================" << std::endl
            << std::endl;

  return simctrl.Exec(argc, argv);
}
<|endoftext|>


<commit_before>#include "State.hpp"
#include "Script.hpp"
#include "../File.hpp"
#include "../Exception.hpp"
#include <cstdlib>
#include <sstream>

BEGIN_INANITY_LUA

State::State()
{
	// создать состояние
	state = lua_newstate(Alloc, this);
	if(!state)
		THROW_PRIMARY_EXCEPTION("Can't create Lua state");

	// установить функцию окончания
//	lua_atpanic(state, Panic);
}

State::~State()
{
	lua_close(state);
}

void* State::Alloc(void* self, void* ptr, size_t osize, size_t nsize)
{
	if(nsize)
		return realloc(ptr, nsize);
	free(ptr);
	return 0;
}

int State::Panic(lua_State* state)
{
	try
	{
		ProcessError(state);
		THROW_PRIMARY_EXCEPTION("Unprocessed lua panic");
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY_EXCEPTION("Lua panic", exception);
	}
}

lua_State* State::GetState()
{
	return state;
}

ptr<Script> State::LoadScript(ptr<File> file)
{
	/// Класс читателя.
	/** Чтение выполняется в один приём. */
	class Reader
	{
	private:
		ptr<File> file;
		bool read;

	public:
		Reader(ptr<File> file) : file(file), read(false) {}

		static const char* Callback(lua_State* state, void* data, size_t* size)
		{
			Reader* reader = (Reader*)data;
			if(reader->read)
			{
				*size = 0;
				return 0;
			}
			reader->read = true;
			*size = reader->file->GetSize();
			return (const char*)reader->file->GetData();
		};
	};

	Reader reader(file);
	if(lua_load(state, Reader::Callback, &reader, "=noname", 0) == LUA_OK)
		// конструктор Script заберёт функцию из стека, и сохранит её себе
		return NEW(Script(this));
	// обработать ошибку
	ProcessError(state);
	// ProcessError никогда не возвращает управления
	return 0;
}

END_INANITY_LUA
<commit_msg>missed lua panic function<commit_after>#include "State.hpp"
#include "Script.hpp"
#include "../File.hpp"
#include "../Exception.hpp"
#include <cstdlib>
#include <sstream>

BEGIN_INANITY_LUA

State::State()
{
	// создать состояние
	state = lua_newstate(Alloc, this);
	if(!state)
		THROW_PRIMARY_EXCEPTION("Can't create Lua state");

	// установить функцию окончания
	lua_atpanic(state, Panic);
}

State::~State()
{
	lua_close(state);
}

void* State::Alloc(void* self, void* ptr, size_t osize, size_t nsize)
{
	if(nsize)
		return realloc(ptr, nsize);
	free(ptr);
	return 0;
}

int State::Panic(lua_State* state)
{
	try
	{
		ProcessError(state);
		THROW_PRIMARY_EXCEPTION("Unprocessed lua panic");
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY_EXCEPTION("Lua panic", exception);
	}
}

lua_State* State::GetState()
{
	return state;
}

ptr<Script> State::LoadScript(ptr<File> file)
{
	/// Класс читателя.
	/** Чтение выполняется в один приём. */
	class Reader
	{
	private:
		ptr<File> file;
		bool read;

	public:
		Reader(ptr<File> file) : file(file), read(false) {}

		static const char* Callback(lua_State* state, void* data, size_t* size)
		{
			Reader* reader = (Reader*)data;
			if(reader->read)
			{
				*size = 0;
				return 0;
			}
			reader->read = true;
			*size = reader->file->GetSize();
			return (const char*)reader->file->GetData();
		};
	};

	Reader reader(file);
	if(lua_load(state, Reader::Callback, &reader, "=noname", 0) == LUA_OK)
		// конструктор Script заберёт функцию из стека, и сохранит её себе
		return NEW(Script(this));
	// обработать ошибку
	ProcessError(state);
	// ProcessError никогда не возвращает управления
	return 0;
}

END_INANITY_LUA
<|endoftext|>


<commit_before>#ifndef SAVE_STATE_H
# define SAVE_STATE_H
# pragma once

struct statebuf
{
  void* sp;
  void* label;
};

#if defined(__GNUC__)
inline __attribute__((always_inline)) bool savestate(statebuf& ssb) noexcept
{
  bool r;

#if defined(i386) || defined(__i386) || defined(__i386__)
  asm volatile (
    "movl %%esp, %0\n\t" // store sp
    "movl $1f, %1\n\t" // store label
    "movb $0, %2\n\t" // return false
    "jmp 2f\n\t"
    "1:movb $1, %2\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
  asm volatile (
    "movq %%rsp, %0\n\t" // store sp
    "movq $1f, %1\n\t" // store label
    "movb $0, %2\n\t" // return false
    "jmp 2f\n\r"
    "1:movb $1, %2\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#endif

  return r;
}
#elif defined(_MSC_VER)
  __forceinline bool savestate(statebuf& ssb) noexcept
  {
  register bool r;

  __asm {
    lea ecx, ssb
    mov [ecx]statebuf.sp, esp
    mov [ecx]statebuf.label, offset _1f
    mov r, 0x0
    jmp _2f
    _1f : mov r, 0x1
    _2f :
  }

    return r;
  }
#else
# error ""
#endif

#if defined(__GNUC__)
#if defined(i386) || defined(__i386) || defined(__i386__)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movl %0, %%esp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (ssb.sp), "m" (ssb.label)\
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movq %0, %%rsp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (SSB.sp), "m" (SSB.label)\
  );
#else
# error ""
#endif
#elif defined(_MSC_VER)
#define restorestate(SSB)          \
  __asm lea eax, this.SSB        \
  __asm mov esp, [eax]statebuf.sp\
  __asm jmp [eax]statebuf.label
#else
# error "unsupported compiler"
#endif

#endif // SAVE_STATE_H
<commit_msg>some fixes<commit_after>#ifndef SAVE_STATE_H
# define SAVE_STATE_H
# pragma once

struct statebuf
{
  void* sp;
  void* label;
};

#if defined(__GNUC__)
inline __attribute__((always_inline)) bool savestate(statebuf& ssb) noexcept
{
  bool r;

#if defined(i386) || defined(__i386) || defined(__i386__)
  asm volatile (
    "movl %%esp, %0\n\t" // store sp
    "movl $1f, %1\n\t" // store label
    "movb $0, %2\n\t" // return false
    "jmp 2f\n\t"
    "1:movb $1, %2\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
  asm volatile (
    "movq %%rsp, %0\n\t" // store sp
    "movq $1f, %1\n\t" // store label
    "movb $0, %2\n\t" // return false
    "jmp 2f\n\r"
    "1:movb $1, %2\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#endif

  return r;
}
#elif defined(_MSC_VER)
  __forceinline bool savestate(statebuf& ssb) noexcept
  {
  register bool r;

  __asm {
    lea ecx, ssb
    mov [ecx]statebuf.sp, esp
    mov [ecx]statebuf.label, offset _1f
    mov r, 0x0
    jmp _2f
    _1f : mov r, 0x1
    _2f :
  }

    return r;
  }
#else
# error "unsupported compiler"
#endif

#if defined(__GNUC__)
#if defined(i386) || defined(__i386) || defined(__i386__)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movl %0, %%esp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (ssb.sp), "m" (ssb.label)\
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movq %0, %%rsp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (SSB.sp), "m" (SSB.label)\
  );
#else
# error "unsupported architecture"
#endif
#elif defined(_MSC_VER)
#define restorestate(SSB)          \
  __asm lea eax, this.SSB        \
  __asm mov esp, [eax]statebuf.sp\
  __asm jmp [eax]statebuf.label
#else
# error "unsupported compiler"
#endif

#endif // SAVE_STATE_H
<|endoftext|>


<commit_before>#ifndef SAVE_STATE_H
# define SAVE_STATE_H
# pragma once

struct statebuf
{
  void* sp;
  void* bp;
  void* label;
};

#if defined(__GNUC__)
static inline bool __attribute__((always_inline)) savestate(
  statebuf& ssb) noexcept
{
  bool r;

#if defined(i386) || defined(__i386) || defined(__i386__)
  asm volatile (
    "movl %%esp, %0\n\t" // store sp
    "movl %%ebp, %1\n\t" // store bp
    "movl $1f, %2\n\t" // store label
    "movb $0, %3\n\t" // return false
    "jmp 2f\n\t"
    "1:\n\t"
    "movb $1, %3\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.bp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
  asm volatile (
    "movq %%rsp, %0\n\t" // store sp
    "movq %%rbp, %1\n\t" // store bp
    "movq $1f, %2\n\t" // store label
    "movb $0, %3\n\t" // return false
    "jmp 2f\n\t"
    "1:\n\t"
    "movb $1, %3\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.bp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#elif defined(__arm__)
  asm volatile (
    "str sp, %0\n\t" // store sp
    "str fp, %1\n\t" // store fp
    "ldr r3, =1f\n\t"// load label into r3
    "str r3, %2\n\t" // store r3 into label
    "mov %3, $0\n\t" // store 0 into result
    "b 2f\n\t"
    "1:"             // restore fp
    "mov %3, $1\n\t" // store 1 into result
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.bp), "=m" (ssb.label), "=r" (r)
    :
    : "r3", "memory"
  );
#endif

  return r;
}
#elif defined(_MSC_VER)
__forceinline bool savestate(statebuf& ssb) noexcept
{
  bool r;

  __asm {
    mov ebx, ssb
    mov [ebx]ssb.sp, esp
    mov [ebx]ssb.bp, ebp
    mov [ebx]ssb.label, offset _1f
    mov r, 0x0
    jmp _2f
    _1f:
    mov r, 0x1
    _2f:
  }

  return r;
}
#else
# error "unsupported compiler"
#endif

#if defined(__GNUC__)
#if defined(i386) || defined(__i386) || defined(__i386__)
#define restorestate(SSB)                        \
  asm volatile (                                 \
    "movl %0, %%esp\n\t"                         \
    "movl %1, %%ebp\n\t"                         \
    "jmp *%2"                                    \
    :                                            \
    : "m" (SSB.sp), "m" (SSB.bp), "m" (SSB.label)\
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define restorestate(SSB)                        \
  asm volatile (                                 \
    "movq %0, %%rsp\n\t"                         \
    "movq %1, %%rbp\n\t"                         \
    "jmp *%2"                                    \
    :                                            \
    : "m" (SSB.sp), "m" (SSB.bp), "m" (SSB.label)\
  );
#elif defined(__arm__)
#define restorestate(SSB)          \
  asm volatile (                   \
    "ldr sp, %0\n\t"               \
    "ldr fp, %1\n\t"               \
    "ldr pc, %2"                   \
    :                              \
    : "m" (SSB.sp), "m" (SSB.bp), "m" (SSB.label)\
  );
#else
# error "unsupported architecture"
#endif
#elif defined(_MSC_VER)
#define restorestate(SSB)   \
  __asm mov ebx, this       \
  __asm add ebx, [SSB]      \
  __asm mov esp, [ebx]SSB.sp\
  __asm mov ebp, [ebx]SSB.bp\
  __asm jmp [ebx]SSB.label
#else
# error "unsupported compiler"
#endif

#endif // SAVE_STATE_H
<commit_msg>some fixes<commit_after>#ifndef SAVE_STATE_H
# define SAVE_STATE_H
# pragma once

struct statebuf
{
  void* sp;
  void* bp;
  void* label;
};

#if defined(__GNUC__)
static inline bool __attribute__((always_inline)) savestate(
  statebuf& ssb) noexcept
{
  bool r;

#if defined(i386) || defined(__i386) || defined(__i386__)
  asm volatile (
    "movl %%esp, %0\n\t" // store sp
    "movl %%ebp, %1\n\t" // store bp
    "movl $1f, %2\n\t" // store label
    "movb $0, %3\n\t" // return false
    "jmp 2f\n\t"
    "1:\n\t"
    "movb $1, %3\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.bp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
  asm volatile (
    "movq %%rsp, %0\n\t" // store sp
    "movq %%rbp, %1\n\t" // store bp
    "movq $1f, %2\n\t" // store label
    "movb $0, %3\n\t" // return false
    "jmp 2f\n\t"
    "1:\n\t"
    "movb $1, %3\n\t" // return true
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.bp), "=m" (ssb.label), "=r" (r)
    :
    : "memory"
  );
#elif defined(__arm__)
  asm volatile (
    "str sp, %0\n\t" // store sp
    "str fp, %1\n\t" // store fp
    "ldr r3, =1f\n\t" // load label into r3
    "str r3, %2\n\t" // store r3 into label
    "mov %3, $0\n\t" // store 0 into result
    "b 2f\n\t"
    "1:"             // restore fp
    "mov %3, $1\n\t" // store 1 into result
    "2:"
    : "=m" (ssb.sp), "=m" (ssb.bp), "=m" (ssb.label), "=r" (r)
    :
    : "r3", "memory"
  );
#endif

  return r;
}
#elif defined(_MSC_VER)
__forceinline bool savestate(statebuf& ssb) noexcept
{
  bool r;

  __asm {
    mov ebx, ssb
    mov [ebx]ssb.sp, esp
    mov [ebx]ssb.bp, ebp
    mov [ebx]ssb.label, offset _1f
    mov r, 0x0
    jmp _2f
    _1f:
    mov r, 0x1
    _2f:
  }

  return r;
}
#else
# error "unsupported compiler"
#endif

#if defined(__GNUC__)
#if defined(i386) || defined(__i386) || defined(__i386__)
#define restorestate(SSB)                        \
  asm volatile (                                 \
    "movl %0, %%esp\n\t"                         \
    "movl %1, %%ebp\n\t"                         \
    "jmp *%2"                                    \
    :                                            \
    : "m" (SSB.sp), "m" (SSB.bp), "m" (SSB.label)\
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define restorestate(SSB)                        \
  asm volatile (                                 \
    "movq %0, %%rsp\n\t"                         \
    "movq %1, %%rbp\n\t"                         \
    "jmp *%2"                                    \
    :                                            \
    : "m" (SSB.sp), "m" (SSB.bp), "m" (SSB.label)\
  );
#elif defined(__arm__)
#define restorestate(SSB)                        \
  asm volatile (                                 \
    "ldr sp, %0\n\t"                             \
    "ldr fp, %1\n\t"                             \
    "ldr pc, %2"                                 \
    :                                            \
    : "m" (SSB.sp), "m" (SSB.bp), "m" (SSB.label)\
  );
#else
# error "unsupported architecture"
#endif
#elif defined(_MSC_VER)
#define restorestate(SSB)   \
  __asm mov ebx, this       \
  __asm add ebx, [SSB]      \
  __asm mov esp, [ebx]SSB.sp\
  __asm mov ebp, [ebx]SSB.bp\
  __asm jmp [ebx]SSB.label
#else
# error "unsupported compiler"
#endif

#endif // SAVE_STATE_H
<|endoftext|>