<commit_before>/*
 * Copyright (c) 2015-2018 Dubalu LLC
 *
 * 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.
 */

#include "traceback.h"

#include "config.h"           // for XAPIAND_TRACEBACKS

#include <mutex>              // for std::mutex, std::lock_guard
#include <cstdlib>            // for free
#include <cxxabi.h>           // for abi::__cxa_demangle
#include <dlfcn.h>            // for dladdr

#include "likely.h"

#ifndef NDEBUG
#ifndef XAPIAND_TRACEBACKS
#define XAPIAND_TRACEBACKS 1
#endif
#endif

#define BUFFER_SIZE 1024

#ifdef __APPLE__
#include <util.h>             // for forkpty
#include <unistd.h>           // for execlp
#include <termios.h>          // for termios, cfmakeraw
#ifdef HAVE_POLL
#include <poll.h>             // for poll
#else
#include <sys/select.h>       // for select
#endif
/* Use `atos` to do symbol lookup, can lookup non-dynamic symbols and also line
 * numbers. This function is more complicated than you'd expect because `atos`
 * doesn't flush after each line, so plain pipe() or socketpair() won't work
 * until we close the write side. But the whole point is we want to keep `atos`
 * around so we don't have to reprocess the symbol table over and over. What we
 * wind up doing is using `forkpty()` to make a new pseudoterminal for atos to
 * run in, and thus will use line-buffering for stdout, and then we can get
 * each line.
 */
static std::string
atos(const void* address)
{
	static std::mutex mtx;
	std::lock_guard lk(mtx);

	char tmp[20];
	static int fd = -1;
	if (fd == -1) {
		Dl_info info;
		memset(&info, 0, sizeof(Dl_info));
		if (dladdr(reinterpret_cast<const void*>(&atos), &info) == 0) {
			perror("Could not get base address for `atos`.");
			return "";
		}

		struct termios term_opts;
		cfmakeraw(&term_opts);  // have to set this first, otherwise queries echo until child kicks in
		pid_t childpid;
		if unlikely((childpid = forkpty(&fd, nullptr, &term_opts, nullptr)) < 0) {
			perror("Could not forkpty for `atos` call.");
			return "";
		}

		if (childpid == 0) {
			snprintf(tmp, sizeof(tmp), "%p", static_cast<const void *>(info.dli_fbase));
			execlp("/usr/bin/atos", "atos", "-o", info.dli_fname, "-l", tmp, nullptr);
			fprintf(stderr,"Could not exec `atos` for stack trace!\n");
			exit(1);
		}

		int size = snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);

		// atos can take a while to parse symbol table on first request, which
		// is why we leave it running if we see a delay, explain what's going on...
		int err = 0;

#ifdef HAVE_POLL
		struct pollfd fds;
		fds.fd = fd;
		fds.events = POLLIN;
		err = poll(&fds, 1, 3000);
#else
		if (fdin < FD_SETSIZE) {
			fd_set fds;
			FD_ZERO(&fds);
			FD_SET(fd, &fds);
			struct timeval tv = {3, 0};
			err = select(fd + 1, &fds, nullptr, nullptr, &tv);
		}
#endif
		if unlikely(err < 0) {
			perror("Generating... first call takes some time for `atos` to cache the symbol table.");
		} else if (err == 0) {  // timeout
			fprintf(stderr, "Generating... first call takes some time for `atos` to cache the symbol table.\n");
		}
	} else {
		int size = snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);
	}

	const unsigned int MAXLINE = 1024;
	char line[MAXLINE];
	char c = '\0';
	size_t nread = 0;
	while (c != '\n' && nread < MAXLINE) {
		if unlikely(read(fd, &c, 1) <= 0) {
			perror("Lost `atos` connection.");
			close(fd);
			fd = -1;
			return "";
		}
		if (c != '\n') {
			line[nread++] = c;
		}
	}
	if (nread < MAXLINE) {
		return std::string(line, nread);
	}
	fprintf(stderr, "Line read from `atos` was too long.\n");
	return "";
}
#else
static inline std::string
atos(const void*)
{
	return "";
}
#endif

std::string
traceback(const char* function, const char* filename, int line, const std::vector<void*>& callstack, int skip)
{
	char tmp[20];

	std::string tb = "\n== Traceback (most recent call first): ";
	tb.append(filename);
	tb.push_back(':');
	tb.append(std::to_string(line));
	tb.append(" at ");
	tb.append(function);

	int frames = callstack.size();

	if (frames == 0) {
		tb.append(":\n    <empty traceback>");
		return tb;
	}

	if (frames < 2) {
		tb.append(":\n    <no traceback>");
		return tb;
	}

	tb.push_back(':');

	// Iterate over the callstack. Skip the first, it is the address of this function.
	std::string result;
	for (int i = skip; i < frames; ++i) {
		auto address = callstack[i];

		result.assign(std::to_string(frames - i - 1));
		result.push_back(' ');

		auto address_string = atos(address);
		if (address_string.size() > 2 && address_string.compare(0, 2, "0x") != 0) {
			result.append(address_string);
		} else {
			Dl_info info;
			memset(&info, 0, sizeof(Dl_info));
			if (dladdr(address, &info) != 0) {
				// Address:
				snprintf(tmp, sizeof(tmp), "%p ", address);
				result.append(tmp);
				// Symbol name:
				if (info.dli_sname != nullptr) {
					int status = 0;
					char* unmangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
					if (status != 0) {
						result.append(info.dli_sname);
					} else {
						try {
							result.append(unmangled);
							free(unmangled);
						} catch(...) {
							free(unmangled);
							throw;
						}
					}
				} else {
					result.append("[unknown symbol]");
				}
				// Offset:
				snprintf(tmp, sizeof(tmp), " + %zu", static_cast<const char*>(address) - static_cast<const char*>(info.dli_saddr));
				result.append(tmp);
			} else {
				snprintf(tmp, sizeof(tmp), "%p [unknown symbol]", address);
				result.append(tmp);
			}
		}
		tb.append("\n    ");
		tb.append(result);
	}

	return tb;
}

std::string
traceback(const char* function, const char* filename, int line, int skip)
{
	std::vector<void*> callstack;

	// retrieve current stack addresses
	callstack.resize(128);
	callstack.resize(backtrace(callstack.data(), callstack.size()));
	callstack.shrink_to_fit();

	auto tb = traceback(function, filename, line, callstack, skip);

	return tb;
}

extern "C" void
__assert_tb(const char* function, const char* filename, unsigned int line, const char* expression)
{
#ifdef XAPIAND_TRACEBACKS
	(void)fprintf(stderr, "Assertion failed: %s in %s %s:%u%s\n",
		expression, function, filename, line, traceback(function, filename, line, 2).c_str());
#else
	(void)fprintf(stderr, "Assertion failed: %s in %s %s:%u\n",
		expression, function, filename, line);
#endif
	abort();
}
<commit_msg>Added missing include for memset<commit_after>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * 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.
 */

#include "traceback.h"

#include "config.h"           // for XAPIAND_TRACEBACKS

#include <cstdlib>            // for std::free, std::exit
#include <cstdio>             // for std::perror, std::snprintf, std::fprintf
#include <cstring>            // for std::memset
#include <cxxabi.h>           // for abi::__cxa_demangle
#include <dlfcn.h>            // for dladdr
#include <mutex>              // for std::mutex, std::lock_guard

#include "likely.h"

#ifndef NDEBUG
#ifndef XAPIAND_TRACEBACKS
#define XAPIAND_TRACEBACKS 1
#endif
#endif

#define BUFFER_SIZE 1024

#ifdef __APPLE__
#include <util.h>             // for forkpty
#include <unistd.h>           // for execlp
#include <termios.h>          // for termios, cfmakeraw
#ifdef HAVE_POLL
#include <poll.h>             // for poll
#else
#include <sys/select.h>       // for select
#endif
/* Use `atos` to do symbol lookup, can lookup non-dynamic symbols and also line
 * numbers. This function is more complicated than you'd expect because `atos`
 * doesn't flush after each line, so plain pipe() or socketpair() won't work
 * until we close the write side. But the whole point is we want to keep `atos`
 * around so we don't have to reprocess the symbol table over and over. What we
 * wind up doing is using `forkpty()` to make a new pseudoterminal for atos to
 * run in, and thus will use line-buffering for stdout, and then we can get
 * each line.
 */
