<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

// for peepopt_pm
bool did_something;

#include "passes/pmgen/test_pmgen_pm.h"
#include "passes/pmgen/ice40_dsp_pm.h"
#include "passes/pmgen/xilinx_srl_pm.h"
#include "passes/pmgen/peepopt_pm.h"

void reduce_chain(test_pmgen_pm &pm)
{
	auto &st = pm.st_reduce;
	auto &ud = pm.ud_reduce;

	if (ud.longest_chain.empty())
		return;

	log("Found chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));

	SigSpec A;
	SigSpec Y = ud.longest_chain.front().first->getPort(ID(Y));
	auto last_cell = ud.longest_chain.back().first;

	for (auto it : ud.longest_chain) {
		auto cell = it.first;
		if (cell == last_cell) {
			A.append(cell->getPort(ID(A)));
			A.append(cell->getPort(ID(B)));
		} else {
			A.append(cell->getPort(it.second == ID(A) ? ID(B) : ID(A)));
		}
		log("    %s\n", log_id(cell));
		pm.autoremove(cell);
	}

	Cell *c;

	if (last_cell->type == ID($_AND_))
		c = pm.module->addReduceAnd(NEW_ID, A, Y);
	else if (last_cell->type == ID($_OR_))
		c = pm.module->addReduceOr(NEW_ID, A, Y);
	else if (last_cell->type == ID($_XOR_))
		c = pm.module->addReduceXor(NEW_ID, A, Y);
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

void reduce_tree(test_pmgen_pm &pm)
{
	auto &st = pm.st_reduce;
	auto &ud = pm.ud_reduce;

	if (ud.longest_chain.empty())
		return;

	SigSpec A = ud.leaves;
	SigSpec Y = st.first->getPort(ID(Y));
	pm.autoremove(st.first);

	log("Found %s tree with %d leaves for %s (%s).\n", log_id(st.first->type),
			GetSize(A), log_signal(Y), log_id(st.first));

	Cell *c;

	if (st.first->type == ID($_AND_))
		c = pm.module->addReduceAnd(NEW_ID, A, Y);
	else if (st.first->type == ID($_OR_))
		c = pm.module->addReduceOr(NEW_ID, A, Y);
	else if (st.first->type == ID($_XOR_))
		c = pm.module->addReduceXor(NEW_ID, A, Y);
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

void opt_eqpmux(test_pmgen_pm &pm)
{
	auto &st = pm.st_eqpmux;

	SigSpec Y = st.pmux->getPort(ID::Y);
	int width = GetSize(Y);

	SigSpec EQ = st.pmux->getPort(ID::B).extract(st.pmux_slice_eq*width, width);
	SigSpec NE = st.pmux->getPort(ID::B).extract(st.pmux_slice_ne*width, width);

	log("Found eqpmux circuit driving %s (eq=%s, ne=%s, pmux=%s).\n",
			log_signal(Y), log_id(st.eq), log_id(st.ne), log_id(st.pmux));

	pm.autoremove(st.pmux);
	Cell *c = pm.module->addMux(NEW_ID, NE, EQ, st.eq->getPort(ID::Y), Y);
	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

#define GENERATE_PATTERN(pmclass, pattern) \
	generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)

void pmtest_addports(Module *module)
{
	pool<SigBit> driven_bits, used_bits;
	SigMap sigmap(module);
	int icnt = 0, ocnt = 0;

	for (auto cell : module->cells())
	for (auto conn : cell->connections())
	{
		if (cell->input(conn.first))
			for (auto bit : sigmap(conn.second))
				used_bits.insert(bit);
		if (cell->output(conn.first))
			for (auto bit : sigmap(conn.second))
				driven_bits.insert(bit);
	}

	for (auto wire : vector<Wire*>(module->wires()))
	{
		SigSpec ibits, obits;
		for (auto bit : sigmap(wire)) {
			if (!used_bits.count(bit))
				obits.append(bit);
			if (!driven_bits.count(bit))
				ibits.append(bit);
		}
		if (!ibits.empty()) {
			Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits));
			w->port_input = true;
			module->connect(ibits, w);
		}
		if (!obits.empty()) {
			Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits));
			w->port_output = true;
			module->connect(w, obits);
		}
	}

	module->fixup_ports();
}

template <class pm>
void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design)
{
	log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);

	int modcnt = 0;
	int maxmodcnt = 100;
	int maxsubcnt = 4;
	int timeout = 0;
	vector<Module*> mods;

	while (modcnt < maxmodcnt)
	{
		int submodcnt = 0, itercnt = 0, cellcnt = 0;
		Module *mod = design->addModule(NEW_ID);

		while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
		{
			if (timeout++ > 10000)
				log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");

			pm matcher(mod, mod->cells());

			matcher.rng(1);
			matcher.rngseed += modcnt;
			matcher.rng(1);
			matcher.rngseed += submodcnt;
			matcher.rng(1);
			matcher.rngseed += itercnt;
			matcher.rng(1);
			matcher.rngseed += cellcnt;
			matcher.rng(1);

			if (GetSize(mod->cells()) != cellcnt)
			{
				bool found_match = false;
				run(matcher, [&](){ found_match = true; });
				cellcnt = GetSize(mod->cells());

				if (found_match) {
					Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d",
							pmclass, pattern, modcnt++));
					log("Creating module %s with %d cells.\n", log_id(m), cellcnt);
					mod->cloneInto(m);
					pmtest_addports(m);
					mods.push_back(m);
					submodcnt++;
					timeout = 0;
				}
			}

			matcher.generate_mode = true;
			run(matcher, [](){});
		}

		if (submodcnt)
			maxsubcnt *= 2;

		design->remove(mod);
	}

	Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern));
	log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods));
	for (auto mod : mods) {
		Cell *c = m->addCell(mod->name, mod->name);
		for (auto port : mod->ports) {
			Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port)));
			c->setPort(port, w);
		}
	}
	pmtest_addports(m);
}

struct TestPmgenPass : public Pass {
	TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    test_pmgen -reduce_chain [options] [selection]\n");
		log("\n");
		log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n");
		log("\n");

		log("\n");
		log("    test_pmgen -reduce_tree [options] [selection]\n");
		log("\n");
		log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n");
		log("\n");

		log("\n");
		log("    test_pmgen -eqpmux [options] [selection]\n");
		log("\n");
		log("Demo for recursive pmgen patterns. Optimize EQ/NE/PMUX circuits.\n");
		log("\n");

		log("\n");
		log("    test_pmgen -generate [options] <pattern_name>\n");
		log("\n");
		log("Create modules that match the specified pattern.\n");
		log("\n");
	}

	void execute_reduce_chain(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-reduce_chain).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
			while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {}
	}

	void execute_reduce_tree(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-reduce_tree).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
			test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree);
	}

	void execute_eqpmux(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-eqpmux).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
			test_pmgen_pm(module, module->selected_cells()).run_eqpmux(opt_eqpmux);
	}

	void execute_generate(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-generate).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}

		if (argidx+1 != args.size())
			log_cmd_error("Expected exactly one pattern.\n");

		string pattern = args[argidx];

		if (pattern == "reduce")
			return GENERATE_PATTERN(test_pmgen_pm, reduce);

		if (pattern == "eqpmux")
			return GENERATE_PATTERN(test_pmgen_pm, eqpmux);

		if (pattern == "ice40_dsp")
			return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);

		if (pattern == "xilinx_srl_fixed")
			return GENERATE_PATTERN(xilinx_srl_pm, fixed);
		if (pattern == "xilinx_srl_variable")
			return GENERATE_PATTERN(xilinx_srl_pm, variable);

		if (pattern == "peepopt-muldiv")
			return GENERATE_PATTERN(peepopt_pm, muldiv);

		if (pattern == "peepopt-shiftmul")
			return GENERATE_PATTERN(peepopt_pm, shiftmul);

		log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
	}

	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		if (GetSize(args) > 1)
		{
			if (args[1] == "-reduce_chain")
				return execute_reduce_chain(args, design);
			if (args[1] == "-reduce_tree")
				return execute_reduce_tree(args, design);
			if (args[1] == "-eqpmux")
				return execute_eqpmux(args, design);
			if (args[1] == "-generate")
				return execute_generate(args, design);
		}
		help();
		log_cmd_error("Missing or unsupported mode parameter.\n");
	}
} TestPmgenPass;

PRIVATE_NAMESPACE_END
<commit_msg>Account for maxsubcnt overflowing<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

// for peepopt_pm
bool did_something;

#include "passes/pmgen/test_pmgen_pm.h"
#include "passes/pmgen/ice40_dsp_pm.h"
#include "passes/pmgen/xilinx_srl_pm.h"
#include "passes/pmgen/peepopt_pm.h"

void reduce_chain(test_pmgen_pm &pm)
{
	auto &st = pm.st_reduce;
	auto &ud = pm.ud_reduce;

	if (ud.longest_chain.empty())
		return;

	log("Found chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));

	SigSpec A;
	SigSpec Y = ud.longest_chain.front().first->getPort(ID(Y));
	auto last_cell = ud.longest_chain.back().first;

	for (auto it : ud.longest_chain) {
		auto cell = it.first;
		if (cell == last_cell) {
			A.append(cell->getPort(ID(A)));
			A.append(cell->getPort(ID(B)));
		} else {
			A.append(cell->getPort(it.second == ID(A) ? ID(B) : ID(A)));
		}
		log("    %s\n", log_id(cell));
		pm.autoremove(cell);
	}

	Cell *c;

	if (last_cell->type == ID($_AND_))
		c = pm.module->addReduceAnd(NEW_ID, A, Y);
	else if (last_cell->type == ID($_OR_))
		c = pm.module->addReduceOr(NEW_ID, A, Y);
	else if (last_cell->type == ID($_XOR_))
		c = pm.module->addReduceXor(NEW_ID, A, Y);
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

void reduce_tree(test_pmgen_pm &pm)
{
	auto &st = pm.st_reduce;
	auto &ud = pm.ud_reduce;

	if (ud.longest_chain.empty())
		return;

	SigSpec A = ud.leaves;
	SigSpec Y = st.first->getPort(ID(Y));
	pm.autoremove(st.first);

	log("Found %s tree with %d leaves for %s (%s).\n", log_id(st.first->type),
			GetSize(A), log_signal(Y), log_id(st.first));

	Cell *c;

	if (st.first->type == ID($_AND_))
		c = pm.module->addReduceAnd(NEW_ID, A, Y);
	else if (st.first->type == ID($_OR_))
		c = pm.module->addReduceOr(NEW_ID, A, Y);
	else if (st.first->type == ID($_XOR_))
		c = pm.module->addReduceXor(NEW_ID, A, Y);
	else
		log_abort();

	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

void opt_eqpmux(test_pmgen_pm &pm)
{
	auto &st = pm.st_eqpmux;

	SigSpec Y = st.pmux->getPort(ID::Y);
	int width = GetSize(Y);

	SigSpec EQ = st.pmux->getPort(ID::B).extract(st.pmux_slice_eq*width, width);
	SigSpec NE = st.pmux->getPort(ID::B).extract(st.pmux_slice_ne*width, width);

	log("Found eqpmux circuit driving %s (eq=%s, ne=%s, pmux=%s).\n",
			log_signal(Y), log_id(st.eq), log_id(st.ne), log_id(st.pmux));

	pm.autoremove(st.pmux);
	Cell *c = pm.module->addMux(NEW_ID, NE, EQ, st.eq->getPort(ID::Y), Y);
	log("    -> %s (%s)\n", log_id(c), log_id(c->type));
}

#define GENERATE_PATTERN(pmclass, pattern) \
	generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)

void pmtest_addports(Module *module)
{
	pool<SigBit> driven_bits, used_bits;
	SigMap sigmap(module);
	int icnt = 0, ocnt = 0;

	for (auto cell : module->cells())
	for (auto conn : cell->connections())
	{
		if (cell->input(conn.first))
			for (auto bit : sigmap(conn.second))
				used_bits.insert(bit);
		if (cell->output(conn.first))
			for (auto bit : sigmap(conn.second))
				driven_bits.insert(bit);
	}

	for (auto wire : vector<Wire*>(module->wires()))
	{
		SigSpec ibits, obits;
		for (auto bit : sigmap(wire)) {
			if (!used_bits.count(bit))
				obits.append(bit);
			if (!driven_bits.count(bit))
				ibits.append(bit);
		}
		if (!ibits.empty()) {
			Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits));
			w->port_input = true;
			module->connect(ibits, w);
		}
		if (!obits.empty()) {
			Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits));
			w->port_output = true;
			module->connect(w, obits);
		}
	}

	module->fixup_ports();
}

