<commit_before>/*
 * Copyright (c) 2010 Nathaniel McCallum <nathaniel@natemccallum.com>
 *
 * 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 <iostream>
#include <fstream>
#include <algorithm>
#include <set>
#include <vector>
#include <string>
using namespace std;

#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <sys/stat.h>

#include <readline/readline.h>
#include <readline/history.h>

#define I_ACKNOWLEDGE_THAT_NATUS_IS_NOT_STABLE
#include <natus/natus.hpp>
using namespace natus;

#define _str(s) # s
#define __str(s) _str(s)

#define PWSIZE 1024
#define PROMPT ">>> "
#define NPRMPT "... "
#define HISTORYFILE ".natus_history"
#define error(status, num, ...) { \
        fprintf(stderr, __VA_ARGS__); \
        return status; \
}

Value* glbl;

static Value alert(Value& ths, Value& fnc, Value& args) {
	NATUS_CHECK_ARGUMENTS(fnc, "s");

	fprintf(stderr, "%s\n", args[0].to<UTF8>().c_str());
	return ths.newUndefined();
}

static Value dir(Value& ths, Value& fnc, Value& args) {
	Value obj = ths.getGlobal();
	if (args.get("length").to<long>() > 0)
		obj = args[0];

	Value names = obj.enumerate();
	for (long i=0 ; i < names.get("length").to<long>() ; i++)
		fprintf(stderr, "\t%s\n", names[i].to<UTF8>().c_str());
	return ths.newUndefined();
}

static bool inblock(const char *str) {
	char strstart = '\0';
	int cnt = 0;
	for (char prv='\0' ; *str ; prv=*str++) {
		if (strstart) {
			if (strstart == '"' && *str == '"' && prv != '\\')
				strstart = '\0';
			else if (strstart == '\'' && *str == '\'' && prv != '\\')
				strstart = '\0';
			continue;
		}

		// We are not inside a string literal
		switch (*str) {
			case '"':
				// We're starting a double-quoted string
				strstart = '"';
				break;
			case '\'':
				// We're starting a single-quoted string
				strstart = '\'';
				break;
			case '{':
				// We're starting a new block
				cnt++;
				break;
			case '}':
				// We're ending an existing block
				cnt--;
				break;
		}
	}
	return cnt > 0;
}

static vector<string> pathparser(string path) {
	if (path.find(':') == string::npos) {
		vector<string> tmp;
		if (path != "")
			tmp.push_back(path);
		return tmp;
	}

	string segment = path.substr(path.find_last_of(':')+1);
	vector<string> paths = pathparser(path.substr(0, path.find_last_of(':')));
	if (segment != "") paths.push_back(segment);
	return paths;
}

static char* completion_generator(const char* text, int state) {
	static set<string> names;
	static set<string>::iterator it;
	char* last = (char*) strrchr(text, '.');

	if (state == 0) {
		Value obj = *glbl;
		if (last) {
			char* base = new char[last-text+1];
			memset(base, 0, sizeof(char) * (last-text+1));
			strncpy(base, text, last-text);
			obj = obj.get(base);
			delete[] base;
			if (obj.isUndefined()) return NULL;
		}

		Value nm = obj.enumerate();
		for (long i=0 ; i < nm.get("length").to<long>() ; i++)
			names.insert(nm[i].to<UTF8>().c_str());
		it = names.begin();
	}
	if (last) last++;

	for ( ; it != names.end() ; it++) {
		if ((last && it->find(last) == 0) || (!last && it->find(text) == 0)) {
			string tmp = last ? (string(text) + (it->c_str()+strlen(last))) : *it;
			char* ret = strdup(tmp.c_str());
			it++;
			return ret;
		}
	}

	return NULL;
}

static char** completion(const char* text, int start, int end) {
	return rl_completion_matches(text, completion_generator);
}

static Value set_path(Value& ctx, Require::HookStep step, char* name, void* misc) {
	if (step == Require::HookStepProcess && !strcmp(name, "system")) {
		Value args = ctx.get("args");
		if (!args.isArray()) return NULL;

		const char** argv = (const char **) misc;
		for (int i=0 ; argv[i] ; i++)
			args.newArrayBuilder(argv[i]);
	}

	return NULL;
}

int main(int argc, char** argv) {
	Engine engine;
	Value  global;
	vector<string> configs;
	const char *eng = NULL;
	const char *eval = NULL;
	int c=0, exitcode=0;
	glbl = &global;

	// The engine argument can be embedded in a symlink
	if (strstr(argv[0], "natus-") == argv[0])
		eng = strchr(argv[0], '-')+1;

	// Get the path
	string path;
	if (getenv("NATUS_PATH"))
		path = string(getenv("NATUS_PATH")) + ":";
	path += __str(MODULEDIR);

	opterr = 0;
	while ((c = getopt (argc, argv, "+C:c:e:n")) != -1) {
		switch (c) {
		case 'C':
			configs.push_back(optarg);
			break;
		case 'c':
			eval = optarg;
			break;
		case 'e':
			eng = optarg;
			break;
		case 'n':
			path.clear();
			break;
		case '?':
			if (optopt == 'e')
				fprintf(stderr, "Option -%c requires an engine name.\n", optopt);
			else {
				fprintf(stderr, "Usage: %s [-C <config>=<jsonval>|-c <javascript>|-e <engine>|-n|<scriptfile>]\n\n", argv[0]);
				fprintf(stderr, "Unknown option `-%c'.\n", optopt);
			}
			return 1;
		default:
			abort();
	   }
	}

	// Initialize the engine
	if (!engine.initialize(eng))
		error(1, 0, "Unable to init engine!");

	// Setup our global
	global = engine.newGlobal();
	if (global.isUndefined() || global.isException())
		error(2, 0, "Unable to init global!");

	// Setup our config
	Value cfg = global.newObject();
	if (!path.empty()) {
		while (path.find(':') != string::npos)
			path = path.substr(0, path.find(':')) + "\", \"" + path.substr(path.find(':')+1);
		cfg.setRecursive("natus.path", fromJSON(global, "[\"" + path + "\"]"), Value::PropAttrNone, true);
	}
	for (vector<string>::iterator it=configs.begin() ; it != configs.end() ; it++) {
		if (access(it->c_str(), R_OK) == 0) {
			ifstream file(it->c_str());
			for (string line ; getline(file, line) ; ) {
				if (line.find('=') == string::npos) continue;
				string key = line.substr(0, line.find('='));
				Value  val = fromJSON(global, line.substr(line.find('=')+1).c_str());
				if (val.isException()) continue;
				cfg.setRecursive(key, val, Value::PropAttrNone, true);
			}
			file.close();
		} else if (it->find("=") != string::npos) {
			string key = it->substr(0, it->find('='));
			Value  val = fromJSON(global, it->substr(it->find('=')+1));
			if (val.isException()) continue;
			cfg.setRecursive(key, val, Value::PropAttrNone, true);
		}
	}

	// Bring up the Module Loader
	Require req(global);
	if (!req.initialize(cfg))
		error(3, 0, "Unable to init module loader!");

	// Export some basic functions
	global.set("alert", global.newFunction(alert));
	global.set("dir",   global.newFunction(dir));

	// Do the evaluation
	if (eval) {
		Value res = global.evaluate(eval);

		// Print uncaught exceptions
		if (res.isException())
			error(8, 0, "Uncaught Exception: %s", res.to<UTF8>().c_str());

		// Let the script return exitcodes
		if (res.isNumber())
			exitcode = (int) res.to<long>();

		return exitcode;
	}

	// Start the shell
	if (optind >= argc) {
		fprintf(stderr, "Natus v" PACKAGE_VERSION " - Using: %s\n", engine.getName());
		if (getenv("HOME"))
			read_history((string(getenv("HOME")) + "/" + HISTORYFILE).c_str());
		rl_readline_name = (char*) "natus";
		rl_attempted_completion_function = completion;
		read_history(HISTORYFILE);

		char* line;
		const char* prompt = PROMPT;
		string prev = "";

		long lcnt = 0;
		for (line=readline(prompt) ; line ; line=readline(prompt)) {
			// Strip the line
			string l = line;
			free(line);
			string::size_type start = l.find_first_not_of(" \t");
			l = start == string::npos ? string("") : l.substr(start, l.find_last_not_of(" \t")-start+1);
			if (l == "") continue;

			// Append the line to the buffer
			prev.append(l);

			// Continue to the next line
		    if (inblock(prev.c_str())) {
		    	prompt = NPRMPT;
		    	continue;
		    }
		    prompt = PROMPT;
			add_history(prev.c_str());

			Value res = global.evaluate(prev.c_str(), "", lcnt++);
			string fmt;
			if (res.isException()) {
				fmt = "Uncaught Exception: %s\n";
				if (prev == "exit" || prev == "quit") {
					res = res.newUndefined();
					fprintf(stderr, "Use Ctrl-D (i.e. EOF) to exit\n");
				}
			} else
				fmt = "%s\n";

			if (!res.isUndefined() && !res.isNull())
				fprintf(stderr, fmt.c_str(), res.to<UTF8>().c_str());
			prev = "";
		}
		printf("\n");
		if (getenv("HOME"))
			write_history((string(getenv("HOME")) + "/" + HISTORYFILE).c_str());
		return 0;
	}

	// Get the script filename
	char *filename = argv[optind];

	// Stat our file
	struct stat st;
	if (stat(filename, &st))
		error(3, 0, "Script file does not exist: %s", filename);

	// Read in our script file
	FILE *file = fopen(filename, "r");
	if (!file)
		error(4, 0, "Unable to open file: %s", filename);
	char *jscript = new char[st.st_size + 1];
	if (!jscript) {
		fclose(file);
		error(5, 0, "Out of memory!");
	}
	if ((size_t) fread(jscript, 1, st.st_size, file) != (size_t) st.st_size) {
		delete[] jscript;
		fclose(file);
		error(6, 0, "Error reading file: %s", filename);
	}
	fclose(file);
	jscript[st.st_size] = '\0';

	// Evaluate it
	req.addHook("system_args", set_path, argv+optind);
	Value res = global.evaluate(jscript, filename ? filename : "");
	delete[] jscript;

	// Print uncaught exceptions
	if (res.isException())
		error(8, 0, "Uncaught Exception: %s", res.to<UTF8>().c_str());

	// Let the script return exitcodes
	if (res.isNumber())
		exitcode = (int) res.to<long>();

	return exitcode;
}
<commit_msg>fix shell error output<commit_after>/*
 * Copyright (c) 2010 Nathaniel McCallum <nathaniel@natemccallum.com>
 *
 * 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 <iostream>
#include <fstream>
#include <algorithm>
#include <set>
#include <vector>
#include <string>
using namespace std;

#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <sys/stat.h>

#include <readline/readline.h>
#include <readline/history.h>

#define I_ACKNOWLEDGE_THAT_NATUS_IS_NOT_STABLE
#include <natus/natus.hpp>
using namespace natus;

#define _str(s) # s
#define __str(s) _str(s)

#define PWSIZE 1024
#define PROMPT ">>> "
#define NPRMPT "... "
#define HISTORYFILE ".natus_history"
#define error(status, num, ...) { \
        fprintf(stderr, __VA_ARGS__); \
        return status; \
}

Value* glbl;

static Value alert(Value& ths, Value& fnc, Value& args) {
	NATUS_CHECK_ARGUMENTS(fnc, "s");

	fprintf(stderr, "%s\n", args[0].to<UTF8>().c_str());
	return ths.newUndefined();
}

static Value dir(Value& ths, Value& fnc, Value& args) {
	Value obj = ths.getGlobal();
	if (args.get("length").to<long>() > 0)
		obj = args[0];

	Value names = obj.enumerate();
	for (long i=0 ; i < names.get("length").to<long>() ; i++)
		fprintf(stderr, "\t%s\n", names[i].to<UTF8>().c_str());
	return ths.newUndefined();
}

static bool inblock(const char *str) {
	char strstart = '\0';
	int cnt = 0;
	for (char prv='\0' ; *str ; prv=*str++) {
		if (strstart) {
			if (strstart == '"' && *str == '"' && prv != '\\')
				strstart = '\0';
			else if (strstart == '\'' && *str == '\'' && prv != '\\')
				strstart = '\0';
			continue;
		}

		// We are not inside a string literal
		switch (*str) {
			case '"':
				// We're starting a double-quoted string
				strstart = '"';
				break;
			case '\'':
				// We're starting a single-quoted string
				strstart = '\'';
				break;
			case '{':
				// We're starting a new block
				cnt++;
				break;
			case '}':
				// We're ending an existing block
				cnt--;
				break;
		}
	}
	return cnt > 0;
}

static vector<string> pathparser(string path) {
	if (path.find(':') == string::npos) {
		vector<string> tmp;
		if (path != "")
			tmp.push_back(path);
		return tmp;
	}

	string segment = path.substr(path.find_last_of(':')+1);
	vector<string> paths = pathparser(path.substr(0, path.find_last_of(':')));
	if (segment != "") paths.push_back(segment);
	return paths;
}

static char* completion_generator(const char* text, int state) {
	static set<string> names;
	static set<string>::iterator it;
	char* last = (char*) strrchr(text, '.');

	if (state == 0) {
		Value obj = *glbl;
		if (last) {
			char* base = new char[last-text+1];
			memset(base, 0, sizeof(char) * (last-text+1));
			strncpy(base, text, last-text);
			obj = obj.get(base);
			delete[] base;
			if (obj.isUndefined()) return NULL;
		}

		Value nm = obj.enumerate();
		for (long i=0 ; i < nm.get("length").to<long>() ; i++)
			names.insert(nm[i].to<UTF8>().c_str());
		it = names.begin();
	}
	if (last) last++;

	for ( ; it != names.end() ; it++) {
		if ((last && it->find(last) == 0) || (!last && it->find(text) == 0)) {
			string tmp = last ? (string(text) + (it->c_str()+strlen(last))) : *it;
			char* ret = strdup(tmp.c_str());
			it++;
			return ret;
		}
	}

	return NULL;
}

static char** completion(const char* text, int start, int end) {
	return rl_completion_matches(text, completion_generator);
}

static Value set_path(Value& ctx, Require::HookStep step, char* name, void* misc) {
	if (step == Require::HookStepProcess && !strcmp(name, "system")) {
		Value args = ctx.get("args");
		if (!args.isArray()) return NULL;

		const char** argv = (const char **) misc;
		for (int i=0 ; argv[i] ; i++)
			args.newArrayBuilder(argv[i]);
	}

	return NULL;
}

int main(int argc, char** argv) {
	Engine engine;
	Value  global;
	vector<string> configs;
	const char *eng = NULL;
	const char *eval = NULL;
	int c=0, exitcode=0;
	glbl = &global;

	// The engine argument can be embedded in a symlink
	if (strstr(argv[0], "natus-") == argv[0])
		eng = strchr(argv[0], '-')+1;

	// Get the path
	string path;
	if (getenv("NATUS_PATH"))
		path = string(getenv("NATUS_PATH")) + ":";
	path += __str(MODULEDIR);

	opterr = 0;
	while ((c = getopt (argc, argv, "+C:c:e:n")) != -1) {
		switch (c) {
		case 'C':
			configs.push_back(optarg);
			break;
		case 'c':
			eval = optarg;
			break;
		case 'e':
			eng = optarg;
			break;
		case 'n':
			path.clear();
			break;
		case '?':
			if (optopt == 'e')
				fprintf(stderr, "Option -%c requires an engine name.\n", optopt);
			else {
				fprintf(stderr, "Usage: %s [-C <config>=<jsonval>|-c <javascript>|-e <engine>|-n|<scriptfile>]\n\n", argv[0]);
				fprintf(stderr, "Unknown option `-%c'.\n", optopt);
			}
			return 1;
		default:
			abort();
	   }
	}

	// Initialize the engine
	if (!engine.initialize(eng))
		error(1, 0, "Unable to init engine!\n");

	// Setup our global
	global = engine.newGlobal();
	if (global.isUndefined() || global.isException())
		error(2, 0, "Unable to init global!\n");

	// Setup our config
	Value cfg = global.newObject();
	if (!path.empty()) {
		while (path.find(':') != string::npos)
			path = path.substr(0, path.find(':')) + "\", \"" + path.substr(path.find(':')+1);
		cfg.setRecursive("natus.path", fromJSON(global, "[\"" + path + "\"]"), Value::PropAttrNone, true);
	}
	for (vector<string>::iterator it=configs.begin() ; it != configs.end() ; it++) {
		if (access(it->c_str(), R_OK) == 0) {
			ifstream file(it->c_str());
			for (string line ; getline(file, line) ; ) {
				if (line.find('=') == string::npos) continue;
				string key = line.substr(0, line.find('='));
				Value  val = fromJSON(global, line.substr(line.find('=')+1).c_str());
				if (val.isException()) continue;
				cfg.setRecursive(key, val, Value::PropAttrNone, true);
			}
			file.close();
		} else if (it->find("=") != string::npos) {
			string key = it->substr(0, it->find('='));
			Value  val = fromJSON(global, it->substr(it->find('=')+1));
			if (val.isException()) continue;
			cfg.setRecursive(key, val, Value::PropAttrNone, true);
		}
	}

	// Bring up the Module Loader
	Require req(global);
	if (!req.initialize(cfg))
		error(3, 0, "Unable to init module loader\n!");

	// Export some basic functions
	global.set("alert", global.newFunction(alert));
	global.set("dir",   global.newFunction(dir));

	// Do the evaluation
	if (eval) {
		Value res = global.evaluate(eval);

		// Print uncaught exceptions
		if (res.isException())
			error(8, 0, "Uncaught Exception: %s", res.to<UTF8>().c_str());

		// Let the script return exitcodes
		if (res.isNumber())
			exitcode = (int) res.to<long>();

		return exitcode;
	}

	// Start the shell
	if (optind >= argc) {
		fprintf(stderr, "Natus v" PACKAGE_VERSION " - Using: %s\n", engine.getName());
		if (getenv("HOME"))
			read_history((string(getenv("HOME")) + "/" + HISTORYFILE).c_str());
		rl_readline_name = (char*) "natus";
		rl_attempted_completion_function = completion;
		read_history(HISTORYFILE);

		char* line;
		const char* prompt = PROMPT;
		string prev = "";

		long lcnt = 0;
		for (line=readline(prompt) ; line ; line=readline(prompt)) {
			// Strip the line
			string l = line;
			free(line);
			string::size_type start = l.find_first_not_of(" \t");
			l = start == string::npos ? string("") : l.substr(start, l.find_last_not_of(" \t")-start+1);
			if (l == "") continue;

			// Append the line to the buffer
			prev.append(l);

			// Continue to the next line
		    if (inblock(prev.c_str())) {
		    	prompt = NPRMPT;
		    	continue;
		    }
		    prompt = PROMPT;
			add_history(prev.c_str());

			Value res = global.evaluate(prev.c_str(), "", lcnt++);
			string fmt;
			if (res.isException()) {
				fmt = "Uncaught Exception: %s\n";
				if (prev == "exit" || prev == "quit") {
					res = res.newUndefined();
					fprintf(stderr, "Use Ctrl-D (i.e. EOF) to exit\n");
				}
			} else
				fmt = "%s\n";

			if (!res.isUndefined() && !res.isNull())
				fprintf(stderr, fmt.c_str(), res.to<UTF8>().c_str());
			prev = "";
		}
		printf("\n");
		if (getenv("HOME"))
			write_history((string(getenv("HOME")) + "/" + HISTORYFILE).c_str());
		return 0;
	}

	// Get the script filename
	char *filename = argv[optind];

	// Stat our file
	struct stat st;
	if (stat(filename, &st))
		error(3, 0, "Script file does not exist: %s\n", filename);

	// Read in our script file
	FILE *file = fopen(filename, "r");
	if (!file)
		error(4, 0, "Unable to open file: %s\n", filename);
	char *jscript = new char[st.st_size + 1];
	if (!jscript) {
		fclose(file);
		error(5, 0, "Out of memory!\n");
	}
	if ((size_t) fread(jscript, 1, st.st_size, file) != (size_t) st.st_size) {
		delete[] jscript;
		fclose(file);
		error(6, 0, "Error reading file: %s\n", filename);
	}
	fclose(file);
	jscript[st.st_size] = '\0';

	// Evaluate it
	req.addHook("system_args", set_path, argv+optind);
	Value res = global.evaluate(jscript, filename ? filename : "");
	delete[] jscript;

	// Print uncaught exceptions
	if (res.isException())
		error(8, 0, "Uncaught Exception: %s\n", res.to<UTF8>().c_str());

	// Let the script return exitcodes
	if (res.isNumber())
		exitcode = (int) res.to<long>();

	return exitcode;
}
<|endoftext|>


<commit_before>#include "halley/core/graphics/movie/movie_player.h"
#include "resources/resources.h"
#include "halley/concurrency/concurrent.h"
#include "halley/core/graphics/texture_descriptor.h"
#include "halley/core/graphics/texture.h"
#include "halley/core/graphics/render_target/render_target_texture.h"
#include "halley/core/api/video_api.h"
#include "halley/core/graphics/material/material_definition.h"
#include "halley/core/graphics/render_context.h"
#include "halley/audio/audio_clip.h"
#include <chrono>
#include "halley/audio/audio_position.h"

using namespace Halley;
using namespace std::chrono_literals;

MoviePlayer::MoviePlayer(VideoAPI& video, AudioAPI& audio)
	: video(video)
	, audio(audio)
	, threadRunning(false)
{
}

MoviePlayer::~MoviePlayer()
{
	stopThread();
}

void MoviePlayer::play()
{
	if (state == MoviePlayerState::Paused) {
		startThread();

		if (!streamingClip) {
			streamingClip = std::make_shared<StreamingAudioClip>(2);
		}

		renderTexture = video.createTexture(getSize());
		auto desc = TextureDescriptor(getSize(), TextureFormat::RGBA);
		desc.useFiltering = true;
		desc.isRenderTarget = true;
		renderTexture->load(std::move(desc));

		renderTarget = video.createTextureRenderTarget();
		renderTarget->setTarget(0, renderTexture);
		renderTarget->setViewPort(Rect4i(Vector2i(), getSize()));

		state = MoviePlayerState::StartingToPlay;

		for (auto& s: streams) {
			s.playing = true;
		}
	}
}

void MoviePlayer::pause()
{
	if (state == MoviePlayerState::Playing) {
		state = MoviePlayerState::Paused;

		for (auto& s: streams) {
			s.playing = false;
		}

		audioHandle->stop();
	}
}

void MoviePlayer::reset()
{
	stopThread();

	state = MoviePlayerState::Paused;
	time = 0;

	currentTexture.reset();
	pendingFrames.clear();
	streamingClip.reset();
	if (audioHandle) {
		audioHandle->stop();
		audioHandle.reset();
	}

	onReset();
}

void MoviePlayer::update(Time t)
{
	if (state == MoviePlayerState::Playing) {
		time += t;
	}

	if (state == MoviePlayerState::Playing || state == MoviePlayerState::StartingToPlay) {
		if (!pendingFrames.empty()) {
			auto& next = pendingFrames.front();
			if (time >= next.time) {
				currentTexture = next.texture;
				pendingFrames.pop_front();
			}
		}

		if (state == MoviePlayerState::Playing && pendingFrames.empty() && !needsMoreVideoFrames()) {
			state = MoviePlayerState::Finished;
		}

		if (state == MoviePlayerState::StartingToPlay) {
			if (!needsMoreVideoFrames() && !needsMoreAudioFrames()) {
				if (streamingClip) {
					audioHandle = audio.play(streamingClip, AudioPosition::makeUI(), 0.5f);
				}
				state = MoviePlayerState::Playing;
			}
		}
	}

}

void MoviePlayer::render(Resources& resources, RenderContext& rc)
{
	if (currentTexture) {
		Camera cam;
		cam.setPosition(Vector2f(videoSize) * 0.5f);
		cam.setZoom(1.0f);

		auto c = rc.with(*renderTarget).with(cam);
		c.bind([&] (Painter& painter)
		{
			auto matDef = resources.get<MaterialDefinition>("Halley/NV12Video");
			Sprite().setImage(currentTexture, matDef).setTexRect(Rect4f(0, 0, 1, 1)).setSize(Vector2f(videoSize)).draw(painter);
		});
		currentTexture.reset();
	}
}

Sprite MoviePlayer::getSprite(Resources& resources)
{
	if (renderTexture) {
		auto matDef = resources.get<MaterialDefinition>("Halley/Sprite");
		return Sprite().setImage(renderTexture, matDef).setTexRect(Rect4f(0, 0, 1, 1)).setSize(Vector2f(videoSize));
	} else {
		return Sprite().setMaterial(resources, "Halley/SolidColour").setColour(Colour4f(0, 0, 0)).setSize(Vector2f(videoSize));
	}
}

MoviePlayerState MoviePlayer::getState() const
{
	return state;
}

Vector2i MoviePlayer::getSize() const
{
	return videoSize;
}

void MoviePlayer::startThread()
{
	if (!threadRunning) {
		threadRunning = true;
		workerThread = std::thread([=] () {
			threadEntry();
		});
	}
}

void MoviePlayer::stopThread()
{
	if (threadRunning) {
		threadRunning = false;
		workerThread.join();
	}
}

void MoviePlayer::threadEntry()
{
	while (threadRunning) {
		bool hadWork = false;
		if (needsMoreAudioFrames()) {
			requestAudioFrame();
			hadWork = true;
		}
		if (needsMoreVideoFrames()) {
			requestVideoFrame();
			hadWork = true;
		}

		if (!hadWork) {
			std::this_thread::sleep_for(2ms);
		}
	}
}

bool MoviePlayer::needsMoreVideoFrames() const
{
	std::unique_lock<std::mutex> lock(mutex);

	if (state != MoviePlayerState::Playing && state != MoviePlayerState::StartingToPlay) {
		return false;
	}

	const MoviePlayerStream* videoStream = nullptr;
	for (auto& s: streams) {
		if (s.type == MoviePlayerStreamType::Video) {
			videoStream = &s;
			break;
		}
	}

	if (!videoStream) {
		return false;
	}

	return pendingFrames.size() < 7 && !videoStream->eof;
}

bool MoviePlayer::needsMoreAudioFrames() const
{
	std::unique_lock<std::mutex> lock(mutex);

	if (state != MoviePlayerState::Playing && state != MoviePlayerState::StartingToPlay) {
		return false;
	}

	const MoviePlayerStream* audioStream = nullptr;
	for (auto& s: streams) {
		if (s.type == MoviePlayerStreamType::Audio) {
			audioStream = &s;
		}
	}

	if (!audioStream) {
		return false;
	}

	if (!streamingClip) {
		return false;
	}

	return streamingClip->getSamplesLeft() < 20000 && !audioStream->eof;
}

void MoviePlayer::setVideoSize(Vector2i size)
{
	videoSize = size;
}

VideoAPI& MoviePlayer::getVideoAPI() const
{
	return video;
}

AudioAPI& MoviePlayer::getAudioAPI() const
{
	return audio;
}

void MoviePlayer::onVideoFrameAvailable(Time time, TextureDescriptor&& descriptor)
{
	auto desc = std::make_shared<TextureDescriptor>(std::move(descriptor));

	Concurrent::execute(Executors::getVideoAux(), [this, time, desc(std::move(desc))] ()
	{
		auto descriptor = TextureDescriptor(std::move(*desc));
		std::shared_ptr<Texture> tex;

		{
			std::unique_lock<std::mutex> lock(mutex);

			if (recycleTexture.empty()) {
				tex = video.createTexture(descriptor.size);
			} else {
				tex = recycleTexture.front();
				recycleTexture.pop_front();
			}
			tex->startLoading();
			pendingFrames.push_back({tex, time});
		}

		tex->load(std::move(descriptor));
	});
}

void MoviePlayer::onVideoFrameAvailable(Time time, std::shared_ptr<Texture> texture)
{
	pendingFrames.push_back({texture, time});
}

void MoviePlayer::onAudioFrameAvailable(Time time, gsl::span<const short> origSamples)
{
	std::vector<AudioConfig::SampleFormat> samples(origSamples.size());
	for (size_t i = 0; i < origSamples.size(); ++i) {
		samples[i] = origSamples[i] / 32768.0f;
	}
	onAudioFrameAvailable(time, gsl::span<const AudioConfig::SampleFormat>(samples.data(), samples.size()));
}

void MoviePlayer::onAudioFrameAvailable(Time time, gsl::span<const AudioConfig::SampleFormat> samples)
{
	streamingClip->addInterleavedSamples(samples);
}
<commit_msg>Fix warning<commit_after>#include "halley/core/graphics/movie/movie_player.h"
#include "resources/resources.h"
#include "halley/concurrency/concurrent.h"
#include "halley/core/graphics/texture_descriptor.h"
#include "halley/core/graphics/texture.h"
#include "halley/core/graphics/render_target/render_target_texture.h"
#include "halley/core/api/video_api.h"
#include "halley/core/graphics/material/material_definition.h"
#include "halley/core/graphics/render_context.h"
#include "halley/audio/audio_clip.h"
#include <chrono>
#include "halley/audio/audio_position.h"

using namespace Halley;
using namespace std::chrono_literals;

MoviePlayer::MoviePlayer(VideoAPI& video, AudioAPI& audio)
	: video(video)
	, audio(audio)
	, threadRunning(false)
{
}

MoviePlayer::~MoviePlayer()
{
	stopThread();
}

void MoviePlayer::play()
{
	if (state == MoviePlayerState::Paused) {
		startThread();

		if (!streamingClip) {
			streamingClip = std::make_shared<StreamingAudioClip>(2);
		}

		renderTexture = video.createTexture(getSize());
		auto desc = TextureDescriptor(getSize(), TextureFormat::RGBA);
		desc.useFiltering = true;
		desc.isRenderTarget = true;
		renderTexture->load(std::move(desc));

		renderTarget = video.createTextureRenderTarget();
		renderTarget->setTarget(0, renderTexture);
		renderTarget->setViewPort(Rect4i(Vector2i(), getSize()));

		state = MoviePlayerState::StartingToPlay;

		for (auto& s: streams) {
			s.playing = true;
		}
	}
}

void MoviePlayer::pause()
{
	if (state == MoviePlayerState::Playing) {
		state = MoviePlayerState::Paused;

		for (auto& s: streams) {
			s.playing = false;
		}

		audioHandle->stop();
	}
}

void MoviePlayer::reset()
{
	stopThread();

	state = MoviePlayerState::Paused;
	time = 0;

	currentTexture.reset();
	pendingFrames.clear();
	streamingClip.reset();
	if (audioHandle) {
		audioHandle->stop();
		audioHandle.reset();
	}

	onReset();
}

void MoviePlayer::update(Time t)
{
	if (state == MoviePlayerState::Playing) {
		time += t;
	}

	if (state == MoviePlayerState::Playing || state == MoviePlayerState::StartingToPlay) {
		if (!pendingFrames.empty()) {
			auto& next = pendingFrames.front();
			if (time >= next.time) {
				currentTexture = next.texture;
				pendingFrames.pop_front();
			}
		}

		if (state == MoviePlayerState::Playing && pendingFrames.empty() && !needsMoreVideoFrames()) {
			state = MoviePlayerState::Finished;
		}

		if (state == MoviePlayerState::StartingToPlay) {
			if (!needsMoreVideoFrames() && !needsMoreAudioFrames()) {
				if (streamingClip) {
					audioHandle = audio.play(streamingClip, AudioPosition::makeUI(), 0.5f);
				}
				state = MoviePlayerState::Playing;
			}
		}
	}

}

void MoviePlayer::render(Resources& resources, RenderContext& rc)
{
	if (currentTexture) {
		Camera cam;
		cam.setPosition(Vector2f(videoSize) * 0.5f);
		cam.setZoom(1.0f);

		auto c = rc.with(*renderTarget).with(cam);
		c.bind([&] (Painter& painter)
		{
			auto matDef = resources.get<MaterialDefinition>("Halley/NV12Video");
			Sprite().setImage(currentTexture, matDef).setTexRect(Rect4f(0, 0, 1, 1)).setSize(Vector2f(videoSize)).draw(painter);
		});
		currentTexture.reset();
	}
}

Sprite MoviePlayer::getSprite(Resources& resources)
{
	if (renderTexture) {
		auto matDef = resources.get<MaterialDefinition>("Halley/Sprite");
		return Sprite().setImage(renderTexture, matDef).setTexRect(Rect4f(0, 0, 1, 1)).setSize(Vector2f(videoSize));
	} else {
		return Sprite().setMaterial(resources, "Halley/SolidColour").setColour(Colour4f(0, 0, 0)).setSize(Vector2f(videoSize));
	}
}

MoviePlayerState MoviePlayer::getState() const
{
	return state;
}

Vector2i MoviePlayer::getSize() const
{
	return videoSize;
}

void MoviePlayer::startThread()
{
	if (!threadRunning) {
		threadRunning = true;
		workerThread = std::thread([=] () {
			threadEntry();
		});
	}
}

void MoviePlayer::stopThread()
{
	if (threadRunning) {
		threadRunning = false;
		workerThread.join();
	}
}

void MoviePlayer::threadEntry()
{
	while (threadRunning) {
		bool hadWork = false;
		if (needsMoreAudioFrames()) {
			requestAudioFrame();
			hadWork = true;
		}
		if (needsMoreVideoFrames()) {
			requestVideoFrame();
			hadWork = true;
		}

		if (!hadWork) {
			std::this_thread::sleep_for(2ms);
		}
	}
}

bool MoviePlayer::needsMoreVideoFrames() const
{
	std::unique_lock<std::mutex> lock(mutex);

	if (state != MoviePlayerState::Playing && state != MoviePlayerState::StartingToPlay) {
		return false;
	}

	const MoviePlayerStream* videoStream = nullptr;
	for (auto& s: streams) {
		if (s.type == MoviePlayerStreamType::Video) {
			videoStream = &s;
			break;
		}
	}

	if (!videoStream) {
		return false;
	}

	return pendingFrames.size() < 7 && !videoStream->eof;
}

bool MoviePlayer::needsMoreAudioFrames() const
{
	std::unique_lock<std::mutex> lock(mutex);

	if (state != MoviePlayerState::Playing && state != MoviePlayerState::StartingToPlay) {
		return false;
	}

	const MoviePlayerStream* audioStream = nullptr;
	for (auto& s: streams) {
		if (s.type == MoviePlayerStreamType::Audio) {
			audioStream = &s;
		}
	}

	if (!audioStream) {
		return false;
	}

	if (!streamingClip) {
		return false;
	}

	return streamingClip->getSamplesLeft() < 20000 && !audioStream->eof;
}

void MoviePlayer::setVideoSize(Vector2i size)
{
	videoSize = size;
}

VideoAPI& MoviePlayer::getVideoAPI() const
{
	return video;
}

AudioAPI& MoviePlayer::getAudioAPI() const
{
	return audio;
}

void MoviePlayer::onVideoFrameAvailable(Time time, TextureDescriptor&& descriptor)
{
	auto desc = std::make_shared<TextureDescriptor>(std::move(descriptor));

	Concurrent::execute(Executors::getVideoAux(), [this, time, desc(std::move(desc))] ()
	{
		auto descriptor = TextureDescriptor(std::move(*desc));
		std::shared_ptr<Texture> tex;

		{
			std::unique_lock<std::mutex> lock(mutex);

			if (recycleTexture.empty()) {
				tex = video.createTexture(descriptor.size);
			} else {
				tex = recycleTexture.front();
				recycleTexture.pop_front();
			}
			tex->startLoading();
			pendingFrames.push_back({tex, time});
		}

		tex->load(std::move(descriptor));
	});
}

void MoviePlayer::onVideoFrameAvailable(Time time, std::shared_ptr<Texture> texture)
{
	pendingFrames.push_back({texture, time});
}

void MoviePlayer::onAudioFrameAvailable(Time time, gsl::span<const short> origSamples)
{
	std::vector<AudioConfig::SampleFormat> samples(origSamples.size());
	for (size_t i = 0; i < size_t(origSamples.size()); ++i) {
		samples[i] = origSamples[i] / 32768.0f;
	}
	onAudioFrameAvailable(time, gsl::span<const AudioConfig::SampleFormat>(samples.data(), samples.size()));
}

void MoviePlayer::onAudioFrameAvailable(Time time, gsl::span<const AudioConfig::SampleFormat> samples)
{
	streamingClip->addInterleavedSamples(samples);
}
<|endoftext|>


<commit_before>//
// VariantMatchRenderer.cpp
//
// Copyright (C) 2013 by University of Stuttgart (VISUS).
// All rights reserved.
//
// Created on: Jun 15, 2013
//     Author: scharnkn
//

#include <stdafx.h>
#include "VariantMatchRenderer.h"
#include "VariantMatchDataCall.h"
#include "ogl_error_check.h"

#include <param/FloatParam.h>
#include <param/IntParam.h>
#include <CoreInstance.h>
#include <vislib/SimpleFont.h>
#include <vislib/OutlineFont.h>
#include <vislib/ShaderSource.h>
#include <vislib/Log.h>

using namespace megamol;
using namespace megamol::protein;

/*
 * VariantMatchRenderer::VariantMatchRenderer
 */