static std::string
atos(const void* address)
{
	static std::mutex mtx;
	std::lock_guard lk(mtx);

	char tmp[20];
	static int fd = -1;
	if (fd == -1) {
		Dl_info info;
		std::memset(&info, 0, sizeof(Dl_info));
		if (dladdr(reinterpret_cast<const void*>(&atos), &info) == 0) {
			std::perror("Could not get base address for `atos`.");
			return "";
		}

		struct termios term_opts;
		cfmakeraw(&term_opts);  // have to set this first, otherwise queries echo until child kicks in
		pid_t childpid;
		if unlikely((childpid = forkpty(&fd, nullptr, &term_opts, nullptr)) < 0) {
			std::perror("Could not forkpty for `atos` call.");
			return "";
		}

		if (childpid == 0) {
			std::snprintf(tmp, sizeof(tmp), "%p", static_cast<const void *>(info.dli_fbase));
			execlp("/usr/bin/atos", "atos", "-o", info.dli_fname, "-l", tmp, nullptr);
			std::fprintf(stderr,"Could not exec `atos` for stack trace!\n");
			std::exit(1);
		}

		int size = std::snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);

		// atos can take a while to parse symbol table on first request, which
		// is why we leave it running if we see a delay, explain what's going on...
		int err = 0;

#ifdef HAVE_POLL
		struct pollfd fds;
		fds.fd = fd;
		fds.events = POLLIN;
		err = poll(&fds, 1, 3000);
#else
		if (fdin < FD_SETSIZE) {
			fd_set fds;
			FD_ZERO(&fds);
			FD_SET(fd, &fds);
			struct timeval tv = {3, 0};
			err = select(fd + 1, &fds, nullptr, nullptr, &tv);
		}
#endif
		if unlikely(err < 0) {
			std::perror("Generating... first call takes some time for `atos` to cache the symbol table.");
		} else if (err == 0) {  // timeout
			std::fprintf(stderr, "Generating... first call takes some time for `atos` to cache the symbol table.\n");
		}
	} else {
		int size = std::snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);
	}

	const unsigned int MAXLINE = 1024;
	char line[MAXLINE];
	char c = '\0';
	size_t nread = 0;
	while (c != '\n' && nread < MAXLINE) {
		if unlikely(read(fd, &c, 1) <= 0) {
			std::perror("Lost `atos` connection.");
			close(fd);
			fd = -1;
			return "";
		}
		if (c != '\n') {
			line[nread++] = c;
		}
	}
	if (nread < MAXLINE) {
		return std::string(line, nread);
	}
	std::fprintf(stderr, "Line read from `atos` was too long.\n");
	return "";
}
#else
static inline std::string
atos(const void*)
{
	return "";
}
#endif

std::string
traceback(const char* function, const char* filename, int line, const std::vector<void*>& callstack, int skip)
{
	char tmp[20];

	std::string tb = "\n== Traceback (most recent call first): ";
	tb.append(filename);
	tb.push_back(':');
	tb.append(std::to_string(line));
	tb.append(" at ");
	tb.append(function);

	int frames = callstack.size();

	if (frames == 0) {
		tb.append(":\n    <empty traceback>");
		return tb;
	}

	if (frames < 2) {
		tb.append(":\n    <no traceback>");
		return tb;
	}

	tb.push_back(':');

	// Iterate over the callstack. Skip the first, it is the address of this function.
	std::string result;
	for (int i = skip; i < frames; ++i) {
		auto address = callstack[i];

		result.assign(std::to_string(frames - i - 1));
		result.push_back(' ');

		auto address_string = atos(address);
		if (address_string.size() > 2 && address_string.compare(0, 2, "0x") != 0) {
			result.append(address_string);
		} else {
			Dl_info info;
			std::memset(&info, 0, sizeof(Dl_info));
			if (dladdr(address, &info) != 0) {
				// Address:
				std::snprintf(tmp, sizeof(tmp), "%p ", address);
				result.append(tmp);
				// Symbol name:
				if (info.dli_sname != nullptr) {
					int status = 0;
					char* unmangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
					if (status != 0) {
						result.append(info.dli_sname);
					} else {
						try {
							result.append(unmangled);
							std::free(unmangled);
						} catch(...) {
							std::free(unmangled);
							throw;
						}
					}
				} else {
					result.append("[unknown symbol]");
				}
				// Offset:
				std::snprintf(tmp, sizeof(tmp), " + %zu", static_cast<const char*>(address) - static_cast<const char*>(info.dli_saddr));
				result.append(tmp);
			} else {
				std::snprintf(tmp, sizeof(tmp), "%p [unknown symbol]", address);
				result.append(tmp);
			}
		}
		tb.append("\n    ");
		tb.append(result);
	}

	return tb;
}

std::string
traceback(const char* function, const char* filename, int line, int skip)
{
	std::vector<void*> callstack;

	// retrieve current stack addresses
	callstack.resize(128);
	callstack.resize(backtrace(callstack.data(), callstack.size()));
	callstack.shrink_to_fit();

	auto tb = traceback(function, filename, line, callstack, skip);

	return tb;
}

extern "C" void
__assert_tb(const char* function, const char* filename, unsigned int line, const char* expression)
{
#ifdef XAPIAND_TRACEBACKS
	(void)std::fprintf(stderr, "Assertion failed: %s in %s %s:%u%s\n",
		expression, function, filename, line, traceback(function, filename, line, 2).c_str());
#else
	(void)std::fprintf(stderr, "Assertion failed: %s in %s %s:%u\n",
		expression, function, filename, line);
#endif
	abort();
}
<|endoftext|>


<commit_before>
#include "CPipelineState.h"

#include <glad/glad.h>

namespace ion
{
	namespace Graphics
	{
		namespace GL
		{
			CPipelineState::CPipelineState(CWindow * Window)
			{
				this->Window = Window;
			}

			CPipelineState::~CPipelineState()
			{
			}

			void CPipelineState::SetProgram(SharedPointer<IShaderProgram> inShaderProgram)
			{
				if (inShaderProgram)
				{
					ShaderProgram = std::dynamic_pointer_cast<CShaderProgram>(inShaderProgram);
					if (! ShaderProgram->Linked)
					{
						ShaderProgram->Link();

						if (! ShaderProgram->Linked)
						{
							Log::Error("Failed to link shader prograg in PipelineState creation, unsetting shader.");
							ShaderProgram = nullptr;
							return;
						}
					}

					UnboundUniforms = KeySet(ShaderProgram->Uniforms);
					UnboundAttributes = KeySet(ShaderProgram->Attributes);

					Loaded = false;
				}
			}

			void CPipelineState::SetVertexBuffer(uint const Index, SharedPointer<IVertexBuffer> inVertexBuffer)
			{
				if (Index >= VertexBuffers.size())
				{
					VertexBuffers.resize(Index + 1);
				}

				VertexBuffers[Index] = std::dynamic_pointer_cast<CVertexBuffer>(inVertexBuffer);
				// Bound VBOs are not part of VAO state

				Loaded = false;
			}

			void CPipelineState::SetIndexBuffer(SharedPointer<IIndexBuffer> inIndexBuffer)
			{
				IndexBuffer = std::dynamic_pointer_cast<CIndexBuffer>(inIndexBuffer);
				Window->MakeContextCurrent();
				SafeGLCall(glBindVertexArray, (VertexArrayHandle));
				SafeGLCall(glBindBuffer, (GL_ELEMENT_ARRAY_BUFFER, IndexBuffer->Handle));
				SafeGLCall(glBindVertexArray, (0));

				Loaded = false;
			}

			void CPipelineState::SetUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Uniform)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Uniforms[Name] = Uniform;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Uniforms.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Texture)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Textures[Name] = Texture;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Textures.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetFeatureEnabled(EDrawFeature const Feature, bool const Enabled)
			{
				switch (Feature)
				{
				case EDrawFeature::Wireframe:
					DrawWireframe = Enabled;
					break;
				case EDrawFeature::CullFront:
					CullFront = Enabled;
					break;
				case EDrawFeature::CullBack:
					CullBack = Enabled;
					break;
				case EDrawFeature::DisableDepthTest:
					DisableDepthTest = Enabled;
					break;
				case EDrawFeature::DisableDepthWrite:
					DisableDepthWrite = Enabled;
					break;
				}
			}

			void CPipelineState::SetBlendMode(EBlendMode const BlendMode)
			{
				this->BlendMode = BlendMode;
			}