template <class pm>
void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design)
{
	log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);

	int modcnt = 0;
	int maxmodcnt = 100;
	int maxsubcnt = 4;
	int timeout = 0;
	vector<Module*> mods;

	while (modcnt < maxmodcnt)
	{
		int submodcnt = 0, itercnt = 0, cellcnt = 0;
		Module *mod = design->addModule(NEW_ID);

		while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
		{
			if (timeout++ > 10000)
				log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");

			pm matcher(mod, mod->cells());

			matcher.rng(1);
			matcher.rngseed += modcnt;
			matcher.rng(1);
			matcher.rngseed += submodcnt;
			matcher.rng(1);
			matcher.rngseed += itercnt;
			matcher.rng(1);
			matcher.rngseed += cellcnt;
			matcher.rng(1);

			if (GetSize(mod->cells()) != cellcnt)
			{
				bool found_match = false;
				run(matcher, [&](){ found_match = true; });
				cellcnt = GetSize(mod->cells());

				if (found_match) {
					Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d",
							pmclass, pattern, modcnt++));
					log("Creating module %s with %d cells.\n", log_id(m), cellcnt);
					mod->cloneInto(m);
					pmtest_addports(m);
					mods.push_back(m);
					submodcnt++;
					timeout = 0;
				}
			}

			matcher.generate_mode = true;
			run(matcher, [](){});
		}

		if (submodcnt && maxsubcnt < (1 << 16))
			maxsubcnt *= 2;

		design->remove(mod);
	}

	Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern));
	log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods));
	for (auto mod : mods) {
		Cell *c = m->addCell(mod->name, mod->name);
		for (auto port : mod->ports) {
			Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port)));
			c->setPort(port, w);
		}
	}
	pmtest_addports(m);
}

struct TestPmgenPass : public Pass {
	TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    test_pmgen -reduce_chain [options] [selection]\n");
		log("\n");
		log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n");
		log("\n");

		log("\n");
		log("    test_pmgen -reduce_tree [options] [selection]\n");
		log("\n");
		log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n");
		log("\n");

		log("\n");
		log("    test_pmgen -eqpmux [options] [selection]\n");
		log("\n");
		log("Demo for recursive pmgen patterns. Optimize EQ/NE/PMUX circuits.\n");
		log("\n");

		log("\n");
		log("    test_pmgen -generate [options] <pattern_name>\n");
		log("\n");
		log("Create modules that match the specified pattern.\n");
		log("\n");
	}

	void execute_reduce_chain(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-reduce_chain).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
			while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {}
	}

	void execute_reduce_tree(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-reduce_tree).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
			test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree);
	}

	void execute_eqpmux(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-eqpmux).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
			test_pmgen_pm(module, module->selected_cells()).run_eqpmux(opt_eqpmux);
	}

	void execute_generate(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing TEST_PMGEN pass (-generate).\n");

		size_t argidx;
		for (argidx = 2; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-singleton") {
			// 	singleton_mode = true;
			// 	continue;
			// }
			break;
		}

		if (argidx+1 != args.size())
			log_cmd_error("Expected exactly one pattern.\n");

		string pattern = args[argidx];

		if (pattern == "reduce")
			return GENERATE_PATTERN(test_pmgen_pm, reduce);

		if (pattern == "eqpmux")
			return GENERATE_PATTERN(test_pmgen_pm, eqpmux);

		if (pattern == "ice40_dsp")
			return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);

		if (pattern == "xilinx_srl_fixed")
			return GENERATE_PATTERN(xilinx_srl_pm, fixed);
		if (pattern == "xilinx_srl_variable")
			return GENERATE_PATTERN(xilinx_srl_pm, variable);

		if (pattern == "peepopt-muldiv")
			return GENERATE_PATTERN(peepopt_pm, muldiv);

		if (pattern == "peepopt-shiftmul")
			return GENERATE_PATTERN(peepopt_pm, shiftmul);

		log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
	}

	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		if (GetSize(args) > 1)
		{
			if (args[1] == "-reduce_chain")
				return execute_reduce_chain(args, design);
			if (args[1] == "-reduce_tree")
				return execute_reduce_tree(args, design);
			if (args[1] == "-eqpmux")
				return execute_eqpmux(args, design);
			if (args[1] == "-generate")
				return execute_generate(args, design);
		}
		help();
		log_cmd_error("Missing or unsupported mode parameter.\n");
	}
} TestPmgenPass;

PRIVATE_NAMESPACE_END
<|endoftext|>


<commit_before>/*
 * Nouncer Implementation
 */

#include "nouncer.h"

/*
 * Assembles the map<word,phoneme> dictionary by reading one line at a time,
 * separating the word from the pronunciation, encoding the phoneme, and
 * putting the pair in the map
 */

std::vector<char> Nouncer::cons = {
  '9',':',';','<','I','J','K','T','U','V','W',
  'X','Y','h','i','j','k','l','m','v','w','y','z','{'
};

Nouncer::Nouncer() {

  dict = new std::map<std::string, std::string>;

  std::ifstream file(DICTIONARY_FILE);

  if (file) {
    std::string line;

    while (std::getline(file,line)) {
      if (line.substr(0,3) == COMMENT_HEAD) {
        continue;
      }
      else {
        addWord(line);
      }
    }
  }
  else {
    std::cerr << "Unable to load dictionary" << std::endl;
  }
}

Nouncer::~Nouncer() {
  dict->erase(dict->begin(), dict->end());
  delete dict;
}

/*
 * Given a string, return a pointer to the associated nounce
 * or the nounce of "gravy" if the string is not found.
 */
std::string* Nouncer::lookUp(std::string word) {
  try {
    return &(dict->at(Utils::allCaps(word)));
  }
  catch (const std::out_of_range& oor) {
    return &(dict->at("GRAVY"));
  }
}

/*
 * TODO: Let's encode outside this and pass in the nounce
 *
 * Maps a phoneme dictionary entry to a
 * <std::string word, std::string nounce> pair
 * and inserts in the dictionary.
 */
void Nouncer::addWord(std::string line) {
  std::istringstream ss(line);
  std::string word;
  std::string nounce;
  std::string phoneme;

  ss >> word;

  while (ss >> phoneme) {
    char encoded_phoneme = encode(phoneme);
    nounce.push_back(encoded_phoneme);
  }

  std::reverse(nounce.begin(), nounce.end());

  std::pair<std::string, std::string> word_phonemes(word, nounce);
  dict->insert(word_phonemes);
}

/*
 * Encodes the phoneme string as a nounce
 */
char Nouncer::encode(std::string phoneme) {
  char phone = '\0';

  if      (phoneme == "AA")  phone = ' ';
  else if (phoneme == "AA0") phone = '!';
  else if (phoneme == "AA1") phone = '\"';
  else if (phoneme == "AA2") phone = '#';
  else if (phoneme == "AE")  phone = '$';
  else if (phoneme == "AE0") phone = '%';
  else if (phoneme == "AE1") phone = '&';
  else if (phoneme == "AE2") phone = '(';
  else if (phoneme == "AH")  phone = ')';
  else if (phoneme == "AH0") phone = '*';
  else if (phoneme == "AH1") phone = '+';
  else if (phoneme == "AH2") phone = ',';
  else if (phoneme == "AO")  phone = '-';
  else if (phoneme == "AO0") phone = '.';
  else if (phoneme == "AO1") phone = '/';
  else if (phoneme == "AO2") phone = '0';
  else if (phoneme == "AW")  phone = '1';
  else if (phoneme == "AW0") phone = '2';
  else if (phoneme == "AW1") phone = '3';
  else if (phoneme == "AW2") phone = '4';
  else if (phoneme == "AY")  phone = '5';
  else if (phoneme == "AY0") phone = '6';
  else if (phoneme == "AY1") phone = '7';
  else if (phoneme == "AY2") phone = '8';
  else if (phoneme == "B")   phone = '9';
  else if (phoneme == "CH")  phone = ':';
  else if (phoneme == "D")   phone = ';';
  else if (phoneme == "DH")  phone = '<';
  else if (phoneme == "EH")  phone = '=';
  else if (phoneme == "EH0") phone = '>';
  else if (phoneme == "EH1") phone = '?';
  else if (phoneme == "EH2") phone = '@';
  else if (phoneme == "ER")  phone = 'A';
  else if (phoneme == "ER0") phone = 'B';
  else if (phoneme == "ER1") phone = 'C';
  else if (phoneme == "ER2") phone = 'D';
  else if (phoneme == "EY")  phone = 'E';
  else if (phoneme == "EY0") phone = 'F';
  else if (phoneme == "EY1") phone = 'G';
  else if (phoneme == "EY2") phone = 'H';
  else if (phoneme == "F")   phone = 'I';
  else if (phoneme == "G")   phone = 'J';
  else if (phoneme == "HH")  phone = 'K';
  else if (phoneme == "IH")  phone = 'L';
  else if (phoneme == "IH0") phone = 'M';
  else if (phoneme == "IH1") phone = 'N';
  else if (phoneme == "IH2") phone = 'O';
  else if (phoneme == "IY")  phone = 'P';
  else if (phoneme == "IY0") phone = 'Q';
  else if (phoneme == "IY1") phone = 'R';
  else if (phoneme == "IY2") phone = 'S';
  else if (phoneme == "JH")  phone = 'T';
  else if (phoneme == "K")   phone = 'U';
  else if (phoneme == "L")   phone = 'V';
  else if (phoneme == "M")   phone = 'W';
  else if (phoneme == "N")   phone = 'X';
  else if (phoneme == "NG")  phone = 'Y';
  else if (phoneme == "OW")  phone = 'Z';
  else if (phoneme == "OW0") phone = 'a';
  else if (phoneme == "OW1") phone = 'b';
  else if (phoneme == "OW2") phone = 'c';
  else if (phoneme == "OY")  phone = 'd';
  else if (phoneme == "OY0") phone = 'e';
  else if (phoneme == "OY1") phone = 'f';
  else if (phoneme == "OY2") phone = 'g';
  else if (phoneme == "P")   phone = 'h';
  else if (phoneme == "R")   phone = 'i';
  else if (phoneme == "S")   phone = 'j';
  else if (phoneme == "SH")  phone = 'k';
  else if (phoneme == "T")   phone = 'l';
  else if (phoneme == "TH")  phone = 'm';
  else if (phoneme == "UH")  phone = 'n';
  else if (phoneme == "UH0") phone = 'o';
  else if (phoneme == "UH1") phone = 'p';
  else if (phoneme == "UH2") phone = 'q';
  else if (phoneme == "UW")  phone = 'r';
  else if (phoneme == "UW0") phone = 's';
  else if (phoneme == "UW1") phone = 't';
  else if (phoneme == "UW2") phone = 'u';
  else if (phoneme == "V")   phone = 'v';
  else if (phoneme == "W")   phone = 'w';
  else if (phoneme == "Y")   phone = 'y';
  else if (phoneme == "Z")   phone = 'z';
  else if (phoneme == "ZH")  phone = '{';
  else
    std::cerr << "Could not identify phoneme " << phoneme << std::endl;

  return phone;
}

bool Nouncer::isVowel(char& phone) {
  return !(std::find(cons.begin(),cons.end(),phone) != cons.end())
}