VariantMatchRenderer::VariantMatchRenderer(void) : Renderer2DModule () ,
        dataCallerSlot("getData", "Connects the rendering with data storage" ),
        minColSlot("minCol", "..." ),
        maxColSlot("maxCol", "..." ),
        matrixTex(0),
        thefont(vislib::graphics::gl::FontInfo_Verdana) {

    // Data caller slot to get matching matrix
    this->dataCallerSlot.SetCompatibleCall<VariantMatchDataCallDescription>();
    this->MakeSlotAvailable(&this->dataCallerSlot);

    // Parameter for minimum color
    this->minCol = 0.0f;
    this->minColSlot.SetParameter(new core::param::FloatParam(this->minCol));
    this->MakeSlotAvailable(&this->minColSlot);

    // Parameter for maximum color
    this->maxCol = 1.0f;
    this->maxColSlot.SetParameter(new core::param::FloatParam(this->maxCol));
    this->MakeSlotAvailable(&this->maxColSlot);
}

/*
 * VariantMatchRenderer::~VariantMatchRenderer
 */
VariantMatchRenderer::~VariantMatchRenderer(void) {
    this->Release();
}

/*
 * VariantMatchRenderer::create
 */
bool VariantMatchRenderer::create(void) {
    vislib::graphics::gl::ShaderSource vertSrc;
    vislib::graphics::gl::ShaderSource fragSrc;

    megamol::core::CoreInstance *ci = this->GetCoreInstance();
    if(!ci) {
        return false;
    }

    if(!glh_init_extensions("GL_EXT_framebuffer_object GL_ARB_draw_buffers")) {
        return false;
    }
    if(!vislib::graphics::gl::GLSLShader::InitialiseExtensions()) {
        return false;
    }

    if (!glh_init_extensions("GL_ARB_texture_non_power_of_two")) {
        return false;
    }

    // Try to load the ssao shader
    if(!ci->ShaderSourceFactory().MakeShaderSource("2dplot::variantMatrix::vertex", vertSrc)) {
        vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_ERROR,
            "%s: Unable to load variant matrix vertex shader source", this->ClassName() );
        return false;
    }
    if(!ci->ShaderSourceFactory().MakeShaderSource("2dplot::variantMatrix::fragment", fragSrc)) {
        vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_ERROR,
            "%s: Unable to load variant matrix fragment shader source", this->ClassName() );
        return false;
    }
    try {
        if(!this->matrixTexShader.Create(vertSrc.Code(), vertSrc.Count(), fragSrc.Code(), fragSrc.Count()))
            throw vislib::Exception("Generic creation failure", __FILE__, __LINE__);
    }
    catch(vislib::Exception &e){
        vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_ERROR,
            "%s: Unable to create shader: %s\n", this->ClassName(), e.GetMsgA());
        return false;
    }

    return true;
}