			void CPipelineState::OfferUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Uniform)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferUniform: expected non-null Uniform");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Uniforms[Name] = Uniform;
					UnboundUniforms.erase(Name);
				}
			}

			void CPipelineState::OfferTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Texture)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferUniform: expected non-null Uniform");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Textures[Name] = Texture;
					UnboundUniforms.erase(Name);
				}
			}

			set<string> CPipelineState::GetUnboundUniforms() const
			{
				return UnboundUniforms;
			}

			void CPipelineState::Load()
			{
				if (! ShaderProgram || VertexBuffers.empty() || ! IndexBuffer)
				{
					Log::Error("Attempting to load an invalid PipelineState");
					return;
				}

				Window->MakeContextCurrent();
				CheckedGLCall(glUseProgram(ShaderProgram->Handle));
				CheckedGLCall(glBindVertexArray(VertexArrayHandle));

				for (auto & VertexBuffer : VertexBuffers)
				{

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer->Handle));

					//////////////////////////////
					// Set up VBOs (attributes) //
					//////////////////////////////

					// Calculate stride of VBO data
					size_t TotalStride = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						TotalStride += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					size_t CurrentOffset = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						pair<uint, uint> AttributeInfo;
						if (TryMapAccess(ShaderProgram->Attributes, InputLayoutElement.Name, AttributeInfo))
						{
							uint const AttributeLocation = AttributeInfo.first;
							uint const AttributeType = AttributeInfo.second;

							// Validate Attribute Type (does the VBO layout match what the shader wants?)
							{
								bool IsAttributeTypeCorrect = false;
								string ShaderAttributeTypeString = "Unknown";
								switch (AttributeType)
								{
								default:
									Log::Error("Unexpected type for attribute %s: %u", InputLayoutElement.Name, AttributeType);
									break;
								case GL_FLOAT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_FLOAT";
									break;
								case GL_FLOAT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_FLOAT_VEC2";
									break;
								case GL_FLOAT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_FLOAT_VEC3";
									break;
								case GL_FLOAT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_FLOAT_VEC4";
									break;
								case GL_INT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_INT";
									break;
								case GL_INT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_INT_VEC2";
									break;
								case GL_INT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_INT_VEC3";
									break;
								case GL_INT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_INT_VEC4";
									break;
								}

								if (! IsAttributeTypeCorrect)
								{
									Log::Error("Mistmatch for attribute type '%s': VBO supplied %d components of type %s but shader expected '%s'",
										InputLayoutElement.Name,
										InputLayoutElement.Components,
										GetAttributeTypeString(InputLayoutElement.Type),
										ShaderAttributeTypeString);
								}
							}

							CheckedGLCall(glEnableVertexAttribArray(AttributeLocation));

							switch (AttributeType)
							{
							case GL_FLOAT:
							case GL_FLOAT_VEC2:
							case GL_FLOAT_VEC3:
							case GL_FLOAT_VEC4:
								CheckedGLCall(glVertexAttribPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									GL_FALSE,
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							case GL_INT:
							case GL_INT_VEC2:
							case GL_INT_VEC3:
							case GL_INT_VEC4:
								CheckedGLCall(glVertexAttribIPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							}

							if (VertexBuffer->Instancing)
							{
								CheckedGLCall(glVertexAttribDivisor(AttributeLocation, 1));
							}

							UnboundAttributes.erase(InputLayoutElement.Name);
						}

						CurrentOffset += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); // Remember, VBOs are not part of VAO state (that's why we never leave them set in the VAO)
				}

				std::for_each(UnboundAttributes.begin(), UnboundAttributes.end(), [](string const & Attribuite)
				{
					Log::Error("Attribute expected by shader but not provided by VBO: %s", Attribuite);
				});

				CheckedGLCall(glBindVertexArray(0));
				CheckedGLCall(glUseProgram(0));

				/////////////////////
				// Set up Uniforms //
				/////////////////////

				BoundUniforms.clear();
				for (auto const & it : Uniforms)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundUniforms[Handle] = it.second;
					}
				}
				for (auto const & it : Textures)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundTextures[Handle] = it.second;
					}
				}

				std::for_each(UnboundUniforms.begin(), UnboundUniforms.end(), [](string const & Uniform)
				{
					Log::Error("Uniform expected by shader but not provided by PSO: %s", Uniform);
				});

				Loaded = true;
			}

		}
	}
}
<commit_msg>[ionGraphics] Fix incorrect error message in CPipelineState::OfferTexture<commit_after>
#include "CPipelineState.h"

#include <glad/glad.h>

namespace ion
{
	namespace Graphics
	{
		namespace GL
		{
			CPipelineState::CPipelineState(CWindow * Window)
			{
				this->Window = Window;
			}

			CPipelineState::~CPipelineState()
			{
			}

			void CPipelineState::SetProgram(SharedPointer<IShaderProgram> inShaderProgram)
			{
				if (inShaderProgram)
				{
					ShaderProgram = std::dynamic_pointer_cast<CShaderProgram>(inShaderProgram);
					if (! ShaderProgram->Linked)
					{
						ShaderProgram->Link();

						if (! ShaderProgram->Linked)
						{
							Log::Error("Failed to link shader prograg in PipelineState creation, unsetting shader.");
							ShaderProgram = nullptr;
							return;
						}
					}

					UnboundUniforms = KeySet(ShaderProgram->Uniforms);
					UnboundAttributes = KeySet(ShaderProgram->Attributes);

					Loaded = false;
				}
			}

			void CPipelineState::SetVertexBuffer(uint const Index, SharedPointer<IVertexBuffer> inVertexBuffer)
			{
				if (Index >= VertexBuffers.size())
				{
					VertexBuffers.resize(Index + 1);
				}

				VertexBuffers[Index] = std::dynamic_pointer_cast<CVertexBuffer>(inVertexBuffer);
				// Bound VBOs are not part of VAO state

				Loaded = false;
			}

			void CPipelineState::SetIndexBuffer(SharedPointer<IIndexBuffer> inIndexBuffer)
			{
				IndexBuffer = std::dynamic_pointer_cast<CIndexBuffer>(inIndexBuffer);
				Window->MakeContextCurrent();
				SafeGLCall(glBindVertexArray, (VertexArrayHandle));
				SafeGLCall(glBindBuffer, (GL_ELEMENT_ARRAY_BUFFER, IndexBuffer->Handle));
				SafeGLCall(glBindVertexArray, (0));

				Loaded = false;
			}

			void CPipelineState::SetUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Uniform)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Uniforms[Name] = Uniform;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Uniforms.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Texture)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Textures[Name] = Texture;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Textures.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetFeatureEnabled(EDrawFeature const Feature, bool const Enabled)
			{
				switch (Feature)
				{
				case EDrawFeature::Wireframe:
					DrawWireframe = Enabled;
					break;
				case EDrawFeature::CullFront:
					CullFront = Enabled;
					break;
				case EDrawFeature::CullBack:
					CullBack = Enabled;
					break;
				case EDrawFeature::DisableDepthTest:
					DisableDepthTest = Enabled;
					break;
				case EDrawFeature::DisableDepthWrite:
					DisableDepthWrite = Enabled;
					break;
				}
			}

			void CPipelineState::SetBlendMode(EBlendMode const BlendMode)
			{
				this->BlendMode = BlendMode;
			}

			void CPipelineState::OfferUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Uniform)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferUniform: expected non-null Uniform");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Uniforms[Name] = Uniform;
					UnboundUniforms.erase(Name);
				}
			}

			void CPipelineState::OfferTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Texture)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferTexture: expected non-null Texture");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Textures[Name] = Texture;
					UnboundUniforms.erase(Name);
				}
			}

			set<string> CPipelineState::GetUnboundUniforms() const
			{
				return UnboundUniforms;
			}

			void CPipelineState::Load()
			{
				if (! ShaderProgram || VertexBuffers.empty() || ! IndexBuffer)
				{
					Log::Error("Attempting to load an invalid PipelineState");
					return;
				}

				Window->MakeContextCurrent();
				CheckedGLCall(glUseProgram(ShaderProgram->Handle));
				CheckedGLCall(glBindVertexArray(VertexArrayHandle));

				for (auto & VertexBuffer : VertexBuffers)
				{

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer->Handle));

					//////////////////////////////
					// Set up VBOs (attributes) //
					//////////////////////////////

					// Calculate stride of VBO data
					size_t TotalStride = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						TotalStride += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					size_t CurrentOffset = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						pair<uint, uint> AttributeInfo;
						if (TryMapAccess(ShaderProgram->Attributes, InputLayoutElement.Name, AttributeInfo))
						{
							uint const AttributeLocation = AttributeInfo.first;
							uint const AttributeType = AttributeInfo.second;

							// Validate Attribute Type (does the VBO layout match what the shader wants?)
							{
								bool IsAttributeTypeCorrect = false;
								string ShaderAttributeTypeString = "Unknown";
								switch (AttributeType)
								{
								default:
									Log::Error("Unexpected type for attribute %s: %u", InputLayoutElement.Name, AttributeType);
									break;
								case GL_FLOAT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_FLOAT";
									break;
								case GL_FLOAT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_FLOAT_VEC2";
									break;
								case GL_FLOAT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_FLOAT_VEC3";
									break;
								case GL_FLOAT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_FLOAT_VEC4";
									break;
								case GL_INT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_INT";
									break;
								case GL_INT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_INT_VEC2";
									break;
								case GL_INT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_INT_VEC3";
									break;
								case GL_INT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_INT_VEC4";
									break;
								}

								if (! IsAttributeTypeCorrect)
								{
									Log::Error("Mistmatch for attribute type '%s': VBO supplied %d components of type %s but shader expected '%s'",
										InputLayoutElement.Name,
										InputLayoutElement.Components,
										GetAttributeTypeString(InputLayoutElement.Type),
										ShaderAttributeTypeString);
								}
							}

							CheckedGLCall(glEnableVertexAttribArray(AttributeLocation));

							switch (AttributeType)
							{
							case GL_FLOAT:
							case GL_FLOAT_VEC2:
							case GL_FLOAT_VEC3:
							case GL_FLOAT_VEC4:
								CheckedGLCall(glVertexAttribPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									GL_FALSE,
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							case GL_INT:
							case GL_INT_VEC2:
							case GL_INT_VEC3:
							case GL_INT_VEC4:
								CheckedGLCall(glVertexAttribIPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							}

							if (VertexBuffer->Instancing)
							{
								CheckedGLCall(glVertexAttribDivisor(AttributeLocation, 1));
							}

							UnboundAttributes.erase(InputLayoutElement.Name);
						}

						CurrentOffset += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); // Remember, VBOs are not part of VAO state (that's why we never leave them set in the VAO)
				}

				std::for_each(UnboundAttributes.begin(), UnboundAttributes.end(), [](string const & Attribuite)
				{
					Log::Error("Attribute expected by shader but not provided by VBO: %s", Attribuite);
				});

				CheckedGLCall(glBindVertexArray(0));
				CheckedGLCall(glUseProgram(0));

				/////////////////////
				// Set up Uniforms //
				/////////////////////

				BoundUniforms.clear();
				for (auto const & it : Uniforms)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundUniforms[Handle] = it.second;
					}
				}
				for (auto const & it : Textures)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundTextures[Handle] = it.second;
					}
				}

				std::for_each(UnboundUniforms.begin(), UnboundUniforms.end(), [](string const & Uniform)
				{
					Log::Error("Uniform expected by shader but not provided by PSO: %s", Uniform);
				});

				Loaded = true;
			}

		}
	}
}
<|endoftext|>