int Nouncer::getSylCount(std::string word) {
  int count = 0;
  std::string* nounce = lookUp(word);

  for (auto i = nounce->begin(); i != nounce->end(); ++i) {
    if ( isVowel(*i) ) count++;
  }

  return count;
}

int Nouncer::getSize() {
  return dict->size();
}

void Nouncer::print(std::string filename) {
  std::ofstream of(filename);
  for (auto it = dict->begin(); it!=dict->end(); ++it) {
    of<<(*it).first<<":"<<(*it).second<<std::endl;
  }
}
<commit_msg>clean up x2<commit_after>/*
 * Nouncer Implementation
 */

#include "nouncer.h"

/*
 * Assembles the map<word,phoneme> dictionary by reading one line at a time,
 * separating the word from the pronunciation, encoding the phoneme, and
 * putting the pair in the map
 */

std::vector<char> Nouncer::cons = {
  '9',':',';','<','I','J','K','T','U','V','W',
  'X','Y','h','i','j','k','l','m','v','w','y','z','{'
};

Nouncer::Nouncer() {

  dict = new std::map<std::string, std::string>;

  std::ifstream file(DICTIONARY_FILE);

  if (file) {
    std::string line;

    while (std::getline(file,line)) {
      if (line.substr(0,3) == COMMENT_HEAD) {
        continue;
      }
      else {
        addWord(line);
      }
    }
  }
  else {
    std::cerr << "Unable to load dictionary" << std::endl;
  }
}

Nouncer::~Nouncer() {
  dict->erase(dict->begin(), dict->end());
  delete dict;
}

/*
 * Given a string, return a pointer to the associated nounce
 * or the nounce of "gravy" if the string is not found.
 */
std::string* Nouncer::lookUp(std::string word) {
  try {
    return &(dict->at(Utils::allCaps(word)));
  }
  catch (const std::out_of_range& oor) {
    return &(dict->at("GRAVY"));
  }
}

/*
 * TODO: Let's encode outside this and pass in the nounce
 *
 * Maps a phoneme dictionary entry to a
 * <std::string word, std::string nounce> pair
 * and inserts in the dictionary.
 */
void Nouncer::addWord(std::string line) {
  std::istringstream ss(line);
  std::string word;
  std::string nounce;
  std::string phoneme;

  ss >> word;

  while (ss >> phoneme) {
    char encoded_phoneme = encode(phoneme);
    nounce.push_back(encoded_phoneme);
  }

  std::reverse(nounce.begin(), nounce.end());

  std::pair<std::string, std::string> word_phonemes(word, nounce);
  dict->insert(word_phonemes);
}

/*
 * Encodes the phoneme string as a nounce
 */
char Nouncer::encode(std::string phoneme) {
  char phone = '\0';

  if      (phoneme == "AA")  phone = ' ';
  else if (phoneme == "AA0") phone = '!';
  else if (phoneme == "AA1") phone = '\"';
  else if (phoneme == "AA2") phone = '#';
  else if (phoneme == "AE")  phone = '$';
  else if (phoneme == "AE0") phone = '%';
  else if (phoneme == "AE1") phone = '&';
  else if (phoneme == "AE2") phone = '(';
  else if (phoneme == "AH")  phone = ')';
  else if (phoneme == "AH0") phone = '*';
  else if (phoneme == "AH1") phone = '+';
  else if (phoneme == "AH2") phone = ',';
  else if (phoneme == "AO")  phone = '-';
  else if (phoneme == "AO0") phone = '.';
  else if (phoneme == "AO1") phone = '/';
  else if (phoneme == "AO2") phone = '0';
  else if (phoneme == "AW")  phone = '1';
  else if (phoneme == "AW0") phone = '2';
  else if (phoneme == "AW1") phone = '3';
  else if (phoneme == "AW2") phone = '4';
  else if (phoneme == "AY")  phone = '5';
  else if (phoneme == "AY0") phone = '6';
  else if (phoneme == "AY1") phone = '7';
  else if (phoneme == "AY2") phone = '8';
  else if (phoneme == "B")   phone = '9';
  else if (phoneme == "CH")  phone = ':';
  else if (phoneme == "D")   phone = ';';
  else if (phoneme == "DH")  phone = '<';
  else if (phoneme == "EH")  phone = '=';
  else if (phoneme == "EH0") phone = '>';
  else if (phoneme == "EH1") phone = '?';
  else if (phoneme == "EH2") phone = '@';
  else if (phoneme == "ER")  phone = 'A';
  else if (phoneme == "ER0") phone = 'B';
  else if (phoneme == "ER1") phone = 'C';
  else if (phoneme == "ER2") phone = 'D';
  else if (phoneme == "EY")  phone = 'E';
  else if (phoneme == "EY0") phone = 'F';
  else if (phoneme == "EY1") phone = 'G';
  else if (phoneme == "EY2") phone = 'H';
  else if (phoneme == "F")   phone = 'I';
  else if (phoneme == "G")   phone = 'J';
  else if (phoneme == "HH")  phone = 'K';
  else if (phoneme == "IH")  phone = 'L';
  else if (phoneme == "IH0") phone = 'M';
  else if (phoneme == "IH1") phone = 'N';
  else if (phoneme == "IH2") phone = 'O';
  else if (phoneme == "IY")  phone = 'P';
  else if (phoneme == "IY0") phone = 'Q';
  else if (phoneme == "IY1") phone = 'R';
  else if (phoneme == "IY2") phone = 'S';
  else if (phoneme == "JH")  phone = 'T';
  else if (phoneme == "K")   phone = 'U';
  else if (phoneme == "L")   phone = 'V';
  else if (phoneme == "M")   phone = 'W';
  else if (phoneme == "N")   phone = 'X';
  else if (phoneme == "NG")  phone = 'Y';
  else if (phoneme == "OW")  phone = 'Z';
  else if (phoneme == "OW0") phone = 'a';
  else if (phoneme == "OW1") phone = 'b';
  else if (phoneme == "OW2") phone = 'c';
  else if (phoneme == "OY")  phone = 'd';
  else if (phoneme == "OY0") phone = 'e';
  else if (phoneme == "OY1") phone = 'f';
  else if (phoneme == "OY2") phone = 'g';
  else if (phoneme == "P")   phone = 'h';
  else if (phoneme == "R")   phone = 'i';
  else if (phoneme == "S")   phone = 'j';
  else if (phoneme == "SH")  phone = 'k';
  else if (phoneme == "T")   phone = 'l';
  else if (phoneme == "TH")  phone = 'm';
  else if (phoneme == "UH")  phone = 'n';
  else if (phoneme == "UH0") phone = 'o';
  else if (phoneme == "UH1") phone = 'p';
  else if (phoneme == "UH2") phone = 'q';
  else if (phoneme == "UW")  phone = 'r';
  else if (phoneme == "UW0") phone = 's';
  else if (phoneme == "UW1") phone = 't';
  else if (phoneme == "UW2") phone = 'u';
  else if (phoneme == "V")   phone = 'v';
  else if (phoneme == "W")   phone = 'w';
  else if (phoneme == "Y")   phone = 'y';
  else if (phoneme == "Z")   phone = 'z';
  else if (phoneme == "ZH")  phone = '{';
  else
    std::cerr << "Could not identify phoneme " << phoneme << std::endl;

  return phone;
}

bool Nouncer::isVowel(char& phone) {
  return !(std::find(cons.begin(),cons.end(),phone) != cons.end());
}

int Nouncer::getSylCount(std::string word) {
  int count = 0;
  std::string* nounce = lookUp(word);

  for (auto i = nounce->begin(); i != nounce->end(); ++i) {
    if ( isVowel(*i) ) count++;
  }

  return count;
}

int Nouncer::getSize() {
  return dict->size();
}

void Nouncer::print(std::string filename) {
  std::ofstream of(filename);
  for (auto it = dict->begin(); it!=dict->end(); ++it) {
    of<<(*it).first<<":"<<(*it).second<<std::endl;
  }
}
<|endoftext|>


<commit_before>/***************************************************************************
*    ALMA - Atacama Large Millimiter Array
*    (c) European Southern Observatory, 2002
*    Copyright by ESO (in the framework of the ALMA collaboration)
*    and Cosylab 2002, All rights reserved
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation; either
*    version 2.1 of the License, or (at your option) any later version.
*
*    This library 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
*    Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public
*    License along with this library; if not, write to the Free Software
*    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
*    MA 02111-1307  USA
*
* "@(#) $Id: enumpropTestServer.cpp,v 1.45 2005/10/07 13:59:57 bjeram Exp $"
*
* who       when      what
* --------  --------  ----------------------------------------------
* david 2002-08-15 changed argUnpack.h to acsutilArgUnpack.h
* bjeram 2001-12-03 created
*/

#include "vltPort.h"

static char *rcsId="@(#) $Id: enumpropTestServer.cpp,v 1.45 2005/10/07 13:59:57 bjeram Exp $";
static void *use_rcsId = ((void)&use_rcsId,(void *) &rcsId);

#include <iostream>

#include <acsutil.h>
#include <acsutilPorts.h>

#include <baci.h>
#include <baciDB.h>
#include <maciContainerServices.h>

#include "enumpropTestDeviceImpl.h"

#ifdef MAKE_VXWORKS
#	include "rebootLib.h"
#	include "acsutilArgUnpack.h"
#endif

using namespace ACS;
using namespace baci;

// We need an implementation of the ContainerServices here because
// 1. the component fails if the pointer to the ContainerServices it receives
//    in the constructor is NULL
// 2. the smart pointer used to store the ContainerServices deletes all
//    the not-NULL instances of the Container Services
// This clla implements all the methods of the interface but does nothing
class TestContainerServices : public maci::ContainerServices {
 public:
    CORBA::ORB_var m_orb;

    TestContainerServices(ACE_CString& compName, PortableServer::POA_ptr poa, CORBA::ORB_ptr orb):
        maci::ContainerServices(compName,poa), m_orb(CORBA::ORB::_duplicate(orb)) {
            ACS_SHORT_LOG((LM_INFO,"TestContainerServices built"));
        }

        virtual ~TestContainerServices() {
            ACS_SHORT_LOG((LM_INFO,"TestContainerServices destroyed"));
        }

        CORBA::Object* getCORBAComponent(const char* name)
        {
            return (CORBA::Object*)NULL;
        }

        CORBA::Object* getCORBADynamicComponent(maci::ComponentSpec compSpec, bool markAsDefault)
        {
            return (CORBA::Object*)NULL;
        }

        CORBA::Object* getCORBADefaultComponent(const char* idlType)
        {
            return (CORBA::Object*)NULL;
        }

    public:

        maci::ComponentInfo getComponentDescriptor(const char* componentName)
          throw (acsErrTypeContainerServices::GettingCompInfoExImpl)
          {
            ComponentInfo temp;
            return temp;
          }

        ACE_CString_Vector findComponents(const char *nameWilcard, const char *typeWildcard)
        {
          return ACE_CString_Vector();
        }

        void releaseComponent(const char *name){}

        void releaseAllComponents(){}

        CDB::DAL_ptr getCDB()
        {
	    ACE_TCHAR corbalocRef[230];
	    ACE_TCHAR * envRef = ACE_OS::getenv ("DAL_REFERENCE");

	    if (envRef && *envRef)
		{
		ACS_LOG(0, "TestContainerServices::getCDB",
			(LM_INFO, "CDB obtained via environment: '%s'", envRef));
		strcpy(corbalocRef, envRef);
		}
	    else
		{
	    // corbaloc::<hostname>:<port>/CDB
	    const char* hostname = 0;
	    hostname = ACSPorts::getIP();
	    if (hostname==0)
		return (CDB::DAL *)0;

	    ACE_TCHAR corbalocRef[230];
	    ACE_OS::sprintf(corbalocRef, "corbaloc::%s:%s/CDB", hostname, ACSPorts::getCDBPort().c_str());

	    ACS_LOG(0, "TestContainerServices::getCDB",
		    (LM_INFO, "CDB reference generated using localhost address: '%s'", corbalocRef));
		}//if-else

	    CDB::DAL_var dalObj = CDB::DAL::_nil();
	    CORBA::Object_var obj = m_orb->string_to_object(corbalocRef);

	    if (!CORBA::is_nil(obj.in()))
		{
		dalObj = CDB::DAL::_narrow(obj.in());
		if (CORBA::is_nil(dalObj.in()))
		    {
		    ACS_SHORT_LOG((LM_INFO, "TestContainerServices::getCDB() - Failed to narrow CDB"));
		    return (CDB::DAL *)0;
		    }
		}

	    return dalObj._retn();
        }