/*
 * VariantMatchRenderer::GetExtents
 */
bool VariantMatchRenderer::GetExtents(megamol::core::view::CallRender2D& call) {
    call.SetBoundingBox(-1.0f, -1.0f, 1.0f, 1.0f);
    return true;
}

/*
 * VariantMatchRenderer::release
 */
void VariantMatchRenderer::release(void) {
    this->matrixTexShader.Release();
    CheckForGLError();
    if (this->matrixTex) {
        glDeleteTextures(1, &this->matrixTex);
    }
    CheckForGLError();
}

/*
 * VariantMatchRenderer::Render
 */
bool VariantMatchRenderer::Render(megamol::core::view::CallRender2D& call) {

    float gridHalfStep;

    // Update parameters
    this->updateParams();

    // Get pointer to VariantMatchDataCall
    VariantMatchDataCall *vmc = this->dataCallerSlot.CallAs<VariantMatchDataCall>();
    if(vmc == NULL) {
        return false;
    }

    //  Execute call for Getdata
    if (!(*vmc)(VariantMatchDataCall::CallForGetData)) {
        return false;
    }

    // Init matrix texture
    // TODO Only do this if necessary
    glEnable(GL_TEXTURE_2D);
    if (this->matrixTex) {
        ::glDeleteTextures(1, &this->matrixTex);
    }
    glGenTextures(1, &this->matrixTex);
    glBindTexture(GL_TEXTURE_2D, this->matrixTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA32F,
            vmc->GetVariantCnt(),
            vmc->GetVariantCnt(),
            0, GL_ALPHA, GL_FLOAT,
            vmc->GetMatch());

//    // DEBUG Print matrix values
//    printf("Variant count %u\n", vmc->GetVariantCnt());
//    for (int i = 0; i < vmc->GetVariantCnt(); ++i) {
//        for (int j = 0; j < vmc->GetVariantCnt(); ++j) {
//            printf("%.2f ", vmc->GetMatch()[j*vmc->GetVariantCnt() + i]);
//        }
//        printf("\n");
//    }
//    // END DEBUG

//    ::glMatrixMode(GL_PROJECTION);
//    ::glPushMatrix();
//    ::glLoadIdentity();

//    ::glMatrixMode(GL_MODELVIEW);
//    ::glPushMatrix();
//    ::glLoadIdentity();

    //gridStep = (2.0 - this->labelSpace)/static_cast<float>(vmc->GetVariantCnt());
    //gridHalfStep = gridStep*0.5f;

//    printf("Min %f, max %f\n", vmc->GetMin(), vmc->GetMax());

    // Draw matrix texture
//    ::glColor3f(0.0f, 0.6f, 0.6f);
    this->matrixTexShader.Enable();
    glUniform1iARB(this->matrixTexShader.ParameterLocation("matrixTex"), 0);
    glUniform1fARB(this->matrixTexShader.ParameterLocation("minVal"), this->minCol);
    glUniform1fARB(this->matrixTexShader.ParameterLocation("maxVal"), this->maxCol);
    ::glBegin(GL_QUADS);
        ::glTexCoord2f(1.0f, 0.0f);
        ::glVertex2f(-1.0, -1.0);
        ::glTexCoord2f(1.0f, 1.0f);
        ::glVertex2f( 1.0, -1.0);
        ::glTexCoord2f(0.0f, 1.0f);
        ::glVertex2f( 1.0f,  1.0f);
        ::glTexCoord2f(0.0f, 0.0f);
        ::glVertex2f(-1.0,  1.0f);
    ::glEnd();
    this->matrixTexShader.Disable();

    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

    ::glColor3f(1.0f, 1.0f, 1.0f);
    ::glLineWidth(3);
    // Draw vertical lines
    float gridStep = 2.0f/static_cast<float>(vmc->GetVariantCnt());
    ::glBegin(GL_LINES);
        for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
            ::glVertex2f(gridStep*i-1.0, -1.0);
            ::glVertex2f(gridStep*i-1.0,  1.0);
        }
    ::glEnd();

    // Draw horizontal lines
    ::glBegin(GL_LINES);
        for (int i = 1; i <= static_cast<int>(vmc->GetVariantCnt()); ++i) {
            ::glVertex2f(-1.0, gridStep*i-1.0);
            ::glVertex2f( 1.0, gridStep*i-1.0);
        }
    ::glEnd();

    glColor3f(0.0, 0.0, 0.0);