<commit_before>/**
 * Convert a set of aperiodic Wang tiles to an aperiodic set of "action" tiles.
 * A set of action tiles has 2 properties:
 * 1. The tiles are SE-identical.
 *    - South and east edge colors are the same.
 * 2. The set is W-disabling.
 *    - No two tiles having the same west edge color
 *      can be placed directly above/below each other
 *      (i.e., the north color of one tile differs from
 *      the south color of the other tile, and vice-versa).
 *
 * This particular tile set is NW-deterministic, and therefore the action
 * tiles can be used to create a deterministic unidirectional ring protocol
 * that always terminates from any initial state on any ring size,
 * but it is quite hard to prove!
 *
 * This reduction is tweaked from my paper:
 *   "Verifying Livelock Freedom on Parameterized Rings and Chains"
 *   http://dx.doi.org/10.1007/978-3-319-03089-0_12
 * See the paper or tech report for details about problem significance,
 * but see the comment above ReduceToActionTiles() for the new reduction.
 * It will eventually appear in a journal version.
 *
 **/
extern "C" {
#include "cx/syscx.h"
}
#include "cx/synhax.hh"

#include "cx/alphatab.hh"
#include "cx/map.hh"
#include "cx/ofile.hh"
#include "cx/set.hh"
#include "cx/table.hh"
#include "cx/tuple.hh"

using Cx::mk_Tuple;

// Tile set originally from:
//   Grunbaum and Shephard's 1986 book "Tilings and Patterns".
// I pulled it from:
//   Hurd, Kari, and Culik's 1992 paper "The Topological Entropy of Cellular Automata is Uncomputable".
// It's also on Wikipedia as the size 16 set:
//   https://en.wikipedia.org/wiki/List_of_aperiodic_sets_of_tiles
// Edge colors are listed in order: west, north, south, east.
static const uint TileSet[][4] = {
//  W  N  S  E
  { 1, 1, 2, 2 },
  { 1, 5, 4, 1 },
  { 2, 3, 6, 2 },
  { 2, 4, 6, 1 },
  { 2, 5, 3, 1 },
  { 3, 2, 2, 6 },
  { 3, 3, 4, 4 },
  { 3, 4, 4, 5 },
  { 3, 6, 4, 3 },
  { 4, 2, 1, 6 },
  { 4, 3, 5, 4 },
  { 4, 4, 5, 5 },
  { 5, 1, 1, 4 },
  { 5, 2, 1, 3 },
  { 6, 3, 3, 4 },
  { 6, 6, 3, 3 }
};

/** Lookup/create a unique id for a symbol.
 *
 * A symbol is an array of color values from the input Wang tile set.
 **/
static
  uint
LookupSymId (Cx::Map< Cx::Table<uint>, uint >& idmap, const Cx::Table<uint>& key)
{
  return idmap.ensure(key, idmap.sz());
}

/** Populate {ret_acts}, return domain size.
 *
 * Each input Wang tile is converted to some action tiles by the following transformation:
 *                     ________ ________
 *                    |   b1   |    $   |
 *    ________        |        |        |
 *   |    b   |       |a0  abcd|abcd  d0|
 *   |        |       |        |        |
 *   |a      d|  -->  |__abcd__|___d0___|
 *   |        |       |  abcd  |   d0   |
 *   |____c___|       |        |        |
 *                    |$     c1|c1     $|
 *                    |        |        |
 *                    |___c1___|____$___|
 **/
static
  uint
ReduceToActionTiles (Cx::Table< Cx::Tuple<uint,3> >& ret_acts, const Cx::Table< Cx::Tuple<uint,4> >& tiles)
{
  Cx::Map< Cx::Table<uint>, uint > idmap;
  Cx::Set< Cx::Tuple<uint,3> > acts;

  // This is the $ symbol.
  const uint blank = LookupSymId (idmap, Cx::Table<uint>());

  // Reserve low symbol ids (i.e., action tile colors) in the action tile set
  // for those corresponding to Wang tile colors.
  {
    Cx::Set<uint> ri_colors, up_colors;
    for (uint tile_idx = 0; tile_idx < tiles.sz(); ++tile_idx) {
      const Cx::Tuple<uint,4>& tile = tiles[tile_idx];
      up_colors << tile[0] << tile[3];
      ri_colors << tile[1] << tile[2];
    }
    Cx::Set<uint>::const_iterator it;
    for (it = ri_colors.begin(); it != ri_colors.end(); ++it) {
      Cx::Table<uint> sym;
      LookupSymId (idmap, (sym << *it << 0));
    }
    for (it = up_colors.begin(); it != up_colors.end(); ++it) {
      Cx::Table<uint> sym;
      LookupSymId (idmap, (sym << *it << 1));
    }
  }

  for (uint tile_idx = 0; tile_idx < tiles.sz(); ++tile_idx) {
    const Cx::Tuple<uint,4>& tile = tiles[tile_idx];
    Cx::Table<uint> sym;

    sym << tile[0] << tile[1] << tile[2] << tile[3];
    const uint actcolor = LookupSymId (idmap, sym);

    sym.flush() << tile[0] << 0;
    const uint a_ri = LookupSymId (idmap, sym);

    sym.flush() << tile[1] << 1;
    const uint b_up = LookupSymId (idmap, sym);

    sym.flush() << tile[2] << 1;
    const uint c_up = LookupSymId (idmap, sym);

    sym.flush() << tile[3] << 0;
    const uint d_ri = LookupSymId (idmap, sym);

    acts << mk_Tuple(a_ri, b_up, actcolor);
    acts << mk_Tuple(blank, actcolor, c_up);
    acts << mk_Tuple(actcolor, blank, d_ri);
    acts << mk_Tuple(c_up, d_ri, blank);
  }
  acts.fill(ret_acts);
  return idmap.sz();
}