        PortableServer::POA_var getOffShootPOA()
        {
          return NULL;
        }

        ACS::OffShoot_ptr activateOffShoot(PortableServer::Servant cbServant)
        {
          return NULL;
        }

        void deactivateOffShoot(PortableServer::Servant cbServant)
          throw (
           acsErrTypeContainerServices::OffShootDeactivationExImpl,
           acsErrTypeContainerServices::OffShootPOAExImpl){}

        PortableServer::POA_var createOffShootPOA()
        {
          return NULL;
        }

        ComponentStateManager* getComponentStateManager()
        {
          return NULL;
        }
};

bool EPshutting_down = false;

void TerminationSignalHandler(int)
{
    if (EPshutting_down) return;
    EPshutting_down=true;

    // initialize logger (this is a new thread)
    LoggingProxy EP_log (0, 0, 31, 0);
    LoggingProxy::init (&EP_log);

    ACS_LOG(0, "termination", (LM_INFO, "Termination signal detected."));

    try
	{
	// false - avoid deadlock; true would try to wait for all requests
	// to complete before returning, but because we are calling it from within
	// a request, we would be blocking it from
	  BACI_CORBA::getORB()->shutdown(false);

	}
    catch( CORBA::Exception &ex )
	{
	ACE_PRINT_EXCEPTION( ex, "TerminationSignalHandler");
	}
}

int main(int l_argc, char* l_argv[])
{

    // create logging proxy

    LoggingProxy EP_log (0, 0, 31, 0);
    LoggingProxy::init (&EP_log);
    LoggingProxy::ProcessName(l_argv[0]);
    ACS_SHORT_LOG((LM_INFO, "Logging proxy successfully created !"));

    ACS_SHORT_LOG((LM_INFO, "enumpropTestServer: Starting"));
    if (l_argc < 2) {
       ACS_SHORT_LOG((LM_INFO,"Usage: enumpropTestServer <device_name>"));
      return -1;
    }

    // Installs termination signal handlers, to be able
    // to terminate cleanly
    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Installing signal handlers"));
    ACE_OS::signal(SIGINT,  TerminationSignalHandler);  // Ctrl+C
    ACE_OS::signal(SIGTERM, TerminationSignalHandler);  // termination request

    int   devCount = l_argc-1;

    CORBA::String_var ior;
    enumpropTestDeviceImpl *ep_servant;
    ENUMPROP_TEST::enumpropTestDevice_var ep;

    int count = 0;
    char fileName[64];
    FILE *IOR_file;

    try {

    // Initialize the ORB.
    BACI_CORBA::InitCORBA(l_argc, l_argv);

    if (!ACSError::init (BACI_CORBA::getORB()))
	{
	ACS_LOG(LM_RUNTIME_CONTEXT, "baciTestServer",
		(LM_INFO, "Failed to initialize ACSError."));
	return -1;
	}

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Init DB"));

    DBConnector::initDB(":Appl_data:alma");

    // Instantiate the CBDPropertySet singleton
    if (DBConnector::getDBTable())
	CDBPropertySet::createInstance(BACI_CORBA::getORB(),
				       BACI_CORBA::getPOAManager(),
				       BACI_CORBA::getPOARoot());

    for (int n=1; n <= devCount; n++)
	{

	ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Exporting \"%s\"", l_argv[n]));

    // Build the implementation of the containerServices to pass to the constructor
    // of the component (the component will free its memory)
    ACE_CString name(l_argv[n]);
    TestContainerServices* pCS = new TestContainerServices(name,BACI_CORBA::getPOA(),BACI_CORBA::getORB());
	ep_servant = new enumpropTestDeviceImpl(name,pCS);
	ACS_SHORT_LOG((LM_INFO, "enumpropTestDevice: %s created", l_argv[n]));
	ep = ep_servant->_this();
	ep_servant->_remove_ref();

	ior = BACI_CORBA::getORB()->object_to_string (ep.ptr());

	ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Writing IOR for %s into file...", l_argv[n]));
	sprintf(fileName, "%s.ior", name.c_str());
	IOR_file = ACE_OS::fopen (fileName, "w");
	if (IOR_file == 0)
    {
	   ACS_SHORT_LOG ((LM_ERROR,
		  "Cannot open output files for writing IOR: %s", fileName));
	   return  -1;
	}

	int result = ACE_OS::fprintf (IOR_file, "%s",  ior.in());
	if (result < 0)
	    {
	    ACS_SHORT_LOG ((LM_ERROR,
			    "ACE_OS::fprintf failed while writing %s into %s\n", ior.in(), fileName));
	    return  -1;
	    }
	ACE_OS::fclose (IOR_file);
	ACS_SHORT_LOG((LM_INFO, "enumpropTestDevice: %s exported", l_argv[n]));

	count++;

//	PortableServer::ObjectId_var oid = BACI_CORBA::getPOA()->reference_to_id(ep.ptr());
//	BACI_CORBA::getPOA()->deactivate_object(oid.in());

	}//for

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Waiting for requests ... "));
    BACI_CORBA::getORB()->run();

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Exiting."));

    ep_servant->_remove_ref();

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Releasing CORBA."));

    BACI_CORBA::DoneCORBA();

    DBConnector::closeDB();

    //delete t;

    LoggingProxy::done();

    std::cout << std::flush;  // shold be done by: LoggingProxy::done()

    return 0;

    }catch(CORBA::Exception &ex) {
      ACS_SHORT_LOG((LM_INFO, "enumpropTestServer: Exceptions raised while creating Component"));
      return -1;
    }
    return  0;
}

#ifdef MAKE_VXWORKS

int startEnumpropTestServer (char *szCmdLn)
{
  int  l_argc;
  char *l_argv[100];

//  ACE_MAIN_OBJECT_MANAGER;

  l_argc = argUnpack(szCmdLn, l_argv);
  l_argv[0] = "enumpropTestServer";

  return ace_main_i(l_argc, l_argv);
}
#endif

<commit_msg>added getCORBACollocatedComponent<commit_after>/***************************************************************************
*    ALMA - Atacama Large Millimiter Array
*    (c) European Southern Observatory, 2002
*    Copyright by ESO (in the framework of the ALMA collaboration)
*    and Cosylab 2002, All rights reserved
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation; either
*    version 2.1 of the License, or (at your option) any later version.
*
*    This library 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
*    Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public
*    License along with this library; if not, write to the Free Software
*    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
*    MA 02111-1307  USA
*
* "@(#) $Id: enumpropTestServer.cpp,v 1.46 2006/06/13 09:18:16 bjeram Exp $"
*
* who       when      what
* --------  --------  ----------------------------------------------
* david 2002-08-15 changed argUnpack.h to acsutilArgUnpack.h
* bjeram 2001-12-03 created
*/

#include "vltPort.h"

static char *rcsId="@(#) $Id: enumpropTestServer.cpp,v 1.46 2006/06/13 09:18:16 bjeram Exp $";
static void *use_rcsId = ((void)&use_rcsId,(void *) &rcsId);

#include <iostream>

#include <acsutil.h>
#include <acsutilPorts.h>

#include <baci.h>
#include <baciDB.h>
#include <maciContainerServices.h>

#include "enumpropTestDeviceImpl.h"

#ifdef MAKE_VXWORKS
#	include "rebootLib.h"
#	include "acsutilArgUnpack.h"
#endif

using namespace ACS;
using namespace baci;

// We need an implementation of the ContainerServices here because
// 1. the component fails if the pointer to the ContainerServices it receives
//    in the constructor is NULL
// 2. the smart pointer used to store the ContainerServices deletes all
//    the not-NULL instances of the Container Services
// This clla implements all the methods of the interface but does nothing
class TestContainerServices : public maci::ContainerServices {
 public:
    CORBA::ORB_var m_orb;

    TestContainerServices(ACE_CString& compName, PortableServer::POA_ptr poa, CORBA::ORB_ptr orb):
        maci::ContainerServices(compName,poa), m_orb(CORBA::ORB::_duplicate(orb)) {
            ACS_SHORT_LOG((LM_INFO,"TestContainerServices built"));
        }

        virtual ~TestContainerServices() {
            ACS_SHORT_LOG((LM_INFO,"TestContainerServices destroyed"));
        }

        CORBA::Object* getCORBAComponent(const char* name)
        {
            return (CORBA::Object*)NULL;
        }

        CORBA::Object* getCORBADynamicComponent(maci::ComponentSpec compSpec, bool markAsDefault)
        {
            return (CORBA::Object*)NULL;
        }

        CORBA::Object* getCORBADefaultComponent(const char* idlType)
        {
            return (CORBA::Object*)NULL;
        }

    virtual CORBA::Object* getCORBACollocatedComponent(maci::ComponentSpec, bool, const char*)
	{
	    return (CORBA::Object*)NULL;
	}

    public:

        maci::ComponentInfo getComponentDescriptor(const char* componentName)
          throw (acsErrTypeContainerServices::GettingCompInfoExImpl)
          {
            ComponentInfo temp;
            return temp;
          }

        ACE_CString_Vector findComponents(const char *nameWilcard, const char *typeWildcard)
        {
          return ACE_CString_Vector();
        }

        void releaseComponent(const char *name){}

        void releaseAllComponents(){}

        CDB::DAL_ptr getCDB()
        {
	    ACE_TCHAR corbalocRef[230];
	    ACE_TCHAR * envRef = ACE_OS::getenv ("DAL_REFERENCE");

	    if (envRef && *envRef)
		{
		ACS_LOG(0, "TestContainerServices::getCDB",
			(LM_INFO, "CDB obtained via environment: '%s'", envRef));
		strcpy(corbalocRef, envRef);
		}
	    else
		{
	    // corbaloc::<hostname>:<port>/CDB
	    const char* hostname = 0;
	    hostname = ACSPorts::getIP();
	    if (hostname==0)
		return (CDB::DAL *)0;

	    ACE_TCHAR corbalocRef[230];
	    ACE_OS::sprintf(corbalocRef, "corbaloc::%s:%s/CDB", hostname, ACSPorts::getCDBPort().c_str());

	    ACS_LOG(0, "TestContainerServices::getCDB",
		    (LM_INFO, "CDB reference generated using localhost address: '%s'", corbalocRef));
		}//if-else

	    CDB::DAL_var dalObj = CDB::DAL::_nil();
	    CORBA::Object_var obj = m_orb->string_to_object(corbalocRef);

	    if (!CORBA::is_nil(obj.in()))
		{
		dalObj = CDB::DAL::_narrow(obj.in());
		if (CORBA::is_nil(dalObj.in()))
		    {
		    ACS_SHORT_LOG((LM_INFO, "TestContainerServices::getCDB() - Failed to narrow CDB"));
		    return (CDB::DAL *)0;
		    }
		}

	    return dalObj._retn();
        }

        PortableServer::POA_var getOffShootPOA()
        {
          return NULL;
        }

        ACS::OffShoot_ptr activateOffShoot(PortableServer::Servant cbServant)
        {
          return NULL;
        }

        void deactivateOffShoot(PortableServer::Servant cbServant)
          throw (
           acsErrTypeContainerServices::OffShootDeactivationExImpl,
           acsErrTypeContainerServices::OffShootPOAExImpl){}

