<commit_before>#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include "comParse.h"
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace boost;
using namespace std;

#define I_REDIRECT 0
#define O_REDIRECT 1
#define ORD_APPEND 2

struct redirect;
bool redirection(char** argv, redirect& rdts, const int id);
bool setRdct(char** argv, redirect& rdts);

struct redirect {
	// May now be irrelevant b/c rdctInd replaces it
	int indexIR;
	int indexOR;
	int indexPR;
	bool doI_Rdct; // input redirect
	bool doO_Rdct; // output redirect
	int savedP; // for duping
	int pfd; // when opening new fd

	vector<int> v_ind,v_pfd,v_savedP;
	vector<pair<int,int> > v_opfd;

	vector<int*> currentFD;

	redirect() {
		indexOR = -1;
		indexIR = -1;
		indexPR = -1;

		doO_Rdct = false;
		doI_Rdct = false;
		savedP = -1;
		pfd = -1;
	}
	void closeCurrFDs() {
		if (!currentFD.empty()) {
			for (vector<int*>::iterator it = currentFD.begin();
					it != currentFD.end(); it++) {
				if (-1 == close(*(*it))) {
					perror("close");
					exit(-1);
				}
			}

			currentFD.clear();
		}
	}
	~redirect() {
		//closeCurrFDs();
	}
};

int main() {
	while (true) {
		char** argv = NULL;
		vector<char*> commands;
		comParse comPr;
		do {
			cerr << "$ " << flush;

			string input = "";
			getline(cin, input);	// Get command

			commands = comPr.parseLine(input);
		} while (commands.size() == 1); // if empty line, repeat req for command. sz 1 = just NULL delim.

		bool doExec = true; // If false, do not fork and exec command

		int timeout = 0;
		vector<char*> realcom; // command + arguments - input redirection & semicolon
		argv = &commands.at(0);
		redirect rdts;	// contains redirection flags and values

		for (unsigned int i = 0; i < commands.size() - 1; i++) { // NULL at end
			char* c = commands.at(i);
			if ( strcmp(commands.at(i), ";") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;
				timeout = 1;

				break;
			} else if (strchr(c, '<') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg
				rdts.v_opfd.push_back( pair<int,int>(I_REDIRECT, fd) ); // op, fd

				if ( !(i + 1 < commands.size()) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <\n";
					doExec = false;
					break;
				}
				rdts.v_ind.push_back(i + 1); // index
			} else if (strchr(c, '>') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				if (strstr(commands.at(i), ">>>") != NULL) {
					cerr << "rshell: syntax error at >\n";
					doExec = false;
					break;
				}
				else if (strstr(commands.at(i), ">>") != NULL)
					rdts.v_opfd.push_back( pair<int,int>(ORD_APPEND, fd) ); // op, fd
				else rdts.v_opfd.push_back( pair<int,int>(O_REDIRECT, fd) ); // op, fd

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg

				if ( !(i + 1 < commands.size() - 1) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <";
					exit(-1);
				}
				rdts.v_ind.push_back(i + 1); // index
			}

			if (timeout != 0) timeout--;
			else realcom.push_back(commands.at(i));
		}

		realcom.push_back(NULL);

		/* Confusing as heck, let me explain!
		 * char const* means pointer to a constant char
		 * so char const* const means a const pointer to a const char
		 * and char const* const* means a const pointer to a const char pointer!
		 * (Read declarations from right to left to make it make sense -
		 *  char const* = POINTER (*) to a CONST CHAR)
		 */
		//char const* const* c_arr = &commands[0]; // even if Not necessary: keep as really cool discovery!

		char ex[5] = "exit";
		if (strcmp(argv[0], ex) == 0) {
			comPr.deleteCStrings(commands); // Has heap allocated memory, always should be dealt with
			return 0; // EXIT command
		}

		if (doExec != false) doExec = setRdct(argv, rdts);
		//if (rdts.doO_Rdct) doExec = redirection(argv, rdts, STDOUT_FILENO);
		//if (rdts.doE_Rdct) doExec = redirection(argv, rdts, STDERR_FILENO);

		argv = &realcom.at(0); // initialize to char**

		if (doExec) {
			int pid = fork();
			if (pid == -1) {	// Error in fork
				perror("error in fork");
				comPr.deleteCStrings(commands);
				exit(-1);
			} else if (pid == 0) {	// Child Process
				if (-1 == execvp(argv[0], argv)) {
					// All children need to exit! (forgot this)
					if (errno == EACCES) { // Access denied
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOEXEC) { // Not Exec
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOENT) { // Does not exist
						perror(argv[0]);
						exit(-1);
					} else {
						perror("error in execvp");
						exit(-1);
					}
				}
			} else if (pid > 0) { // parent!
				if (-1 == wait(0))
					perror("wait");
				if (errno != 0 && errno != EACCES && errno != ENOEXEC && errno != ENOENT)
					exit(-1);
			}
		}

		for (unsigned int i = 0; i < rdts.v_savedP.size(); i++) {
			if (-1 == dup2(rdts.v_savedP.at(i), rdts.v_pfd.at(i))) {	// Restore saved fds
				perror("dup2");
				exit(-1);
			}
		}

		comPr.deleteCStrings(commands);
		//rdts.closeCurrFDs();
	}

	return 0;
}

bool setRdct(char** argv, redirect& rdts) {
	bool doExec = true;
	for (unsigned int i = 0; i < rdts.v_opfd.size(); i++)
		doExec = redirection(argv, rdts, i) ? doExec : false; // if false, stay false
	return doExec;
}

// closes respective fd and opens file in its place
bool redirection(char** argv, redirect& rdts, const int id) {
	/*int* savedFD = rdts.savedP;
	int* newFD = rdts.pfd;
	int* index = rdts.indexPR;
	*/

	int savedFD,newFD;
	int index = rdts.v_ind.at(id);
	int FLAG;

	// 0 == INPUT, 1 == OUTPUT
	if (rdts.v_opfd.at(id).first == 0) {
		FLAG = O_RDONLY;
		if (rdts.v_opfd.at(id).second == -1)
			rdts.v_opfd.at(id).second = 0; // DEFAULT for input operator
	} else if (rdts.v_opfd.at(id).first == 1) {
		FLAG = O_WRONLY | O_CREAT | O_TRUNC;
		if (rdts.v_opfd.at(id).second == -1)
			rdts.v_opfd.at(id).second = 1; // DEFAULT for output operator
	}

	// dup the fd
	if ( -1 == (savedFD = dup(rdts.v_opfd.at(id).second)) ) {
		perror("dup-redirect" + rdts.v_opfd.at(id).second);
		exit(-1);
	}
	rdts.v_savedP.push_back(savedFD); // Saving fd for dup2 later

	// close fd
	if ( -1 == close(rdts.v_opfd.at(id).second) ) {
		perror("close-redirect" + rdts.v_opfd.at(id).second);
		exit(-1);
	}

	// open file
	// if create file, then give read write by default
	if ( -1 == (newFD = open(argv[index], FLAG, S_IRUSR | S_IWUSR)) ) {
		if (errno == EACCES || errno == ENOENT) { 	//FIXME
			perror(argv[index]);
			rdts.v_pfd.push_back(rdts.v_opfd.at(id).second); // Saving new fd to close later
			return false;
		} else {
			perror("open-redirect" + rdts.v_opfd.at(id).second);
			exit(-1);
		}
	}
	rdts.v_pfd.push_back(newFD); // Saving new fd to close later

	return true;
}

<commit_msg>APPEND!<commit_after>#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include "comParse.h"
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace boost;
using namespace std;

#define I_REDIRECT 0
#define O_REDIRECT 1
#define ORD_APPEND 2

struct redirect;
bool redirection(char** argv, redirect& rdts, const int id);
bool setRdct(char** argv, redirect& rdts);

struct redirect {
	// May now be irrelevant b/c rdctInd replaces it
	int indexIR;
	int indexOR;
	int indexPR;
	bool doI_Rdct; // input redirect
	bool doO_Rdct; // output redirect
	int savedP; // for duping
	int pfd; // when opening new fd

	vector<int> v_ind,v_pfd,v_savedP;
	vector<pair<int,int> > v_opfd;

	vector<int*> currentFD;

	redirect() {
		indexOR = -1;
		indexIR = -1;
		indexPR = -1;

		doO_Rdct = false;
		doI_Rdct = false;
		savedP = -1;
		pfd = -1;
	}
	void closeCurrFDs() {
		if (!currentFD.empty()) {
			for (vector<int*>::iterator it = currentFD.begin();
					it != currentFD.end(); it++) {
				if (-1 == close(*(*it))) {
					perror("close");
					exit(-1);
				}
			}

			currentFD.clear();
		}
	}
	~redirect() {
		//closeCurrFDs();
	}
};