/** Execute me now!**/
int main (int argc, char** argv)
{
  int argi = init_sysCx (&argc, &argv);
  const char* arg = argv[argi];

  if (argi+1 != argc || eq_cstr("-h", arg)) {
    failout_sysCx ("Expect one argument of: -gv, -list, -pml, -prot");
    return 1;
  }

  Cx::Table< Cx::Tuple<uint,4> > wtiles;
  for (uint i = 0; i < ArraySz(TileSet); ++i) {
    const uint* t = TileSet[i];
    wtiles << mk_Tuple(t[0], t[1], t[2], t[3]);
  }

  // Compute equivalent action tiles.
  Cx::Table< Cx::Tuple<uint,3> > acts;
  const uint domsz = ReduceToActionTiles(acts, wtiles);

  Cx::OFile ofile(stdout_OFile ());
  if (eq_cstr ("-list", arg)) {
    for (uint i = 0; i < acts.sz(); ++i) {
      ofile.printf ("%3u %3u %3u\n", acts[i][0], acts[i][1], acts[i][2]);
    }
  }
  else if (eq_cstr ("-gv", arg)) {
    Cx::Map<Cx::Tuple<uint,2>, Cx::String> edges;
    for (uint i = 0; i < acts.sz(); ++i) {
      edges[mk_Tuple(acts[i][0], acts[i][2])].push_delim("|") << acts[i][1];
    }

    ofile << "digraph {";
    Cx::Map<Cx::Tuple<uint,2>, Cx::String>::const_iterator it;
    for (it = edges.begin(); it != edges.end(); ++it) {
      const Cx::Tuple<uint,2> edge = it->first;
      const Cx::String label = it->second;
      ofile.printf ("\n  %3u -> %3u [label = \"%s\"];", edge[0], edge[1], label.ccstr());
    }
    ofile << "\n}\n";
  }
  else if (eq_cstr ("-pml", arg)) {
    ofile
      << "// Promela file. For better quality, use Protocon to create this."
      << "\n#define N 4"
      << "\nbyte x[N];"
      << "\nbyte initializing = N;"
      << "\n#define x_sc x[(_pid+N-1)%N]"
      << "\n#define x_id x[_pid]"
      << "\n#define UniAct(a,b,c)  atomic { (x_sc == a) && (x_id == b) -> x_id = c; }"
      << "\nactive[N] proctype P()"
      << "\n{"
      << "\n  atomic {"
      << "\n    byte tmp;"
      ;
    ofile.printf("\n    select(tmp : 0..%u);", domsz-1);
    ofile
      << "\n    x_id = tmp;"
      << "\n    initializing --;"
      << "\n  }"
      << "\n  (initializing==0);"
      << "\nend_P:"
      << "\n  do"
      ;

    for (uint i = 0; i < acts.sz(); ++i) {
      ofile.printf ("\n  :: UniAct( %3u, %3u, %3u )",
                    acts[i][0], acts[i][1], acts[i][2]);
    }
    ofile
      << "\n  od;"
      << "\n}\n"
      ;
  }
  else if (eq_cstr ("-prot", arg)) {
    ofile
      << "// Protocon file."
      << "\nconstant N := 3;"
      ;
    ofile.printf ("\nconstant M := %u;", domsz);
    ofile.printf ("\nconstant NActs := %u;", acts.sz());
    for (uint abc = 0; abc < 3; ++abc) {
      ofile << "\nconstant " << (char)('a' + abc) << " := ( ";
      for (uint i = 0; i < acts.sz(); ++i) {
        if (i > 0)  ofile << ", ";
        ofile << acts[i][abc];
      }
      ofile << " );";
    }

    ofile
      << "\nvariable x[Nat % N] <- Nat % M;"
      << "\nprocess P[i <- Nat % N]"
      << "\n{"
      << "\n  read: x[i-1];"
      << "\n  write: x[i];"
      << "\n  (future & silent)"
      << "\n    (forall j <- Nat % NActs : !(x[i-1]==a[j] && x[i]==b[j]));"
      << "\n  puppet:"
      ;
    for (uint i = 0; i < acts.sz(); ++i) {
      ofile.printf ("\n    ( x[i-1]==a[%u] && x[i]==b[%u] --> x[i]:=c[%u]; )", i, i, i);
    }
    ofile
      << "\n    ;"
      << "\n}\n";
  }

  lose_sysCx ();
  return 0;
}

<commit_msg>Simplify some variable names in {unidomino}<commit_after>/**
 * Convert a set of aperiodic Wang tiles to an aperiodic set of "action" tiles.
 * A set of action tiles has 2 properties:
 * 1. The tiles are SE-identical.
 *    - South and east edge colors are the same.
 * 2. The set is W-disabling.
 *    - No two tiles having the same west edge color
 *      can be placed directly above/below each other
 *      (i.e., the north color of one tile differs from
 *      the south color of the other tile, and vice-versa).
 *
 * This particular tile set is NW-deterministic, and therefore the action
 * tiles can be used to create a deterministic unidirectional ring protocol
 * that always terminates from any initial state on any ring size,
 * but it is quite hard to prove!
 *
 * This reduction is tweaked from my paper:
 *   "Verifying Livelock Freedom on Parameterized Rings and Chains"
 *   http://dx.doi.org/10.1007/978-3-319-03089-0_12
 * See the paper or tech report for details about problem significance,
 * but see the comment above ReduceToActionTiles() for the new reduction.
 * It will eventually appear in a journal version.
 *
 **/
extern "C" {
#include "cx/syscx.h"
}
#include "cx/synhax.hh"

#include "cx/alphatab.hh"
#include "cx/map.hh"
#include "cx/ofile.hh"
#include "cx/set.hh"
#include "cx/table.hh"
#include "cx/tuple.hh"

using Cx::mk_Tuple;

// Tile set originally from:
//   Grunbaum and Shephard's 1986 book "Tilings and Patterns".
// I pulled it from:
//   Hurd, Kari, and Culik's 1992 paper "The Topological Entropy of Cellular Automata is Uncomputable".
// It's also on Wikipedia as the size 16 set:
//   https://en.wikipedia.org/wiki/List_of_aperiodic_sets_of_tiles
// Edge colors are listed in order: west, north, south, east.
static const uint TileSet[][4] = {
//  W  N  S  E
  { 1, 1, 2, 2 },
  { 1, 5, 4, 1 },
  { 2, 3, 6, 2 },
  { 2, 4, 6, 1 },
  { 2, 5, 3, 1 },
  { 3, 2, 2, 6 },
  { 3, 3, 4, 4 },
  { 3, 4, 4, 5 },
  { 3, 6, 4, 3 },
  { 4, 2, 1, 6 },
  { 4, 3, 5, 4 },
  { 4, 4, 5, 5 },
  { 5, 1, 1, 4 },
  { 5, 2, 1, 3 },
  { 6, 3, 3, 4 },
  { 6, 6, 3, 3 }
};

/** Lookup/create a unique id for a symbol.
 *
 * A symbol is an array of color values from the input Wang tile set.
 **/
static
  uint
LookupSymId (Cx::Map< Cx::Table<uint>, uint >& idmap, const Cx::Table<uint>& key)
{
  return idmap.ensure(key, idmap.sz());
}

/** Populate {ret_acts}, return domain size.
 *
 * Each input Wang tile is converted to some action tiles by the following transformation:
 *                     ________ ________
 *                    |   b1   |    $   |
 *    ________        |        |        |
 *   |    b   |       |a0  abcd|abcd  d0|
 *   |        |       |        |        |
 *   |a      d|  -->  |__abcd__|___d0___|
 *   |        |       |  abcd  |   d0   |
 *   |____c___|       |        |        |
 *                    |$     c1|c1     $|
 *                    |        |        |
 *                    |___c1___|____$___|
 **/
static
  uint
ReduceToActionTiles (Cx::Table< Cx::Tuple<uint,3> >& ret_acts, const Cx::Table< Cx::Tuple<uint,4> >& tiles)
{
  Cx::Map< Cx::Table<uint>, uint > idmap;
  Cx::Set< Cx::Tuple<uint,3> > acts;

  // This is the $ symbol.
  const uint blank = LookupSymId (idmap, Cx::Table<uint>());

  // Reserve low symbol ids (i.e., action tile colors) in the action tile set
  // for those corresponding to Wang tile colors.
  {
    Cx::Set<uint> ri_colors, up_colors;
    for (uint tile_idx = 0; tile_idx < tiles.sz(); ++tile_idx) {
      const Cx::Tuple<uint,4>& tile = tiles[tile_idx];
      up_colors << tile[0] << tile[3];
      ri_colors << tile[1] << tile[2];
    }
    Cx::Set<uint>::const_iterator it;
    for (it = ri_colors.begin(); it != ri_colors.end(); ++it) {
      Cx::Table<uint> sym;
      LookupSymId (idmap, (sym << *it << 0));
    }
    for (it = up_colors.begin(); it != up_colors.end(); ++it) {
      Cx::Table<uint> sym;
      LookupSymId (idmap, (sym << *it << 1));
    }
  }

  for (uint tile_idx = 0; tile_idx < tiles.sz(); ++tile_idx) {
    const Cx::Tuple<uint,4>& tile = tiles[tile_idx];
    Cx::Table<uint> sym;

    sym << tile[0] << tile[1] << tile[2] << tile[3];
    const uint abcd = LookupSymId (idmap, sym);

    sym.flush() << tile[0] << 0;
    const uint a0 = LookupSymId (idmap, sym);

    sym.flush() << tile[1] << 1;
    const uint b1 = LookupSymId (idmap, sym);

    sym.flush() << tile[2] << 1;
    const uint c1 = LookupSymId (idmap, sym);

    sym.flush() << tile[3] << 0;
    const uint d0 = LookupSymId (idmap, sym);

    acts << mk_Tuple( a0    , b1    , abcd  );
    acts << mk_Tuple( blank , abcd  , c1    );
    acts << mk_Tuple( abcd  , blank , d0    );
    acts << mk_Tuple( c1    , d0    , blank );
  }
  acts.fill(ret_acts);
  return idmap.sz();
}