        PortableServer::POA_var createOffShootPOA()
        {
          return NULL;
        }

        ComponentStateManager* getComponentStateManager()
        {
          return NULL;
        }
};

bool EPshutting_down = false;

void TerminationSignalHandler(int)
{
    if (EPshutting_down) return;
    EPshutting_down=true;

    // initialize logger (this is a new thread)
    LoggingProxy EP_log (0, 0, 31, 0);
    LoggingProxy::init (&EP_log);

    ACS_LOG(0, "termination", (LM_INFO, "Termination signal detected."));

    try
	{
	// false - avoid deadlock; true would try to wait for all requests
	// to complete before returning, but because we are calling it from within
	// a request, we would be blocking it from
	  BACI_CORBA::getORB()->shutdown(false);

	}
    catch( CORBA::Exception &ex )
	{
	ACE_PRINT_EXCEPTION( ex, "TerminationSignalHandler");
	}
}

int main(int l_argc, char* l_argv[])
{

    // create logging proxy

    LoggingProxy EP_log (0, 0, 31, 0);
    LoggingProxy::init (&EP_log);
    LoggingProxy::ProcessName(l_argv[0]);
    ACS_SHORT_LOG((LM_INFO, "Logging proxy successfully created !"));

    ACS_SHORT_LOG((LM_INFO, "enumpropTestServer: Starting"));
    if (l_argc < 2) {
       ACS_SHORT_LOG((LM_INFO,"Usage: enumpropTestServer <device_name>"));
      return -1;
    }

    // Installs termination signal handlers, to be able
    // to terminate cleanly
    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Installing signal handlers"));
    ACE_OS::signal(SIGINT,  TerminationSignalHandler);  // Ctrl+C
    ACE_OS::signal(SIGTERM, TerminationSignalHandler);  // termination request

    int   devCount = l_argc-1;

    CORBA::String_var ior;
    enumpropTestDeviceImpl *ep_servant;
    ENUMPROP_TEST::enumpropTestDevice_var ep;

    int count = 0;
    char fileName[64];
    FILE *IOR_file;

    try {

    // Initialize the ORB.
    BACI_CORBA::InitCORBA(l_argc, l_argv);

    if (!ACSError::init (BACI_CORBA::getORB()))
	{
	ACS_LOG(LM_RUNTIME_CONTEXT, "baciTestServer",
		(LM_INFO, "Failed to initialize ACSError."));
	return -1;
	}

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Init DB"));

    DBConnector::initDB(":Appl_data:alma");

    // Instantiate the CBDPropertySet singleton
    if (DBConnector::getDBTable())
	CDBPropertySet::createInstance(BACI_CORBA::getORB(),
				       BACI_CORBA::getPOAManager(),
				       BACI_CORBA::getPOARoot());

    for (int n=1; n <= devCount; n++)
	{

	ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Exporting \"%s\"", l_argv[n]));

    // Build the implementation of the containerServices to pass to the constructor
    // of the component (the component will free its memory)
    ACE_CString name(l_argv[n]);
    TestContainerServices* pCS = new TestContainerServices(name,BACI_CORBA::getPOA(),BACI_CORBA::getORB());
	ep_servant = new enumpropTestDeviceImpl(name,pCS);
	ACS_SHORT_LOG((LM_INFO, "enumpropTestDevice: %s created", l_argv[n]));
	ep = ep_servant->_this();
	ep_servant->_remove_ref();

	ior = BACI_CORBA::getORB()->object_to_string (ep.ptr());

	ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Writing IOR for %s into file...", l_argv[n]));
	sprintf(fileName, "%s.ior", name.c_str());
	IOR_file = ACE_OS::fopen (fileName, "w");
	if (IOR_file == 0)
    {
	   ACS_SHORT_LOG ((LM_ERROR,
		  "Cannot open output files for writing IOR: %s", fileName));
	   return  -1;
	}

	int result = ACE_OS::fprintf (IOR_file, "%s",  ior.in());
	if (result < 0)
	    {
	    ACS_SHORT_LOG ((LM_ERROR,
			    "ACE_OS::fprintf failed while writing %s into %s\n", ior.in(), fileName));
	    return  -1;
	    }
	ACE_OS::fclose (IOR_file);
	ACS_SHORT_LOG((LM_INFO, "enumpropTestDevice: %s exported", l_argv[n]));

	count++;

//	PortableServer::ObjectId_var oid = BACI_CORBA::getPOA()->reference_to_id(ep.ptr());
//	BACI_CORBA::getPOA()->deactivate_object(oid.in());

	}//for

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Waiting for requests ... "));
    BACI_CORBA::getORB()->run();

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Exiting."));

    ep_servant->_remove_ref();

    ACS_SHORT_LOG((LM_INFO,"enumpropTestServer: Releasing CORBA."));

    BACI_CORBA::DoneCORBA();

    DBConnector::closeDB();

    //delete t;

    LoggingProxy::done();

    std::cout << std::flush;  // shold be done by: LoggingProxy::done()

    return 0;

    }catch(CORBA::Exception &ex) {
      ACS_SHORT_LOG((LM_INFO, "enumpropTestServer: Exceptions raised while creating Component"));
      return -1;
    }
    return  0;
}

#ifdef MAKE_VXWORKS

int startEnumpropTestServer (char *szCmdLn)
{
  int  l_argc;
  char *l_argv[100];

//  ACE_MAIN_OBJECT_MANAGER;

  l_argc = argUnpack(szCmdLn, l_argv);
  l_argv[0] = "enumpropTestServer";

  return ace_main_i(l_argc, l_argv);
}
#endif

<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2018.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include <algorithm>
#include <cstdlib>

#include "../../Foundation/Characters/FloatConversion.h"
#include "../../Foundation/Characters/Format.h"
#include "../../Foundation/Characters/String2Int.h"
#include "../../Foundation/Characters/String_Constant.h"
#include "../../Foundation/Characters/ToString.h"
#include "../../Foundation/Containers/Common.h"
#include "../../Foundation/DataExchange/BadFormatException.h"
#include "../../Foundation/Debug/Assertions.h"
#include "../../Foundation/Execution/Exceptions.h"
#include "../../Foundation/IO/FileSystem/FileOutputStream.h"
#include "../../Foundation/IO/FileSystem/WellKnownLocations.h"
#include "../../Foundation/IO/Network/HTTP/Headers.h"
#include "../../Foundation/IO/Network/HTTP/MessageStartTextInputStreamBinaryAdapter.h"
#include "../../Foundation/IO/Network/HTTP/Methods.h"
#include "../../Foundation/Memory/SmallStackBuffer.h"
#include "../../Foundation/Streams/LoggingInputOutputStream.h"
#include "../../Foundation/Streams/SplitterOutputStream.h"

#include "ClientErrorException.h"

#include "Connection.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Memory;

using namespace Stroika::Frameworks;
using namespace Stroika::Frameworks::WebServer;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1

/*
 ********************************************************************************
 ***************************** WebServer::Connection ****************************
 ********************************************************************************
 */
Connection::Connection (const ConnectionOrientedSocket::Ptr& s, const InterceptorChain& interceptorChain)
    : fInterceptorChain_{interceptorChain}
    , fSocket_ (s)
{
    Require (s != nullptr);
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"Created connection for socket %s", Characters::ToString (s).c_str ());
#endif
    fSocketStream_ = SocketStream::New (fSocket_);
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    constexpr bool kWriteLogData_ = true;
#else
    constexpr bool kWriteLogData_ = false;
#endif
    if (kWriteLogData_) {
        fSocketStream_ = Streams::LoggingInputOutputStream<Memory::Byte>::New (
            fSocketStream_,
            IO::FileSystem::FileOutputStream::New (IO::FileSystem::WellKnownLocations::GetTemporary () + Characters::Format (L"socket-%s-input-trace.txt", Characters::ToString (s).c_str ())),
            IO::FileSystem::FileOutputStream::New (IO::FileSystem::WellKnownLocations::GetTemporary () + Characters::Format (L"socket-%s-output-trace.txt", Characters::ToString (s).c_str ())));
    }
}

Connection::~Connection ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"Destroying connection for socket %s, message=%s", Characters::ToString (fSocket_).c_str (), Characters::ToString (fMessage_).c_str ());
#endif
    if (fMessage_ != nullptr) {
        if (fMessage_->PeekResponse ()->GetState () != Response::State::eCompleted) {
            IgnoreExceptionsForCall (fMessage_->PeekResponse ()->Abort ());
        }
        Require (fMessage_->PeekResponse ()->GetState () == Response::State::eCompleted);
    }
    /*
     *  When the connection is completed, make sure the socket is closed so that the calling client knows
     *  as quickly as possible. Probably not generally necessary since when the last reference to the socket
     *  goes away, it will also be closed, but that might take a little longer as its held in some object
     *  that hasn't gone away yet.
     */
    AssertNotNull (fSocket_);
    try {
        fSocket_.Close ();
    }
    catch (...) {
        DbgTrace (L"Exception ignored closing socket: %s", Characters::ToString (current_exception ()).c_str ());
    }
}

bool Connection::ReadHeaders_ (Message* msg)
{
    /*
     * DONT use TextStream::ReadLine - because that asserts SEEKABLE - which may not be true
     * (and probably isn't here anymore)
     * Instead - we need a special variant that looks for CRLF - which doesn't require backtracking...!!!
     */
    using Foundation::IO::Network::HTTP::MessageStartTextInputStreamBinaryAdapter;
    Request*                                      request      = msg->PeekRequest ();
    MessageStartTextInputStreamBinaryAdapter::Ptr inTextStream = MessageStartTextInputStreamBinaryAdapter::New (request->GetInputStream ());
    {
        // Read METHOD line
        String line = inTextStream.ReadLine ();
        if (line.length () == 0) {
            // inTextStream.IsAtEOF () would be blocking, and that's not desired here
            if (inTextStream.ReadNonBlocking () == 0) {
                /*
                 * This is relatively common. There is an outstanding connection (so client did tcp_connect) - but never got around
                 * to sending data cuz the need for the data evaporated.
                 *
                 * I've seen this not so uncommonly with chrome (hit refresh button fast and wait a while) -- LGP 2018-03-04
                 */
                return false;
            }
        }
        Sequence<String> tokens{line.Tokenize (Set<Character>{' '})};
        if (tokens.size () < 3) {
            DbgTrace (L"tokens=%s, line='%s', inTextStream=%s", Characters::ToString (tokens).c_str (), line.c_str (), inTextStream.ToString ().c_str ());
            Execution::Throw (ClientErrorException (Characters::Format (L"Bad METHOD REQUEST HTTP line (%s)", line.c_str ())));
        }
        request->SetHTTPMethod (tokens[0]);
        request->SetHTTPVersion (tokens[2]);
        if (tokens[1].empty ()) {
            // should check if GET/PUT/DELETE etc...
            DbgTrace (L"tokens=%s, line='%s'", Characters::ToString (tokens).c_str (), line.c_str ());
            Execution::Throw (ClientErrorException (String_Constant (L"Bad HTTP REQUEST line - missing host-relative URL")));
        }
        using IO::Network::URL;
        request->SetURL (URL::Parse (tokens[1], URL::eAsRelativeURL));
        if (request->GetHTTPMethod ().empty ()) {
            // should check if GET/PUT/DELETE etc...
            DbgTrace (L"tokens=%s, line='%s'", Characters::ToString (tokens).c_str (), line.c_str ());
            Execution::Throw (ClientErrorException (String_Constant (L"Bad METHOD in REQUEST HTTP line")));
        }
    }
    while (true) {
        String line = inTextStream.ReadLine ();
        if (line == String_Constant (L"\r\n") or line.empty ()) {
            break; // done
        }

        // add subsequent items to the header map
        size_t i = line.find (':');
        if (i == string::npos) {
            DbgTrace (L"line=%s", Characters::ToString (line).c_str ());
            Execution::Throw (ClientErrorException (String_Constant (L"Bad HTTP REQUEST missing colon in headers")));
        }
        else {
            String hdr   = line.SubString (0, i).Trim ();
            String value = line.SubString (i + 1).Trim ();
            request->AddHeader (hdr, value);
        }
    }
    return true;
}