//    vislib::graphics::gl::SimpleFont f;
    vislib::StringA str;
    if (!this->thefont.Initialise()) {
        return false;
    }
    float fontSize = std::min(2.0f/static_cast<float>(vmc->GetVariantCnt()), 0.1f);
    float lineHeight = this->thefont.LineHeight(2.0f/static_cast<float>(vmc->GetVariantCnt()));
    float maxLineWidth = 0.0f;
    for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
        maxLineWidth = std::max(maxLineWidth, this->thefont.LineWidth(fontSize, vmc->GetLabels()[i].PeekBuffer()));
    }

//    f.SetSize(0.5f);
    // Draw vertical labels
    for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
        //printf("LABEL %s\n", vmc->GetLabels()[i].PeekBuffer());
        //str.Format("Variant %2i", static_cast<int>(vmc->GetVariantCnt()) - i);
        this->thefont.DrawString(
                -1.0 - maxLineWidth, // Left coordinate of the rectangle
                1.0 - lineHeight*(i+1),     // Upper coordinate of the rectangle
                maxLineWidth,        // The width of the rectangle
                lineHeight,                       // The height of the rectangle
                fontSize,                       // The font size
                true,                    // Flip y
                vmc->GetLabels()[i].PeekBuffer(),    // The label
                vislib::graphics::AbstractFont::ALIGN_LEFT_MIDDLE);
    }
    // Draw horizontal labels
    ::glMatrixMode(GL_MODELVIEW);
    for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
        ::glPushMatrix();
//        ::glLoadIdentity();
        //        ::glTranslatef(
        //                (this->labelSpace+gridStep*(i))-1.0 + gridHalfStep,
        //                1.0 - this->labelSpace*0.5,
        //                0.0);
        ::glRotatef(90, 0.0, 0.0, 1.0);
        //str.Format("Variant %2i", i);
        this->thefont.DrawString(
                1.0, // Left coordinate of the rectangle
                1.0 - lineHeight*(i+1),     // Upper coordinate of the rectangle
                maxLineWidth,        // The width of the rectangle
                lineHeight,                       // The height of the rectangle
                fontSize,                       // The font size
                true,                    // Flip y
                vmc->GetLabels()[i].PeekBuffer(),    // The label
                vislib::graphics::AbstractFont::ALIGN_LEFT_MIDDLE);
        ::glPopMatrix();
    }

//    ::glPopMatrix();
//
//    ::glMatrixMode(GL_PROJECTION);
//    ::glPopMatrix();

    return true;
}

/*
 * VariantMatchRenderer::updateParams
 */
void VariantMatchRenderer::updateParams() {

    // Min color
    if (this->minColSlot.IsDirty()) {
        this->minCol = this->minColSlot.Param<core::param::FloatParam>()->Value();
        this->minColSlot.ResetDirty();
    }

    // Max color
    if (this->maxColSlot.IsDirty()) {
        this->maxCol = this->maxColSlot.Param<core::param::FloatParam>()->Value();
        this->maxColSlot.ResetDirty();
    }

}
<commit_msg>Added index to label in VariantMatchRenderer<commit_after>//
// VariantMatchRenderer.cpp
//
// Copyright (C) 2013 by University of Stuttgart (VISUS).
// All rights reserved.
//
// Created on: Jun 15, 2013
//     Author: scharnkn
//

#include <stdafx.h>
#include "VariantMatchRenderer.h"
#include "VariantMatchDataCall.h"
#include "ogl_error_check.h"

#include <param/FloatParam.h>
#include <param/IntParam.h>
#include <CoreInstance.h>
#include <vislib/SimpleFont.h>
#include <vislib/OutlineFont.h>
#include <vislib/ShaderSource.h>
#include <vislib/Log.h>

using namespace megamol;
using namespace megamol::protein;

/*
 * VariantMatchRenderer::VariantMatchRenderer
 */
VariantMatchRenderer::VariantMatchRenderer(void) : Renderer2DModule () ,
        dataCallerSlot("getData", "Connects the rendering with data storage" ),
        minColSlot("minCol", "..." ),
        maxColSlot("maxCol", "..." ),
        matrixTex(0),
        thefont(vislib::graphics::gl::FontInfo_Verdana) {

    // Data caller slot to get matching matrix
    this->dataCallerSlot.SetCompatibleCall<VariantMatchDataCallDescription>();
    this->MakeSlotAvailable(&this->dataCallerSlot);

    // Parameter for minimum color
    this->minCol = 0.0f;
    this->minColSlot.SetParameter(new core::param::FloatParam(this->minCol));
    this->MakeSlotAvailable(&this->minColSlot);

    // Parameter for maximum color
    this->maxCol = 1.0f;
    this->maxColSlot.SetParameter(new core::param::FloatParam(this->maxCol));
    this->MakeSlotAvailable(&this->maxColSlot);
}