/** Execute me now!**/
int main (int argc, char** argv)
{
  int argi = init_sysCx (&argc, &argv);
  const char* arg = argv[argi];

  if (argi+1 != argc || eq_cstr("-h", arg)) {
    failout_sysCx ("Expect one argument of: -gv, -list, -pml, -prot");
    return 1;
  }

  Cx::Table< Cx::Tuple<uint,4> > wtiles;
  for (uint i = 0; i < ArraySz(TileSet); ++i) {
    const uint* t = TileSet[i];
    wtiles << mk_Tuple(t[0], t[1], t[2], t[3]);
  }

  // Compute equivalent action tiles.
  Cx::Table< Cx::Tuple<uint,3> > acts;
  const uint domsz = ReduceToActionTiles(acts, wtiles);

  Cx::OFile ofile(stdout_OFile ());
  if (eq_cstr ("-list", arg)) {
    for (uint i = 0; i < acts.sz(); ++i) {
      ofile.printf ("%3u %3u %3u\n", acts[i][0], acts[i][1], acts[i][2]);
    }
  }
  else if (eq_cstr ("-gv", arg)) {
    Cx::Map<Cx::Tuple<uint,2>, Cx::String> edges;
    for (uint i = 0; i < acts.sz(); ++i) {
      edges[mk_Tuple(acts[i][0], acts[i][2])].push_delim("|") << acts[i][1];
    }

    ofile << "digraph {";
    Cx::Map<Cx::Tuple<uint,2>, Cx::String>::const_iterator it;
    for (it = edges.begin(); it != edges.end(); ++it) {
      const Cx::Tuple<uint,2> edge = it->first;
      const Cx::String label = it->second;
      ofile.printf ("\n  %3u -> %3u [label = \"%s\"];", edge[0], edge[1], label.ccstr());
    }
    ofile << "\n}\n";
  }
  else if (eq_cstr ("-pml", arg)) {
    ofile
      << "// Promela file. For better quality, use Protocon to create this."
      << "\n#define N 4"
      << "\nbyte x[N];"
      << "\nbyte initializing = N;"
      << "\n#define x_sc x[(_pid+N-1)%N]"
      << "\n#define x_id x[_pid]"
      << "\n#define UniAct(a,b,c)  atomic { (x_sc == a) && (x_id == b) -> x_id = c; }"
      << "\nactive[N] proctype P()"
      << "\n{"
      << "\n  atomic {"
      << "\n    byte tmp;"
      ;
    ofile.printf("\n    select(tmp : 0..%u);", domsz-1);
    ofile
      << "\n    x_id = tmp;"
      << "\n    initializing --;"
      << "\n  }"
      << "\n  (initializing==0);"
      << "\nend_P:"
      << "\n  do"
      ;

    for (uint i = 0; i < acts.sz(); ++i) {
      ofile.printf ("\n  :: UniAct( %3u, %3u, %3u )",
                    acts[i][0], acts[i][1], acts[i][2]);
    }
    ofile
      << "\n  od;"
      << "\n}\n"
      ;
  }
  else if (eq_cstr ("-prot", arg)) {
    ofile
      << "// Protocon file."
      << "\nconstant N := 3;"
      ;
    ofile.printf ("\nconstant M := %u;", domsz);
    ofile.printf ("\nconstant NActs := %u;", acts.sz());
    for (uint abc = 0; abc < 3; ++abc) {
      ofile << "\nconstant " << (char)('a' + abc) << " := ( ";
      for (uint i = 0; i < acts.sz(); ++i) {
        if (i > 0)  ofile << ", ";
        ofile << acts[i][abc];
      }
      ofile << " );";
    }

    ofile
      << "\nvariable x[Nat % N] <- Nat % M;"
      << "\nprocess P[i <- Nat % N]"
      << "\n{"
      << "\n  read: x[i-1];"
      << "\n  write: x[i];"
      << "\n  (future & silent)"
      << "\n    (forall j <- Nat % NActs : !(x[i-1]==a[j] && x[i]==b[j]));"
      << "\n  puppet:"
      ;
    for (uint i = 0; i < acts.sz(); ++i) {
      ofile.printf ("\n    ( x[i-1]==a[%u] && x[i]==b[%u] --> x[i]:=c[%u]; )", i, i, i);
    }
    ofile
      << "\n    ;"
      << "\n}\n";
  }

  lose_sysCx ();
  return 0;
}

<|endoftext|>


<commit_before>//===-- ARMAsmBackend.cpp - ARM Assembler Backend -------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Target/TargetAsmBackend.h"
#include "ARM.h"
#include "ARMFixupKinds.h"
#include "llvm/ADT/Twine.h"
#include "llvm/MC/ELFObjectWriter.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCObjectFormat.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MachObjectWriter.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/MachO.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetRegistry.h"
#include "llvm/Target/TargetAsmBackend.h"
using namespace llvm;

namespace {
class ARMAsmBackend : public TargetAsmBackend {
public:
  ARMAsmBackend(const Target &T)
    : TargetAsmBackend(T) {
  }

  bool MayNeedRelaxation(const MCInst &Inst) const;

  void RelaxInstruction(const MCInst &Inst, MCInst &Res) const;

  bool WriteNopData(uint64_t Count, MCObjectWriter *OW) const;

  unsigned getPointerSize() const {
    return 4;
  }
};

bool ARMAsmBackend::MayNeedRelaxation(const MCInst &Inst) const {
  // FIXME: Thumb targets, different move constant targets..
  return false;
}

void ARMAsmBackend::RelaxInstruction(const MCInst &Inst, MCInst &Res) const {
  assert(0 && "ARMAsmBackend::RelaxInstruction() unimplemented");
  return;
}

bool ARMAsmBackend::WriteNopData(uint64_t Count, MCObjectWriter *OW) const {
  if ((Count % 4) != 0) {
    // Fixme: % 2 for Thumb?
    return false;
  }
  return true;
}
} // end anonymous namespace

namespace {
// FIXME: This should be in a separate file.
// ELF is an ELF of course...
class ELFARMAsmBackend : public ARMAsmBackend {
  MCELFObjectFormat Format;

public:
  Triple::OSType OSType;
  ELFARMAsmBackend(const Target &T, Triple::OSType _OSType)
    : ARMAsmBackend(T), OSType(_OSType) {
    HasScatteredSymbols = true;
  }

  virtual const MCObjectFormat &getObjectFormat() const {
    return Format;
  }

  void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                  uint64_t Value) const;

  bool isVirtualSection(const MCSection &Section) const {
    const MCSectionELF &SE = static_cast<const MCSectionELF&>(Section);
    return SE.getType() == MCSectionELF::SHT_NOBITS;
  }

  MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
    return new ELFObjectWriter(OS, /*Is64Bit=*/false,
                               OSType, ELF::EM_ARM,
                               /*IsLittleEndian=*/true,
                               /*HasRelocationAddend=*/false);
  }
};

// Fixme: can we raise this to share code between Darwin and ELF?
void ELFARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                                  uint64_t Value) const {
  assert(0 && "ELFARMAsmBackend::ApplyFixup() unimplemented");
}

// FIXME: This should be in a separate file.
class DarwinARMAsmBackend : public ARMAsmBackend {
  MCMachOObjectFormat Format;

public:
  DarwinARMAsmBackend(const Target &T)
    : ARMAsmBackend(T) {
    HasScatteredSymbols = true;
  }

  virtual const MCObjectFormat &getObjectFormat() const {
    return Format;
  }

  void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                  uint64_t Value) const;

  bool isVirtualSection(const MCSection &Section) const {
    const MCSectionMachO &SMO = static_cast<const MCSectionMachO&>(Section);
    return (SMO.getType() == MCSectionMachO::S_ZEROFILL ||
            SMO.getType() == MCSectionMachO::S_GB_ZEROFILL ||
            SMO.getType() == MCSectionMachO::S_THREAD_LOCAL_ZEROFILL);
  }

  MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
    // FIXME: Subtarget info should be derived. Force v7 for now.
    return new MachObjectWriter(OS, /*Is64Bit=*/false, MachO::CPUTypeARM,
                                MachO::CPUSubType_ARM_V7);
  }

  virtual bool doesSectionRequireSymbols(const MCSection &Section) const {
    return false;
  }
};

static unsigned getFixupKindLog2Size(unsigned Kind) {
  switch (Kind) {
  default: llvm_unreachable("Unknown fixup kind!");
  case FK_Data_4: return 2;
  case ARM::fixup_arm_pcrel_12: return 2;
  case ARM::fixup_arm_vfp_pcrel_12: return 1;
  }
}

static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) {
  switch (Kind) {
  default:
    llvm_unreachable("Unknown fixup kind!");
  case FK_Data_4:
  case ARM::fixup_arm_pcrel_12:
    // ARM PC-relative values are offset by 8.
    return Value - 8;
  case ARM::fixup_arm_vfp_pcrel_12:
    // The VFP ld/st immediate value doesn't encode the low two bits since
    // they're always zero. Offset by 8 just as above.
    return (Value - 8) >> 2;
  }
}

void DarwinARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                                  uint64_t Value) const {
  unsigned NumBytes = getFixupKindLog2Size(Fixup.getKind());
  Value = adjustFixupValue(Fixup.getKind(), Value);

  assert(Fixup.getOffset() + NumBytes <= DF.getContents().size() &&
         "Invalid fixup offset!");
  // For each byte of the fragment that the fixup touches, mask in the
  // bits from the fixup value.
  for (unsigned i = 0; i != NumBytes; ++i)
    DF.getContents()[Fixup.getOffset() + i] |= uint8_t(Value >> (i * 8));
}
} // end anonymous namespace

TargetAsmBackend *llvm::createARMAsmBackend(const Target &T,
                                            const std::string &TT) {
  switch (Triple(TT).getOS()) {
  case Triple::Darwin:
    return new DarwinARMAsmBackend(T);
  case Triple::MinGW32:
  case Triple::Cygwin:
  case Triple::Win32:
    assert(0 && "Windows not supported on ARM");
  default:
    return new ELFARMAsmBackend(T, Triple(TT).getOS());
  }
}
<commit_msg>ARM .word data fixups don't need an adjustment.<commit_after>//===-- ARMAsmBackend.cpp - ARM Assembler Backend -------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Target/TargetAsmBackend.h"
#include "ARM.h"
#include "ARMFixupKinds.h"
#include "llvm/ADT/Twine.h"
#include "llvm/MC/ELFObjectWriter.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCObjectFormat.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MachObjectWriter.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/MachO.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetRegistry.h"
#include "llvm/Target/TargetAsmBackend.h"
using namespace llvm;

namespace {
class ARMAsmBackend : public TargetAsmBackend {
public:
  ARMAsmBackend(const Target &T)
    : TargetAsmBackend(T) {
  }

  bool MayNeedRelaxation(const MCInst &Inst) const;

  void RelaxInstruction(const MCInst &Inst, MCInst &Res) const;

  bool WriteNopData(uint64_t Count, MCObjectWriter *OW) const;

  unsigned getPointerSize() const {
    return 4;
  }
};

bool ARMAsmBackend::MayNeedRelaxation(const MCInst &Inst) const {
  // FIXME: Thumb targets, different move constant targets..
  return false;
}

void ARMAsmBackend::RelaxInstruction(const MCInst &Inst, MCInst &Res) const {
  assert(0 && "ARMAsmBackend::RelaxInstruction() unimplemented");
  return;
}

bool ARMAsmBackend::WriteNopData(uint64_t Count, MCObjectWriter *OW) const {
  if ((Count % 4) != 0) {
    // Fixme: % 2 for Thumb?
    return false;
  }
  return true;
}
} // end anonymous namespace

namespace {
// FIXME: This should be in a separate file.
// ELF is an ELF of course...
class ELFARMAsmBackend : public ARMAsmBackend {
  MCELFObjectFormat Format;

public:
  Triple::OSType OSType;
  ELFARMAsmBackend(const Target &T, Triple::OSType _OSType)
    : ARMAsmBackend(T), OSType(_OSType) {
    HasScatteredSymbols = true;
  }

  virtual const MCObjectFormat &getObjectFormat() const {
    return Format;
  }

  void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                  uint64_t Value) const;

  bool isVirtualSection(const MCSection &Section) const {
    const MCSectionELF &SE = static_cast<const MCSectionELF&>(Section);
    return SE.getType() == MCSectionELF::SHT_NOBITS;
  }

  MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
    return new ELFObjectWriter(OS, /*Is64Bit=*/false,
                               OSType, ELF::EM_ARM,
                               /*IsLittleEndian=*/true,
                               /*HasRelocationAddend=*/false);
  }
};

// Fixme: can we raise this to share code between Darwin and ELF?
void ELFARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                                  uint64_t Value) const {
  assert(0 && "ELFARMAsmBackend::ApplyFixup() unimplemented");
}

// FIXME: This should be in a separate file.
class DarwinARMAsmBackend : public ARMAsmBackend {
  MCMachOObjectFormat Format;

public:
  DarwinARMAsmBackend(const Target &T)
    : ARMAsmBackend(T) {
    HasScatteredSymbols = true;
  }

  virtual const MCObjectFormat &getObjectFormat() const {
    return Format;
  }

  void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                  uint64_t Value) const;

  bool isVirtualSection(const MCSection &Section) const {
    const MCSectionMachO &SMO = static_cast<const MCSectionMachO&>(Section);
    return (SMO.getType() == MCSectionMachO::S_ZEROFILL ||
            SMO.getType() == MCSectionMachO::S_GB_ZEROFILL ||
            SMO.getType() == MCSectionMachO::S_THREAD_LOCAL_ZEROFILL);
  }

  MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
    // FIXME: Subtarget info should be derived. Force v7 for now.
    return new MachObjectWriter(OS, /*Is64Bit=*/false, MachO::CPUTypeARM,
                                MachO::CPUSubType_ARM_V7);
  }

  virtual bool doesSectionRequireSymbols(const MCSection &Section) const {
    return false;
  }
};

static unsigned getFixupKindLog2Size(unsigned Kind) {
  switch (Kind) {
  default: llvm_unreachable("Unknown fixup kind!");
  case FK_Data_4: return 2;
  case ARM::fixup_arm_pcrel_12: return 2;
  case ARM::fixup_arm_vfp_pcrel_12: return 1;
  }
}

static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) {
  switch (Kind) {
  default:
    llvm_unreachable("Unknown fixup kind!");
  case FK_Data_4:
    return Value;
  case ARM::fixup_arm_pcrel_12:
    // ARM PC-relative values are offset by 8.
    return Value - 8;
  case ARM::fixup_arm_vfp_pcrel_12:
    // The VFP ld/st immediate value doesn't encode the low two bits since
    // they're always zero. Offset by 8 just as above.
    return (Value - 8) >> 2;
  }
}

void DarwinARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
                                  uint64_t Value) const {
  unsigned NumBytes = getFixupKindLog2Size(Fixup.getKind());
  Value = adjustFixupValue(Fixup.getKind(), Value);

  assert(Fixup.getOffset() + NumBytes <= DF.getContents().size() &&
         "Invalid fixup offset!");
  // For each byte of the fragment that the fixup touches, mask in the
  // bits from the fixup value.
  for (unsigned i = 0; i != NumBytes; ++i)
    DF.getContents()[Fixup.getOffset() + i] |= uint8_t(Value >> (i * 8));
}
} // end anonymous namespace

TargetAsmBackend *llvm::createARMAsmBackend(const Target &T,
                                            const std::string &TT) {
  switch (Triple(TT).getOS()) {
  case Triple::Darwin:
    return new DarwinARMAsmBackend(T);
  case Triple::MinGW32:
  case Triple::Cygwin:
  case Triple::Win32:
    assert(0 && "Windows not supported on ARM");
  default:
    return new ELFARMAsmBackend(T, Triple(TT).getOS());
  }
}
<|endoftext|>


<commit_before>//===-- asan_test_main.cc -------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
#include "asan_test_utils.h"