void Connection::Close ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (L"Connection::Close");
#endif
    fMessage_->PeekResponse ()->End ();
    fSocket_.Close ();
}

bool Connection::ReadAndProcessMessage ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"Connection::ReadAndProcessMessage", L"this->socket=%s", Characters::ToString (fSocket_).c_str ())};
#endif
#if qCompilerAndStdLib_copy_elision_Warning_too_aggressive_when_not_copyable_Buggy
    DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wpessimizing-move\"");
#endif
    {
        fMessage_ = make_shared<Message> (
            move (Request (fSocketStream_)),
            move (Response (fSocket_, fSocketStream_, DataExchange::PredefinedInternetMediaType::kOctetStream)),
            fSocket_.GetPeerAddress ());
    }
#if qCompilerAndStdLib_copy_elision_Warning_too_aggressive_when_not_copyable_Buggy
    DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wpessimizing-move\"");
#endif

    if (not ReadHeaders_ (fMessage_.get ())) {
        DbgTrace (L"ReadHeaders failed - typically because the client closed the connection before we could handle it (e.g. in web browser hitting refresh button fast).");
        return false;
    }

    {
        // @see https://tools.ietf.org/html/rfc2068#page-43 19.7.1.1 The Keep-Alive Header
        if (auto aliveHeaderValue = fMessage_->PeekRequest ()->GetHeaders ().Lookup (IO::Network::HTTP::HeaderName::kKeepAlive)) {
            for (String token : aliveHeaderValue->Tokenize (Set<Character>{' ', ','})) {
                Containers::Sequence<String> kvp = token.Tokenize (Set<Character>{'='});
                if (kvp.length () == 2) {
                    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive
                    if (kvp[0] == L"timeout") {
                        Time::DurationSecondsType toAt = Characters::String2Float<> (kvp[1]);
                        Remaining                 r    = GetRemainingConnectionLimits ().Value ();
                        r.fTimeoutAt                   = Time::GetTickCount () + toAt;
                        this->SetRemainingConnectionMessages (r);
                    }
                    else if (kvp[0] == L"max") {
                        unsigned int maxMsg = Characters::String2Int<unsigned int> (kvp[1]);
                        Remaining    r      = GetRemainingConnectionLimits ().Value ();
                        r.fMessages         = maxMsg;
                        this->SetRemainingConnectionMessages (r);
                    }
                    else {
                        DbgTrace (L"Keep-Alive header bad: %s", aliveHeaderValue->c_str ());
                    }
                }
                else {
                    DbgTrace (L"Keep-Alive header bad: %s", aliveHeaderValue->c_str ());
                }
            }
        }
    }

#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"Handing request %s to interceptor chain", Characters::ToString (GetRequest ()).c_str ());
#endif
    try {
        fInterceptorChain_.HandleMessage (fMessage_.get ());
    }
    catch (...) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        DbgTrace (L"Interceptor-Chain caught exception handling message: %s", Characters::ToString (current_exception ()).c_str ());
#endif
    }

    bool thisMessageKeepAlive = fMessage_->PeekRequest ()->GetKeepAliveRequested ();
    if (thisMessageKeepAlive) {
        if (not GetResponse ().IsContentLengthKnown ()) {
            thisMessageKeepAlive = false;
        }
    }
    if (thisMessageKeepAlive) {
        // if missing, no limits
        if (auto oRemaining = GetRemainingConnectionLimits ()) {
            if (oRemaining->fMessages) {
                if (oRemaining->fMessages == 0) {
                    thisMessageKeepAlive = false;
                }
                else {
                    oRemaining->fMessages = *oRemaining->fMessages - 1;
                }
            }
            if (oRemaining->fTimeoutAt) {
                if (*oRemaining->fTimeoutAt < Time::GetTickCount ()) {
                    thisMessageKeepAlive = false;
                }
            }
        }
    }

    if (thisMessageKeepAlive) {
        // be sure we advance the read pointer over the message body, lest we start reading part of the previous message as the next message

        // @todo must fix this for support of Transfer-Encoding, but from:
        //
        /*
         *  https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
         *      The rules for when a message-body is allowed in a message differ for requests and responses.
         *
         *      The presence of a message-body in a request is signaled by the inclusion of a Content-Length
         *      or Transfer-Encoding header field in the request's message-headers/
         */
        if (GetRequest ().GetContentLength ()) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"Assuring all data read; REQ=%s", Characters::ToString (GetRequest ()).c_str ());
#endif
            // @todo - this can be more efficient in the rare case we ignore the body - but thats rare enough to not matter mcuh
            (void)fMessage_->GetRequestBody ();
        }
#if 0
        else if (GetRequest ().GetHTTPMethod ().Equals (IO::Network::HTTP::Methods::kGet, Characters::CompareOptions::eCaseInsensitive)) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"This request should be closed but thats much less efficient, and chrome seems todo this - sure???; REQ=%s", Characters::ToString (GetRequest ()).c_str ());
#endif
        }
        else {
            thisMessageKeepAlive = false;
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"forced close connection because the request header lacked a Content-Length: REQ=%s", Characters::ToString (GetRequest ()).c_str ());
#endif
        }
#endif
    }

    // From https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    //      HTTP/1.1 applications that do not support persistent connections MUST include the "close" connection option in every message.
    GetResponse ().AddHeader (IO::Network::HTTP::HeaderName::kConnection,
                              thisMessageKeepAlive ? String_Constant{L"Keep-Alive"} : String_Constant{L"close"});

    GetResponse ().End ();
    return thisMessageKeepAlive;
}
<commit_msg>tweak kWriteLogData_ support in WebServer/Connection<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2018.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include <algorithm>
#include <cstdlib>

#include "../../Foundation/Characters/FloatConversion.h"
#include "../../Foundation/Characters/Format.h"
#include "../../Foundation/Characters/String2Int.h"
#include "../../Foundation/Characters/String_Constant.h"
#include "../../Foundation/Characters/ToString.h"
#include "../../Foundation/Containers/Common.h"
#include "../../Foundation/DataExchange/BadFormatException.h"
#include "../../Foundation/Debug/Assertions.h"
#include "../../Foundation/Execution/Exceptions.h"
#include "../../Foundation/IO/FileSystem/FileOutputStream.h"
#include "../../Foundation/IO/FileSystem/WellKnownLocations.h"
#include "../../Foundation/IO/Network/HTTP/Headers.h"
#include "../../Foundation/IO/Network/HTTP/MessageStartTextInputStreamBinaryAdapter.h"
#include "../../Foundation/IO/Network/HTTP/Methods.h"
#include "../../Foundation/Memory/SmallStackBuffer.h"
#include "../../Foundation/Streams/LoggingInputOutputStream.h"
#include "../../Foundation/Streams/SplitterOutputStream.h"
#include "../../Foundation/Time/DateTime.h"

#include "ClientErrorException.h"

#include "Connection.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Memory;

using namespace Stroika::Frameworks;
using namespace Stroika::Frameworks::WebServer;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1

/*
 ********************************************************************************
 ***************************** WebServer::Connection ****************************
 ********************************************************************************
 */
Connection::Connection (const ConnectionOrientedSocket::Ptr& s, const InterceptorChain& interceptorChain)
    : fInterceptorChain_{interceptorChain}
    , fSocket_ (s)
{
    Require (s != nullptr);
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"Created connection for socket %s", Characters::ToString (s).c_str ());
#endif
    fSocketStream_ = SocketStream::New (fSocket_);
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    constexpr bool kWriteLogData_ = true;
#else
    constexpr bool kWriteLogData_ = false;
#endif
    if (kWriteLogData_) {
        String socketName = Characters::Format (L"%s-%d", Time::DateTime::Now ().Format (Time::DateTime::PrintFormat::eISO8601).ReplaceAll (L":", L"-").c_str (), s.GetNativeSocket ());
        fSocketStream_    = Streams::LoggingInputOutputStream<Memory::Byte>::New (
            fSocketStream_,
            IO::FileSystem::FileOutputStream::New (IO::FileSystem::WellKnownLocations::GetTemporary () + Characters::Format (L"socket-%s-input-trace.txt", socketName.c_str ())),
            IO::FileSystem::FileOutputStream::New (IO::FileSystem::WellKnownLocations::GetTemporary () + Characters::Format (L"socket-%s-output-trace.txt", socketName.c_str ())));
    }
}

Connection::~Connection ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"Destroying connection for socket %s, message=%s", Characters::ToString (fSocket_).c_str (), Characters::ToString (fMessage_).c_str ());
#endif
    if (fMessage_ != nullptr) {
        if (fMessage_->PeekResponse ()->GetState () != Response::State::eCompleted) {
            IgnoreExceptionsForCall (fMessage_->PeekResponse ()->Abort ());
        }
        Require (fMessage_->PeekResponse ()->GetState () == Response::State::eCompleted);
    }
    /*
     *  When the connection is completed, make sure the socket is closed so that the calling client knows
     *  as quickly as possible. Probably not generally necessary since when the last reference to the socket
     *  goes away, it will also be closed, but that might take a little longer as its held in some object
     *  that hasn't gone away yet.
     */
    AssertNotNull (fSocket_);
    try {
        fSocket_.Close ();
    }
    catch (...) {
        DbgTrace (L"Exception ignored closing socket: %s", Characters::ToString (current_exception ()).c_str ());
    }
}

bool Connection::ReadHeaders_ (Message* msg)
{
    /*
     * DONT use TextStream::ReadLine - because that asserts SEEKABLE - which may not be true
     * (and probably isn't here anymore)
     * Instead - we need a special variant that looks for CRLF - which doesn't require backtracking...!!!
     */
    using Foundation::IO::Network::HTTP::MessageStartTextInputStreamBinaryAdapter;
    Request*                                      request      = msg->PeekRequest ();
    MessageStartTextInputStreamBinaryAdapter::Ptr inTextStream = MessageStartTextInputStreamBinaryAdapter::New (request->GetInputStream ());
    {
        // Read METHOD line
        String line = inTextStream.ReadLine ();
        if (line.length () == 0) {
            // inTextStream.IsAtEOF () would be blocking, and that's not desired here
            if (inTextStream.ReadNonBlocking () == 0) {
                /*
                 * This is relatively common. There is an outstanding connection (so client did tcp_connect) - but never got around
                 * to sending data cuz the need for the data evaporated.
                 *
                 * I've seen this not so uncommonly with chrome (hit refresh button fast and wait a while) -- LGP 2018-03-04
                 */
                return false;
            }
        }
        Sequence<String> tokens{line.Tokenize (Set<Character>{' '})};
        if (tokens.size () < 3) {
            DbgTrace (L"tokens=%s, line='%s', inTextStream=%s", Characters::ToString (tokens).c_str (), line.c_str (), inTextStream.ToString ().c_str ());
            Execution::Throw (ClientErrorException (Characters::Format (L"Bad METHOD REQUEST HTTP line (%s)", line.c_str ())));
        }
        request->SetHTTPMethod (tokens[0]);
        request->SetHTTPVersion (tokens[2]);
        if (tokens[1].empty ()) {
            // should check if GET/PUT/DELETE etc...
            DbgTrace (L"tokens=%s, line='%s'", Characters::ToString (tokens).c_str (), line.c_str ());
            Execution::Throw (ClientErrorException (String_Constant (L"Bad HTTP REQUEST line - missing host-relative URL")));
        }
        using IO::Network::URL;
        request->SetURL (URL::Parse (tokens[1], URL::eAsRelativeURL));
        if (request->GetHTTPMethod ().empty ()) {
            // should check if GET/PUT/DELETE etc...
            DbgTrace (L"tokens=%s, line='%s'", Characters::ToString (tokens).c_str (), line.c_str ());
            Execution::Throw (ClientErrorException (String_Constant (L"Bad METHOD in REQUEST HTTP line")));
        }
    }
    while (true) {
        String line = inTextStream.ReadLine ();
        if (line == String_Constant (L"\r\n") or line.empty ()) {
            break; // done
        }

        // add subsequent items to the header map
        size_t i = line.find (':');
        if (i == string::npos) {
            DbgTrace (L"line=%s", Characters::ToString (line).c_str ());
            Execution::Throw (ClientErrorException (String_Constant (L"Bad HTTP REQUEST missing colon in headers")));
        }
        else {
            String hdr   = line.SubString (0, i).Trim ();
            String value = line.SubString (i + 1).Trim ();
            request->AddHeader (hdr, value);
        }
    }
    return true;
}