/*
 * VariantMatchRenderer::~VariantMatchRenderer
 */
VariantMatchRenderer::~VariantMatchRenderer(void) {
    this->Release();
}

/*
 * VariantMatchRenderer::create
 */
bool VariantMatchRenderer::create(void) {
    vislib::graphics::gl::ShaderSource vertSrc;
    vislib::graphics::gl::ShaderSource fragSrc;

    megamol::core::CoreInstance *ci = this->GetCoreInstance();
    if(!ci) {
        return false;
    }

    if(!glh_init_extensions("GL_EXT_framebuffer_object GL_ARB_draw_buffers")) {
        return false;
    }
    if(!vislib::graphics::gl::GLSLShader::InitialiseExtensions()) {
        return false;
    }

    if (!glh_init_extensions("GL_ARB_texture_non_power_of_two")) {
        return false;
    }

    // Try to load the ssao shader
    if(!ci->ShaderSourceFactory().MakeShaderSource("2dplot::variantMatrix::vertex", vertSrc)) {
        vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_ERROR,
            "%s: Unable to load variant matrix vertex shader source", this->ClassName() );
        return false;
    }
    if(!ci->ShaderSourceFactory().MakeShaderSource("2dplot::variantMatrix::fragment", fragSrc)) {
        vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_ERROR,
            "%s: Unable to load variant matrix fragment shader source", this->ClassName() );
        return false;
    }
    try {
        if(!this->matrixTexShader.Create(vertSrc.Code(), vertSrc.Count(), fragSrc.Code(), fragSrc.Count()))
            throw vislib::Exception("Generic creation failure", __FILE__, __LINE__);
    }
    catch(vislib::Exception &e){
        vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_ERROR,
            "%s: Unable to create shader: %s\n", this->ClassName(), e.GetMsgA());
        return false;
    }

    return true;
}

/*
 * VariantMatchRenderer::GetExtents
 */
bool VariantMatchRenderer::GetExtents(megamol::core::view::CallRender2D& call) {
    call.SetBoundingBox(-1.0f, -1.0f, 1.0f, 1.0f);
    return true;
}

/*
 * VariantMatchRenderer::release
 */
void VariantMatchRenderer::release(void) {
    this->matrixTexShader.Release();
    CheckForGLError();
    if (this->matrixTex) {
        glDeleteTextures(1, &this->matrixTex);
    }
    CheckForGLError();
}

/*
 * VariantMatchRenderer::Render
 */
bool VariantMatchRenderer::Render(megamol::core::view::CallRender2D& call) {

    float gridHalfStep;

    // Update parameters
    this->updateParams();

    // Get pointer to VariantMatchDataCall
    VariantMatchDataCall *vmc = this->dataCallerSlot.CallAs<VariantMatchDataCall>();
    if(vmc == NULL) {
        return false;
    }

    //  Execute call for Getdata
    if (!(*vmc)(VariantMatchDataCall::CallForGetData)) {
        return false;
    }

    // Init matrix texture
    // TODO Only do this if necessary
    glEnable(GL_TEXTURE_2D);
    if (this->matrixTex) {
        ::glDeleteTextures(1, &this->matrixTex);
    }
    glGenTextures(1, &this->matrixTex);
    glBindTexture(GL_TEXTURE_2D, this->matrixTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA32F,
            vmc->GetVariantCnt(),
            vmc->GetVariantCnt(),
            0, GL_ALPHA, GL_FLOAT,
            vmc->GetMatch());

//    // DEBUG Print matrix values
//    printf("Variant count %u\n", vmc->GetVariantCnt());
//    for (int i = 0; i < vmc->GetVariantCnt(); ++i) {
//        for (int j = 0; j < vmc->GetVariantCnt(); ++j) {
//            printf("%.2f ", vmc->GetMatch()[j*vmc->GetVariantCnt() + i]);
//        }
//        printf("\n");
//    }
//    // END DEBUG

//    ::glMatrixMode(GL_PROJECTION);
//    ::glPushMatrix();
//    ::glLoadIdentity();

//    ::glMatrixMode(GL_MODELVIEW);
//    ::glPushMatrix();
//    ::glLoadIdentity();

    //gridStep = (2.0 - this->labelSpace)/static_cast<float>(vmc->GetVariantCnt());
    //gridHalfStep = gridStep*0.5f;

//    printf("Min %f, max %f\n", vmc->GetMin(), vmc->GetMax());

    // Draw matrix texture
//    ::glColor3f(0.0f, 0.6f, 0.6f);
    this->matrixTexShader.Enable();
    glUniform1iARB(this->matrixTexShader.ParameterLocation("matrixTex"), 0);
    glUniform1fARB(this->matrixTexShader.ParameterLocation("minVal"), this->minCol);
    glUniform1fARB(this->matrixTexShader.ParameterLocation("maxVal"), this->maxCol);
    ::glBegin(GL_QUADS);
        ::glTexCoord2f(1.0f, 0.0f);
        ::glVertex2f(-1.0, -1.0);
        ::glTexCoord2f(1.0f, 1.0f);
        ::glVertex2f( 1.0, -1.0);
        ::glTexCoord2f(0.0f, 1.0f);
        ::glVertex2f( 1.0f,  1.0f);
        ::glTexCoord2f(0.0f, 0.0f);
        ::glVertex2f(-1.0,  1.0f);
    ::glEnd();
    this->matrixTexShader.Disable();

    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

    ::glColor3f(1.0f, 1.0f, 1.0f);
    ::glLineWidth(3);
    // Draw vertical lines
    float gridStep = 2.0f/static_cast<float>(vmc->GetVariantCnt());
    ::glBegin(GL_LINES);
        for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
            ::glVertex2f(gridStep*i-1.0, -1.0);
            ::glVertex2f(gridStep*i-1.0,  1.0);
        }
    ::glEnd();

    // Draw horizontal lines
    ::glBegin(GL_LINES);
        for (int i = 1; i <= static_cast<int>(vmc->GetVariantCnt()); ++i) {
            ::glVertex2f(-1.0, gridStep*i-1.0);
            ::glVertex2f( 1.0, gridStep*i-1.0);
        }
    ::glEnd();

    glColor3f(0.0, 0.0, 0.0);
//    vislib::graphics::gl::SimpleFont f;
    vislib::StringA str;
    if (!this->thefont.Initialise()) {
        return false;
    }
    float fontSize = std::min(2.0f/static_cast<float>(vmc->GetVariantCnt()), 0.1f);
    float lineHeight = this->thefont.LineHeight(2.0f/static_cast<float>(vmc->GetVariantCnt()));
    float maxLineWidth = 0.0f;
    for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
        maxLineWidth = std::max(maxLineWidth, this->thefont.LineWidth(fontSize, vmc->GetLabels()[i].PeekBuffer()));
    }
    maxLineWidth += this->thefont.LineWidth(fontSize, "000 : ");

//    f.SetSize(0.5f);
    // Draw vertical labels
    for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
        //printf("LABEL %s\n", vmc->GetLabels()[i].PeekBuffer());
        str.Format("%3i : ", i);
        str.Append(vmc->GetLabels()[i].PeekBuffer());
        this->thefont.DrawString(
                -1.0 - maxLineWidth, // Left coordinate of the rectangle
                1.0 - lineHeight*(i+1),     // Upper coordinate of the rectangle
                maxLineWidth,        // The width of the rectangle
                lineHeight,                       // The height of the rectangle
                fontSize,                       // The font size
                true,                    // Flip y
                //vmc->GetLabels()[i].PeekBuffer(),    // The label
                str.PeekBuffer(),
                vislib::graphics::AbstractFont::ALIGN_LEFT_MIDDLE);
    }
    // Draw horizontal labels
    ::glMatrixMode(GL_MODELVIEW);
    for (int i = 0; i < static_cast<int>(vmc->GetVariantCnt()); ++i) {
        ::glPushMatrix();
//        ::glLoadIdentity();
        //        ::glTranslatef(
        //                (this->labelSpace+gridStep*(i))-1.0 + gridHalfStep,
        //                1.0 - this->labelSpace*0.5,
        //                0.0);
        ::glRotatef(90, 0.0, 0.0, 1.0);
        str.Format("%3i : ", i);
        str.Append(vmc->GetLabels()[i].PeekBuffer());
        this->thefont.DrawString(
                1.0, // Left coordinate of the rectangle
                1.0 - lineHeight*(i+1),     // Upper coordinate of the rectangle
                maxLineWidth,        // The width of the rectangle
                lineHeight,                       // The height of the rectangle
                fontSize,                       // The font size
                true,                    // Flip y
                //vmc->GetLabels()[i].PeekBuffer(),    // The label
                str.PeekBuffer(),    // The label
                vislib::graphics::AbstractFont::ALIGN_LEFT_MIDDLE);
        ::glPopMatrix();
    }

//    ::glPopMatrix();
//
//    ::glMatrixMode(GL_PROJECTION);
//    ::glPopMatrix();

    return true;
}

/*
 * VariantMatchRenderer::updateParams
 */
void VariantMatchRenderer::updateParams() {

    // Min color
    if (this->minColSlot.IsDirty()) {
        this->minCol = this->minColSlot.Param<core::param::FloatParam>()->Value();
        this->minColSlot.ResetDirty();
    }

    // Max color
    if (this->maxColSlot.IsDirty()) {
        this->maxCol = this->maxColSlot.Param<core::param::FloatParam>()->Value();
        this->maxColSlot.ResetDirty();
    }

}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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 "google/cloud/storage/client.h"
#include <functional>
#include <iostream>
#include <map>
#include <sstream>

namespace {
struct Usage {
  std::string msg;
};

char const* ConsumeArg(int& argc, char* argv[]) {
  if (argc < 2) {
    return nullptr;
  }
  char const* result = argv[1];
  std::copy(argv + 2, argv + argc, argv + 1);
  argc--;
  return result;
}

std::string command_usage;

void PrintUsage(int argc, char* argv[], std::string const& msg) {
  std::string const cmd = argv[0];
  auto last_slash = std::string(cmd).find_last_of('/');
  auto program = cmd.substr(last_slash + 1);
  std::cerr << msg << "\nUsage: " << program << " <command> [arguments]\n\n"
            << "Commands:\n"
            << command_usage << "\n";
}

void GetServiceAccount(google::cloud::storage::Client client, int& argc,
                       char* argv[]) {
  if (argc != 1) {
    throw Usage{"get-service-account"};
  }
  //! [get service account]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client) {
    StatusOr<gcs::ServiceAccount> service_account_details =
        client.GetServiceAccount();

    if (!service_account_details) {
      throw std::runtime_error(service_account_details.status().message());
    }
    std::cout << "The service account details are " << *service_account_details
              << "\n";
  }
  //! [get service account]
  (std::move(client));
}

void GetServiceAccountForProject(google::cloud::storage::Client client,
                                 int& argc, char* argv[]) {
  if (argc != 2) {
    throw Usage{"get-service-account-for-project <project-id>"};
  }
  auto project_id = ConsumeArg(argc, argv);
  //! [get service account for project]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string project_id) {
    StatusOr<gcs::ServiceAccount> service_account_details =
        client.GetServiceAccountForProject(project_id);

    if (!service_account_details) {
      throw std::runtime_error(service_account_details.status().message());
    }

    std::cout << "The service account details for project " << project_id
              << " are " << *service_account_details << "\n";
  }
  //! [get service account for project]
  (std::move(client), project_id);
}

void ListHmacKeys(google::cloud::storage::Client client, int& argc,
                  char* argv[]) {
  if (argc != 1) {
    throw Usage{"list-hmac-keys"};
  }
  //! [list hmac keys] [START storage_list_hmac_keys]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client) {
    int count = 0;
    gcs::ListHmacKeysReader hmac_keys_list = client.ListHmacKeys();
    for (auto&& hmac_key_metadata : hmac_keys_list) {
      if (!hmac_key_metadata) {
        throw std::runtime_error(hmac_key_metadata.status().message());
      }
      std::cout << "service_account_email = "
                << hmac_key_metadata->service_account_email()
                << "\naccess_id = " << hmac_key_metadata->access_id() << "\n";
      ++count;
    }
    if (count == 0) {
      std::cout << "No HMAC keys in default project\n";
    }
  }
  //! [list hmac keys] [END storage_list_hmac_keys]
  (std::move(client));
}