int main() {
	while (true) {
		char** argv = NULL;
		vector<char*> commands;
		comParse comPr;
		do {
			cerr << "$ " << flush;

			string input = "";
			getline(cin, input);	// Get command

			commands = comPr.parseLine(input);
		} while (commands.size() == 1); // if empty line, repeat req for command. sz 1 = just NULL delim.

		bool doExec = true; // If false, do not fork and exec command

		int timeout = 0;
		vector<char*> realcom; // command + arguments - input redirection & semicolon
		argv = &commands.at(0);
		redirect rdts;	// contains redirection flags and values

		for (unsigned int i = 0; i < commands.size() - 1; i++) { // NULL at end
			char* c = commands.at(i);
			if ( strcmp(commands.at(i), ";") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;
				timeout = 1;

				break;
			} else if (strchr(c, '<') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg
				rdts.v_opfd.push_back( pair<int,int>(I_REDIRECT, fd) ); // op, fd

				if ( !(i + 1 < commands.size()) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <\n";
					doExec = false;
					break;
				}
				rdts.v_ind.push_back(i + 1); // index
			} else if (strchr(c, '>') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				if (strstr(commands.at(i), ">>>") != NULL) {
					cerr << "rshell: syntax error at >\n";
					doExec = false;
					break;
				}
				else if (strstr(commands.at(i), ">>") != NULL)
					rdts.v_opfd.push_back( pair<int,int>(ORD_APPEND, fd) ); // op, fd
				else rdts.v_opfd.push_back( pair<int,int>(O_REDIRECT, fd) ); // op, fd

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg

				if ( !(i + 1 < commands.size() - 1) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <";
					exit(-1);
				}
				rdts.v_ind.push_back(i + 1); // index
			}

			if (timeout != 0) timeout--;
			else realcom.push_back(commands.at(i));
		}

		realcom.push_back(NULL);

		/* Confusing as heck, let me explain!
		 * char const* means pointer to a constant char
		 * so char const* const means a const pointer to a const char
		 * and char const* const* means a const pointer to a const char pointer!
		 * (Read declarations from right to left to make it make sense -
		 *  char const* = POINTER (*) to a CONST CHAR)
		 */
		//char const* const* c_arr = &commands[0]; // even if Not necessary: keep as really cool discovery!

		char ex[5] = "exit";
		if (strcmp(argv[0], ex) == 0) {
			comPr.deleteCStrings(commands); // Has heap allocated memory, always should be dealt with
			return 0; // EXIT command
		}

		if (doExec != false) doExec = setRdct(argv, rdts);
		//if (rdts.doO_Rdct) doExec = redirection(argv, rdts, STDOUT_FILENO);
		//if (rdts.doE_Rdct) doExec = redirection(argv, rdts, STDERR_FILENO);

		argv = &realcom.at(0); // initialize to char**

		if (doExec) {
			int pid = fork();
			if (pid == -1) {	// Error in fork
				perror("error in fork");
				comPr.deleteCStrings(commands);
				exit(-1);
			} else if (pid == 0) {	// Child Process
				if (-1 == execvp(argv[0], argv)) {
					// All children need to exit! (forgot this)
					if (errno == EACCES) { // Access denied
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOEXEC) { // Not Exec
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOENT) { // Does not exist
						perror(argv[0]);
						exit(-1);
					} else {
						perror("error in execvp");
						exit(-1);
					}
				}
			} else if (pid > 0) { // parent!
				if (-1 == wait(0))
					perror("wait");
				if (errno != 0 && errno != EACCES && errno != ENOEXEC && errno != ENOENT)
					exit(-1);
			}
		}

		for (unsigned int i = 0; i < rdts.v_savedP.size(); i++) {
			if (-1 == dup2(rdts.v_savedP.at(i), rdts.v_pfd.at(i))) {	// Restore saved fds
				perror("dup2");
				exit(-1);
			}
		}

		comPr.deleteCStrings(commands);
		//rdts.closeCurrFDs();
	}

	return 0;
}

bool setRdct(char** argv, redirect& rdts) {
	bool doExec = true;
	for (unsigned int i = 0; i < rdts.v_opfd.size(); i++)
		doExec = redirection(argv, rdts, i) ? doExec : false; // if false, stay false
	return doExec;
}

// closes respective fd and opens file in its place
bool redirection(char** argv, redirect& rdts, const int id) {
	/*int* savedFD = rdts.savedP;
	int* newFD = rdts.pfd;
	int* index = rdts.indexPR;
	*/

	int savedFD,newFD;
	int index = rdts.v_ind.at(id);
	int FLAG;

	// 0 == INPUT, 1 == OUTPUT
	if (rdts.v_opfd.at(id).first == I_REDIRECT) {
		FLAG = O_RDONLY;
		if (rdts.v_opfd.at(id).second == -1)
			rdts.v_opfd.at(id).second = 0; // DEFAULT for input operator
	} else if (rdts.v_opfd.at(id).first == O_REDIRECT) {
		FLAG = O_WRONLY | O_CREAT | O_TRUNC;
		if (rdts.v_opfd.at(id).second == -1)
			rdts.v_opfd.at(id).second = 1; // DEFAULT for output operator
	} else if (rdts.v_opfd.at(id).first == ORD_APPEND) {
		FLAG = O_WRONLY | O_CREAT | O_APPEND;
		if (rdts.v_opfd.at(id).second == -1)
			rdts.v_opfd.at(id).second = 1; // DEFAULT for output operator
	}

	// dup the fd
	if ( -1 == (savedFD = dup(rdts.v_opfd.at(id).second)) ) {
		perror("dup-redirect" + rdts.v_opfd.at(id).second);
		exit(-1);
	}
	rdts.v_savedP.push_back(savedFD); // Saving fd for dup2 later

	// close fd
	if ( -1 == close(rdts.v_opfd.at(id).second) ) {
		perror("close-redirect" + rdts.v_opfd.at(id).second);
		exit(-1);
	}

	// open file
	// if create file, then give read write by default
	if ( -1 == (newFD = open(argv[index], FLAG, S_IRUSR | S_IWUSR)) ) {
		if (errno == EACCES || errno == ENOENT) { 	//FIXME
			perror(argv[index]);
			rdts.v_pfd.push_back(rdts.v_opfd.at(id).second); // Saving new fd to close later
			return false;
		} else {
			perror("open-redirect" + rdts.v_opfd.at(id).second);
			exit(-1);
		}
	}
	rdts.v_pfd.push_back(newFD); // Saving new fd to close later

	return true;
}

<|endoftext|>


<commit_before>// Copyright 2017 Global Phasing Ltd.
//
// Utilities. Mostly for working with strings and vectors.

#ifndef GEMMI_UTIL_HPP_
#define GEMMI_UTIL_HPP_

#include <algorithm>  // for equal, find, remove_if
#include <cctype>     // for tolower
#include <cstring>    // for strncmp
#include <iterator>   // for begin, end, make_move_iterator
#include <string>
#include <vector>

namespace gemmi {

//   #####   string helpers   #####

inline bool starts_with(const std::string& str, const std::string& prefix) {
  size_t sl = prefix.length();
  return str.length() >= sl && str.compare(0, sl, prefix) == 0;
}

template<size_t N> bool starts_with(const char* a, const char (&b)[N]) {
  return std::strncmp(a, b, N-1) == 0;
}

inline bool ends_with(const std::string& str, const std::string& suffix) {
  size_t sl = suffix.length();
  return str.length() >= sl && str.compare(str.length() - sl, sl, suffix) == 0;
}

// Case-insensitive comparisons. The second arg must be lowercase.

inline bool iequal(const std::string& str, const std::string& low) {
  return str.length() == low.length() &&
         std::equal(std::begin(low), std::end(low), str.begin(),
                    [](char c1, char c2) { return c1 == std::tolower(c2); });
}

inline bool iequal_from(const std::string& str, size_t offset, const std::string& low) {
  return str.length() == low.length() + offset &&
         std::equal(std::begin(low), std::end(low), str.begin() + offset,
                    [](char c1, char c2) { return c1 == std::tolower(c2); });
}

inline bool istarts_with(const std::string& str, const std::string& prefix) {
  return str.length() >= prefix.length() &&
         std::equal(std::begin(prefix), std::end(prefix), str.begin(),
                    [](char c1, char c2) { return c1 == std::tolower(c2); });
}
inline bool iends_with(const std::string& str, const std::string& suffix) {
  size_t sl = suffix.length();
  return str.length() >= sl &&
         std::equal(std::begin(suffix), std::end(suffix), str.end() - sl,
                    [](char c1, char c2) { return c1 == std::tolower(c2); });
}

inline bool giends_with(const std::string& str, const std::string& suffix) {
  return iends_with(str, suffix) || iends_with(str, suffix + ".gz");
}

inline std::string to_lower(std::string str) {
  for (char& c : str)
    if (c >= 'A' && c <= 'Z')
      c |= 0x20;
  return str;
}

inline std::string to_upper(std::string str) {
  for (char& c : str)
    if (c >= 'a' && c <= 'z')
      c &= ~0x20;
  return str;
}

// works as expected only for a-zA-Z
inline char alpha_up(char c) { return c & ~0x20; }

inline std::string trim_str(const std::string& str) {
  const std::string ws = " \r\n\t";
  std::string::size_type first = str.find_first_not_of(ws);
  if (first == std::string::npos)
    return std::string{};
  std::string::size_type last = str.find_last_not_of(ws);
  return str.substr(first, last - first + 1);
}

inline std::string rtrim_str(const std::string& str) {
  std::string::size_type last = str.find_last_not_of(" \r\n\t");
  return str.substr(0, last == std::string::npos ? 0 : last + 1);
}

// end is after the last character of the string (typically \0)
inline const char* rtrim_cstr(const char* start, const char* end=nullptr) {
  if (!start)
    return nullptr;
  if (!end) {
    end = start;
    while (*end != '\0')
      ++end;
  }
  while (end > start && std::isspace(end[-1]))
    --end;
  return end;
}

namespace impl {
inline size_t length(char) { return 1; }
inline size_t length(const std::string& s) { return s.length(); }
}

// takes a single separator (usually char or string);
// may return empty fields
template<typename S>
void split_str_into(const std::string& str, S sep,
                    std::vector<std::string>& result) {
  std::size_t start = 0, end;
  while ((end = str.find(sep, start)) != std::string::npos) {
    result.emplace_back(str, start, end - start);
    start = end + impl::length(sep);
  }
  result.emplace_back(str, start);
}

template<typename S>
std::vector<std::string> split_str(const std::string& str, S sep) {
  std::vector<std::string> result;
  split_str_into(str, sep, result);
  return result;
}

// _multi variants takes multiple 1-char separators as a string;
// discards empty fields
inline void split_str_into_multi(const std::string& str, const char* seps,
                                 std::vector<std::string>& result) {
  std::size_t start = str.find_first_not_of(seps);
  while (start != std::string::npos) {
    std::size_t end = str.find_first_of(seps, start);
    result.emplace_back(str, start, end - start);
    start = str.find_first_not_of(seps, end);
  }
}

inline std::vector<std::string> split_str_multi(const std::string& str,
                                                const char* seps=" \t") {
  std::vector<std::string> result;
  split_str_into_multi(str, seps, result);
  return result;
}

template<typename T, typename S, typename F>
std::string join_str(T begin, T end, const S& sep, const F& getter) {
  std::string r;
  bool first = true;
  for (T i = begin; i != end; ++i) {
    if (!first)
      r += sep;
    r += getter(*i);
    first = false;
  }
  return r;
}

template<typename T, typename S>
std::string join_str(T begin, T end, const S& sep) {
  return join_str(begin, end, sep, [](const std::string& t) { return t; });
}

template<typename T, typename S, typename F>
std::string join_str(const T& iterable, const S& sep, const F& getter) {
  return join_str(iterable.begin(), iterable.end(), sep, getter);
}

template<typename T, typename S>
std::string join_str(const T& iterable, const S& sep) {
  return join_str(iterable.begin(), iterable.end(), sep);
}

template<typename T, typename S>
void string_append_sep(std::string& str, S sep, const T& item) {
  if (!str.empty())
    str += sep;
  str += item;
}

inline void replace_all(std::string &s,
                        const std::string &old, const std::string &new_) {
  std::string::size_type pos = 0;
  while ((pos = s.find(old, pos)) != std::string::npos) {
    s.replace(pos, old.size(), new_);
    pos += new_.size();
  }
}

// list is a comma separated string
inline bool is_in_list(const std::string& name, const std::string& list,
                       char sep=',') {
  if (name.length() >= list.length())
    return name == list;
  for (size_t start=0, end=0; end != std::string::npos; start=end+1) {
    end = list.find(sep, start);
    if (list.compare(start, end - start, name) == 0)
      return true;
  }
  return false;
}

//   #####   vector helpers   #####

template <class T>
bool in_vector(const T& x, const std::vector<T>& v) {
  return std::find(v.begin(), v.end(), x) != v.end();
}

template <typename F, typename T>
bool in_vector_f(F f, const std::vector<T>& v) {
  return std::find_if(v.begin(), v.end(), f) != v.end();
}

template <class T>
T* vector_end_ptr(std::vector<T>& v) { return v.data() + v.size(); }
template <class T>
const T* vector_end_ptr(const std::vector<T>& v) { return v.data() + v.size(); }

template <class T>
void vector_move_extend(std::vector<T>& dst, std::vector<T>&& src) {
  if (dst.empty())
    dst = std::move(src);
  else
    dst.insert(dst.end(), std::make_move_iterator(src.begin()),
                          std::make_move_iterator(src.end()));
}

// wrapper around the erase-remove idiom
template <class T, typename F>
void vector_remove_if(std::vector<T>& v, F&& condition) {
  v.erase(std::remove_if(v.begin(), v.end(), condition), v.end());
}

//   #####   other helpers   #####

// Numeric ID used for case-insensitive comparison of 4 letters.
// s must have 4 chars or 3 chars + NUL, ' ' and NUL are equivalent in s.
constexpr int ialpha4_id(const char* s) {
  return (s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]) & ~0x20202020;
}
// Numeric ID used for case-insensitive comparison of 3 letters.
constexpr int ialpha3_id(const char* s) {
  return (s[0] << 16 | s[1] << 8 | s[2]) & ~0x20202020;
}

} // namespace gemmi
#endif
<commit_msg>helper function lower(char) -> char (micro-optimization)<commit_after>// Copyright 2017 Global Phasing Ltd.
//
// Utilities. Mostly for working with strings and vectors.