void Connection::Close ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (L"Connection::Close");
#endif
    fMessage_->PeekResponse ()->End ();
    fSocket_.Close ();
}

bool Connection::ReadAndProcessMessage ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"Connection::ReadAndProcessMessage", L"this->socket=%s", Characters::ToString (fSocket_).c_str ())};
#endif
#if qCompilerAndStdLib_copy_elision_Warning_too_aggressive_when_not_copyable_Buggy
    DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wpessimizing-move\"");
#endif
    {
        fMessage_ = make_shared<Message> (
            move (Request (fSocketStream_)),
            move (Response (fSocket_, fSocketStream_, DataExchange::PredefinedInternetMediaType::kOctetStream)),
            fSocket_.GetPeerAddress ());
    }
#if qCompilerAndStdLib_copy_elision_Warning_too_aggressive_when_not_copyable_Buggy
    DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wpessimizing-move\"");
#endif

    if (not ReadHeaders_ (fMessage_.get ())) {
        DbgTrace (L"ReadHeaders failed - typically because the client closed the connection before we could handle it (e.g. in web browser hitting refresh button fast).");
        return false;
    }

    {
        // @see https://tools.ietf.org/html/rfc2068#page-43 19.7.1.1 The Keep-Alive Header
        if (auto aliveHeaderValue = fMessage_->PeekRequest ()->GetHeaders ().Lookup (IO::Network::HTTP::HeaderName::kKeepAlive)) {
            for (String token : aliveHeaderValue->Tokenize (Set<Character>{' ', ','})) {
                Containers::Sequence<String> kvp = token.Tokenize (Set<Character>{'='});
                if (kvp.length () == 2) {
                    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive
                    if (kvp[0] == L"timeout") {
                        Time::DurationSecondsType toAt = Characters::String2Float<> (kvp[1]);
                        Remaining                 r    = GetRemainingConnectionLimits ().Value ();
                        r.fTimeoutAt                   = Time::GetTickCount () + toAt;
                        this->SetRemainingConnectionMessages (r);
                    }
                    else if (kvp[0] == L"max") {
                        unsigned int maxMsg = Characters::String2Int<unsigned int> (kvp[1]);
                        Remaining    r      = GetRemainingConnectionLimits ().Value ();
                        r.fMessages         = maxMsg;
                        this->SetRemainingConnectionMessages (r);
                    }
                    else {
                        DbgTrace (L"Keep-Alive header bad: %s", aliveHeaderValue->c_str ());
                    }
                }
                else {
                    DbgTrace (L"Keep-Alive header bad: %s", aliveHeaderValue->c_str ());
                }
            }
        }
    }

#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"Handing request %s to interceptor chain", Characters::ToString (GetRequest ()).c_str ());
#endif
    try {
        fInterceptorChain_.HandleMessage (fMessage_.get ());
    }
    catch (...) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        DbgTrace (L"Interceptor-Chain caught exception handling message: %s", Characters::ToString (current_exception ()).c_str ());
#endif
    }

    bool thisMessageKeepAlive = fMessage_->PeekRequest ()->GetKeepAliveRequested ();
    if (thisMessageKeepAlive) {
        if (not GetResponse ().IsContentLengthKnown ()) {
            thisMessageKeepAlive = false;
        }
    }
    if (thisMessageKeepAlive) {
        // if missing, no limits
        if (auto oRemaining = GetRemainingConnectionLimits ()) {
            if (oRemaining->fMessages) {
                if (oRemaining->fMessages == 0) {
                    thisMessageKeepAlive = false;
                }
                else {
                    oRemaining->fMessages = *oRemaining->fMessages - 1;
                }
            }
            if (oRemaining->fTimeoutAt) {
                if (*oRemaining->fTimeoutAt < Time::GetTickCount ()) {
                    thisMessageKeepAlive = false;
                }
            }
        }
    }

    if (thisMessageKeepAlive) {
        // be sure we advance the read pointer over the message body, lest we start reading part of the previous message as the next message

        // @todo must fix this for support of Transfer-Encoding, but from:
        //
        /*
         *  https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
         *      The rules for when a message-body is allowed in a message differ for requests and responses.
         *
         *      The presence of a message-body in a request is signaled by the inclusion of a Content-Length
         *      or Transfer-Encoding header field in the request's message-headers/
         */
        if (GetRequest ().GetContentLength ()) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"Assuring all data read; REQ=%s", Characters::ToString (GetRequest ()).c_str ());
#endif
            // @todo - this can be more efficient in the rare case we ignore the body - but thats rare enough to not matter mcuh
            (void)fMessage_->GetRequestBody ();
        }
#if 0
        else if (GetRequest ().GetHTTPMethod ().Equals (IO::Network::HTTP::Methods::kGet, Characters::CompareOptions::eCaseInsensitive)) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"This request should be closed but thats much less efficient, and chrome seems todo this - sure???; REQ=%s", Characters::ToString (GetRequest ()).c_str ());
#endif
        }
        else {
            thisMessageKeepAlive = false;
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"forced close connection because the request header lacked a Content-Length: REQ=%s", Characters::ToString (GetRequest ()).c_str ());
#endif
        }
#endif
    }

    // From https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    //      HTTP/1.1 applications that do not support persistent connections MUST include the "close" connection option in every message.
    GetResponse ().AddHeader (IO::Network::HTTP::HeaderName::kConnection,
                              thisMessageKeepAlive ? String_Constant{L"Keep-Alive"} : String_Constant{L"close"});

    GetResponse ().End ();
    return thisMessageKeepAlive;
}
<|endoftext|>


<commit_before>//
// Created by Kyle on 5/11/2016.
//

#include "GLWindow.h"

Peon::GLWindow::GLWindow(const GLVideoMode & videoMode,
    const GLContextSettings & ctxSettings,
    const GLWindowSettings & windowSettings)
    : mIsFullscreen(false),
      mIsVsyncEnabled(false),
      mFullscreenMonitor(nullptr),
      mContext(Shared<GLContext>(new GLContext(videoMode, ctxSettings, windowSettings)))
{
}

Peon::GLWindow::GLWindow(Shared<GLContext> context,
    const GLVideoMode & videoMode,
    const GLWindowSettings & windowSettings)
    : GLWindow(context.get(), videoMode, windowSettings)
{ }

Peon::GLWindow::GLWindow(const GLContext* const context,
    const GLVideoMode & videoMode,
    const GLWindowSettings & windowSettings)
    : mIsFullscreen(false),
    mIsVsyncEnabled(false),
    mFullscreenMonitor(nullptr),
    mContext(nullptr)
{
    assert(context != nullptr);
    mContext = Shared<GLContext>(new GLContext(videoMode, context->mSettings, windowSettings, context->mWindow));
}

Peon::GLWindow::~GLWindow() {
    if (!glfwWindowShouldClose(mContext->mWindow)) {
        glfwSetWindowShouldClose(mContext->mWindow, GL_TRUE);
    }
}

void Peon::GLWindow::CloseWindow() {
    assert(mContext->mWindow);
    glfwSetWindowShouldClose(mContext->mWindow, true);
}

void Peon::GLWindow::SetTitle(const string & title) {
    assert(mContext->mWindow);
    glfwSetWindowTitle(mContext->mWindow, title.c_str());
}

void Peon::GLWindow::SetIcon(unsigned int width, unsigned int height, uint8* pixels) {
    assert(pixels);
    assert(width > 0 && height > 0);
    GLFWimage image;
    image.height = height;
    image.width = width;
    image.pixels = static_cast<unsigned char*>(pixels);
    glfwSetWindowIcon(mContext->mWindow, 1, &image);
}

void Peon::GLWindow::SetPosition(const ivec2 & position) {
    assert(mContext->mWindow);
    glfwSetWindowPos(mContext->mWindow, position.x, position.y);
}

void Peon::GLWindow::SetVideoMode(const GLVideoMode & videoMode) {
    if (!mIsFullscreen) {
        uvec2 pos = GetPosition();
        glfwSetWindowMonitor(mContext->mWindow, nullptr, pos.x, pos.y,
            videoMode.width, videoMode.height, videoMode.refreshRate);
    } else {
        glfwSetWindowMonitor(mContext->mWindow, mFullscreenMonitor.mMonitor,
            0, 0, videoMode.width, videoMode.height, videoMode.refreshRate);
    }
    mVideoMode = videoMode;
}

void Peon::GLWindow::SetFullscreen(bool fullscreen, GLMonitor monitor) {
    if (!mIsFullscreen && fullscreen) {
        mWindowedPos = GetPosition();
        mFullscreenMonitor = monitor;
        glfwSetWindowMonitor(mContext->mWindow, monitor.mMonitor, 0, 0,
            mVideoMode.width, mVideoMode.height, mVideoMode.refreshRate);
    } else if (mIsFullscreen && !fullscreen) {
        glfwSetWindowMonitor(mContext->mWindow, nullptr, mWindowedPos.x, mWindowedPos.y,
            mVideoMode.width, mVideoMode.height, mVideoMode.refreshRate);
    }
    mIsFullscreen = fullscreen;
}

void Peon::GLWindow::SetVsync(bool on) {
    glfwSwapInterval(static_cast<int>(on));
    mIsVsyncEnabled = on;
}

Peon::GLVideoMode Peon::GLWindow::GetVideoMode() const {
    return mVideoMode;
}

ivec2 Peon::GLWindow::GetPosition() const {
    ivec2 position;
    glfwGetWindowPos(mContext->mWindow, &position.x, &position.y);
    return position;
}

ivec2 Peon::GLWindow::GetFramebufferSize() const {
    ivec2 size;
    glfwGetFramebufferSize(mContext->mWindow, &size.x, &size.y);
    return size;
}

Peon::Shared<Peon::GLContext> Peon::GLWindow::GetContext() const {
    assert(mContext.get() != nullptr);
    return mContext;
}

Peon::GLMonitor Peon::GLWindow::GetCurrentMonitor() const {
    uvec2 thisPos = this->GetPosition();
    vector<GLMonitor> monitors = GLMonitor::GetMonitors();
     for (GLMonitor & monitor : monitors) {
         const uvec2 & thatPos = monitor.GetPosition();
         const GLVideoMode & thatMode = monitor.GetVideoMode();
         if (thisPos.x >= thatPos.x && thisPos.x < thatPos.x + thatMode.width &&
             thisPos.y >= thatPos.y && thisPos.y < thatPos.y + thatMode.height) {
             return monitor;
         }
     }
     return GLMonitor(nullptr);
}

void Peon::GLWindow::Maximize() {
    assert(mContext->mWindow);
    glfwMaximizeWindow(mContext->mWindow);
}

void Peon::GLWindow::Minimize() {
    assert(mContext->mWindow);
    glfwIconifyWindow(mContext->mWindow);
}

void Peon::GLWindow::Restore() {
    assert(mContext->mWindow);
    glfwRestoreWindow(mContext->mWindow);
}

void Peon::GLWindow::Show() {
    assert(mContext->mWindow);
    glfwShowWindow(mContext->mWindow);
}

void Peon::GLWindow::Hide() {
    assert(mContext->mWindow);
    glfwHideWindow(mContext->mWindow);
}