void ListHmacKeysWithServiceAccount(google::cloud::storage::Client client,
                                    int& argc, char* argv[]) {
  if (argc != 2) {
    throw Usage{"list-hmac-keys-with-service-account <service-account>"};
  }
  //! [list hmac keys service account]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string service_account) {
    int count = 0;
    gcs::ListHmacKeysReader hmac_keys_list =
        client.ListHmacKeys(gcs::ServiceAccountFilter(service_account));
    for (auto&& hmac_key_metadata : hmac_keys_list) {
      if (!hmac_key_metadata) {
        throw std::runtime_error(hmac_key_metadata.status().message());
      }
      std::cout << "service_account_email = "
                << hmac_key_metadata->service_account_email()
                << "\naccess_id = " << hmac_key_metadata->access_id() << "\n";
      ++count;
    }
    if (count == 0) {
      std::cout << "No HMAC keys for service account " << service_account
                << " in default project\n";
    }
  }
  //! [list hmac keys service account]
  (std::move(client), argv[1]);
}

void CreateHmacKey(google::cloud::storage::Client client, int& argc,
                   char* argv[]) {
  if (argc != 2) {
    throw Usage{"create-hmac-key <service-account-email>"};
  }
  //! [create hmac key] [START storage_create_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string service_account_email) {
    StatusOr<std::pair<gcs::HmacKeyMetadata, std::string>> hmac_key_details =
        client.CreateHmacKey(service_account_email);

    if (!hmac_key_details) {
      throw std::runtime_error(hmac_key_details.status().message());
    }
    std::cout << "The base64 encoded secret is: " << hmac_key_details->second
              << "\nDo not miss that secret, there is no API to recover it."
              << "\nThe HMAC key metadata is: " << hmac_key_details->first
              << "\n";
  }
  //! [create hmac key] [END storage_create_hmac_key]
  (std::move(client), argv[1]);
}

void CreateHmacKeyForProject(google::cloud::storage::Client client, int& argc,
                             char* argv[]) {
  if (argc != 3) {
    throw Usage{
        "create-hmac-key-for-project <project-id> <service-account-email>"};
  }
  //! [create hmac key project]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string project_id,
     std::string service_account_email) {
    StatusOr<std::pair<gcs::HmacKeyMetadata, std::string>> hmac_key_details =
        client.CreateHmacKey(service_account_email,
                             gcs::OverrideDefaultProject(project_id));

    if (!hmac_key_details) {
      throw std::runtime_error(hmac_key_details.status().message());
    }
    std::cout << "The base64 encoded secret is: " << hmac_key_details->second
              << "\nDo not miss that secret, there is no API to recover it."
              << "\nThe HMAC key metadata is: " << hmac_key_details->first
              << "\n";
  }
  //! [create hmac key project]
  (std::move(client), argv[1], argv[2]);
}

void DeleteHmacKey(google::cloud::storage::Client client, int& argc,
                   char* argv[]) {
  if (argc != 2) {
    throw Usage{"delete-hmac-key <access-id>"};
  }
  //! [delete hmac key] [START storage_delete_hmac_key]
  namespace gcs = google::cloud::storage;
  [](gcs::Client client, std::string access_id) {
    google::cloud::Status status = client.DeleteHmacKey(access_id);

    if (!status.ok()) {
      throw std::runtime_error(status.message());
    }
    std::cout << "The key is deleted, though it may still appear"
              << " in ListHmacKeys() results.\n";
  }
  //! [delete hmac key] [END storage_delete_hmac_key]
  (std::move(client), argv[1]);
}

void GetHmacKey(google::cloud::storage::Client client, int& argc,
                char* argv[]) {
  if (argc != 2) {
    throw Usage{"get-hmac-key <access-id>"};
  }
  //! [get hmac key] [START storage_get_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id) {
    StatusOr<gcs::HmacKeyMetadata> hmac_key_details =
        client.GetHmacKey(access_id);

    if (!hmac_key_details) {
      throw std::runtime_error(hmac_key_details.status().message());
    }
    std::cout << "The HMAC key metadata is: " << *hmac_key_details << "\n";
  }
  //! [get hmac key] [END storage_get_hmac_key]
  (std::move(client), argv[1]);
}

void UpdateHmacKey(google::cloud::storage::Client client, int& argc,
                   char* argv[]) {
  if (argc != 3) {
    throw Usage{"update-hmac-key <access-id> <state>"};
  }
  //! [update hmac key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id, std::string state) {
    StatusOr<gcs::HmacKeyMetadata> current_metadata =
        client.GetHmacKey(access_id);
    if (!current_metadata) {
      throw std::runtime_error(current_metadata.status().message());
    }

    StatusOr<gcs::HmacKeyMetadata> updated_metadata = client.UpdateHmacKey(
        access_id, gcs::HmacKeyMetadata()
                       .set_state(std::move(state))
                       .set_etag(current_metadata->etag()));

    if (!updated_metadata) {
      throw std::runtime_error(updated_metadata.status().message());
    }
    std::cout << "The updated HMAC key metadata is: " << *updated_metadata
              << "\n";
  }
  //! [update hmac key]
  (std::move(client), argv[1], argv[2]);
}

void ActivateHmacKey(google::cloud::storage::Client client, int& argc,
                     char* argv[]) {
  if (argc != 2) {
    throw Usage{"activate-hmac-key <access-id>"};
  }
  //! [START storage_activate_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id, std::string state) {
    StatusOr<gcs::HmacKeyMetadata> current_metadata =
        client.GetHmacKey(access_id);
    if (!current_metadata) {
      throw std::runtime_error(current_metadata.status().message());
    }

    StatusOr<gcs::HmacKeyMetadata> updated_metadata = client.UpdateHmacKey(
        access_id, gcs::HmacKeyMetadata()
                       .set_state(gcs::HmacKeyMetadata::state_active())
                       .set_etag(current_metadata->etag()));

    if (!updated_metadata) {
      throw std::runtime_error(updated_metadata.status().message());
    }
    if (updated_metadata->state() != gcs::HmacKeyMetadata::state_active()) {
      throw std::runtime_error(
          "The HMAC key is NOT active, this is unexpected");
    }
    std::cout << "The HMAC key is now active\nFull metadata: "
              << *updated_metadata << "\n";
  }
  //! [END storage_activate_hmac_key]
  (std::move(client), argv[1], argv[2]);
}

void DeactivateHmacKey(google::cloud::storage::Client client, int& argc,
                       char* argv[]) {
  if (argc != 2) {
    throw Usage{"deactivate-hmac-key <access-id>"};
  }
  //! [START storage_deactivate_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id, std::string state) {
    StatusOr<gcs::HmacKeyMetadata> current_metadata =
        client.GetHmacKey(access_id);
    if (!current_metadata) {
      throw std::runtime_error(current_metadata.status().message());
    }

    StatusOr<gcs::HmacKeyMetadata> updated_metadata = client.UpdateHmacKey(
        access_id, gcs::HmacKeyMetadata()
                       .set_state(gcs::HmacKeyMetadata::state_inactive())
                       .set_etag(current_metadata->etag()));

    if (!updated_metadata) {
      throw std::runtime_error(updated_metadata.status().message());
    }
    if (updated_metadata->state() != gcs::HmacKeyMetadata::state_inactive()) {
      throw std::runtime_error("The HMAC key is active, this is unexpected");
    }
    std::cout << "The HMAC key is now inactive\nFull metadata: "
              << *updated_metadata << "\n";
  }
  //! [END storage_deactivate_hmac_key]
  (std::move(client), argv[1], argv[2]);
}

}  // anonymous namespace

int main(int argc, char* argv[]) try {
  // Create a client to communicate with Google Cloud Storage.
  google::cloud::StatusOr<google::cloud::storage::Client> client =
      google::cloud::storage::Client::CreateDefaultClient();
  if (!client) {
    std::cerr << "Failed to create Storage Client, status=" << client.status()
              << "\n";
    return 1;
  }

  using CommandType =
      std::function<void(google::cloud::storage::Client, int&, char* [])>;
  std::map<std::string, CommandType> commands = {
      {"get-service-account", &GetServiceAccount},
      {"get-service-account-for-project", &GetServiceAccountForProject},
      {"list-hmac-keys", &ListHmacKeys},
      {"list-hmac-keys-with-service-account", &ListHmacKeysWithServiceAccount},
      {"create-hmac-key-for-project", &CreateHmacKeyForProject},
      {"create-hmac-key", &CreateHmacKey},
      {"delete-hmac-key", &DeleteHmacKey},
      {"get-hmac-key", &GetHmacKey},
      {"update-hmac-key", &UpdateHmacKey},
      {"activate-hmac-key", &ActivateHmacKey},
      {"deactivate-hmac-key", &DeactivateHmacKey},
  };
  for (auto&& kv : commands) {
    try {
      int fake_argc = 0;
      kv.second(*client, fake_argc, argv);
    } catch (Usage const& u) {
      command_usage += "    ";
      command_usage += u.msg;
      command_usage += "\n";
    } catch (...) {
      // ignore other exceptions.
    }
  }

  if (argc < 2) {
    PrintUsage(argc, argv, "Missing command");
    return 1;
  }

  std::string const command = ConsumeArg(argc, argv);
  auto it = commands.find(command);
  if (commands.end() == it) {
    PrintUsage(argc, argv, "Unknown command: " + command);
    return 1;
  }

  it->second(*client, argc, argv);

  return 0;
} catch (Usage const& ex) {
  PrintUsage(argc, argv, ex.msg);
  return 1;
} catch (std::exception const& ex) {
  std::cerr << "Standard C++ exception raised: " << ex.what() << "\n";
  return 1;
}
<commit_msg>Fix cut&paste mistakes in HMAC key examples. (#2445)<commit_after>// Copyright 2019 Google LLC
//
// 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 "google/cloud/storage/client.h"
#include <functional>
#include <iostream>
#include <map>
#include <sstream>

namespace {
struct Usage {
  std::string msg;
};

char const* ConsumeArg(int& argc, char* argv[]) {
  if (argc < 2) {
    return nullptr;
  }
  char const* result = argv[1];
  std::copy(argv + 2, argv + argc, argv + 1);
  argc--;
  return result;
}

std::string command_usage;

void PrintUsage(int argc, char* argv[], std::string const& msg) {
  std::string const cmd = argv[0];
  auto last_slash = std::string(cmd).find_last_of('/');
  auto program = cmd.substr(last_slash + 1);
  std::cerr << msg << "\nUsage: " << program << " <command> [arguments]\n\n"
            << "Commands:\n"
            << command_usage << "\n";
}

void GetServiceAccount(google::cloud::storage::Client client, int& argc,
                       char* argv[]) {
  if (argc != 1) {
    throw Usage{"get-service-account"};
  }
  //! [get service account]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client) {
    StatusOr<gcs::ServiceAccount> service_account_details =
        client.GetServiceAccount();

    if (!service_account_details) {
      throw std::runtime_error(service_account_details.status().message());
    }
    std::cout << "The service account details are " << *service_account_details
              << "\n";
  }
  //! [get service account]
  (std::move(client));
}

void GetServiceAccountForProject(google::cloud::storage::Client client,
                                 int& argc, char* argv[]) {
  if (argc != 2) {
    throw Usage{"get-service-account-for-project <project-id>"};
  }
  auto project_id = ConsumeArg(argc, argv);
  //! [get service account for project]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string project_id) {
    StatusOr<gcs::ServiceAccount> service_account_details =
        client.GetServiceAccountForProject(project_id);

    if (!service_account_details) {
      throw std::runtime_error(service_account_details.status().message());
    }

    std::cout << "The service account details for project " << project_id
              << " are " << *service_account_details << "\n";
  }
  //! [get service account for project]
  (std::move(client), project_id);
}

void ListHmacKeys(google::cloud::storage::Client client, int& argc,
                  char* argv[]) {
  if (argc != 1) {
    throw Usage{"list-hmac-keys"};
  }
  //! [list hmac keys] [START storage_list_hmac_keys]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client) {
    int count = 0;
    gcs::ListHmacKeysReader hmac_keys_list = client.ListHmacKeys();
    for (auto&& hmac_key_metadata : hmac_keys_list) {
      if (!hmac_key_metadata) {
        throw std::runtime_error(hmac_key_metadata.status().message());
      }
      std::cout << "service_account_email = "
                << hmac_key_metadata->service_account_email()
                << "\naccess_id = " << hmac_key_metadata->access_id() << "\n";
      ++count;
    }
    if (count == 0) {
      std::cout << "No HMAC keys in default project\n";
    }
  }
  //! [list hmac keys] [END storage_list_hmac_keys]
  (std::move(client));
}