#ifndef GEMMI_UTIL_HPP_
#define GEMMI_UTIL_HPP_

#include <algorithm>  // for equal, find, remove_if
#include <cctype>     // for isspace
#include <cstring>    // for strncmp
#include <iterator>   // for begin, end, make_move_iterator
#include <string>
#include <vector>

namespace gemmi {

//   #####   string helpers   #####

inline bool starts_with(const std::string& str, const std::string& prefix) {
  size_t sl = prefix.length();
  return str.length() >= sl && str.compare(0, sl, prefix) == 0;
}

template<size_t N> bool starts_with(const char* a, const char (&b)[N]) {
  return std::strncmp(a, b, N-1) == 0;
}

inline bool ends_with(const std::string& str, const std::string& suffix) {
  size_t sl = suffix.length();
  return str.length() >= sl && str.compare(str.length() - sl, sl, suffix) == 0;
}

// can be faster than std::tolower() b/c it takes char not int
inline char lower(char c) {
  if (c >= 'A' && c <= 'Z')
    return c | 0x20;
  return c;
}

// works as expected only for a-zA-Z
inline char alpha_up(char c) { return c & ~0x20; }

inline std::string to_lower(std::string str) {
  for (char& c : str)
    if (c >= 'A' && c <= 'Z')
      c |= 0x20;
  return str;
}

inline std::string to_upper(std::string str) {
  for (char& c : str)
    if (c >= 'a' && c <= 'z')
      c &= ~0x20;
  return str;
}

// Case-insensitive comparisons. The second arg must be lowercase.

inline bool iequal(const std::string& str, const std::string& low) {
  return str.length() == low.length() &&
         std::equal(std::begin(low), std::end(low), str.begin(),
                    [](char c1, char c2) { return c1 == lower(c2); });
}

inline bool iequal_from(const std::string& str, size_t offset, const std::string& low) {
  return str.length() == low.length() + offset &&
         std::equal(std::begin(low), std::end(low), str.begin() + offset,
                    [](char c1, char c2) { return c1 == lower(c2); });
}

inline bool istarts_with(const std::string& str, const std::string& prefix) {
  return str.length() >= prefix.length() &&
         std::equal(std::begin(prefix), std::end(prefix), str.begin(),
                    [](char c1, char c2) { return c1 == lower(c2); });
}
inline bool iends_with(const std::string& str, const std::string& suffix) {
  size_t sl = suffix.length();
  return str.length() >= sl &&
         std::equal(std::begin(suffix), std::end(suffix), str.end() - sl,
                    [](char c1, char c2) { return c1 == lower(c2); });
}

inline bool giends_with(const std::string& str, const std::string& suffix) {
  return iends_with(str, suffix) || iends_with(str, suffix + ".gz");
}

inline std::string trim_str(const std::string& str) {
  const std::string ws = " \r\n\t";
  std::string::size_type first = str.find_first_not_of(ws);
  if (first == std::string::npos)
    return std::string{};
  std::string::size_type last = str.find_last_not_of(ws);
  return str.substr(first, last - first + 1);
}

inline std::string rtrim_str(const std::string& str) {
  std::string::size_type last = str.find_last_not_of(" \r\n\t");
  return str.substr(0, last == std::string::npos ? 0 : last + 1);
}

// end is after the last character of the string (typically \0)
inline const char* rtrim_cstr(const char* start, const char* end=nullptr) {
  if (!start)
    return nullptr;
  if (!end) {
    end = start;
    while (*end != '\0')
      ++end;
  }
  while (end > start && std::isspace(end[-1]))
    --end;
  return end;
}

namespace impl {
inline size_t length(char) { return 1; }
inline size_t length(const std::string& s) { return s.length(); }
}

// takes a single separator (usually char or string);
// may return empty fields
template<typename S>
void split_str_into(const std::string& str, S sep,
                    std::vector<std::string>& result) {
  std::size_t start = 0, end;
  while ((end = str.find(sep, start)) != std::string::npos) {
    result.emplace_back(str, start, end - start);
    start = end + impl::length(sep);
  }
  result.emplace_back(str, start);
}

template<typename S>
std::vector<std::string> split_str(const std::string& str, S sep) {
  std::vector<std::string> result;
  split_str_into(str, sep, result);
  return result;
}

// _multi variants takes multiple 1-char separators as a string;
// discards empty fields
inline void split_str_into_multi(const std::string& str, const char* seps,
                                 std::vector<std::string>& result) {
  std::size_t start = str.find_first_not_of(seps);
  while (start != std::string::npos) {
    std::size_t end = str.find_first_of(seps, start);
    result.emplace_back(str, start, end - start);
    start = str.find_first_not_of(seps, end);
  }
}

inline std::vector<std::string> split_str_multi(const std::string& str,
                                                const char* seps=" \t") {
  std::vector<std::string> result;
  split_str_into_multi(str, seps, result);
  return result;
}

template<typename T, typename S, typename F>
std::string join_str(T begin, T end, const S& sep, const F& getter) {
  std::string r;
  bool first = true;
  for (T i = begin; i != end; ++i) {
    if (!first)
      r += sep;
    r += getter(*i);
    first = false;
  }
  return r;
}

template<typename T, typename S>
std::string join_str(T begin, T end, const S& sep) {
  return join_str(begin, end, sep, [](const std::string& t) { return t; });
}

template<typename T, typename S, typename F>
std::string join_str(const T& iterable, const S& sep, const F& getter) {
  return join_str(iterable.begin(), iterable.end(), sep, getter);
}

template<typename T, typename S>
std::string join_str(const T& iterable, const S& sep) {
  return join_str(iterable.begin(), iterable.end(), sep);
}

template<typename T, typename S>
void string_append_sep(std::string& str, S sep, const T& item) {
  if (!str.empty())
    str += sep;
  str += item;
}

inline void replace_all(std::string &s,
                        const std::string &old, const std::string &new_) {
  std::string::size_type pos = 0;
  while ((pos = s.find(old, pos)) != std::string::npos) {
    s.replace(pos, old.size(), new_);
    pos += new_.size();
  }
}

// list is a comma separated string
inline bool is_in_list(const std::string& name, const std::string& list,
                       char sep=',') {
  if (name.length() >= list.length())
    return name == list;
  for (size_t start=0, end=0; end != std::string::npos; start=end+1) {
    end = list.find(sep, start);
    if (list.compare(start, end - start, name) == 0)
      return true;
  }
  return false;
}

//   #####   vector helpers   #####

template <class T>
bool in_vector(const T& x, const std::vector<T>& v) {
  return std::find(v.begin(), v.end(), x) != v.end();
}

template <typename F, typename T>
bool in_vector_f(F f, const std::vector<T>& v) {
  return std::find_if(v.begin(), v.end(), f) != v.end();
}

template <class T>
T* vector_end_ptr(std::vector<T>& v) { return v.data() + v.size(); }
template <class T>
const T* vector_end_ptr(const std::vector<T>& v) { return v.data() + v.size(); }

template <class T>
void vector_move_extend(std::vector<T>& dst, std::vector<T>&& src) {
  if (dst.empty())
    dst = std::move(src);
  else
    dst.insert(dst.end(), std::make_move_iterator(src.begin()),
                          std::make_move_iterator(src.end()));
}

// wrapper around the erase-remove idiom
template <class T, typename F>
void vector_remove_if(std::vector<T>& v, F&& condition) {
  v.erase(std::remove_if(v.begin(), v.end(), condition), v.end());
}

//   #####   other helpers   #####

// Numeric ID used for case-insensitive comparison of 4 letters.
// s must have 4 chars or 3 chars + NUL, ' ' and NUL are equivalent in s.
constexpr int ialpha4_id(const char* s) {
  return (s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]) & ~0x20202020;
}
// Numeric ID used for case-insensitive comparison of 3 letters.
constexpr int ialpha3_id(const char* s) {
  return (s[0] << 16 | s[1] << 8 | s[2]) & ~0x20202020;
}

} // namespace gemmi
#endif
<|endoftext|>