void Peon::GLWindow::Focus() {
    assert(mContext->mWindow);
    if (!this->IsMinimized() && this->IsVisible()) {
        glfwFocusWindow(mContext->mWindow);
    }
}

bool Peon::GLWindow::IsOpen() const {
    return glfwWindowShouldClose(mContext->mWindow) == 0;
}

bool Peon::GLWindow::IsVisible() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_VISIBLE) != 0;
}

bool Peon::GLWindow::IsFocused() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_FOCUSED) != 0;
}

bool Peon::GLWindow::IsMinimized() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_ICONIFIED) != 0;
}

bool Peon::GLWindow::IsMaximized() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_MAXIMIZED) != 0;
}

bool Peon::GLWindow::IsFullscreen() const {
    return mIsFullscreen;
}

bool Peon::GLWindow::IsVsyncEnabled() const {
    return mIsVsyncEnabled;
}

void Peon::GLWindow::SwapBuffers() {
    assert(mContext->mWindow);
    glfwSwapBuffers(mContext->mWindow);
    glfwPollEvents();
}

void Peon::GLWindow::MakeContextCurrent() {
    mContext->MakeContextCurrent();
}
<commit_msg>get current monitor for fullscreen fix<commit_after>//
// Created by Kyle on 5/11/2016.
//

#include "GLWindow.h"

Peon::GLWindow::GLWindow(const GLVideoMode & videoMode,
    const GLContextSettings & ctxSettings,
    const GLWindowSettings & windowSettings)
    : mIsFullscreen(false),
      mIsVsyncEnabled(false),
      mFullscreenMonitor(nullptr),
      mContext(Shared<GLContext>(new GLContext(videoMode, ctxSettings, windowSettings)))
{
}

Peon::GLWindow::GLWindow(Shared<GLContext> context,
    const GLVideoMode & videoMode,
    const GLWindowSettings & windowSettings)
    : GLWindow(context.get(), videoMode, windowSettings)
{ }

Peon::GLWindow::GLWindow(const GLContext* const context,
    const GLVideoMode & videoMode,
    const GLWindowSettings & windowSettings)
    : mIsFullscreen(false),
    mIsVsyncEnabled(false),
    mFullscreenMonitor(nullptr),
    mContext(nullptr)
{
    assert(context != nullptr);
    mContext = Shared<GLContext>(new GLContext(videoMode, context->mSettings, windowSettings, context->mWindow));
}

Peon::GLWindow::~GLWindow() {
    if (!glfwWindowShouldClose(mContext->mWindow)) {
        glfwSetWindowShouldClose(mContext->mWindow, GL_TRUE);
    }
}

void Peon::GLWindow::CloseWindow() {
    assert(mContext->mWindow);
    glfwSetWindowShouldClose(mContext->mWindow, true);
}

void Peon::GLWindow::SetTitle(const string & title) {
    assert(mContext->mWindow);
    glfwSetWindowTitle(mContext->mWindow, title.c_str());
}

void Peon::GLWindow::SetIcon(unsigned int width, unsigned int height, uint8* pixels) {
    assert(pixels);
    assert(width > 0 && height > 0);
    GLFWimage image;
    image.height = height;
    image.width = width;
    image.pixels = static_cast<unsigned char*>(pixels);
    glfwSetWindowIcon(mContext->mWindow, 1, &image);
}

void Peon::GLWindow::SetPosition(const ivec2 & position) {
    assert(mContext->mWindow);
    glfwSetWindowPos(mContext->mWindow, position.x, position.y);
}

void Peon::GLWindow::SetVideoMode(const GLVideoMode & videoMode) {
    if (!mIsFullscreen) {
        uvec2 pos = GetPosition();
        glfwSetWindowMonitor(mContext->mWindow, nullptr, pos.x, pos.y,
            videoMode.width, videoMode.height, videoMode.refreshRate);
    } else {
        glfwSetWindowMonitor(mContext->mWindow, mFullscreenMonitor.mMonitor,
            0, 0, videoMode.width, videoMode.height, videoMode.refreshRate);
    }
    mVideoMode = videoMode;
}

void Peon::GLWindow::SetFullscreen(bool fullscreen, GLMonitor monitor) {
    if (!mIsFullscreen && fullscreen) {
        mWindowedPos = GetPosition();
        mFullscreenMonitor = monitor;
        glfwSetWindowMonitor(mContext->mWindow, monitor.mMonitor, 0, 0,
            mVideoMode.width, mVideoMode.height, mVideoMode.refreshRate);
    } else if (mIsFullscreen && !fullscreen) {
        glfwSetWindowMonitor(mContext->mWindow, nullptr, mWindowedPos.x, mWindowedPos.y,
            mVideoMode.width, mVideoMode.height, mVideoMode.refreshRate);
    }
    mIsFullscreen = fullscreen;
}

void Peon::GLWindow::SetVsync(bool on) {
    glfwSwapInterval(static_cast<int>(on));
    mIsVsyncEnabled = on;
}

Peon::GLVideoMode Peon::GLWindow::GetVideoMode() const {
    return mVideoMode;
}

ivec2 Peon::GLWindow::GetPosition() const {
    ivec2 position;
    glfwGetWindowPos(mContext->mWindow, &position.x, &position.y);
    return position;
}

ivec2 Peon::GLWindow::GetFramebufferSize() const {
    ivec2 size;
    glfwGetFramebufferSize(mContext->mWindow, &size.x, &size.y);
    return size;
}

Peon::Shared<Peon::GLContext> Peon::GLWindow::GetContext() const {
    assert(mContext.get() != nullptr);
    return mContext;
}

Peon::GLMonitor Peon::GLWindow::GetCurrentMonitor() const {
    if (mIsFullscreen) {
        return mFullscreenMonitor;
    }
    uvec2 thisPos = this->GetPosition();
    vector<GLMonitor> monitors = GLMonitor::GetMonitors();
     for (GLMonitor & monitor : monitors) {
         const uvec2 & thatPos = monitor.GetPosition();
         const GLVideoMode & thatMode = monitor.GetVideoMode();
         if (thisPos.x >= thatPos.x && thisPos.x < thatPos.x + thatMode.width &&
             thisPos.y >= thatPos.y && thisPos.y < thatPos.y + thatMode.height) {
             return monitor;
         }
     }
     return GLMonitor(nullptr);
}

void Peon::GLWindow::Maximize() {
    assert(mContext->mWindow);
    glfwMaximizeWindow(mContext->mWindow);
}

void Peon::GLWindow::Minimize() {
    assert(mContext->mWindow);
    glfwIconifyWindow(mContext->mWindow);
}

void Peon::GLWindow::Restore() {
    assert(mContext->mWindow);
    glfwRestoreWindow(mContext->mWindow);
}

void Peon::GLWindow::Show() {
    assert(mContext->mWindow);
    glfwShowWindow(mContext->mWindow);
}

void Peon::GLWindow::Hide() {
    assert(mContext->mWindow);
    glfwHideWindow(mContext->mWindow);
}

void Peon::GLWindow::Focus() {
    assert(mContext->mWindow);
    if (!this->IsMinimized() && this->IsVisible()) {
        glfwFocusWindow(mContext->mWindow);
    }
}

bool Peon::GLWindow::IsOpen() const {
    return glfwWindowShouldClose(mContext->mWindow) == 0;
}

bool Peon::GLWindow::IsVisible() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_VISIBLE) != 0;
}

bool Peon::GLWindow::IsFocused() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_FOCUSED) != 0;
}

bool Peon::GLWindow::IsMinimized() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_ICONIFIED) != 0;
}

bool Peon::GLWindow::IsMaximized() const {
    assert(mContext->mWindow);
    return glfwGetWindowAttrib(mContext->mWindow, GLFW_MAXIMIZED) != 0;
}

bool Peon::GLWindow::IsFullscreen() const {
    return mIsFullscreen;
}

bool Peon::GLWindow::IsVsyncEnabled() const {
    return mIsVsyncEnabled;
}

void Peon::GLWindow::SwapBuffers() {
    assert(mContext->mWindow);
    glfwSwapBuffers(mContext->mWindow);
    glfwPollEvents();
}

void Peon::GLWindow::MakeContextCurrent() {
    mContext->MakeContextCurrent();
}
<|endoftext|>


<commit_before>#pragma once

#include "stdafx.h"
#include "TimeoutGuard.h"

namespace utility
{
	TimeoutGuard::TimeoutGuard(
		clock::duration timeout,
		std::function<void( void )> alarm,
		clock::duration naptime
	)
		: timeout( timeout )
		, alarm( alarm )
		, naptime( naptime )
	{
		idle.store( true );
		live.store( true );

		guard_thread = std::thread( std::bind( &TimeoutGuard::guard, this ) );
	}

	TimeoutGuard::TimeoutGuard(
		clock::duration timeout,
		std::function<void( void )> alarm
	)
	: TimeoutGuard( timeout, alarm, timeout )
	{};

	TimeoutGuard::~TimeoutGuard()
	{
		live.store( false );
		wakeup.notify_all();
		guard_thread.join();
	}

	void TimeoutGuard::guard()
	{
		while ( live.load() )
		{
			if ( idle.load() )
			{
				// Sleep indefinitely until either told to become active or destruct
				std::unique_lock<std::mutex> live_lock( guard_mutex );
				wakeup.wait( live_lock, [this]() { return ! idle.load() || ! this->live.load(); } );
			};

			// quit the loop if destructing
			if ( ! live.load() ) break;

			// the actual timeout checking
			auto now = clock::now();

			if ( ( now - touched.load() ) > timeout )
			{
				idle.store( true );
				alarm();
			}

			{
				// sleep until next timeout check or destruction
				std::unique_lock<std::mutex> live_lock( guard_mutex );
				wakeup.wait_for( live_lock, naptime, [this](){ return ! this->live.load(); } );
			}
		};
	}

	void TimeoutGuard::watch()
	{
		touch();
		idle.store( false );
		wakeup.notify_all();
	}

	void TimeoutGuard::touch()
	{
		touched.store( clock::now() );
	}
}
<commit_msg>Fixed a bug when the guard will not react to watch() immediately<commit_after>#include "stdafx.h"
#include "TimeoutGuard.h"

namespace utility
{
	TimeoutGuard::TimeoutGuard(
		clock::duration timeout,
		std::function<void( void )> alarm,
		clock::duration naptime
	)
		: timeout( timeout )
		, alarm( alarm )
		, naptime( naptime )
	{
		idle.store( true );
		live.store( true );

		guard_thread = std::thread( std::bind( &TimeoutGuard::guard, this ) );
	}

	TimeoutGuard::TimeoutGuard(
		clock::duration timeout,
		std::function<void( void )> alarm
	)
	: TimeoutGuard( timeout, alarm, timeout )
	{};

	TimeoutGuard::~TimeoutGuard()
	{
		live.store( false );
		wakeup.notify_all();
		guard_thread.join();
	}

	void TimeoutGuard::guard()
	{
		while ( live.load() )
		{
			if ( idle.load() )
			{
				// Sleep indefinitely until either told to become active or destruct
				std::unique_lock<std::mutex> live_lock( guard_mutex );
				wakeup.wait( live_lock, [this]() { return ! this->idle.load() || ! this->live.load(); } );
			};

			// quit the loop if destructing
			if ( ! live.load() ) break;

			// the actual timeout checking
			auto now = clock::now();

			if ( ( now - touched.load() ) > timeout )
			{
				idle.store( true );
				alarm();
				continue; // skip waiting for next timeout
			}

			{
				// sleep until next timeout check or destruction
				std::unique_lock<std::mutex> live_lock( guard_mutex );
				wakeup.wait_for( live_lock, naptime, [this](){ return ! this->live.load(); } );
			}
		};
	}

	void TimeoutGuard::watch()
	{
		touch();
		idle.store( false );
		wakeup.notify_all();
	}

	void TimeoutGuard::touch()
	{
		touched.store( clock::now() );
	}
}
<|endoftext|>