void ListHmacKeysWithServiceAccount(google::cloud::storage::Client client,
                                    int& argc, char* argv[]) {
  if (argc != 2) {
    throw Usage{"list-hmac-keys-with-service-account <service-account>"};
  }
  //! [list hmac keys service account]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string service_account) {
    int count = 0;
    gcs::ListHmacKeysReader hmac_keys_list =
        client.ListHmacKeys(gcs::ServiceAccountFilter(service_account));
    for (auto&& hmac_key_metadata : hmac_keys_list) {
      if (!hmac_key_metadata) {
        throw std::runtime_error(hmac_key_metadata.status().message());
      }
      std::cout << "service_account_email = "
                << hmac_key_metadata->service_account_email()
                << "\naccess_id = " << hmac_key_metadata->access_id() << "\n";
      ++count;
    }
    if (count == 0) {
      std::cout << "No HMAC keys for service account " << service_account
                << " in default project\n";
    }
  }
  //! [list hmac keys service account]
  (std::move(client), argv[1]);
}

void CreateHmacKey(google::cloud::storage::Client client, int& argc,
                   char* argv[]) {
  if (argc != 2) {
    throw Usage{"create-hmac-key <service-account-email>"};
  }
  //! [create hmac key] [START storage_create_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string service_account_email) {
    StatusOr<std::pair<gcs::HmacKeyMetadata, std::string>> hmac_key_details =
        client.CreateHmacKey(service_account_email);

    if (!hmac_key_details) {
      throw std::runtime_error(hmac_key_details.status().message());
    }
    std::cout << "The base64 encoded secret is: " << hmac_key_details->second
              << "\nDo not miss that secret, there is no API to recover it."
              << "\nThe HMAC key metadata is: " << hmac_key_details->first
              << "\n";
  }
  //! [create hmac key] [END storage_create_hmac_key]
  (std::move(client), argv[1]);
}

void CreateHmacKeyForProject(google::cloud::storage::Client client, int& argc,
                             char* argv[]) {
  if (argc != 3) {
    throw Usage{
        "create-hmac-key-for-project <project-id> <service-account-email>"};
  }
  //! [create hmac key project]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string project_id,
     std::string service_account_email) {
    StatusOr<std::pair<gcs::HmacKeyMetadata, std::string>> hmac_key_details =
        client.CreateHmacKey(service_account_email,
                             gcs::OverrideDefaultProject(project_id));

    if (!hmac_key_details) {
      throw std::runtime_error(hmac_key_details.status().message());
    }
    std::cout << "The base64 encoded secret is: " << hmac_key_details->second
              << "\nDo not miss that secret, there is no API to recover it."
              << "\nThe HMAC key metadata is: " << hmac_key_details->first
              << "\n";
  }
  //! [create hmac key project]
  (std::move(client), argv[1], argv[2]);
}

void DeleteHmacKey(google::cloud::storage::Client client, int& argc,
                   char* argv[]) {
  if (argc != 2) {
    throw Usage{"delete-hmac-key <access-id>"};
  }
  //! [delete hmac key] [START storage_delete_hmac_key]
  namespace gcs = google::cloud::storage;
  [](gcs::Client client, std::string access_id) {
    google::cloud::Status status = client.DeleteHmacKey(access_id);

    if (!status.ok()) {
      throw std::runtime_error(status.message());
    }
    std::cout << "The key is deleted, though it may still appear"
              << " in ListHmacKeys() results.\n";
  }
  //! [delete hmac key] [END storage_delete_hmac_key]
  (std::move(client), argv[1]);
}

void GetHmacKey(google::cloud::storage::Client client, int& argc,
                char* argv[]) {
  if (argc != 2) {
    throw Usage{"get-hmac-key <access-id>"};
  }
  //! [get hmac key] [START storage_get_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id) {
    StatusOr<gcs::HmacKeyMetadata> hmac_key_details =
        client.GetHmacKey(access_id);

    if (!hmac_key_details) {
      throw std::runtime_error(hmac_key_details.status().message());
    }
    std::cout << "The HMAC key metadata is: " << *hmac_key_details << "\n";
  }
  //! [get hmac key] [END storage_get_hmac_key]
  (std::move(client), argv[1]);
}

void UpdateHmacKey(google::cloud::storage::Client client, int& argc,
                   char* argv[]) {
  if (argc != 3) {
    throw Usage{"update-hmac-key <access-id> <state>"};
  }
  //! [update hmac key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id, std::string state) {
    StatusOr<gcs::HmacKeyMetadata> current_metadata =
        client.GetHmacKey(access_id);
    if (!current_metadata) {
      throw std::runtime_error(current_metadata.status().message());
    }

    StatusOr<gcs::HmacKeyMetadata> updated_metadata = client.UpdateHmacKey(
        access_id, gcs::HmacKeyMetadata()
                       .set_state(std::move(state))
                       .set_etag(current_metadata->etag()));

    if (!updated_metadata) {
      throw std::runtime_error(updated_metadata.status().message());
    }
    std::cout << "The updated HMAC key metadata is: " << *updated_metadata
              << "\n";
  }
  //! [update hmac key]
  (std::move(client), argv[1], argv[2]);
}

void ActivateHmacKey(google::cloud::storage::Client client, int& argc,
                     char* argv[]) {
  if (argc != 2) {
    throw Usage{"activate-hmac-key <access-id>"};
  }
  //! [START storage_activate_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id) {
    StatusOr<gcs::HmacKeyMetadata> current_metadata =
        client.GetHmacKey(access_id);
    if (!current_metadata) {
      throw std::runtime_error(current_metadata.status().message());
    }

    StatusOr<gcs::HmacKeyMetadata> updated_metadata = client.UpdateHmacKey(
        access_id, gcs::HmacKeyMetadata()
                       .set_state(gcs::HmacKeyMetadata::state_active())
                       .set_etag(current_metadata->etag()));

    if (!updated_metadata) {
      throw std::runtime_error(updated_metadata.status().message());
    }
    if (updated_metadata->state() != gcs::HmacKeyMetadata::state_active()) {
      throw std::runtime_error(
          "The HMAC key is NOT active, this is unexpected");
    }
    std::cout << "The HMAC key is now active\nFull metadata: "
              << *updated_metadata << "\n";
  }
  //! [END storage_activate_hmac_key]
  (std::move(client), argv[1]);
}

void DeactivateHmacKey(google::cloud::storage::Client client, int& argc,
                       char* argv[]) {
  if (argc != 2) {
    throw Usage{"deactivate-hmac-key <access-id>"};
  }
  //! [START storage_deactivate_hmac_key]
  namespace gcs = google::cloud::storage;
  using ::google::cloud::StatusOr;
  [](gcs::Client client, std::string access_id) {
    StatusOr<gcs::HmacKeyMetadata> current_metadata =
        client.GetHmacKey(access_id);
    if (!current_metadata) {
      throw std::runtime_error(current_metadata.status().message());
    }

    StatusOr<gcs::HmacKeyMetadata> updated_metadata = client.UpdateHmacKey(
        access_id, gcs::HmacKeyMetadata()
                       .set_state(gcs::HmacKeyMetadata::state_inactive())
                       .set_etag(current_metadata->etag()));

    if (!updated_metadata) {
      throw std::runtime_error(updated_metadata.status().message());
    }
    if (updated_metadata->state() != gcs::HmacKeyMetadata::state_inactive()) {
      throw std::runtime_error("The HMAC key is active, this is unexpected");
    }
    std::cout << "The HMAC key is now inactive\nFull metadata: "
              << *updated_metadata << "\n";
  }
  //! [END storage_deactivate_hmac_key]
  (std::move(client), argv[1]);
}

}  // anonymous namespace

int main(int argc, char* argv[]) try {
  // Create a client to communicate with Google Cloud Storage.
  google::cloud::StatusOr<google::cloud::storage::Client> client =
      google::cloud::storage::Client::CreateDefaultClient();
  if (!client) {
    std::cerr << "Failed to create Storage Client, status=" << client.status()
              << "\n";
    return 1;
  }

  using CommandType =
      std::function<void(google::cloud::storage::Client, int&, char* [])>;
  std::map<std::string, CommandType> commands = {
      {"get-service-account", &GetServiceAccount},
      {"get-service-account-for-project", &GetServiceAccountForProject},
      {"list-hmac-keys", &ListHmacKeys},
      {"list-hmac-keys-with-service-account", &ListHmacKeysWithServiceAccount},
      {"create-hmac-key-for-project", &CreateHmacKeyForProject},
      {"create-hmac-key", &CreateHmacKey},
      {"delete-hmac-key", &DeleteHmacKey},
      {"get-hmac-key", &GetHmacKey},
      {"update-hmac-key", &UpdateHmacKey},
      {"activate-hmac-key", &ActivateHmacKey},
      {"deactivate-hmac-key", &DeactivateHmacKey},
  };
  for (auto&& kv : commands) {
    try {
      int fake_argc = 0;
      kv.second(*client, fake_argc, argv);
    } catch (Usage const& u) {
      command_usage += "    ";
      command_usage += u.msg;
      command_usage += "\n";
    } catch (...) {
      // ignore other exceptions.
    }
  }

  if (argc < 2) {
    PrintUsage(argc, argv, "Missing command");
    return 1;
  }

  std::string const command = ConsumeArg(argc, argv);
  auto it = commands.find(command);
  if (commands.end() == it) {
    PrintUsage(argc, argv, "Unknown command: " + command);
    return 1;
  }

  it->second(*client, argc, argv);

  return 0;
} catch (Usage const& ex) {
  PrintUsage(argc, argv, ex.msg);
  return 1;
} catch (std::exception const& ex) {
  std::cerr << "Standard C++ exception raised: " << ex.what() << "\n";
  return 1;
}
<|endoftext|>


<commit_before>//==- DeadStores.cpp - Check for stores to dead variables --------*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines a DeadStores, a flow-sensitive checker that looks for
//  stores to variables that are no longer live.
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/LocalCheckers.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "llvm/Support/Compiler.h"

using namespace clang;

namespace {

class VISIBILITY_HIDDEN DeadStoreObs : public LiveVariables::ObserverTy {
  ASTContext &Ctx;
  Diagnostic &Diags;
  DiagnosticClient &Client;
  ParentMap& Parents;

public:
  DeadStoreObs(ASTContext &ctx, Diagnostic &diags, DiagnosticClient &client,
               ParentMap& parents)
    : Ctx(ctx), Diags(diags), Client(client), Parents(parents) {}

  virtual ~DeadStoreObs() {}

  unsigned GetDiag(VarDecl* VD, bool inEnclosing = false) {
    std::string name(VD->getName());

    std::string msg = inEnclosing
      ? "Although the value stored to '" + name +
        "' is used in the enclosing expression, the value is never actually read"
        " from '" + name + "'"
      : "Value stored to '" + name + "' is never read";

    return Diags.getCustomDiagID(Diagnostic::Warning, msg.c_str());
  }

  void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
                    bool hasEnclosing,
                    const LiveVariables::AnalysisDataTy& AD,
                    const LiveVariables::ValTy& Live) {

    if (VD->hasLocalStorage() && !Live(VD, AD)) {
      SourceRange R = Val->getSourceRange();
      Diags.Report(&Client,
                   Ctx.getFullLoc(Ex->getSourceRange().getBegin()),
                   GetDiag(VD, hasEnclosing), 0, 0, &R, 1);
    }
  }

  void CheckDeclRef(DeclRefExpr* DR, Expr* Val,
                    const LiveVariables::AnalysisDataTy& AD,
                    const LiveVariables::ValTy& Live) {

    if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
      CheckVarDecl(VD, DR, Val, false, AD, Live);
  }

  virtual void ObserveStmt(Stmt* S,
                           const LiveVariables::AnalysisDataTy& AD,
                           const LiveVariables::ValTy& Live) {

    // Skip statements in macros.
    if (S->getLocStart().isMacroID())
      return;

    if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
      if (!B->isAssignmentOp()) return; // Skip non-assignments.

      if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
        if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {

          // Special case: check for assigning null to a pointer.  This
          //  is a common form of defensive programming.
          // FIXME: Make this optional?

          Expr* Val = B->getRHS();
          llvm::APSInt Result(Ctx.getTypeSize(Val->getType()));

          if (VD->getType()->isPointerType() &&
              Val->IgnoreParenCasts()->isIntegerConstantExpr(Result, Ctx, 0))
            if (Result == 0)
              return;

          CheckVarDecl(VD, DR, Val, Parents.isSubExpr(B), AD, Live);
        }
    }
    else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
      if (!U->isIncrementOp())
        return;