<commit_before>#pragma once
/*
**  Copyright (C) 2013 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QITYPE_OBJECTFACTORY_HPP_
#define _QITYPE_OBJECTFACTORY_HPP_

#include <vector>

#include <boost/function.hpp>

#include <qitype/api.hpp>
#include <qitype/fwd.hpp>
#include <qitype/typeinterface.hpp>
#include <qitype/dynamicobjectbuilder.hpp>
#include <qitype/objecttypebuilder.hpp>

namespace qi {

  QITYPE_API bool registerObjectFactory(const std::string& name, boost::function<qi::AnyObject (const std::string&)> factory);

  QITYPE_API qi::AnyObject createObject(const std::string& name);

  /// Get all factory names. Order is guaranteed to be the registration order
  QITYPE_API std::vector<std::string> listObjectFactories();

  /// Load a shared library and return the list of new available factory names.
  QITYPE_API std::vector<std::string> loadObject(const std::string& name, int flags = -1);
}

/// register \p func as factory for object named \p name
#define QI_REGISTER_OBJECT_FACTORY(name, func) \
  static bool BOOST_PP_CAT(_register_factory_ ,__COUNTER__) = ::qi::registerObjectFactory(name, func)

/// register \p name's default constructor as factory for \p name
#define QI_REGISTER_OBJECT_FACTORY_CONSTRUCTOR(name) \
  QI_REGISTER_OBJECT_FACTORY(#name, boost::bind(::qi::detail::constructObject<name>))

/// register \p cls's default constructor as factory for \p name
#define QI_REGISTER_OBJECT_FACTORY_CONSTRUCTOR_FOR(name, cls) \
  QI_REGISTER_OBJECT_FACTORY(#name, boost::bind(::qi::detail::constructObject<cls>))

/** Register a factory on \p name that creates an object with a single method
  * \p func named \p funcName
  */
#define QI_REGISTER_OBJECT_FACTORY_METHOD(name, funcName, func) \
  QI_REGISTER_OBJECT_FACTORY(name, ::qi::detail::makeObjectFactory(funcName, func))

/** Register an object as a factory for an other object
 * @param name target class name
 *
 * Will crate a factory for generated object 'name + "Service"',
 * with a create() method
 * that returns a newly created instance of \p name.
 */