int main(int argc, char **argv) {
  testing::GTEST_FLAG(death_test_style) = "threadsafe";
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>[asan] Speed up ASan unit tests by turning off symbolication<commit_after>//===-- asan_test_main.cc -------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
#include "asan_test_utils.h"

// Default ASAN_OPTIONS for the unit tests. Let's turn symbolication off to
// speed up testing (unit tests don't use it anyway).
extern "C" const char* __asan_default_options() {
  return "symbolize=false";
}

int main(int argc, char **argv) {
  testing::GTEST_FLAG(death_test_style) = "threadsafe";
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
    kmime_charfreq.cpp

    KMime, the KDE internet mail/usenet news message library.
    Copyright (c) 2001-2002 Marc Mutz <mutz@kde.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 2 of the License.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, US
*/

#include "kmime_charfreq.h"

namespace KMime {

CharFreq::CharFreq( const QByteArray & buf )
  : NUL(0),
    CTL(0),
    CR(0), LF(0),
    CRLF(0),
    printable(0),
    eightBit(0),
    total(0),
    lineMin(0xffffffff),
    lineMax(0),
    mTrailingWS(false),
    mLeadingFrom(false)
{
  if ( !buf.isEmpty() )
    count( buf.data(), buf.size() );
}

CharFreq::CharFreq( const char * buf, size_t len )
  : NUL(0),
    CTL(0),
    CR(0), LF(0),
    CRLF(0),
    printable(0),
    eightBit(0),
    total(0),
    lineMin(0xffffffff),
    lineMax(0),
    mTrailingWS(false),
    mLeadingFrom(false)
{
  if ( buf && len > 0 )
    count( buf, len );
}

static inline bool isWS( char ch ) { return ( ch == '\t' || ch == ' ' ); }

void CharFreq::count( const char * it, size_t len ) {

  const char * end = it + len;
  uint currentLineLength = 0;
  // initialize the prevChar with LF so that From_ detection works w/o
  // special-casing:
  char prevChar = '\n';
  char prevPrevChar = 0;

  for ( ; it != end ; ++it ) {
    ++currentLineLength;
    switch ( *it ) {
    case '\0': ++NUL; break;
    case '\r': ++CR;  break;
    case '\n': ++LF;
      if ( prevChar == '\r' ) { --currentLineLength; ++CRLF; }
      if ( currentLineLength >= lineMax ) lineMax = currentLineLength-1;
      if ( currentLineLength <= lineMin ) lineMin = currentLineLength-1;
      if ( !mTrailingWS )
	if ( isWS( prevChar ) || ( prevChar == '\r' && isWS( prevPrevChar ) ) )
	  mTrailingWS = true;
      currentLineLength = 0;
      break;
    case 'F': // check for lines starting with From_ if not found already:
      if ( !mLeadingFrom )
	if ( prevChar == '\n' && end - it >= 5 && !qstrncmp( "From ", it, 5 ) )
	  mLeadingFrom = true;
      ++printable;
      break;
    default:
      {
	uchar c = *it;
	if ( c == '\t' || c >= ' ' && c <= '~' )
	  ++printable;
	else if ( c == 127 || c < ' ' )
	  ++CTL;
	else
	  ++eightBit;
      }
    }
    prevPrevChar = prevChar;
    prevChar = *it;
  }

  // check whether the last character is tab or space
  if ( isWS( prevChar ) )
    mTrailingWS = true;

  total = len;
}

bool CharFreq::isEightBitData() const {
  return type() == EightBitData;
}

bool CharFreq::isEightBitText() const {
  return type() == EightBitText;
}

bool CharFreq::isSevenBitData() const {
  return type() == SevenBitData;
}

bool CharFreq::isSevenBitText() const {
  return type() == SevenBitText;
}

bool CharFreq::hasTrailingWhitespace() const {
  return mTrailingWS;
}

bool CharFreq::hasLeadingFrom() const {
  return mLeadingFrom;
}

CharFreq::Type CharFreq::type() const {
#if 0
  qDebug( "Total: %d; NUL: %d; CTL: %d;\n"
	  "CR: %d; LF: %d; CRLF: %d;\n"
	  "lineMin: %d; lineMax: %d;\n"
	  "printable: %d; eightBit: %d;\n"
          "trailing whitespace: %s;\n"
          "leading 'From ': %s;\n",
	  total, NUL, CTL, CR, LF, CRLF, lineMin, lineMax,
	  printable, eightBit,
	  mTrailingWS ? "yes" : "no" , mLeadingFrom ? "yes" : "no" );
#endif
  if ( NUL ) // must be binary
    return Binary;

  // doesn't contain NUL's:
  if ( eightBit ) {
    if ( lineMax > 988 ) return EightBitData; // not allowed in 8bit
    if ( CR != CRLF || controlCodesRatio() > 0.2 ) return EightBitData;
    return EightBitText;
  }

  // doesn't contain NUL's, nor 8bit chars:
  if ( lineMax > 988 ) return SevenBitData;
  if ( CR != CRLF || controlCodesRatio() > 0.2 ) return SevenBitData;

  // no NUL, no 8bit chars, no excessive CTLs and no lines > 998 chars:
  return SevenBitText;
}

float CharFreq::printableRatio() const {
  if ( total ) return float(printable) / float(total);
  else         return 0;
}

float CharFreq::controlCodesRatio() const {
  if ( total ) return float(CTL) / float(total);
  else         return 0;
}

} // namespace KMime

<commit_msg>Backport fix for bug 74254 (attaching files with long lines causes message loss).<commit_after>/*
    kmime_charfreq.cpp

    KMime, the KDE internet mail/usenet news message library.
    Copyright (c) 2001-2002 Marc Mutz <mutz@kde.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 2 of the License.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, US
*/

#include "kmime_charfreq.h"

namespace KMime {

CharFreq::CharFreq( const QByteArray & buf )
  : NUL(0),
    CTL(0),
    CR(0), LF(0),
    CRLF(0),
    printable(0),
    eightBit(0),
    total(0),
    lineMin(0xffffffff),
    lineMax(0),
    mTrailingWS(false),
    mLeadingFrom(false)
{
  if ( !buf.isEmpty() )
    count( buf.data(), buf.size() );
}

CharFreq::CharFreq( const char * buf, size_t len )
  : NUL(0),
    CTL(0),
    CR(0), LF(0),
    CRLF(0),
    printable(0),
    eightBit(0),
    total(0),
    lineMin(0xffffffff),
    lineMax(0),
    mTrailingWS(false),
    mLeadingFrom(false)
{
  if ( buf && len > 0 )
    count( buf, len );
}

static inline bool isWS( char ch ) { return ( ch == '\t' || ch == ' ' ); }

void CharFreq::count( const char * it, size_t len ) {

  const char * end = it + len;
  uint currentLineLength = 0;
  // initialize the prevChar with LF so that From_ detection works w/o
  // special-casing:
  char prevChar = '\n';
  char prevPrevChar = 0;

  for ( ; it != end ; ++it ) {
    ++currentLineLength;
    switch ( *it ) {
    case '\0': ++NUL; break;
    case '\r': ++CR;  break;
    case '\n': ++LF;
      if ( prevChar == '\r' ) { --currentLineLength; ++CRLF; }
      if ( currentLineLength >= lineMax ) lineMax = currentLineLength-1;
      if ( currentLineLength <= lineMin ) lineMin = currentLineLength-1;
      if ( !mTrailingWS )
	if ( isWS( prevChar ) || ( prevChar == '\r' && isWS( prevPrevChar ) ) )
	  mTrailingWS = true;
      currentLineLength = 0;
      break;
    case 'F': // check for lines starting with From_ if not found already:
      if ( !mLeadingFrom )
	if ( prevChar == '\n' && end - it >= 5 && !qstrncmp( "From ", it, 5 ) )
	  mLeadingFrom = true;
      ++printable;
      break;
    default:
      {
	uchar c = *it;
	if ( c == '\t' || c >= ' ' && c <= '~' )
	  ++printable;
	else if ( c == 127 || c < ' ' )
	  ++CTL;
	else
	  ++eightBit;
      }
    }
    prevPrevChar = prevChar;
    prevChar = *it;
  }

  // consider the length of the last line
  if ( currentLineLength >= lineMax ) lineMax = currentLineLength;
  if ( currentLineLength <= lineMin ) lineMin = currentLineLength;

  // check whether the last character is tab or space
  if ( isWS( prevChar ) )
    mTrailingWS = true;

  total = len;
}

bool CharFreq::isEightBitData() const {
  return type() == EightBitData;
}

bool CharFreq::isEightBitText() const {
  return type() == EightBitText;
}

bool CharFreq::isSevenBitData() const {
  return type() == SevenBitData;
}

bool CharFreq::isSevenBitText() const {
  return type() == SevenBitText;
}

bool CharFreq::hasTrailingWhitespace() const {
  return mTrailingWS;
}

bool CharFreq::hasLeadingFrom() const {
  return mLeadingFrom;
}

CharFreq::Type CharFreq::type() const {
#if 0
  qDebug( "Total: %d; NUL: %d; CTL: %d;\n"
	  "CR: %d; LF: %d; CRLF: %d;\n"
	  "lineMin: %d; lineMax: %d;\n"
	  "printable: %d; eightBit: %d;\n"
          "trailing whitespace: %s;\n"
          "leading 'From ': %s;\n",
	  total, NUL, CTL, CR, LF, CRLF, lineMin, lineMax,
	  printable, eightBit,
	  mTrailingWS ? "yes" : "no" , mLeadingFrom ? "yes" : "no" );
#endif
  if ( NUL ) // must be binary
    return Binary;

  // doesn't contain NUL's:
  if ( eightBit ) {
    if ( lineMax > 988 ) return EightBitData; // not allowed in 8bit
    if ( CR != CRLF || controlCodesRatio() > 0.2 ) return EightBitData;
    return EightBitText;
  }

  // doesn't contain NUL's, nor 8bit chars:
  if ( lineMax > 988 ) return SevenBitData;
  if ( CR != CRLF || controlCodesRatio() > 0.2 ) return SevenBitData;

  // no NUL, no 8bit chars, no excessive CTLs and no lines > 998 chars:
  return SevenBitText;
}

float CharFreq::printableRatio() const {
  if ( total ) return float(printable) / float(total);
  else         return 0;
}

float CharFreq::controlCodesRatio() const {
  if ( total ) return float(CTL) / float(total);
  else         return 0;
}

} // namespace KMime

<|endoftext|>