      Expr *Ex = U->getSubExpr()->IgnoreParenCasts();

      if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
        CheckDeclRef(DR, U, AD, Live);
    }
    else if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
      // Iterate through the decls.  Warn if any initializers are complex
      // expressions that are not live (never used).
      for (ScopedDecl* SD = DS->getDecl(); SD; SD = SD->getNextDeclarator()) {

        VarDecl* V = dyn_cast<VarDecl>(SD);
        if (!V) continue;

        if (V->hasLocalStorage())
          if (Expr* E = V->getInit()) {
            if (!Live(V, AD)) {
              // Special case: check for initializations with constants.
              //
              //  e.g. : int x = 0;
              //
              // If x is EVER assigned a new value later, don't issue
              // a warning.  This is because such initialization can be
              // due to defensive programming.
              if (!E->isConstantExpr(Ctx,NULL)) {
                // Flag a warning.
                SourceRange R = E->getSourceRange();
                Diags.Report(&Client,
                             Ctx.getFullLoc(V->getLocation()),
                             GetDiag(V), 0, 0, &R, 1);
              }
            }
          }
      }
  }
};

} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Driver function to invoke the Dead-Stores checker on a CFG.
//===----------------------------------------------------------------------===//

void clang::CheckDeadStores(CFG& cfg, ASTContext &Ctx,
                            ParentMap& Parents, Diagnostic &Diags) {
  LiveVariables L(cfg);
  L.runOnCFG(cfg);
  DeadStoreObs A(Ctx, Diags, Diags.getClient(), Parents);
  L.runOnAllBlocks(cfg, &A);
}

//===----------------------------------------------------------------------===//
// BugReporter-based invocation of the Dead-Stores checker.
//===----------------------------------------------------------------------===//

namespace {

class VISIBILITY_HIDDEN DiagBugReport : public RangedBugReport {
  std::list<std::string> Strs;
  FullSourceLoc L;
public:
  DiagBugReport(BugType& D, FullSourceLoc l) :
    RangedBugReport(D, NULL), L(l) {}

  virtual ~DiagBugReport() {}
  virtual FullSourceLoc getLocation(SourceManager&) { return L; }

  void addString(const std::string& s) { Strs.push_back(s); }

  typedef std::list<std::string>::const_iterator str_iterator;
  str_iterator str_begin() const { return Strs.begin(); }
  str_iterator str_end() const { return Strs.end(); }
};

class VISIBILITY_HIDDEN DiagCollector : public DiagnosticClient {
  std::list<DiagBugReport> Reports;
  BugType& D;
public:
  DiagCollector(BugType& d) : D(d) {}

  virtual ~DiagCollector() {}

  virtual void HandleDiagnostic(Diagnostic &Diags,
                                Diagnostic::Level DiagLevel,
                                FullSourceLoc Pos,
                                diag::kind ID,
                                const std::string *Strs,
                                unsigned NumStrs,
                                const SourceRange *Ranges,
                                unsigned NumRanges) {

    // FIXME: Use a map from diag::kind to BugType, instead of having just
    //  one BugType.

    Reports.push_back(DiagBugReport(D, Pos));
    DiagBugReport& R = Reports.back();

    for ( ; NumRanges ; --NumRanges, ++Ranges)
      R.addRange(*Ranges);

    for ( ; NumStrs ; --NumStrs, ++Strs)
      R.addString(*Strs);
  }

  // Iterators.

  typedef std::list<DiagBugReport>::iterator iterator;
  iterator begin() { return Reports.begin(); }
  iterator end() { return Reports.end(); }
};

class VISIBILITY_HIDDEN DeadStoresChecker : public BugTypeCacheLocation {
public:
  virtual const char* getName() const {
    return "dead store";
  }

  virtual const char* getDescription() const {
    return "Value stored to variable is never subsequently read.";
  }

  virtual void EmitWarnings(BugReporter& BR) {

    // Run the dead store checker and collect the diagnostics.
    DiagCollector C(*this);
    DeadStoreObs A(BR.getContext(), BR.getDiagnostic(), C, BR.getParentMap());

    GRExprEngine& Eng = BR.getEngine();
    Eng.getLiveness().runOnAllBlocks(BR.getCFG(), &A);

    // Emit the bug reports.

    for (DiagCollector::iterator I = C.begin(), E = C.end(); I != E; ++I)
      BR.EmitWarning(*I);
  }
};
} // end anonymous namespace

BugType* clang::MakeDeadStoresChecker() {
  return new DeadStoresChecker();
}
<commit_msg>When using the dead-stores checker with the BugReporter class, properly capture the diagnostic associated with a specific warning and emit it.<commit_after>//==- DeadStores.cpp - Check for stores to dead variables --------*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines a DeadStores, a flow-sensitive checker that looks for
//  stores to variables that are no longer live.
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/LocalCheckers.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "llvm/Support/Compiler.h"

using namespace clang;

namespace {

class VISIBILITY_HIDDEN DeadStoreObs : public LiveVariables::ObserverTy {
  ASTContext &Ctx;
  Diagnostic &Diags;
  DiagnosticClient &Client;
  ParentMap& Parents;

public:
  DeadStoreObs(ASTContext &ctx, Diagnostic &diags, DiagnosticClient &client,
               ParentMap& parents)
    : Ctx(ctx), Diags(diags), Client(client), Parents(parents) {}

  virtual ~DeadStoreObs() {}

  unsigned GetDiag(VarDecl* VD, bool inEnclosing = false) {
    std::string name(VD->getName());

    std::string msg = inEnclosing
      ? "Although the value stored to '" + name +
        "' is used in the enclosing expression, the value is never actually read"
        " from '" + name + "'"
      : "Value stored to '" + name + "' is never read";

    return Diags.getCustomDiagID(Diagnostic::Warning, msg.c_str());
  }

  void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
                    bool hasEnclosing,
                    const LiveVariables::AnalysisDataTy& AD,
                    const LiveVariables::ValTy& Live) {

    if (VD->hasLocalStorage() && !Live(VD, AD)) {
      SourceRange R = Val->getSourceRange();
      Diags.Report(&Client,
                   Ctx.getFullLoc(Ex->getSourceRange().getBegin()),
                   GetDiag(VD, hasEnclosing), 0, 0, &R, 1);
    }
  }

  void CheckDeclRef(DeclRefExpr* DR, Expr* Val,
                    const LiveVariables::AnalysisDataTy& AD,
                    const LiveVariables::ValTy& Live) {

    if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
      CheckVarDecl(VD, DR, Val, false, AD, Live);
  }

  virtual void ObserveStmt(Stmt* S,
                           const LiveVariables::AnalysisDataTy& AD,
                           const LiveVariables::ValTy& Live) {

    // Skip statements in macros.
    if (S->getLocStart().isMacroID())
      return;

    if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
      if (!B->isAssignmentOp()) return; // Skip non-assignments.

      if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
        if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {

          // Special case: check for assigning null to a pointer.  This
          //  is a common form of defensive programming.
          // FIXME: Make this optional?

          Expr* Val = B->getRHS();
          llvm::APSInt Result(Ctx.getTypeSize(Val->getType()));

          if (VD->getType()->isPointerType() &&
              Val->IgnoreParenCasts()->isIntegerConstantExpr(Result, Ctx, 0))
            if (Result == 0)
              return;

          CheckVarDecl(VD, DR, Val, Parents.isSubExpr(B), AD, Live);
        }
    }
    else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
      if (!U->isIncrementOp())
        return;

      Expr *Ex = U->getSubExpr()->IgnoreParenCasts();

      if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
        CheckDeclRef(DR, U, AD, Live);
    }
    else if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
      // Iterate through the decls.  Warn if any initializers are complex
      // expressions that are not live (never used).
      for (ScopedDecl* SD = DS->getDecl(); SD; SD = SD->getNextDeclarator()) {

        VarDecl* V = dyn_cast<VarDecl>(SD);
        if (!V) continue;

        if (V->hasLocalStorage())
          if (Expr* E = V->getInit()) {
            if (!Live(V, AD)) {
              // Special case: check for initializations with constants.
              //
              //  e.g. : int x = 0;
              //
              // If x is EVER assigned a new value later, don't issue
              // a warning.  This is because such initialization can be
              // due to defensive programming.
              if (!E->isConstantExpr(Ctx,NULL)) {
                // Flag a warning.
                SourceRange R = E->getSourceRange();
                Diags.Report(&Client,
                             Ctx.getFullLoc(V->getLocation()),
                             GetDiag(V), 0, 0, &R, 1);
              }
            }
          }
      }
  }
};

} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Driver function to invoke the Dead-Stores checker on a CFG.
//===----------------------------------------------------------------------===//

void clang::CheckDeadStores(CFG& cfg, ASTContext &Ctx,
                            ParentMap& Parents, Diagnostic &Diags) {
  LiveVariables L(cfg);
  L.runOnCFG(cfg);
  DeadStoreObs A(Ctx, Diags, Diags.getClient(), Parents);
  L.runOnAllBlocks(cfg, &A);
}

//===----------------------------------------------------------------------===//
// BugReporter-based invocation of the Dead-Stores checker.
//===----------------------------------------------------------------------===//

namespace {

class VISIBILITY_HIDDEN DiagBugReport : public RangedBugReport {
  std::list<std::string> Strs;
  FullSourceLoc L;
  const char* description;
public:
  DiagBugReport(const char* desc, BugType& D, FullSourceLoc l) :
    RangedBugReport(D, NULL), L(l), description(desc) {}

  virtual ~DiagBugReport() {}
  virtual FullSourceLoc getLocation(SourceManager&) { return L; }

  virtual const char* getDescription() const {
    return description;
  }

  void addString(const std::string& s) { Strs.push_back(s); }

  typedef std::list<std::string>::const_iterator str_iterator;
  str_iterator str_begin() const { return Strs.begin(); }
  str_iterator str_end() const { return Strs.end(); }
};

class VISIBILITY_HIDDEN DiagCollector : public DiagnosticClient {
  std::list<DiagBugReport> Reports;
  BugType& D;
public:
  DiagCollector(BugType& d) : D(d) {}

  virtual ~DiagCollector() {}

  virtual void HandleDiagnostic(Diagnostic &Diags,
                                Diagnostic::Level DiagLevel,
                                FullSourceLoc Pos,
                                diag::kind ID,
                                const std::string *Strs,
                                unsigned NumStrs,
                                const SourceRange *Ranges,
                                unsigned NumRanges) {

    // FIXME: Use a map from diag::kind to BugType, instead of having just
    //  one BugType.

    Reports.push_back(DiagBugReport(Diags.getDescription(ID), D, Pos));
    DiagBugReport& R = Reports.back();

    for ( ; NumRanges ; --NumRanges, ++Ranges)
      R.addRange(*Ranges);

    for ( ; NumStrs ; --NumStrs, ++Strs)
      R.addString(*Strs);
  }

  // Iterators.

  typedef std::list<DiagBugReport>::iterator iterator;
  iterator begin() { return Reports.begin(); }
  iterator end() { return Reports.end(); }
};

class VISIBILITY_HIDDEN DeadStoresChecker : public BugTypeCacheLocation {
public:
  virtual const char* getName() const {
    return "dead store";
  }

  virtual const char* getDescription() const {
    return "Value stored to variable is never subsequently read.";
  }

  virtual void EmitWarnings(BugReporter& BR) {

    // Run the dead store checker and collect the diagnostics.
    DiagCollector C(*this);
    DeadStoreObs A(BR.getContext(), BR.getDiagnostic(), C, BR.getParentMap());

    GRExprEngine& Eng = BR.getEngine();
    Eng.getLiveness().runOnAllBlocks(BR.getCFG(), &A);

    // Emit the bug reports.

    for (DiagCollector::iterator I = C.begin(), E = C.end(); I != E; ++I)
      BR.EmitWarning(*I);
  }
};
} // end anonymous namespace

BugType* clang::MakeDeadStoresChecker() {
  return new DeadStoresChecker();
}
<|endoftext|>