#define QI_REGISTER_OBJECT_FACTORY_BUILDER(name)                      \
  QI_REGISTER_OBJECT_FACTORY_METHOD(#name "Service", "create", ::qi::detail::constructObject<name>)

#endif  // _QITYPE_OBJECTFACTORY_HPP_
<commit_msg>Factory: QI_REGISTER_OBJECT_FACTORY_BUILDER_FOR.<commit_after>#pragma once
/*
**  Copyright (C) 2013 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QITYPE_OBJECTFACTORY_HPP_
#define _QITYPE_OBJECTFACTORY_HPP_

#include <vector>

#include <boost/function.hpp>

#include <qitype/api.hpp>
#include <qitype/fwd.hpp>
#include <qitype/typeinterface.hpp>
#include <qitype/dynamicobjectbuilder.hpp>
#include <qitype/objecttypebuilder.hpp>
#include <qitype/anyobject.hpp>

namespace qi {

  QITYPE_API bool registerObjectFactory(const std::string& name, boost::function<qi::AnyObject (const std::string&)> factory);

  QITYPE_API qi::AnyObject createObject(const std::string& name);

  /// Get all factory names. Order is guaranteed to be the registration order
  QITYPE_API std::vector<std::string> listObjectFactories();

  /// Load a shared library and return the list of new available factory names.
  QITYPE_API std::vector<std::string> loadObject(const std::string& name, int flags = -1);
}

/// register \p func as factory for object named \p name
#define QI_REGISTER_OBJECT_FACTORY(name, func) \
  static bool BOOST_PP_CAT(_register_factory_ ,__COUNTER__) = ::qi::registerObjectFactory(name, func)

/// register \p name's default constructor as factory for \p name
#define QI_REGISTER_OBJECT_FACTORY_CONSTRUCTOR(name) \
  QI_REGISTER_OBJECT_FACTORY(#name, boost::bind(::qi::detail::constructObject<name>))

/// register \p cls's default constructor as factory for \p name
#define QI_REGISTER_OBJECT_FACTORY_CONSTRUCTOR_FOR(name, cls) \
  QI_REGISTER_OBJECT_FACTORY(#name, boost::bind(::qi::detail::constructObject<cls>))

/** Register a factory on \p name that creates an object with a single method
  * \p func named \p funcName
  */
#define QI_REGISTER_OBJECT_FACTORY_METHOD(name, funcName, func) \
  QI_REGISTER_OBJECT_FACTORY(name, ::qi::detail::makeObjectFactory(funcName, func))

/** Register an object as a factory for an other object
 * @param name target class name
 *
 * Will crate a factory for generated object 'name + "Service"',
 * with a create() method
 * that returns a newly created instance of \p name.
 */
#define QI_REGISTER_OBJECT_FACTORY_BUILDER(name)                      \
  QI_REGISTER_OBJECT_FACTORY_METHOD(#name "Service", "create", ::qi::detail::constructObject<name>)

#define QI_REGISTER_OBJECT_FACTORY_BUILDER_FOR(name, cls)                      \
  QI_REGISTER_OBJECT_FACTORY_METHOD(#name "Service", "create", ::qi::detail::constructObject<cls>)

#endif  // _QITYPE_OBJECTFACTORY_HPP_
<|endoftext|>


<commit_before>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.7 $  $Author: trey $  $Date: 2006-06-16 14:45:20 $

 @file    LSModelFile.cc
 @brief   No brief

 Copyright (c) 2006, Trey Smith. All rights reserved.

 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 <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>

#include "LSModelFile.h"
#include "zmdpCommonDefs.h"

using namespace std;

/**********************************************************************
 * DATA STRUCTURES
 **********************************************************************/

typedef std::map<std::string, std::string> ParamLookup;

/**********************************************************************
 * LOCAL HELPER FUNCTIONS
 **********************************************************************/

static void convertToDoubleVector(std::vector<double>& result,
				  const std::string& s)
{
  string::size_type p1, p2;
  p1 = 0;
  while (1) {
    p2 = s.find_first_of(" \t", p1);
    if (string::npos == p2) {
      result.push_back(atof(s.substr(p1).c_str()));
      break;
    } else {
      if (p1 != p2) {
	result.push_back(atof(s.substr(p1,(p2-p1)).c_str()));
      }
      p1 = p2+1;
    }
  }
}

static void writeDoubleVectorToFile(FILE* outFile,
				    const char* header,
				    const std::vector<double>& vec)
{
  fprintf(outFile, "%s ", header);
  FOR_EACH (dp, vec) {
    fprintf(outFile, "%lf ", *dp);
  }
  fprintf(outFile, "\n");
}

static std::string getVal(const ParamLookup& params,
			  const std::string& paramName)
{
  typeof(params.begin()) i = params.find(paramName);
  if (params.end() == i) {
    fprintf(stderr, "ERROR: LSModelFile: preamble does not specify a value for parameter '%s'\n",
	    paramName.c_str());
    exit(EXIT_FAILURE);
  } else {
    return i->second;
  }
}

static std::string parseRow(const char* inRow,
			    int isOdd,
			    const std::string& fname,
			    int lnum)
{
  int n = strlen(inRow);
  std::string result(n/2+1, ' ');

  FOR (inIndex, n) {
    char c = inRow[inIndex];
    if (0 == ((isOdd + inIndex) % 2)) {
      /* even position */
      result[inIndex / 2] = c;
    } else {
      /* odd position -- expect a space character */
      if (' ' != c) {
	fprintf(stderr, "ERROR: %s: line %d: found an unexpected non-space character, map should have a checkerboard structure\n",
		fname.c_str(), lnum);
	printf("isOdd=%d inIndex=%d\n", isOdd, inIndex);
	exit(EXIT_FAILURE);
      }
    }
  }

  return result;
}

/**********************************************************************
 * LSGRID FUNCTIONS
 **********************************************************************/

LSGrid::LSGrid(void) :
  width(0),
  height(0)
{}

unsigned char LSGrid::getCellBounded(const LSPos& pos) const
{
  if (0 <= pos.x && pos.x < ((int)width) && 0 <= pos.y && pos.y < ((int)height)) {
    return getCell(pos);
  } else {
    return LS_OBSTACLE;
  }
}

bool LSGrid::getAtExit(const LSPos& pos) const
{
  /* exiting is legal if there are no non-obstacle cells further to the
     east of this cell in the same row */
  for (int x=pos.x+1; x < (int)width; x++) {
    if (LS_OBSTACLE != getCell(LSPos(x, pos.y))) {
      return false;
    }
  }
  return true;
}

void LSGrid::writeToFile(FILE* outFile, bool binaryMap) const
{
  int y;
  if (binaryMap) {
    y = height;
    fprintf(outFile, "  ");
    if ((y % 2) == 1) fputc(' ', outFile);
    for (unsigned int x=(y+1)/2; x < width; x++) {
      fprintf(outFile, "%d ", x%10);
    }
    fprintf(outFile, "\n");
  }
  FOR (y0, height) {
    y = height - y0 - 1;
    if (binaryMap) {
      fprintf(outFile, "%d ", y%10);
    }
    if ((y % 2) == 1) fputc(' ', outFile);
    for (unsigned int x=(y+1)/2; x < width; x++) {
      unsigned char cell = getCell(LSPos(x,y));
      char outCell;
      if (LS_OBSTACLE == cell) {
	outCell = ' ';
      } else {
	if (binaryMap && ((0 == cell) || (1 == cell))) {
	  if (cell) {
	    outCell = 'O';
	  } else {
	    outCell = '*';
	  }
	} else {
	  outCell = cell + 97;
	}
      }
      fputc(outCell, outFile);
      fputc(' ', outFile);
    }
    if (binaryMap) {
      fprintf(outFile, " %d", y%10);
    }
    fputc('\n', outFile);
  }
  if (binaryMap) {
    fprintf(outFile, "   ");
    for (unsigned int x=0; x < width; x++) {
      fprintf(outFile, "%d ", x%10);
    }
    fprintf(outFile, "\n");
  }
}

int LSGrid::getMaxCellValue(void)
{
  int maxValue = 0;
  FOR (y, height) {
    FOR (x, width) {
      int cell = getCell(LSPos(x,y));
      if (LS_OBSTACLE != cell) {
	maxValue = std::max(cell, maxValue);
      }
    }
  }
  maxValue++;
  return maxValue;
}

/**********************************************************************
 * LSMODEL FUNCTIONS
 **********************************************************************/

LSModelFile::LSModelFile(void) :
  startX(-1),
  startY(-1)
{}

void LSModelFile::readFromFile(const std::string& fname)
{
  FILE* modelFile = fopen(fname.c_str(), "r");
  if (NULL == modelFile) {
    fprintf(stderr, "ERROR: LSModelFile: couldn't open %s for reading: %s\n", fname.c_str(),
	    strerror(errno));
    exit(EXIT_FAILURE);
  }

  char lbuf[1024];
  char key[1024], value[1024];
  int lnum;

  /* read parameters from preamble */
  ParamLookup params;
  lnum = 0;
  while (NULL != fgets(lbuf, sizeof(lbuf), modelFile)) {
    lnum++;
    if (strlen(lbuf) <= 1) continue;
    if ('#' == lbuf[0]) continue;
    if ('-' == lbuf[0]) goto preambleDone;
    lbuf[strlen(lbuf)-1] = '\0'; // truncate newline
    if (2 != sscanf(lbuf, "%s %[-0-9.eE \t]", key, value)) {
      fprintf(stderr, "ERROR: %s: line %d: syntax error, expected '<key> <value>'\n",
	      fname.c_str(), lnum);
      exit(EXIT_FAILURE);
    }
    params[key] = value;
  }
  fprintf(stderr, "ERROR: %s: reached EOF while still parsing preamble\n",
	  fname.c_str());
  exit(EXIT_FAILURE);
 preambleDone:

  /* store parameters */
  startX = atoi(getVal(params, "startX").c_str());
  startY = atoi(getVal(params, "startY").c_str());
  baseCost = atof(getVal(params, "baseCost").c_str());
  convertToDoubleVector(regionPriors,
			getVal(params, "regionPriors"));
  convertToDoubleVector(obsDistributionLifeAbsent,
			getVal(params, "obsDistributionLifeAbsent"));
  convertToDoubleVector(obsDistributionLifePresent,
			getVal(params, "obsDistributionLifePresent"));
  if ((obsDistributionLifeAbsent.size() != LS_BASE_NUM_OBSERVATIONS)
      || (obsDistributionLifePresent.size() != LS_BASE_NUM_OBSERVATIONS)) {
    fprintf(stderr, "ERROR: %s: obsDistributionLife{Absent,Present} vectors must have %d entries each\n",
	    fname.c_str(), LS_BASE_NUM_OBSERVATIONS);
    exit(EXIT_FAILURE);
  }

  /* read map data into intermediate data structure */
  std::vector<std::string> rows;
  while (NULL != fgets(lbuf, sizeof(lbuf), modelFile)) {
    lnum++;
    if (strlen(lbuf) <= 1) {
      if (feof(modelFile)) {
	break;
      } else {
	fprintf(stderr, "ERROR: %s: line %d: found unexpected zero-length line in map\n",
		fname.c_str(), lnum);
	exit(EXIT_FAILURE);
      }
    }
    lbuf[strlen(lbuf)-1] = '\0'; // truncate trailing newline
    rows.push_back(parseRow(lbuf, rows.size() % 2, fname, lnum));
  }

  /* calculate dimensions of map */
  int maxRowWidth = 0;
  FOR (y0, rows.size()) {
    int y = rows.size() - y0 - 1;
    maxRowWidth = std::max(maxRowWidth, (int) ((y+1)/2 + rows[y0].size()));
  }

  /* allocate final map data structure */
  grid.width = maxRowWidth;
  grid.height = rows.size();
  grid.data.resize(grid.width * grid.height, LS_OBSTACLE);

  /* convert from immediate to final data structure */
  FOR (y0, rows.size()) {
    int y = rows.size() - y0 - 1;
    FOR (x0, rows[y0].size()) {
      int x = (y+1)/2 + x0;
      char c = rows[y0][x0];
      unsigned char cell;
      if (' ' == c) {
	cell = LS_OBSTACLE;
      } else if (97 <= c && c <= 122) {
	cell = c-97;
      } else {
	fprintf(stderr, "ERROR: %s: while parsing map, found character '%c', expected space or a-z.\n",
		fname.c_str(), c);
	exit(EXIT_FAILURE);
      }
      grid.setCell(LSPos(x, y), cell);
    }
  }

  if ((int)regionPriors.size() != grid.getMaxCellValue()) {
    int n = grid.getMaxCellValue();
    fprintf(stderr, "ERROR: %s: number of regionPriors (%d) should match number of regions in map (a-%c=%d)\n",
	    fname.c_str(), (int)regionPriors.size(), (char) ((n-1)+97), n);
    exit(EXIT_FAILURE);
  }
}

void LSModelFile::writeToFile(FILE* outFile) const
{
  fprintf(outFile, "startX %d\n", startX);
  fprintf(outFile, "startY %d\n", startY);
  fprintf(outFile, "baseCost %lf\n", baseCost);
  writeDoubleVectorToFile(outFile, "regionPriors", regionPriors);
  writeDoubleVectorToFile(outFile, "obsDistributionLifeAbsent", obsDistributionLifeAbsent);
  writeDoubleVectorToFile(outFile, "obsDistributionLifePresent", obsDistributionLifePresent);
  fprintf(outFile, "---\n");
  grid.writeToFile(outFile);
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.6  2006/06/13 18:28:34  trey
 * added baseCost parameter
 *
 * Revision 1.5  2006/06/13 14:43:14  trey
 * renamed getExitLegal() -> getAtExit()
 *
 * Revision 1.4  2006/06/13 01:02:20  trey
 * fixed off-by-one error in odd rows of map
 *
 * Revision 1.3  2006/06/12 21:09:10  trey
 * added support for obsDistributionXXX parameters
 *
 * Revision 1.2  2006/06/12 18:44:58  trey
 * regionPriors now implemented correctly
 *
 * Revision 1.1  2006/06/12 18:12:08  trey
 * renamed LSModel to LSModelFile; minor updates
 *
 * Revision 1.1  2006/06/11 14:37:38  trey
 * initial check-in
 *
 *
 ***************************************************************************/
<commit_msg>now normalize obsDistribution parameters<commit_after>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.8 $  $Author: trey $  $Date: 2006-06-20 01:22:25 $

 @file    LSModelFile.cc
 @brief   No brief

 Copyright (c) 2006, Trey Smith. All rights reserved.

 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 <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>

#include "LSModelFile.h"
#include "zmdpCommonDefs.h"

using namespace std;

/**********************************************************************
 * DATA STRUCTURES
 **********************************************************************/

typedef std::map<std::string, std::string> ParamLookup;

/**********************************************************************
 * LOCAL HELPER FUNCTIONS
 **********************************************************************/

static void convertToDoubleVector(std::vector<double>& result,
				  const std::string& s,
				  int doNormalizeVector)
{
  string::size_type p1, p2;
  p1 = 0;
  while (1) {
    p2 = s.find_first_of(" \t", p1);
    if (string::npos == p2) {
      result.push_back(atof(s.substr(p1).c_str()));
      break;
    } else {
      if (p1 != p2) {
	result.push_back(atof(s.substr(p1,(p2-p1)).c_str()));
      }
      p1 = p2+1;
    }
  }

  if (doNormalizeVector) {
    double sum = 0.0;
    FOR_EACH (vp, result) {
      sum += *vp;
    }
    FOR_EACH (vp, result) {
      *vp /= sum;
    }
  }
}

static void writeDoubleVectorToFile(FILE* outFile,
				    const char* header,
				    const std::vector<double>& vec)
{
  fprintf(outFile, "%s ", header);
  FOR_EACH (dp, vec) {
    fprintf(outFile, "%lf ", *dp);
  }
  fprintf(outFile, "\n");
}

static std::string getVal(const ParamLookup& params,
			  const std::string& paramName)
{
  typeof(params.begin()) i = params.find(paramName);
  if (params.end() == i) {
    fprintf(stderr, "ERROR: LSModelFile: preamble does not specify a value for parameter '%s'\n",
	    paramName.c_str());
    exit(EXIT_FAILURE);
  } else {
    return i->second;
  }
}

static std::string parseRow(const char* inRow,
			    int isOdd,
			    const std::string& fname,
			    int lnum)
{
  int n = strlen(inRow);
  std::string result(n/2+1, ' ');

  FOR (inIndex, n) {
    char c = inRow[inIndex];
    if (0 == ((isOdd + inIndex) % 2)) {
      /* even position */
      result[inIndex / 2] = c;
    } else {
      /* odd position -- expect a space character */
      if (' ' != c) {
	fprintf(stderr, "ERROR: %s: line %d: found an unexpected non-space character, map should have a checkerboard structure\n",
		fname.c_str(), lnum);
	printf("isOdd=%d inIndex=%d\n", isOdd, inIndex);
	exit(EXIT_FAILURE);
      }
    }
  }

  return result;
}

/**********************************************************************
 * LSGRID FUNCTIONS
 **********************************************************************/

LSGrid::LSGrid(void) :
  width(0),
  height(0)
{}

unsigned char LSGrid::getCellBounded(const LSPos& pos) const
{
  if (0 <= pos.x && pos.x < ((int)width) && 0 <= pos.y && pos.y < ((int)height)) {
    return getCell(pos);
  } else {
    return LS_OBSTACLE;
  }
}

bool LSGrid::getAtExit(const LSPos& pos) const
{
  /* exiting is legal if there are no non-obstacle cells further to the
     east of this cell in the same row */
  for (int x=pos.x+1; x < (int)width; x++) {
    if (LS_OBSTACLE != getCell(LSPos(x, pos.y))) {
      return false;
    }
  }
  return true;
}

void LSGrid::writeToFile(FILE* outFile, bool binaryMap) const
{
  int y;
  if (binaryMap) {
    y = height;
    fprintf(outFile, "  ");
    if ((y % 2) == 1) fputc(' ', outFile);
    for (unsigned int x=(y+1)/2; x < width; x++) {
      fprintf(outFile, "%d ", x%10);
    }
    fprintf(outFile, "\n");
  }
  FOR (y0, height) {
    y = height - y0 - 1;
    if (binaryMap) {
      fprintf(outFile, "%d ", y%10);
    }
    if ((y % 2) == 1) fputc(' ', outFile);
    for (unsigned int x=(y+1)/2; x < width; x++) {
      unsigned char cell = getCell(LSPos(x,y));
      char outCell;
      if (LS_OBSTACLE == cell) {
	outCell = ' ';
      } else {
	if (binaryMap && ((0 == cell) || (1 == cell))) {
	  if (cell) {
	    outCell = 'O';
	  } else {
	    outCell = '*';
	  }
	} else {
	  outCell = cell + 97;
	}
      }
      fputc(outCell, outFile);
      fputc(' ', outFile);
    }
    if (binaryMap) {
      fprintf(outFile, " %d", y%10);
    }
    fputc('\n', outFile);
  }
  if (binaryMap) {
    fprintf(outFile, "   ");
    for (unsigned int x=0; x < width; x++) {
      fprintf(outFile, "%d ", x%10);
    }
    fprintf(outFile, "\n");
  }
}

int LSGrid::getMaxCellValue(void)
{
  int maxValue = 0;
  FOR (y, height) {
    FOR (x, width) {
      int cell = getCell(LSPos(x,y));
      if (LS_OBSTACLE != cell) {
	maxValue = std::max(cell, maxValue);
      }
    }
  }
  maxValue++;
  return maxValue;
}

/**********************************************************************
 * LSMODEL FUNCTIONS
 **********************************************************************/

LSModelFile::LSModelFile(void) :
  startX(-1),
  startY(-1)
{}

void LSModelFile::readFromFile(const std::string& fname)
{
  FILE* modelFile = fopen(fname.c_str(), "r");
  if (NULL == modelFile) {
    fprintf(stderr, "ERROR: LSModelFile: couldn't open %s for reading: %s\n", fname.c_str(),
	    strerror(errno));
    exit(EXIT_FAILURE);
  }

  char lbuf[1024];
  char key[1024], value[1024];
  int lnum;

  /* read parameters from preamble */
  ParamLookup params;
  lnum = 0;
  while (NULL != fgets(lbuf, sizeof(lbuf), modelFile)) {
    lnum++;
    if (strlen(lbuf) <= 1) continue;
    if ('#' == lbuf[0]) continue;
    if ('-' == lbuf[0]) goto preambleDone;
    lbuf[strlen(lbuf)-1] = '\0'; // truncate newline
    if (2 != sscanf(lbuf, "%s %[-0-9.eE \t]", key, value)) {
      fprintf(stderr, "ERROR: %s: line %d: syntax error, expected '<key> <value>'\n",
	      fname.c_str(), lnum);
      exit(EXIT_FAILURE);
    }
    params[key] = value;
  }
  fprintf(stderr, "ERROR: %s: reached EOF while still parsing preamble\n",
	  fname.c_str());
  exit(EXIT_FAILURE);
 preambleDone:

  /* store parameters */
  startX = atoi(getVal(params, "startX").c_str());
  startY = atoi(getVal(params, "startY").c_str());
  baseCost = atof(getVal(params, "baseCost").c_str());
  convertToDoubleVector(regionPriors,
			getVal(params, "regionPriors"),
			/* doNormalizeVector = */ 0);
  convertToDoubleVector(obsDistributionLifeAbsent,
			getVal(params, "obsDistributionLifeAbsent"),
			/* doNormalizeVector = */ 1);
  convertToDoubleVector(obsDistributionLifePresent,
			getVal(params, "obsDistributionLifePresent"),
			/* doNormalizeVector = */ 1);
  if ((obsDistributionLifeAbsent.size() != LS_BASE_NUM_OBSERVATIONS)
      || (obsDistributionLifePresent.size() != LS_BASE_NUM_OBSERVATIONS)) {
    fprintf(stderr, "ERROR: %s: obsDistributionLife{Absent,Present} vectors must have %d entries each\n",
	    fname.c_str(), LS_BASE_NUM_OBSERVATIONS);
    exit(EXIT_FAILURE);
  }

  /* read map data into intermediate data structure */
  std::vector<std::string> rows;
  while (NULL != fgets(lbuf, sizeof(lbuf), modelFile)) {
    lnum++;
    if (strlen(lbuf) <= 1) {
      if (feof(modelFile)) {
	break;
      } else {
	fprintf(stderr, "ERROR: %s: line %d: found unexpected zero-length line in map\n",
		fname.c_str(), lnum);
	exit(EXIT_FAILURE);
      }
    }
    lbuf[strlen(lbuf)-1] = '\0'; // truncate trailing newline
    rows.push_back(parseRow(lbuf, rows.size() % 2, fname, lnum));
  }

  /* calculate dimensions of map */
  int maxRowWidth = 0;
  FOR (y0, rows.size()) {
    int y = rows.size() - y0 - 1;
    maxRowWidth = std::max(maxRowWidth, (int) ((y+1)/2 + rows[y0].size()));
  }

  /* allocate final map data structure */
  grid.width = maxRowWidth;
  grid.height = rows.size();
  grid.data.resize(grid.width * grid.height, LS_OBSTACLE);

  /* convert from immediate to final data structure */
  FOR (y0, rows.size()) {
    int y = rows.size() - y0 - 1;
    FOR (x0, rows[y0].size()) {
      int x = (y+1)/2 + x0;
      char c = rows[y0][x0];
      unsigned char cell;
      if (' ' == c) {
	cell = LS_OBSTACLE;
      } else if (97 <= c && c <= 122) {
	cell = c-97;
      } else {
	fprintf(stderr, "ERROR: %s: while parsing map, found character '%c', expected space or a-z.\n",
		fname.c_str(), c);
	exit(EXIT_FAILURE);
      }
      grid.setCell(LSPos(x, y), cell);
    }
  }

  if ((int)regionPriors.size() != grid.getMaxCellValue()) {
    int n = grid.getMaxCellValue();
    fprintf(stderr, "ERROR: %s: number of regionPriors (%d) should match number of regions in map (a-%c=%d)\n",
	    fname.c_str(), (int)regionPriors.size(), (char) ((n-1)+97), n);
    exit(EXIT_FAILURE);
  }
}

void LSModelFile::writeToFile(FILE* outFile) const
{
  fprintf(outFile, "startX %d\n", startX);
  fprintf(outFile, "startY %d\n", startY);
  fprintf(outFile, "baseCost %lf\n", baseCost);
  writeDoubleVectorToFile(outFile, "regionPriors", regionPriors);
  writeDoubleVectorToFile(outFile, "obsDistributionLifeAbsent", obsDistributionLifeAbsent);
  writeDoubleVectorToFile(outFile, "obsDistributionLifePresent", obsDistributionLifePresent);
  fprintf(outFile, "---\n");
  grid.writeToFile(outFile);
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.7  2006/06/16 14:45:20  trey
 * added ability to generate target map
 *
 * Revision 1.6  2006/06/13 18:28:34  trey
 * added baseCost parameter
 *
 * Revision 1.5  2006/06/13 14:43:14  trey
 * renamed getExitLegal() -> getAtExit()
 *
 * Revision 1.4  2006/06/13 01:02:20  trey
 * fixed off-by-one error in odd rows of map
 *
 * Revision 1.3  2006/06/12 21:09:10  trey
 * added support for obsDistributionXXX parameters
 *
 * Revision 1.2  2006/06/12 18:44:58  trey
 * regionPriors now implemented correctly
 *
 * Revision 1.1  2006/06/12 18:12:08  trey
 * renamed LSModel to LSModelFile; minor updates
 *
 * Revision 1.1  2006/06/11 14:37:38  trey
 * initial check-in
 *
 *
 ***************************************************************************/
<|endoftext|>


<commit_before>/*********************************************************************/
/* Copyright (c) 2011 - 2012, The University of Texas at Austin.     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. Redistributions in binary form must reproduce the above      */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer in the documentation and/or other materials       */
/*      provided with the distribution.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``AS IS''  AND ANY  EXPRESS OR  IMPLIED WARRANTIES,    */
/*    INCLUDING, BUT  NOT LIMITED  TO, THE IMPLIED  WARRANTIES OF    */
/*    MERCHANTABILITY  AND FITNESS FOR  A PARTICULAR  PURPOSE ARE    */
/*    DISCLAIMED.  IN  NO EVENT SHALL THE UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN OR CONTRIBUTORS BE  LIABLE FOR ANY DIRECT, INDIRECT,    */
/*    INCIDENTAL,  SPECIAL, EXEMPLARY,  OR  CONSEQUENTIAL DAMAGES    */
/*    (INCLUDING, BUT  NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE    */
/*    GOODS  OR  SERVICES; LOSS  OF  USE,  DATA,  OR PROFITS;  OR    */
/*    BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON ANY THEORY OF    */
/*    LIABILITY, WHETHER  IN CONTRACT, STRICT  LIABILITY, OR TORT    */
/*    (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY OUT    */
/*    OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF ADVISED  OF  THE    */
/*    POSSIBILITY OF SUCH DAMAGE.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#include "PixelStream.h"
#include "main.h"
#include "log.h"

PixelStream::PixelStream(std::string uri)
{
    // defaults
    textureId_ = 0;
    textureWidth_ = 0;
    textureHeight_ = 0;
    textureBound_ = false;
    imageReady_ = false;
    autoUpdateTexture_ = true;

    // assign values
    uri_ = uri;

    // initialize libjpeg-turbo handle
    handle_ = tjInitDecompress();
}

PixelStream::~PixelStream()
{
    if(textureBound_ == true)
    {
        // delete bound texture
        glDeleteTextures(1, &textureId_); // it appears deleteTexture() below is not actually deleting the texture from the GPU...
        g_mainWindow->getGLWindow()->deleteTexture(textureId_);
    }

    // destroy libjpeg-turbo handle
    tjDestroy(handle_);
}

void PixelStream::getDimensions(int &width, int &height)
{
    width = textureWidth_;
    height = textureHeight_;
}

bool PixelStream::render(float tX, float tY, float tW, float tH)
{
    updateRenderedFrameCount();

    // automatically upload a new texture if a new image is available
    if(autoUpdateTexture_ == true)
    {
        updateTextureIfAvailable();
    }

    if(textureBound_ != true)
    {
        return false;
    }

    // draw the texture
    glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureId_);

    // on zoom-out, clamp to edge (instead of showing the texture tiled / repeated)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glBegin(GL_QUADS);

    glTexCoord2f(tX,tY);
    glVertex2f(0.,0.);

    glTexCoord2f(tX+tW,tY);
    glVertex2f(1.,0.);

    glTexCoord2f(tX+tW,tY+tH);
    glVertex2f(1.,1.);

    glTexCoord2f(tX,tY+tH);
    glVertex2f(0.,1.);

    glEnd();

    glPopAttrib();

    return true;
}

bool PixelStream::setImageData(QByteArray imageData)
{
    // drop frames if we're currently processing
    if(loadImageDataThread_.isRunning() == true)
    {
        return false;
    }

    loadImageDataThread_ = QtConcurrent::run(loadImageDataThread, shared_from_this(), imageData);

    return true;
}

bool PixelStream::getLoadImageDataThreadRunning()
{
    return loadImageDataThread_.isRunning();
}

void PixelStream::setAutoUpdateTexture(bool set)
{
    autoUpdateTexture_ = set;
}

void PixelStream::updateTextureIfAvailable()
{
    // upload a new texture if a new image is available
    QMutexLocker locker(&imageReadyMutex_);

    if(imageReady_ == true)
    {
        updateTexture(image_);
        imageReady_ = false;
    }
}

tjhandle PixelStream::getHandle()
{
    return handle_;
}

void PixelStream::imageReady(QImage image)
{
    QMutexLocker locker(&imageReadyMutex_);
    imageReady_ = true;
    image_ = image;
}

void PixelStream::updateTexture(QImage & image)
{
    // todo: consider if the image has changed dimensions

    if(textureBound_ == false)
    {
        // want mipmaps disabled
        textureId_ = g_mainWindow->getGLWindow()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
        textureWidth_ = image.width();
        textureHeight_ = image.height();
        textureBound_ = true;
    }
    else
    {
        // update the texture. note that generally we would need to convert the image to an OpenGL supported format
        // however, we're lucky and can use GL_BGRA on the original image...
        // example conversion to GL format: QImage glImage = QGLWidget::convertToGLFormat(image);

        // if the size has changed, create a new texture
        if(image.width() != textureWidth_ || image.height() != textureHeight_)
        {
            // delete bound texture
            glDeleteTextures(1, &textureId_); // it appears deleteTexture() below is not actually deleting the texture from the GPU...
            g_mainWindow->getGLWindow()->deleteTexture(textureId_);

            textureId_ = g_mainWindow->getGLWindow()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
            textureWidth_ = image.width();
            textureHeight_ = image.height();
        }
        else
        {
            glBindTexture(GL_TEXTURE_2D, textureId_);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, image.width(), image.height(), GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
        }
    }
}

void loadImageDataThread(boost::shared_ptr<PixelStream> pixelStream, QByteArray imageData)
{
    // use libjpeg-turbo for JPEG conversion
    tjhandle handle = pixelStream->getHandle();

    // get information from header
    int width, height, jpegSubsamp;
    int success =  tjDecompressHeader2(handle, (unsigned char *)imageData.data(), (unsigned long)imageData.size(), &width, &height, &jpegSubsamp);

    if(success != 0)
    {
        put_flog(LOG_ERROR, "libjpeg-turbo header decompression failure");
        return;
    }

    // decompress image data
    int pixelFormat = TJPF_BGRX;
    int pitch = width * tjPixelSize[pixelFormat];
    int flags = TJ_FASTUPSAMPLE;

    QImage image = QImage(width, height, QImage::Format_RGB32);

    success = tjDecompress2(handle, (unsigned char *)imageData.data(), (unsigned long)imageData.size(), (unsigned char *)image.scanLine(0), width, pitch, height, pixelFormat, flags);

    if(success != 0)
    {
        put_flog(LOG_ERROR, "libjpeg-turbo image decompression failure");
        return;
    }

    pixelStream->imageReady(image);
}
<commit_msg>PixelStream textures are now deleted by the main thread.<commit_after>/*********************************************************************/
/* Copyright (c) 2011 - 2012, The University of Texas at Austin.     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. Redistributions in binary form must reproduce the above      */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer in the documentation and/or other materials       */
/*      provided with the distribution.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``AS IS''  AND ANY  EXPRESS OR  IMPLIED WARRANTIES,    */
/*    INCLUDING, BUT  NOT LIMITED  TO, THE IMPLIED  WARRANTIES OF    */
/*    MERCHANTABILITY  AND FITNESS FOR  A PARTICULAR  PURPOSE ARE    */
/*    DISCLAIMED.  IN  NO EVENT SHALL THE UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN OR CONTRIBUTORS BE  LIABLE FOR ANY DIRECT, INDIRECT,    */
/*    INCIDENTAL,  SPECIAL, EXEMPLARY,  OR  CONSEQUENTIAL DAMAGES    */
/*    (INCLUDING, BUT  NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE    */
/*    GOODS  OR  SERVICES; LOSS  OF  USE,  DATA,  OR PROFITS;  OR    */
/*    BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON ANY THEORY OF    */
/*    LIABILITY, WHETHER  IN CONTRACT, STRICT  LIABILITY, OR TORT    */
/*    (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY OUT    */
/*    OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF ADVISED  OF  THE    */
/*    POSSIBILITY OF SUCH DAMAGE.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#include "PixelStream.h"
#include "main.h"
#include "log.h"

PixelStream::PixelStream(std::string uri)
{
    // defaults
    textureId_ = 0;
    textureWidth_ = 0;
    textureHeight_ = 0;
    textureBound_ = false;
    imageReady_ = false;
    autoUpdateTexture_ = true;

    // assign values
    uri_ = uri;

    // initialize libjpeg-turbo handle
    handle_ = tjInitDecompress();
}

PixelStream::~PixelStream()
{
    // delete bound texture
    if(textureBound_ == true)
    {
        // let the OpenGL window delete the texture, so the destructor can occur in any thread...
        g_mainWindow->getGLWindow()->insertPurgeTextureId(textureId_);

        textureBound_ = false;
    }

    // destroy libjpeg-turbo handle
    tjDestroy(handle_);
}

void PixelStream::getDimensions(int &width, int &height)
{
    width = textureWidth_;
    height = textureHeight_;
}

bool PixelStream::render(float tX, float tY, float tW, float tH)
{
    updateRenderedFrameCount();

    // automatically upload a new texture if a new image is available
    if(autoUpdateTexture_ == true)
    {
        updateTextureIfAvailable();
    }

    if(textureBound_ != true)
    {
        return false;
    }

    // draw the texture
    glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureId_);

    // on zoom-out, clamp to edge (instead of showing the texture tiled / repeated)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glBegin(GL_QUADS);

    glTexCoord2f(tX,tY);
    glVertex2f(0.,0.);

    glTexCoord2f(tX+tW,tY);
    glVertex2f(1.,0.);

    glTexCoord2f(tX+tW,tY+tH);
    glVertex2f(1.,1.);

    glTexCoord2f(tX,tY+tH);
    glVertex2f(0.,1.);

    glEnd();

    glPopAttrib();

    return true;
}

bool PixelStream::setImageData(QByteArray imageData)
{
    // drop frames if we're currently processing
    if(loadImageDataThread_.isRunning() == true)
    {
        return false;
    }

    loadImageDataThread_ = QtConcurrent::run(loadImageDataThread, shared_from_this(), imageData);

    return true;
}

bool PixelStream::getLoadImageDataThreadRunning()
{
    return loadImageDataThread_.isRunning();
}

void PixelStream::setAutoUpdateTexture(bool set)
{
    autoUpdateTexture_ = set;
}

void PixelStream::updateTextureIfAvailable()
{
    // upload a new texture if a new image is available
    QMutexLocker locker(&imageReadyMutex_);

    if(imageReady_ == true)
    {
        updateTexture(image_);
        imageReady_ = false;
    }
}

tjhandle PixelStream::getHandle()
{
    return handle_;
}

void PixelStream::imageReady(QImage image)
{
    QMutexLocker locker(&imageReadyMutex_);
    imageReady_ = true;
    image_ = image;
}

void PixelStream::updateTexture(QImage & image)
{
    // todo: consider if the image has changed dimensions

    if(textureBound_ == false)
    {
        // want mipmaps disabled
        textureId_ = g_mainWindow->getGLWindow()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
        textureWidth_ = image.width();
        textureHeight_ = image.height();
        textureBound_ = true;
    }
    else
    {
        // update the texture. note that generally we would need to convert the image to an OpenGL supported format
        // however, we're lucky and can use GL_BGRA on the original image...
        // example conversion to GL format: QImage glImage = QGLWidget::convertToGLFormat(image);

        // if the size has changed, create a new texture
        if(image.width() != textureWidth_ || image.height() != textureHeight_)
        {
            // delete bound texture
            glDeleteTextures(1, &textureId_); // it appears deleteTexture() below is not actually deleting the texture from the GPU...
            g_mainWindow->getGLWindow()->deleteTexture(textureId_);

            textureId_ = g_mainWindow->getGLWindow()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
            textureWidth_ = image.width();
            textureHeight_ = image.height();
        }
        else
        {
            glBindTexture(GL_TEXTURE_2D, textureId_);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, image.width(), image.height(), GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
        }
    }
}

void loadImageDataThread(boost::shared_ptr<PixelStream> pixelStream, QByteArray imageData)
{
    // use libjpeg-turbo for JPEG conversion
    tjhandle handle = pixelStream->getHandle();

    // get information from header
    int width, height, jpegSubsamp;
    int success =  tjDecompressHeader2(handle, (unsigned char *)imageData.data(), (unsigned long)imageData.size(), &width, &height, &jpegSubsamp);

    if(success != 0)
    {
        put_flog(LOG_ERROR, "libjpeg-turbo header decompression failure");
        return;
    }

    // decompress image data
    int pixelFormat = TJPF_BGRX;
    int pitch = width * tjPixelSize[pixelFormat];
    int flags = TJ_FASTUPSAMPLE;

    QImage image = QImage(width, height, QImage::Format_RGB32);

    success = tjDecompress2(handle, (unsigned char *)imageData.data(), (unsigned long)imageData.size(), (unsigned char *)image.scanLine(0), width, pitch, height, pixelFormat, flags);

    if(success != 0)
    {
        put_flog(LOG_ERROR, "libjpeg-turbo image decompression failure");
        return;
    }

    pixelStream->imageReady(image);
}
<|endoftext|>


<commit_before>/*****************************************************************************

    (c) 2013 Hobu, Inc. hobu.inc@gmail.com

    Author: Andrew Bell andrew.bell.ia at gmail.com

    This is free software; you can redistribute and/or modify it under the
    terms of the GNU Lesser General Licence as published by the Free Software
    Foundation. See the COPYING file for more information.

    This software is distributed WITHOUT ANY WARRANTY and without even the
    implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

*****************************************************************************/

#ifndef INCLUDED_PSHAPE_PATH_HPP
#define INCLUDED_PSHAPE_PATH_HPP

#include <vector>

#include "Mathpair.hpp"
#include "Segment.hpp"

namespace hexer
{

enum Orientation
{
    CLOCKWISE,     // Outer
    ANTICLOCKWISE  // Hole
};

class HexGrid;

class Path
{
public:
    Path(HexGrid *m_grid, Orientation orient) :
        m_parent(NULL), m_orientation(orient)
    {}

    ~Path()
    {
        for (int i = 0; i < m_children.size(); ++i)
            delete m_children[i];
    }

    void push_back(const Segment& s)
        { m_segs.push_back(s); }
    Segment rootSegment()
        { return m_segs[0]; }
    Path *parent()
        { return m_parent; }
    void setParent(Path *p)
        { m_parent = p; }
    void addChild(Path *p)
        { m_children.push_back(p); }
    void finalize(Orientation o)
    {
        m_orientation = o;
        for (size_t i = 0; i < m_children.size(); ++i)
            m_children[i]->finalize(o == CLOCKWISE ? ANTICLOCKWISE : CLOCKWISE);
    }
    int pathLength()
        { return m_segs.size(); }
    Point getPoint(int pointnum);
    Orientation orientation() const
        { return m_orientation; }
    std::vector<Point> points();
    std::vector<Path *> subPaths()
        { return m_children; }

private:
    /// Grid that owns the path.
    HexGrid *m_grid;
    /// Parent path (NULL if root path)
    Path *m_parent;
    /// Children
    std::vector<Path *> m_children;
    /// Orientation of path AT EXTRACTION - segments are ALWAYS ordered
    /// clockwise.
    Orientation m_orientation;
    /// List of segments that make up the path.
    std::vector<Segment> m_segs;
};

} //namespace

#endif
<commit_msg>copy the grid reference when constructing the Path<commit_after>/*****************************************************************************

    (c) 2013 Hobu, Inc. hobu.inc@gmail.com

    Author: Andrew Bell andrew.bell.ia at gmail.com

    This is free software; you can redistribute and/or modify it under the
    terms of the GNU Lesser General Licence as published by the Free Software
    Foundation. See the COPYING file for more information.

    This software is distributed WITHOUT ANY WARRANTY and without even the
    implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

*****************************************************************************/

#ifndef INCLUDED_PSHAPE_PATH_HPP
#define INCLUDED_PSHAPE_PATH_HPP

#include <vector>

#include "Mathpair.hpp"
#include "Segment.hpp"

namespace hexer
{

enum Orientation
{
    CLOCKWISE,     // Outer
    ANTICLOCKWISE  // Hole
};

class HexGrid;

class Path
{
public:
    Path(HexGrid *m_grid, Orientation orient) :
        m_grid(m_grid), m_parent(NULL), m_orientation(orient)
    {}

    ~Path()
    {
        for (int i = 0; i < m_children.size(); ++i)
            delete m_children[i];
    }

    void push_back(const Segment& s)
        { m_segs.push_back(s); }
    Segment rootSegment()
        { return m_segs[0]; }
    Path *parent()
        { return m_parent; }
    void setParent(Path *p)
        { m_parent = p; }
    void addChild(Path *p)
        { m_children.push_back(p); }
    void finalize(Orientation o)
    {
        m_orientation = o;
        for (size_t i = 0; i < m_children.size(); ++i)
            m_children[i]->finalize(o == CLOCKWISE ? ANTICLOCKWISE : CLOCKWISE);
    }
    int pathLength()
        { return m_segs.size(); }
    Point getPoint(int pointnum);
    Orientation orientation() const
        { return m_orientation; }
    std::vector<Point> points();
    std::vector<Path *> subPaths()
        { return m_children; }

private:
    /// Grid that owns the path.
    HexGrid *m_grid;
    /// Parent path (NULL if root path)
    Path *m_parent;
    /// Children
    std::vector<Path *> m_children;
    /// Orientation of path AT EXTRACTION - segments are ALWAYS ordered
    /// clockwise.
    Orientation m_orientation;
    /// List of segments that make up the path.
    std::vector<Segment> m_segs;
};

} //namespace

#endif
<|endoftext|>


<commit_before><commit_msg>osx compile fixes<commit_after><|endoftext|>


<commit_before>/** \file    augment_773a.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool for filling in 773$a if the 773 field exists and $a is missing.
 */

/*
    Copyright (C) 2016-2017, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <unordered_map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " [--verbose] marc_input marc_output\n"
              << "       \"marc_input\" is the file that will be augmented and converted.\n"
              << "       \"marc_input\" will be scoured for titles that\n"
              << "       may be filled into 773$a fields where appropriate.\n"
              << "       Populates 773$a where it is missing and uplinks exist in 773$x.\n";
    std::exit(EXIT_FAILURE);
}

static std::unordered_map<std::string, std::string> control_numbers_to_titles_map;

bool RecordControlNumberToTitleMapping(MarcRecord * const record, MarcWriter * const /*marc_writer*/,
                 std::string * const /* err_msg */)
{
    size_t _245_index;
    if (likely((_245_index = record->getFieldIndex("245")) != MarcRecord::FIELD_NOT_FOUND)) {
        const Subfields _245_subfields(record->getSubfields(_245_index));
        std::string title(_245_subfields.getFirstSubfieldValue('a'));
        if (_245_subfields.hasSubfield('b'))
            title += " " + _245_subfields.getFirstSubfieldValue('b');
        StringUtil::RightTrim(" \t/", &title);
        if (likely(not title.empty()))
            control_numbers_to_titles_map[record->getControlNumber()] = title;
    }

    return true;
}

void CollectControlNumberToTitleMappings(const bool verbose, MarcReader * const marc_reader) {
    if (verbose)
        std::cout << "Extracting control numbers to title mappings from \"" << marc_reader->getPath() << "\".\n";

    std::string err_msg;
    if (not MarcRecord::ProcessRecords(marc_reader, RecordControlNumberToTitleMapping,
                                       /* marc_writer = */nullptr, &err_msg))
        logger->error("error while looking for control numbers to title mappings: " + err_msg);

    if (verbose)
        std::cout << "Found " << control_numbers_to_titles_map.size() << " control number to title mappings.\n";
}

static unsigned patch_count;

// Looks for the existence of a 773 field.  Iff such a field exists and 773$a is missing, we try to add it.
bool PatchUpOne773a(MarcRecord * const record, MarcWriter * const marc_writer, std::string * const /*err_msg*/) {
    size_t _773_index;
    if ((_773_index = record->getFieldIndex("773")) != MarcRecord::FIELD_NOT_FOUND) {
        Subfields _773_subfields(record->getSubfields(_773_index));
        if (not _773_subfields.hasSubfield('a') and _773_subfields.hasSubfield('w')) {
            const std::string w_subfield(_773_subfields.getFirstSubfieldValue('w'));
            if (StringUtil::StartsWith(w_subfield, "(DE-576)")) {
                const std::string parent_control_number(w_subfield.substr(8));
                const auto control_number_and_title(control_numbers_to_titles_map.find(parent_control_number));
                if (control_number_and_title != control_numbers_to_titles_map.end()) {
                    _773_subfields.addSubfield('a', control_number_and_title->second);
                    record->updateField(_773_index, _773_subfields.toString());
                    ++patch_count;
                }
            }
        }
    }

    marc_writer->write(*record);

    return true;
}

// Iterates over all records in a collection and attempts to in 773$a subfields were they are missing.
void PatchUp773aSubfields(const bool verbose, MarcReader * const marc_reader, MarcWriter * const marc_writer) {
    std::string err_msg;
    if (not MarcRecord::ProcessRecords(marc_reader, PatchUpOne773a, marc_writer, &err_msg))
        logger->error("error while adding 773$a subfields to some records: " + err_msg);

    if (verbose)
        std::cout << "Added 773$a subfields to " << patch_count << " records.\n";
}

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc < 2)
        Usage();

    bool verbose(false);
    if (std::strcmp(argv[1], "--verbose") == 0) {
        verbose = true;
        --argc;
        ++argv;
    }

    if (argc != 3)
        Usage();

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(argv[1], MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(argv[2], MarcWriter::BINARY));

    try {
        CollectControlNumberToTitleMappings(verbose, marc_reader.get());

        marc_reader->rewind();
        PatchUp773aSubfields(verbose, marc_reader.get(), marc_writer.get());
    } catch (const std::exception &x) {
        logger->error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Adjust to new API<commit_after>/** \file    augment_773a.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool for filling in 773$a if the 773 field exists and $a is missing.
 */

/*
    Copyright (C) 2016-2018, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <unordered_map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "MARC.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " [--verbose] marc_input marc_output\n"
              << "       \"marc_input\" is the file that will be augmented and converted.\n"
              << "       \"marc_input\" will be scoured for titles that\n"
              << "       may be filled into 773$a fields where appropriate.\n"
              << "       Populates 773$a where it is missing and uplinks exist in 773$x.\n";
    std::exit(EXIT_FAILURE);
}

static std::unordered_map<std::string, std::string> control_numbers_to_titles_map;

bool RecordControlNumberToTitleMapping(MARC::Record * const record)
{
    for (auto &_245_field : record->getTagRange("245")) {
        std::string title(_245_field.getFirstSubfieldWithCode('a'));
        if (_245_field.hasSubfield('b'))
            title += " " + _245_field.getFirstSubfieldWithCode('b');
        StringUtil::RightTrim(" \t/", &title);
        if (likely(not title.empty()))
            control_numbers_to_titles_map[record->getControlNumber()] = title;
    }

    return true;
}

void CollectControlNumberToTitleMappings(const bool verbose, MARC::Reader * const marc_reader) {
    if (verbose)
        std::cout << "Extracting control numbers to title mappings from \"" << marc_reader->getPath() << "\".\n";

    while (auto record = marc_reader->read())
        RecordControlNumberToTitleMapping(&record);

    if (verbose)
        std::cout << "Found " << control_numbers_to_titles_map.size() << " control number to title mappings.\n";
}

static unsigned patch_count;

// Looks for the existence of a 773 field.  Iff such a field exists and 773$a is missing, we try to add it.
bool PatchUpOne773a(MARC::Record * const record, MARC::Writer * const marc_writer) {
    for (auto &_773_field : record->getTagRange("773")) {
        if (not _773_field.hasSubfield('a') and _773_field.hasSubfield('w')) {
            const std::string w_subfield(_773_field.getFirstSubfieldWithCode('w'));
            if (StringUtil::StartsWith(w_subfield, "(DE-576)")) {
                const std::string parent_control_number(w_subfield.substr(8));
                const auto control_number_and_title(control_numbers_to_titles_map.find(parent_control_number));
                if (control_number_and_title != control_numbers_to_titles_map.end()) {
                    _773_field.insertOrReplaceSubfield('a', control_number_and_title->second);
                    ++patch_count;
                }
            }
        }
    }

    marc_writer->write(*record);

    return true;
}

// Iterates over all records in a collection and attempts to in 773$a subfields were they are missing.
void PatchUp773aSubfields(const bool verbose, MARC::Reader * const marc_reader, MARC::Writer * const marc_writer) {
    while (auto record = marc_reader->read())
        PatchUpOne773a(&record, marc_writer);

    if (verbose)
        std::cout << "Added 773$a subfields to " << patch_count << " records.\n";
}

int Main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc < 2)
        Usage();

    bool verbose(false);
    if (std::strcmp(argv[1], "--verbose") == 0) {
        verbose = true;
        --argc;
        ++argv;
    }

    if (argc != 3)
        Usage();

    auto marc_reader(MARC::Reader::Factory(argv[1]));
    auto marc_writer(MARC::Writer::Factory(argv[2]));

    CollectControlNumberToTitleMappings(verbose, marc_reader.get());
    marc_reader->rewind();
    PatchUp773aSubfields(verbose, marc_reader.get(), marc_writer.get());
    return EXIT_SUCCESS;
}
<|endoftext|>