<commit_before>#include "../utils/utils.hpp"
#include <cstdio>
#include <vector>
#include <string>
#include <cerrno>
#include <cstring>
#include <cmath>
#include <dirent.h>
#include <sys/utsname.h>

struct Domain {
	std::string name, fmt;
};

const Domain doms[] = {
	{ "tiles", "./tiles/15md_solver %s < %s" },
	{ "pancake", "./pancake/50pancake_solver %s < %s" },
	{ "gridnav", "./gridnav/gridnav_solver %s < %s" },
	{ "plat2d", "./plat2d/plat2d_solver %s -lvl %s" },
};

struct Algorithm {
	std::string name, cmd;
};

const Algorithm algs[] = {
	{ "astar", "astar" },
	{ "greedy", "greedy" },
	{ "wastar", "wastar -wt 1" },
};

// Separate since it doesn't work well on all domains.
const Algorithm idastar = { "idastar", "idastar" };

enum {
	Ndoms = sizeof(doms) / sizeof(doms[0]),
	Nalgs = sizeof(algs) / sizeof(algs[0]),
};

// Result holds results from a run.
struct Result {
	Result(void) : time(-1), len(0), nruns(0), stdev(-1) { }

	double time;	// time or mean time
	unsigned int len;	// solution length
	unsigned int nruns;	// only for saved benchmarks
	double stdev;	// only for saved benchmarks
};

char mname[256];

static bool bench(const Domain&, const Algorithm&);
static std::string instpath(const Domain&, const std::string&);
static std::string benchpath(const Domain&, const Algorithm&,const std::string&);
static bool chkbench(const Domain&, const Algorithm&, const std::string&);
static void mkbench(const Domain&, const Algorithm&, const std::string&);
static Result readresult(FILE*);
static void dfline(std::vector<const char *>&, void*);
static Result run(const Domain&, const Algorithm&, const std::string&);

int main() {
	if (gethostname(mname, sizeof(mname)) == -1)
		fatalx(errno, "failed to get the machine name");

	printf("machine: %s\n", mname);
	for (unsigned int i = 0; i < Ndoms; i++) {
		printf("domain: %s\n", doms[i].name.c_str());
		for (unsigned int j = 0; j < Nalgs; j++) {
			printf("algorithm: %s\n", algs[j].name.c_str());
			if (!bench(doms[i], algs[j]))
				return 1;
		}
		if (doms[i].name == "tiles" || doms[i].name == "pancake") {
			printf("algorithm: %s\n", idastar.name.c_str());
			if (!bench(doms[i], idastar))
				return 1;
		}
	}

	return 0;
}

// bench benchmarks the given domain and algorithm,
// returning true if the benchmark matched previous
// results.
static bool bench(const Domain &dom, const Algorithm &alg) {
	const std::string dir = "bench/insts/" + dom.name;
	std::vector<std::string> insts = readdir(dir, false);

	for (unsigned int i = 0; i < insts.size(); i++) {
		std::string inst = insts[i];
		printf("instance: %s (%s)\n", inst.c_str(),
			benchpath(dom, alg, inst).c_str());

		if (fileexists(benchpath(dom, alg, inst))) {
			if (!chkbench(dom, alg, inst)) {
				printf("failed\n");
				return false;
			}
		} else {
			mkbench(dom, alg, inst);
		}
	}

	return true;
}

static std::string instpath(const Domain &dom, const std::string &inst) {
	return "bench/insts/" + dom.name + "/" + inst;
}

static std::string benchpath(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	return "bench/data/" + std::string(mname) + "_" +
		dom.name + "_" + alg.name + "_" + inst;
}

// chkbench returns true if the current behavior is
// consistent with the benchmark.
static bool chkbench(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	const std::string ipath = benchpath(dom, alg, inst);
	FILE *f = fopen(ipath.c_str(), "r");
	if (!f)
		fatalx(errno, "failed to open %s for reading", ipath.c_str());
	Result bench = readresult(f);
	fclose(f);

	// Must pass 1 of 3 runs within 2σ of the mean time
	double rng = 2*bench.stdev;
	unsigned int ok = 0;
	for (unsigned int i = 0; i < 3 && ok < 1; i++) {
		Result r = run(dom, alg, inst);
		if (r.len != bench.len) {
			printf("	expected %u solution length\n", bench.len);
			continue;
		}
		if (r.time > bench.time + rng) {
			printf("	expected %g seconds + %g\n", bench.time, rng);
			continue;
		}
		ok++;
	}

	return ok >= 1;
}

// mkbench makes a new benchmark file.
static void mkbench(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	static const unsigned int N = 5;
	const std::string opath = benchpath(dom, alg, inst);
	printf("	making %s\n", opath.c_str());

	printf("	warmup run:	");
	run(dom, alg, inst);

	Result r[N];
	double meantime = 0;
	for (unsigned int i = 0; i < N; i++) {
		r[i] = run(dom, alg, inst);
		meantime += r[i].time;
	}
	meantime /= N;

	double vartime = 0;
	for (unsigned int i = 1; i < N; i++) {
		if (r[i].len != r[0].len)
			fatal("different lengths when solving %s %s %s",
				dom.name.c_str(), alg.name.c_str(), inst.c_str());
		double diff = r[i].time - meantime;
		vartime += diff * diff;
	}
	vartime /= N;
	double stdev = sqrt(vartime);

	printf("	%g avg. seconds, %g stdev\n", meantime, stdev);

	ensuredir(opath);
	FILE *o = fopen(opath.c_str(), "w");
	if (!o)
		fatalx(errno, "failed to open %s for writing", opath.c_str());

	dfpair(o, "machine name", "%s", mname);
	dfpair(o, "domain", "%s", dom.name.c_str());
	dfpair(o, "algorithm", "%s", alg.name.c_str());
	dfpair(o, "instance", "%s", inst.c_str());
	dfpair(o, "num runs", "%u", (unsigned int) N);
	dfpair(o, "mean wall time", "%g", meantime);
	dfpair(o, "stdev wall time", "%g", stdev);
	dfpair(o, "final sol length", "%u", (unsigned int) r[0].len);
	fclose(o);
}

// readresult reads the datafile from the stream
// and returns the result data.
static Result readresult(FILE *f) {
	Result res;
	dfread(f, dfline, &res);
	return res;
}

static void dfline(std::vector<const char *> &toks, void *_res) {
	Result *res = static_cast<Result*>(_res);
	if (strcmp(toks[1], "total wall time") == 0) {
		res->time = strtod(toks[2], NULL);
	} else if (strcmp(toks[1], "final sol length") == 0) {
		res->len = strtol(toks[2], NULL, 10);
	} else if (strcmp(toks[1], "mean wall time") == 0) {
		res->time = strtod(toks[2], NULL);
	} else if (strcmp(toks[1], "stdev wall time") == 0) {
		res->stdev = strtod(toks[2], NULL);
	} else if (strcmp(toks[1], "num runs") == 0) {
		res->nruns = strtol(toks[2], NULL, 10);
	}
}

// run runs the solver on the given instance using the
// given algorithm, returning the Result.
static Result run(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	char cmd[1024];
	const std::string ipath = instpath(dom, inst);
	unsigned int res = snprintf(cmd, sizeof(cmd), dom.fmt.c_str(),
		alg.cmd.c_str(), ipath.c_str());
	if (res >= sizeof(cmd))
		fatal("mark.cc run(): buffer is too small");

	FILE *f = popen(cmd, "r");
	if (!f)
		fatal("failed to execute [%s]", cmd);
	Result r = readresult(f);
	pclose(f);

	printf("	%u sol length, %g seconds\n", r.len, r.time);
	return r;
}<commit_msg>bench: allow for within 110% of mean. The standard deviation is just too tight for many instances.<commit_after>#include "../utils/utils.hpp"
#include <cstdio>
#include <vector>
#include <string>
#include <cerrno>
#include <cstring>
#include <cmath>
#include <dirent.h>
#include <sys/utsname.h>

struct Domain {
	std::string name, fmt;
};

const Domain doms[] = {
	{ "tiles", "./tiles/15md_solver %s < %s" },
	{ "pancake", "./pancake/50pancake_solver %s < %s" },
	{ "gridnav", "./gridnav/gridnav_solver %s < %s" },
	{ "plat2d", "./plat2d/plat2d_solver %s -lvl %s" },
};

struct Algorithm {
	std::string name, cmd;
};

const Algorithm algs[] = {
	{ "astar", "astar" },
	{ "greedy", "greedy" },
	{ "wastar", "wastar -wt 1" },
};

// Separate since it doesn't work well on all domains.
const Algorithm idastar = { "idastar", "idastar" };

enum {
	Ndoms = sizeof(doms) / sizeof(doms[0]),
	Nalgs = sizeof(algs) / sizeof(algs[0]),
};

// Result holds results from a run.
struct Result {
	Result(void) : time(-1), len(0), nruns(0), stdev(-1) { }

	double time;	// time or mean time
	unsigned int len;	// solution length
	unsigned int nruns;	// only for saved benchmarks
	double stdev;	// only for saved benchmarks
};

char mname[256];

static bool bench(const Domain&, const Algorithm&);
static std::string instpath(const Domain&, const std::string&);
static std::string benchpath(const Domain&, const Algorithm&,const std::string&);
static bool chkbench(const Domain&, const Algorithm&, const std::string&);
static void mkbench(const Domain&, const Algorithm&, const std::string&);
static Result readresult(FILE*);
static void dfline(std::vector<const char *>&, void*);
static Result run(const Domain&, const Algorithm&, const std::string&);

int main() {
	if (gethostname(mname, sizeof(mname)) == -1)
		fatalx(errno, "failed to get the machine name");

	printf("machine: %s\n", mname);
	for (unsigned int i = 0; i < Ndoms; i++) {
		printf("domain: %s\n", doms[i].name.c_str());
		for (unsigned int j = 0; j < Nalgs; j++) {
			printf("algorithm: %s\n", algs[j].name.c_str());
			if (!bench(doms[i], algs[j]))
				return 1;
		}
		if (doms[i].name == "tiles" || doms[i].name == "pancake") {
			printf("algorithm: %s\n", idastar.name.c_str());
			if (!bench(doms[i], idastar))
				return 1;
		}
	}

	return 0;
}

// bench benchmarks the given domain and algorithm,
// returning true if the benchmark matched previous
// results.
static bool bench(const Domain &dom, const Algorithm &alg) {
	const std::string dir = "bench/insts/" + dom.name;
	std::vector<std::string> insts = readdir(dir, false);

	for (unsigned int i = 0; i < insts.size(); i++) {
		std::string inst = insts[i];
		printf("instance: %s (%s)\n", inst.c_str(),
			benchpath(dom, alg, inst).c_str());

		if (fileexists(benchpath(dom, alg, inst))) {
			if (!chkbench(dom, alg, inst)) {
				printf("failed\n");
				return false;
			}
		} else {
			mkbench(dom, alg, inst);
		}
	}

	return true;
}

static std::string instpath(const Domain &dom, const std::string &inst) {
	return "bench/insts/" + dom.name + "/" + inst;
}

static std::string benchpath(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	return "bench/data/" + std::string(mname) + "_" +
		dom.name + "_" + alg.name + "_" + inst;
}

// chkbench returns true if the current behavior is
// consistent with the benchmark.
static bool chkbench(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	const std::string ipath = benchpath(dom, alg, inst);
	FILE *f = fopen(ipath.c_str(), "r");
	if (!f)
		fatalx(errno, "failed to open %s for reading", ipath.c_str());
	Result bench = readresult(f);
	fclose(f);

	// Must pass 1 of 3 runs within 110% of the mean time
	double lim = bench.time * 1.1;
	unsigned int ok = 0;
	for (unsigned int i = 0; i < 3 && ok < 1; i++) {
		Result r = run(dom, alg, inst);
		if (r.len != bench.len) {
			printf("	expected %u solution length\n", bench.len);
			continue;
		}
		if (r.time > lim) {
			printf("	expected ≤ %g seconds (mean: %gs)\n", lim, bench.time);
			continue;
		}
		ok++;
	}

	return ok >= 1;
}

// mkbench makes a new benchmark file.
static void mkbench(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	static const unsigned int N = 5;
	const std::string opath = benchpath(dom, alg, inst);
	printf("	making %s\n", opath.c_str());

	printf("	warmup run:	");
	run(dom, alg, inst);

	Result r[N];
	double meantime = 0;
	for (unsigned int i = 0; i < N; i++) {
		r[i] = run(dom, alg, inst);
		meantime += r[i].time;
	}
	meantime /= N;

	double vartime = 0;
	for (unsigned int i = 1; i < N; i++) {
		if (r[i].len != r[0].len)
			fatal("different lengths when solving %s %s %s",
				dom.name.c_str(), alg.name.c_str(), inst.c_str());
		double diff = r[i].time - meantime;
		vartime += diff * diff;
	}
	vartime /= N;
	double stdev = sqrt(vartime);

	printf("	%g avg. seconds, %g stdev\n", meantime, stdev);

	ensuredir(opath);
	FILE *o = fopen(opath.c_str(), "w");
	if (!o)
		fatalx(errno, "failed to open %s for writing", opath.c_str());

	dfpair(o, "machine name", "%s", mname);
	dfpair(o, "domain", "%s", dom.name.c_str());
	dfpair(o, "algorithm", "%s", alg.name.c_str());
	dfpair(o, "instance", "%s", inst.c_str());
	dfpair(o, "num runs", "%u", (unsigned int) N);
	dfpair(o, "mean wall time", "%g", meantime);
	dfpair(o, "stdev wall time", "%g", stdev);
	dfpair(o, "final sol length", "%u", (unsigned int) r[0].len);
	fclose(o);
}

// readresult reads the datafile from the stream
// and returns the result data.
static Result readresult(FILE *f) {
	Result res;
	dfread(f, dfline, &res);
	return res;
}

static void dfline(std::vector<const char *> &toks, void *_res) {
	Result *res = static_cast<Result*>(_res);
	if (strcmp(toks[1], "total wall time") == 0) {
		res->time = strtod(toks[2], NULL);
	} else if (strcmp(toks[1], "final sol length") == 0) {
		res->len = strtol(toks[2], NULL, 10);
	} else if (strcmp(toks[1], "mean wall time") == 0) {
		res->time = strtod(toks[2], NULL);
	} else if (strcmp(toks[1], "stdev wall time") == 0) {
		res->stdev = strtod(toks[2], NULL);
	} else if (strcmp(toks[1], "num runs") == 0) {
		res->nruns = strtol(toks[2], NULL, 10);
	}
}

// run runs the solver on the given instance using the
// given algorithm, returning the Result.
static Result run(const Domain &dom, const Algorithm &alg,
		const std::string &inst) {
	char cmd[1024];
	const std::string ipath = instpath(dom, inst);
	unsigned int res = snprintf(cmd, sizeof(cmd), dom.fmt.c_str(),
		alg.cmd.c_str(), ipath.c_str());
	if (res >= sizeof(cmd))
		fatal("mark.cc run(): buffer is too small");

	FILE *f = popen(cmd, "r");
	if (!f)
		fatal("failed to execute [%s]", cmd);
	Result r = readresult(f);
	pclose(f);

	printf("	%u sol length, %g seconds\n", r.len, r.time);
	return r;
}<|endoftext|>


<commit_before><commit_msg>Fix benchmark.<commit_after><|endoftext|>


<commit_before>#include "kernel.hpp"

#include "util/split.hpp"
#include "util/xml_helpers.hpp"
#include "util/string_mult.hpp"
#include "util/vec3_helpers.hpp"
#include "util/obj_mesh.hpp"

#include "util/thread_pool.hpp"

#include <algorithm>
#include <iostream>
#include <sstream>
#include <thread>
#include <chrono>

using namespace raytracer;

Kernel::Kernel() {
    verbose = true;

    handlers = {

        // General
        { "general", [this](ParamMap& params) {
            verbose = extractBool(params, "verbose");
            background_color = extractVec3(params, "background_color");
            num_threads = extractInt(params, "num_threads", std::max(std::thread::hardware_concurrency(), 1u));
            world_ki = extractFloat(params, "world_ki", 1.000293);
        } },

        // Spatial Indecies
        { "naive_index", [this](ParamMap& params) {
            spatial_index = std::make_shared<NaiveSpatialIndex>();
        } },

        { "kdtree_index", [this](ParamMap& params) {
            spatial_index = std::make_shared<KDTreeSpatialIndex>();
        } },

        // Cameras
        { "pinhole", [this](ParamMap& params) {
            cameras.push_back(std::make_shared<PinholeCamera>(
                extractFloat(params, "pixel_size"),
                extractInt(params, "num_samples"),
                extractInt(params, "num_bounces"),
                extractVec3(params, "eye"),
                extractVec3(params, "up"),
                extractVec3(params, "look_at"),
                extractFloat(params, "view_distance")));
        } },

        // ViewPlanes
        { "ppm_viewplane", [this](ParamMap& params) {
            cameras.at(cameras.size() - 1)->view_plane =
                std::make_shared<PPMViewPlane>(
                    extractInt(params, "hres"),
                    extractInt(params, "vres"),
                    extractString(params, "filename"));
        } },

        // Lights
        { "ambient", [this](ParamMap& params) {
            lights.push_back(std::make_shared<AmbientLight>(
                extractFloat(params, "intensity"),
                lookup_color(extractString(params, "color"))));
        } },

        { "directional", [this](ParamMap& params) {
            lights.push_back(std::make_shared<DirectionalLight>(
                extractFloat(params, "intensity"),
                glm::normalize(extractVec3(params, "direction")),
                lookup_color(extractString(params, "color"))));
        } },

        { "point", [this](ParamMap& params) {
            lights.push_back(std::make_shared<PointLight>(
                extractFloat(params, "intensity"),
                extractVec3(params, "point"),
                lookup_color(extractString(params, "color"))));
        } },

        // Color
        { "color", [this](ParamMap& params) {
            std::string name = extractString(params, "name");
            colors.emplace(extractString(params, "name"), extractVec3(params, "value"));
        } },

        // Materials
        { "phong", [this](ParamMap& params) {
            std::string name = extractString(params, "name");

            auto material =
                std::make_shared<Phong>(extractFloat(params, "ka"),
                                        extractFloat(params, "kd"),
                                        extractFloat(params, "ks"),
                                        extractFloat(params, "ke"),
                                        extractFloat(params, "kr"),
                                        extractFloat(params, "kt"),
                                        extractFloat(params, "ki"));

            last_material = material;

            materials.insert(std::make_pair(std::move(name), material));
        } },

        // Samplers
        { "solid", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<SolidSampler>(
                lookup_color(extractString(params, "color")));
        } },

        { "checkered", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<CheckerboardSampler>(
                extractFloat(params, "size"),
                lookup_color(extractString(params, "color1")),
                lookup_color(extractString(params, "color2")));
        } },

        { "circles", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<CirclesSampler>(
                extractFloat(params, "ring_size"),
                lookup_color(extractString(params, "color1")),
                lookup_color(extractString(params, "color2")));
        } },

        { "rainbow", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<RainbowSampler>(
                lookup_color(extractString(params, "color1")),
                lookup_color(extractString(params, "color2")));
        } },

        { "png", [this](ParamMap& params) {
            auto texture = std::unique_ptr<PNGTexture>(new PNGTexture());
            texture->load(extractString(params, "filename"));

            last_material->sampler =
                std::make_shared<PNGTextureSampler>(std::move(texture));
        } },

        // Shapes
        { "sphere", [this](ParamMap& params) {
            auto materialName = extractString(params, "material");
            auto material = lookup_material(materialName);
            if (material == nullptr) {
                std::cerr << "WARNING: Material '" << materialName << "' not found!" << std::endl;
            }

            spatial_index->insert(std::make_shared<Sphere>(
                extractVec3(params, "point"),
                extractFloat(params, "radius"), material));
        } },

        { "triangle", [this](ParamMap& params) {
            auto materialName = extractString(params, "material");
            auto material = lookup_material(materialName);
            if (material == nullptr) {
                std::cerr << "WARNING: Material '" << materialName << "' not found!" << std::endl;
            }

            spatial_index->insert(std::make_shared<Triangle>(
                extractVec3(params, "p0"),
                extractVec2(params, "uv0"),
                extractVec3(params, "p1"),
                extractVec2(params, "uv1"),
                extractVec3(params, "p2"),
                extractVec2(params, "uv2"), material));
        } },

        { "obj_mesh", [this](ParamMap& params) {

            auto point = extractVec3(params, "point");
            auto rotate = extractVec3(params, "rotate");
            auto scale = extractFloat(params, "scale");

            auto materialName = extractString(params, "material");
            auto material = lookup_material(materialName);
            if (material == nullptr) {
                std::cerr << "WARNING: Material '" << materialName << "' not found!" << std::endl;
            }

            loadObj(extractString(params, "filename"), point, rotate, scale, material, spatial_index);
        } }
    };
}

glm::vec3 Kernel::lookup_color(std::string name) {
    auto find_itr = colors.find(name);
    if (find_itr != colors.end()) {
        return find_itr->second;
    }
    throw std::runtime_error(std::string("Could not find color: '") + name + "'");
}

MaterialPtr Kernel::lookup_material(std::string name) {
    auto find_itr = materials.find(name);
    if (find_itr != materials.end()) {
        return find_itr->second;
    }
    throw std::runtime_error(std::string("Could not find material: '") + name + "'");
}

void Kernel::open(std::string& tag, ParamMap& params) {
    auto find_itr = handlers.find(tag);
    if (find_itr != handlers.end()) {
        find_itr->second(params);
    }
}

std::string Kernel::toString(size_t depth) {
    std::string tabdepth = std::string("\t") * depth;

    std::stringstream ss;

    // Now we'll print out the pieces.
    ss << tabdepth << "CAMERAS: \n";
    for (CameraPtr& camera : cameras) {
        ss << camera->toString(depth + 1);
    }

    ss << tabdepth << "LIGHTS: \n";
    for (auto& light : lights) {
        ss << tabdepth << light->toString(depth + 1);
    }

    ss << tabdepth << "COLORS: \n";
    for (auto& color : colors) {
        ss << tabdepth << '\t' << color.first << ":\n";
        ss << tabdepth << "\t\t" << color.second << "\n";
    }

    ss << tabdepth << "MATERIALS: \n";
    for (auto& material : materials) {
        ss << tabdepth << '\t' << material.first << ":\n";
        ss << material.second->toString(depth + 2);
    }

    ss << tabdepth << "SPATIAL INDEX: \n";
    ss << spatial_index->toString(depth + 1);

    return ss.str();
}

void Kernel::render() {
    typedef std::chrono::high_resolution_clock Clock;

    size_t i = 1;
    for (CameraPtr& camera : cameras) {
        auto start = Clock::now();

        // Now fire up a thread pool that does hit calculations and tone
        // reproduction.
        ThreadPool tp(num_threads);

        // Give the camera a function to call on each ray it generates.
        camera->spawn_rays([&](size_t row, size_t col, Ray& ray) {
            tp.enqueue([=]() {
                camera->view_plane->set_pixel(row, col, get_color_rec(ray, 1, camera->get_num_bounces()));
            });
        });

        // Now we wait for everyone to finish up.
        tp.stop();
        tp.join();

        // Signal to the view plane that we are finished writing.
        camera->view_plane->finish();

        auto end = Clock::now();

        std::cout << "Camera " << i << " Render Time: "
                  << std::chrono::duration_cast<std::chrono::milliseconds>(
                         end - start).count() << " miliseconds" << std::endl;

        ++i;
    }
}

glm::vec3 Kernel::get_color_rec(const Ray& ray, size_t num_bounces, size_t max_bounces)
{
    bool hit_found = false;
    glm::vec3 result;

    spatial_index->find_closest_hit(ray, [=, &hit_found, &result] (HitResult& hit) {

        // Get Direct Illumination
        result = hit.shape->material->get_color(this, hit);

        float kr = hit.shape->material->kr;
        float kt = hit.shape->material->kt;
        float ki = hit.shape->material->ki;

        if(num_bounces < max_bounces)
        {
            // Get Reflected Illumination
            if(kr > 0.0)
            {
                Ray refl{hit.intersection_point - hit.incoming_ray.direction * 0.05f, glm::reflect(hit.incoming_ray.direction, hit.intersection_normal)};
                refl.update();
                result += kr * get_color_rec(refl, num_bounces + 1, max_bounces);
            }

            // Get Transmitted Illumination
            if(kt > 0.0)
            {
                float dir = glm::dot(hit.incoming_ray.direction, hit.intersection_normal);

                if(dir < 0.0f)
                {
                    // We're entering the object.
                    Ray trans{hit.intersection_point + hit.incoming_ray.direction * 0.05f, glm::refract(-hit.incoming_ray.direction, hit.intersection_normal, world_ki / ki ) };
                    trans.update();
                    result += kt * get_color_rec(trans, num_bounces + 1, max_bounces);
                }
                else if(dir > 0.0f)
                {
                    // We're leaving the object.
                    // Pop our object's ki
                    Ray trans{hit.intersection_point + hit.incoming_ray.direction * 0.05f, glm::refract(-hit.incoming_ray.direction, -hit.intersection_normal, ki / world_ki) };
                    trans.update();
                    result += kt * get_color_rec(trans, num_bounces + 1, max_bounces);
                }
                else
                {
                    // We're hitting the object tangentially - no transmission
                }

            }
        }

        hit_found = true;
    });

    // Determine if we need the background color.
    if(!hit_found) {
        return background_color;
    }

    return result;
}
<commit_msg>more work towards working transmission.<commit_after>#include "kernel.hpp"

#include "util/split.hpp"
#include "util/xml_helpers.hpp"
#include "util/string_mult.hpp"
#include "util/vec3_helpers.hpp"
#include "util/obj_mesh.hpp"

#include "util/thread_pool.hpp"

#include <algorithm>
#include <iostream>
#include <sstream>
#include <thread>
#include <chrono>

using namespace raytracer;

Kernel::Kernel() {
    verbose = true;

    handlers = {

        // General
        { "general", [this](ParamMap& params) {
            verbose = extractBool(params, "verbose");
            background_color = extractVec3(params, "background_color");
            num_threads = extractInt(params, "num_threads", std::max(std::thread::hardware_concurrency(), 1u));
            world_ki = extractFloat(params, "world_ki", 1.000293);
        } },

        // Spatial Indecies
        { "naive_index", [this](ParamMap& params) {
            spatial_index = std::make_shared<NaiveSpatialIndex>();
        } },

        { "kdtree_index", [this](ParamMap& params) {
            spatial_index = std::make_shared<KDTreeSpatialIndex>();
        } },

        // Cameras
        { "pinhole", [this](ParamMap& params) {
            cameras.push_back(std::make_shared<PinholeCamera>(
                extractFloat(params, "pixel_size"),
                extractInt(params, "num_samples"),
                extractInt(params, "num_bounces"),
                extractVec3(params, "eye"),
                extractVec3(params, "up"),
                extractVec3(params, "look_at"),
                extractFloat(params, "view_distance")));
        } },

        // ViewPlanes
        { "ppm_viewplane", [this](ParamMap& params) {
            cameras.at(cameras.size() - 1)->view_plane =
                std::make_shared<PPMViewPlane>(
                    extractInt(params, "hres"),
                    extractInt(params, "vres"),
                    extractString(params, "filename"));
        } },

        // Lights
        { "ambient", [this](ParamMap& params) {
            lights.push_back(std::make_shared<AmbientLight>(
                extractFloat(params, "intensity"),
                lookup_color(extractString(params, "color"))));
        } },

        { "directional", [this](ParamMap& params) {
            lights.push_back(std::make_shared<DirectionalLight>(
                extractFloat(params, "intensity"),
                glm::normalize(extractVec3(params, "direction")),
                lookup_color(extractString(params, "color"))));
        } },

        { "point", [this](ParamMap& params) {
            lights.push_back(std::make_shared<PointLight>(
                extractFloat(params, "intensity"),
                extractVec3(params, "point"),
                lookup_color(extractString(params, "color"))));
        } },

        // Color
        { "color", [this](ParamMap& params) {
            std::string name = extractString(params, "name");
            colors.emplace(extractString(params, "name"), extractVec3(params, "value"));
        } },

        // Materials
        { "phong", [this](ParamMap& params) {
            std::string name = extractString(params, "name");

            auto material =
                std::make_shared<Phong>(extractFloat(params, "ka"),
                                        extractFloat(params, "kd"),
                                        extractFloat(params, "ks"),
                                        extractFloat(params, "ke"),
                                        extractFloat(params, "kr"),
                                        extractFloat(params, "kt"),
                                        extractFloat(params, "ki"));

            last_material = material;

            materials.insert(std::make_pair(std::move(name), material));
        } },

        // Samplers
        { "solid", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<SolidSampler>(
                lookup_color(extractString(params, "color")));
        } },

        { "checkered", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<CheckerboardSampler>(
                extractFloat(params, "size"),
                lookup_color(extractString(params, "color1")),
                lookup_color(extractString(params, "color2")));
        } },

        { "circles", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<CirclesSampler>(
                extractFloat(params, "ring_size"),
                lookup_color(extractString(params, "color1")),
                lookup_color(extractString(params, "color2")));
        } },

        { "rainbow", [this](ParamMap& params) {
            last_material->sampler = std::make_shared<RainbowSampler>(
                lookup_color(extractString(params, "color1")),
                lookup_color(extractString(params, "color2")));
        } },

        { "png", [this](ParamMap& params) {
            auto texture = std::unique_ptr<PNGTexture>(new PNGTexture());
            texture->load(extractString(params, "filename"));

            last_material->sampler =
                std::make_shared<PNGTextureSampler>(std::move(texture));
        } },

        // Shapes
        { "sphere", [this](ParamMap& params) {
            auto materialName = extractString(params, "material");
            auto material = lookup_material(materialName);
            if (material == nullptr) {
                std::cerr << "WARNING: Material '" << materialName << "' not found!" << std::endl;
            }

            spatial_index->insert(std::make_shared<Sphere>(
                extractVec3(params, "point"),
                extractFloat(params, "radius"), material));
        } },

        { "triangle", [this](ParamMap& params) {
            auto materialName = extractString(params, "material");
            auto material = lookup_material(materialName);
            if (material == nullptr) {
                std::cerr << "WARNING: Material '" << materialName << "' not found!" << std::endl;
            }

            spatial_index->insert(std::make_shared<Triangle>(
                extractVec3(params, "p0"),
                extractVec2(params, "uv0"),
                extractVec3(params, "p1"),
                extractVec2(params, "uv1"),
                extractVec3(params, "p2"),
                extractVec2(params, "uv2"), material));
        } },

        { "obj_mesh", [this](ParamMap& params) {

            auto point = extractVec3(params, "point");
            auto rotate = extractVec3(params, "rotate");
            auto scale = extractFloat(params, "scale");

            auto materialName = extractString(params, "material");
            auto material = lookup_material(materialName);
            if (material == nullptr) {
                std::cerr << "WARNING: Material '" << materialName << "' not found!" << std::endl;
            }

            loadObj(extractString(params, "filename"), point, rotate, scale, material, spatial_index);
        } }
    };
}

glm::vec3 Kernel::lookup_color(std::string name) {
    auto find_itr = colors.find(name);
    if (find_itr != colors.end()) {
        return find_itr->second;
    }
    throw std::runtime_error(std::string("Could not find color: '") + name + "'");
}

MaterialPtr Kernel::lookup_material(std::string name) {
    auto find_itr = materials.find(name);
    if (find_itr != materials.end()) {
        return find_itr->second;
    }
    throw std::runtime_error(std::string("Could not find material: '") + name + "'");
}

void Kernel::open(std::string& tag, ParamMap& params) {
    auto find_itr = handlers.find(tag);
    if (find_itr != handlers.end()) {
        find_itr->second(params);
    }
}

std::string Kernel::toString(size_t depth) {
    std::string tabdepth = std::string("\t") * depth;

    std::stringstream ss;

    // Now we'll print out the pieces.
    ss << tabdepth << "CAMERAS: \n";
    for (CameraPtr& camera : cameras) {
        ss << camera->toString(depth + 1);
    }

    ss << tabdepth << "LIGHTS: \n";
    for (auto& light : lights) {
        ss << tabdepth << light->toString(depth + 1);
    }

    ss << tabdepth << "COLORS: \n";
    for (auto& color : colors) {
        ss << tabdepth << '\t' << color.first << ":\n";
        ss << tabdepth << "\t\t" << color.second << "\n";
    }

    ss << tabdepth << "MATERIALS: \n";
    for (auto& material : materials) {
        ss << tabdepth << '\t' << material.first << ":\n";
        ss << material.second->toString(depth + 2);
    }

    ss << tabdepth << "SPATIAL INDEX: \n";
    ss << spatial_index->toString(depth + 1);

    return ss.str();
}

void Kernel::render() {
    typedef std::chrono::high_resolution_clock Clock;

    size_t i = 1;
    for (CameraPtr& camera : cameras) {
        auto start = Clock::now();

        // Now fire up a thread pool that does hit calculations and tone
        // reproduction.
        ThreadPool tp(num_threads);

        // Give the camera a function to call on each ray it generates.
        camera->spawn_rays([&](size_t row, size_t col, Ray& ray) {
            tp.enqueue([=]() {
                camera->view_plane->set_pixel(row, col, get_color_rec(ray, 1, camera->get_num_bounces()));
            });
        });

        // Now we wait for everyone to finish up.
        tp.stop();
        tp.join();

        // Signal to the view plane that we are finished writing.
        camera->view_plane->finish();

        auto end = Clock::now();

        std::cout << "Camera " << i << " Render Time: "
                  << std::chrono::duration_cast<std::chrono::milliseconds>(
                         end - start).count() << " miliseconds" << std::endl;

        ++i;
    }
}

glm::vec3 Kernel::get_color_rec(const Ray& ray, size_t num_bounces, size_t max_bounces)
{
    bool hit_found = false;
    glm::vec3 result;

    spatial_index->find_closest_hit(ray, [=, &hit_found, &result] (HitResult& hit) {

        // Get Direct Illumination
        result = hit.shape->material->get_color(this, hit);

        float kr = hit.shape->material->kr;
        float kt = hit.shape->material->kt;
        float ki = hit.shape->material->ki;

        if(num_bounces < max_bounces)
        {
            // Get Reflected Illumination
            if(kr > 0.0)
            {
                Ray refl{hit.intersection_point - hit.incoming_ray.direction * 0.05f, glm::reflect(hit.incoming_ray.direction, hit.intersection_normal)};
                refl.update();
                result += kr * get_color_rec(refl, num_bounces + 1, max_bounces);
            }

            // Get Transmitted Illumination
            if(kt > 0.0)
            {
                float dir = glm::dot(hit.intersection_normal, hit.incoming_ray.direction);

                auto normal = ((dir <= 0.0f) ? -1.0f : 1.0f) * hit.intersection_normal;

                auto refract1 = glm::refract( hit.incoming_ray.direction, normal, world_ki / ki );
                if(refract1 != glm::vec3(0.0f))
                {
                    auto refract2 = glm::refract( refract1, -normal, ki / world_ki );
                    if(refract2 != glm::vec3(0.0f))
                    {
                        Ray trans{ hit.intersection_point + refract2 * 0.05f, refract2 };
                        trans.update();
                        result += kt * get_color_rec(trans, num_bounces + 1, max_bounces);
                    }
                    else
                    {
                        Ray refl{hit.intersection_point - refract1 * 0.05f, glm::reflect(refract1, -normal)};
                        refl.update();
                        result += kt * get_color_rec(refl, num_bounces + 1, max_bounces);
                    }
                }
                else
                {
                    Ray refl{hit.intersection_point - hit.incoming_ray.direction * 0.05f, glm::reflect(hit.incoming_ray.direction, normal)};
                    refl.update();
                    result += kt * get_color_rec(refl, num_bounces + 1, max_bounces);
                }

                /* First perform the transmission
                auto refracted_dir = glm::refract( -hit.incoming_ray.direction, hit.intersection_normal, world_ki / ki );
                if(refracted_dir != glm::vec3(0.0f))
                {
                    // We're entering the object.
                    Ray trans{hit.intersection_point + hit.incoming_ray.direction * 0.05f, refracted_dir};
                    trans.update();
                    result += kt * get_color_rec(trans, num_bounces + 1, max_bounces);
                }
                else
                {
                    Ray refl{hit.intersection_point + hit.incoming_ray.direction * 0.05f, glm::reflect(hit.incoming_ray.direction, hit.intersection_normal)};
                    refl.update();
                    result += kt * get_color_rec(refl, num_bounces + 1, max_bounces);
                }

                auto refracted_dir = glm::refract( -hit.incoming_ray.direction, -hit.intersection_normal, ki / world_ki );
                if(refracted_dir != glm::vec3(0.0f))
                {
                    // We're leaving the object.
                    Ray trans{hit.intersection_point + hit.incoming_ray.direction * 0.05f, refracted_dir};
                    trans.update();
                    result += kt * get_color_rec(trans, num_bounces + 1, max_bounces);
                }
                else
                {
                    Ray refl{hit.intersection_point + hit.incoming_ray.direction * 0.05f, glm::reflect(hit.incoming_ray.direction, -hit.intersection_normal)};
                    refl.update();
                    result += kt * get_color_rec(refl, num_bounces + 1, max_bounces);
                }
                */
            }
        }

        hit_found = true;
    });

    // Determine if we need the background color.
    if(!hit_found) {
        return background_color;
    }

    return result;
}
<|endoftext|>


<commit_before>///
/// @file  benchmark.cpp
/// @brief Simple benchmark program for libpopcnt.h, repeatedly
///        counts the 1 bits inside a vector.
///
/// Usage: ./benchmark [array bytes] [iters]
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the LICENSE
/// file in the top level directory.
///

#include <libpopcnt.h>

#include <iostream>
#include <iomanip>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <stdint.h>
#include <string>

double get_seconds()
{
  return (double) std::clock() / CLOCKS_PER_SEC;
}

// init vector with random data
template <typename T>
void init(std::vector<T> & v)
{
  std::srand((unsigned) std::time(0));

  for (uint64_t i = 0; i < v.size(); i++)
    v[i] = (uint8_t) std::rand();
}

// count 1 bits inside vector
template <typename T>
uint64_t benchmark(std::vector<T> v, int iters)
{
  uint64_t total = 0;
  int old = - 1;

  for (int i = 0; i < iters; i++)
  {
    int percent = (int)(100.0 * i / iters);
    if (percent > old)
    {
      std::cout << "\rStatus: " << percent << "%" << std::flush;
      old = percent;
    }
    total += popcnt(&v[0], v.size());
  }

  return total;
}

void verify(uint64_t cnt, uint64_t total, int iters)
{
  if (cnt != total / iters)
  {
    std::cerr << "libpopcnt verification failed!" << std::endl;
    std::exit(1);
  }
}

int main(int argc, char* argv[])
{
  int bytes = (1 << 10) * 16;
  int iters = 10000000;

  if (argc > 1)
    bytes = std::atoi(argv[1]);
  if (argc > 2)
    iters = std::atoi(argv[2]);

  uint64_t cnt = 0;
  std::vector<uint8_t> v(bytes);
  std::string algo;
  init(v);

#if defined(X86_OR_X64)
  #if defined(HAVE_CPUID)
    int cpuid = get_cpuid();
    if ((cpuid & bit_AVX2) && bytes >= 512)
      algo = "AVX2";
    else if (cpuid & bit_POPCNT)
      algo = "POPCNT";
  #endif
#elif defined(__ARM_NEON) || \
      defined(__aarch64__)
  algo = "NEON";
#elif defined(__PPC64__)
  algo = "POPCNTD";
#endif

  if (algo.empty())
    algo = "integer popcount";

  std::cout << "Algorithm: " << algo << std::endl;

  for (uint64_t i = 0; i < v.size(); i++)
    cnt += popcount64(v[i]);

  double seconds = get_seconds();
  uint64_t total = benchmark(v, iters);
  seconds = get_seconds() - seconds;

  std::cout << "\rStatus: 100%" << std::endl;
  std::cout << "Seconds: " << std::fixed << std::setprecision(2) << seconds << std::endl;

  double total_bytes = (double) bytes * (double) iters;
  double GB = total_bytes / 1e9;
  double GBs = GB / seconds;

  std::cout << std::fixed << std::setprecision(1) << GBs << " GB/s" << std::endl;
  verify(cnt, total, iters);

  return 0;
}
<commit_msg>Print iters and array size<commit_after>///
/// @file  benchmark.cpp
/// @brief Simple benchmark program for libpopcnt.h, repeatedly
///        counts the 1 bits inside a vector.
///
/// Usage: ./benchmark [array bytes] [iters]
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the LICENSE
/// file in the top level directory.
///

#include <libpopcnt.h>

#include <iostream>
#include <iomanip>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <stdint.h>
#include <string>

double get_seconds()
{
  return (double) std::clock() / CLOCKS_PER_SEC;
}

// init vector with random data
template <typename T>
void init(std::vector<T>& v)
{
  std::srand((unsigned) std::time(0));

  for (uint64_t i = 0; i < v.size(); i++)
    v[i] = (uint8_t) std::rand();
}

// count 1 bits inside vector
template <typename T>
uint64_t benchmark(std::vector<T> v, int iters)
{
  uint64_t total = 0;
  int old = - 1;

  for (int i = 0; i < iters; i++)
  {
    int percent = (int)(100.0 * i / iters);
    if (percent > old)
    {
      std::cout << "\rStatus: " << percent << "%" << std::flush;
      old = percent;
    }
    total += popcnt(&v[0], v.size());
  }

  return total;
}

void verify(uint64_t cnt, uint64_t total, int iters)
{
  if (cnt != total / iters)
  {
    std::cerr << "libpopcnt verification failed!" << std::endl;
    std::exit(1);
  }
}

int main(int argc, char* argv[])
{
  int bytes = (1 << 10) * 16;
  int iters = 10000000;

  if (argc > 1)
    bytes = std::atoi(argv[1]);
  if (argc > 2)
    iters = std::atoi(argv[2]);

  uint64_t cnt = 0;
  std::vector<uint8_t> v(bytes);
  std::string algo;
  init(v);

  std::cout << "Iters: " << iters << std::endl;

  if (bytes < 1024)
    std::cout << "Array size: " << bytes << " bytes" << std::endl;
  else if (bytes < 1024 * 1024)
    std::cout << "Array size: " << std::fixed << std::setprecision(2) << bytes / 1024.0 << " KB" << std::endl;
  else
    std::cout << "Array size: " << std::fixed << std::setprecision(2) << bytes / (1024.0 * 1024.0) << " MB" << std::endl;

#if defined(X86_OR_X64)
  #if defined(HAVE_CPUID)
    int cpuid = get_cpuid();
    if ((cpuid & bit_AVX2) && bytes >= 512)
      algo = "AVX2";
    else if (cpuid & bit_POPCNT)
      algo = "POPCNT";
  #endif
#elif defined(__ARM_NEON) || \
      defined(__aarch64__)
  algo = "NEON";
#elif defined(__PPC64__)
  algo = "POPCNTD";
#endif

  if (algo.empty())
    algo = "integer popcount";

  std::cout << "Algorithm: " << algo << std::endl;

  for (uint64_t i = 0; i < v.size(); i++)
    cnt += popcount64(v[i]);

  double seconds = get_seconds();
  uint64_t total = benchmark(v, iters);
  seconds = get_seconds() - seconds;

  std::cout << "\rStatus: 100%" << std::endl;
  std::cout << "Seconds: " << std::fixed << std::setprecision(2) << seconds << std::endl;

  double total_bytes = (double) bytes * (double) iters;
  double GB = total_bytes / 1e9;
  double GBs = GB / seconds;

  std::cout << std::fixed << std::setprecision(1) << GBs << " GB/s" << std::endl;
  verify(cnt, total, iters);

  return 0;
}
<|endoftext|>


<commit_before>#include "lexer.hpp"

#include <cctype>
#include <algorithm>
#include <vector>

namespace klang {

Token::Token()
  : type_(TokenType::UNKNOWN), str_(), line_(-1)
{}

Token::Token(TokenType type, const std::string& str, int line)
  : type_(type), str_(str), line_(line)
{}

TokenType Token::type() const { return type_; }
std::string Token::str() const { return str_; }
int Token::line() const { return line_; }

bool alphabet(char c) {
  return std::isalpha(c);
}

bool alphabet_or_bar(char c) {
  return (c == '_' || alphabet(c));
}

bool nonzero_digit(char c) {
  return (c >= '1' && c <= '9');
}

bool decimal_digit(char c) {
  return std::isdigit(c);
}

bool identifier(const std::string& str) {
  if (str.empty() || !alphabet_or_bar(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!alphabet_or_bar(c) && !decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool decimal_integer(const std::string& str) {
  if (str == "0") return true;
  if (str.empty() || !nonzero_digit(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool symbol(const std::string& str) {
  using std::begin;
  using std::end;
  static std::vector<std::string> const symbol_list = {
    "~", "+", "-", "*", "/", "%",
    ":=", ":+=", ":-=", ":*=", ":/=", ":%=",
    "=", "=/", "<", ">", "<=", ">=",
    ";", "(", ")", "{", "}",
    "and", "or", "not", "int", "def", "var",
    "if", "else", "while", "for", "break", "continue", "return"
  };
  return (std::find(begin(symbol_list), end(symbol_list), str) != end(symbol_list));
}

bool ignore(const std::string& str) {
  return (str == " " || str == "\n");
}

TokenType match_type(std::string const& str) {
  if (symbol(str)) return TokenType::SYMBOL;
  if (identifier(str)) return TokenType::IDENTIFIER;
  if (decimal_integer(str)) return TokenType::NUMBER;
  if (ignore(str)) return TokenType::IGNORE;
  return TokenType::UNKNOWN;
}

TokenVector tokenize(std::istream& is) {
  std::string str, code;
  while (std::getline(is, str)) {
    code += str + '\n';
  }
  TokenVector tokens;
  str.clear();
  TokenType prev = TokenType::UNKNOWN;
  int line = 0;
  for (char c : code) {
    if (c == '\n') ++line;
    TokenType next = match_type(str + c);
    if (prev != TokenType::UNKNOWN && next == TokenType::UNKNOWN) {
      if (prev != TokenType::IGNORE) {
        tokens.push_back(Token(prev, str, line));
      }
      str = c;
      prev = match_type(str);
    } else {
      str += c;
      prev = next;
    }
  }
  return tokens;
}

}  // namespace klang
<commit_msg>Add -> token<commit_after>#include "lexer.hpp"

#include <cctype>
#include <algorithm>
#include <vector>

namespace klang {

Token::Token()
  : type_(TokenType::UNKNOWN), str_(), line_(-1)
{}

Token::Token(TokenType type, const std::string& str, int line)
  : type_(type), str_(str), line_(line)
{}

TokenType Token::type() const { return type_; }
std::string Token::str() const { return str_; }
int Token::line() const { return line_; }

bool alphabet(char c) {
  return std::isalpha(c);
}

bool alphabet_or_bar(char c) {
  return (c == '_' || alphabet(c));
}

bool nonzero_digit(char c) {
  return (c >= '1' && c <= '9');
}

bool decimal_digit(char c) {
  return std::isdigit(c);
}

bool identifier(const std::string& str) {
  if (str.empty() || !alphabet_or_bar(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!alphabet_or_bar(c) && !decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool decimal_integer(const std::string& str) {
  if (str == "0") return true;
  if (str.empty() || !nonzero_digit(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool symbol(const std::string& str) {
  using std::begin;
  using std::end;
  static std::vector<std::string> const symbol_list = {
    "~", "+", "-", "*", "/", "%",
    ":=", ":+=", ":-=", ":*=", ":/=", ":%=",
    "=", "=/", "<", ">", "<=", ">=",
    ";", "(", ")", "{", "}", "->",
    "and", "or", "not", "int", "def", "var",
    "if", "else", "while", "for", "break", "continue", "return"
  };
  return (std::find(begin(symbol_list), end(symbol_list), str) != end(symbol_list));
}

bool ignore(const std::string& str) {
  return (str == " " || str == "\n");
}

TokenType match_type(std::string const& str) {
  if (symbol(str)) return TokenType::SYMBOL;
  if (identifier(str)) return TokenType::IDENTIFIER;
  if (decimal_integer(str)) return TokenType::NUMBER;
  if (ignore(str)) return TokenType::IGNORE;
  return TokenType::UNKNOWN;
}

TokenVector tokenize(std::istream& is) {
  std::string str, code;
  while (std::getline(is, str)) {
    code += str + '\n';
  }
  TokenVector tokens;
  str.clear();
  TokenType prev = TokenType::UNKNOWN;
  int line = 0;
  for (char c : code) {
    if (c == '\n') ++line;
    TokenType next = match_type(str + c);
    if (prev != TokenType::UNKNOWN && next == TokenType::UNKNOWN) {
      if (prev != TokenType::IGNORE) {
        tokens.push_back(Token(prev, str, line));
      }
      str = c;
      prev = match_type(str);
    } else {
      str += c;
      prev = next;
    }
  }
  return tokens;
}

}  // namespace klang
<|endoftext|>


<commit_before>#include "scenemanager.hpp"
#include "scene.hpp"
#include "renderstates.hpp"
#include "../core/rendertarget.hpp"
#include <lib/core/log.hpp>
#include <lib/core/window.hpp>
#include <lib/core/resourcemanager.hpp>
#include <lib/core/appcontroller.hpp>
#include <lib/core/events/inputevent.hpp>

namespace lib
{
	namespace draw
	{

		SceneManager::SceneManager()
		{
			LOG_DESTRUCT_NOPARAMS;
		}

		SceneManager::~SceneManager()
		{
			for (auto scene : _scenes)
			{
				scene->deinit();
			}
			_scenes.clear();
			LOG_DESTRUCT_NOPARAMS;
		}

		void SceneManager::Init()
		{
			AppService::Init();
//			m_renderStates = sptr<RenderStates>(new RenderStates);
		}

		void SceneManager::addScene(sptr<Scene> newScene)
		{
			__ASSERT(newScene, "Cannot add a null scene");
			_scenes.push_back(newScene);
			newScene->setProvider(this);
			newScene->init();
		}

		void SceneManager::setScene(const std::string &name)
		{
			sptr<Scene> scene = getSceneByName(name);

			if (scene) {
				if (b_lock) {
					_nextScene = scene;
					LOG_DEBUG("Deferred Change scene to " << name);
				}
				else {
					b_lock = true;
					setScene(scene);
					b_lock = false;
				}
			}
			else {
				LOG_ERROR("Scene " << name << " not found in SceneManager");
			}
		}

		void SceneManager::addScenes(const std::vector<sptr<Scene>>&& sceneVector)
		{
			for (auto scene : sceneVector) {
				addScene(scene);
			}
		}

		void SceneManager::setScene(sptr<Scene> scene)
		{
			__ASSERT(scene, "Cannot change to a nullptr Scene");
			if (_currentScene) {
				_currentScene->privateOnExitScene();
			}
			else {
				LOG_DEBUG("Set first scene");
			}
			_currentScene = scene;
			_currentScene->privateOnEnterScene();
		}

		sptr<Scene> SceneManager::getSceneByName(const std::string &name) const
		{
			for (auto scene : _scenes) {
				if (scene->name() == name) {
					return scene;
				}
			}
			return nullptr;
		}

		void SceneManager::update()
		{
			if (!_currentScene) {
				if (_scenes.size() > 0) {
					setScene(_scenes[0]);
				}
			}
			else if (_nextScene) {
				setScene(_nextScene);
				_nextScene = nullptr;
			}
			else {
				if (_currentScene) {
					_currentScene->update();
				}
			}

			__ASSERT(m_renderStates.size() == 0, "Render states still on the stack");
			m_renderStates.emplace(RenderStates{service<core::Window>()->renderTarget().get()});
//			_currentScene->draw();
			visit(_currentScene);
			m_renderStates.pop();
			__ASSERT(m_renderStates.size() == 0, "Render states still on the stack");
		}

		void SceneManager::visit(const sptr<Node>& node)
		{
			service<Input>()->updateNode(node);
			const RenderStates &top{ m_renderStates.top() };
			RenderStates rStates{ top.blendMode, top.transform, top.texture, top.shader, top.currentTarget };

			if (auto transformableNode = as<Transformable>(node)) {
				rStates.transform *= transformableNode->transformation();
			}

			if (auto renderableNode = as<RenderNode>(node)) {
				rStates.texture = renderableNode->texture();
			}

			m_renderStates.push(rStates);

			if (auto drawableNode = as<RenderNode>(node)) {
				if (drawableNode->vertexArray().getVertexCount() > 0) {
					rStates.currentTarget->draw(drawableNode->vertexArray(), rStates);
				}
//				drawableNode->draw();
			}

			if (auto renderGroupNode = as<RenderGroup>(node)) {
				for (auto node_ : renderGroupNode->renderNodes()) {
					visit(node_);
				}
			}

			if (auto transformableNode = as<Transformable>(node)) {
				m_renderStates.pop();
			}

		}

		void SceneManager::exitProgram()
		{
//			p_parentWindow->exitProgram();
		}

		const RenderStates &SceneManager::frameRenderStates() const
		{
			return m_renderStates.top();
		}
	}
}
<commit_msg>reorder<commit_after>#include "scenemanager.hpp"
#include "scene.hpp"
#include "renderstates.hpp"
#include "../core/rendertarget.hpp"
#include <lib/core/log.hpp>
#include <lib/core/window.hpp>
#include <lib/core/resourcemanager.hpp>
#include <lib/core/appcontroller.hpp>
#include <lib/core/events/inputevent.hpp>

namespace lib
{
	namespace draw
	{

		SceneManager::SceneManager()
		{
			LOG_DESTRUCT_NOPARAMS;
		}

		SceneManager::~SceneManager()
		{
			for (auto scene : _scenes)
			{
				scene->deinit();
			}
			_scenes.clear();
			LOG_DESTRUCT_NOPARAMS;
		}

		void SceneManager::Init()
		{
			AppService::Init();
//			m_renderStates = sptr<RenderStates>(new RenderStates);
		}

		void SceneManager::addScene(sptr<Scene> newScene)
		{
			__ASSERT(newScene, "Cannot add a null scene");
			_scenes.push_back(newScene);
			newScene->setProvider(this);
			newScene->init();
		}

		void SceneManager::setScene(const std::string &name)
		{
			sptr<Scene> scene = getSceneByName(name);

			if (scene) {
				if (b_lock) {
					_nextScene = scene;
					LOG_DEBUG("Deferred Change scene to " << name);
				}
				else {
					b_lock = true;
					setScene(scene);
					b_lock = false;
				}
			}
			else {
				LOG_ERROR("Scene " << name << " not found in SceneManager");
			}
		}

		void SceneManager::addScenes(const std::vector<sptr<Scene>>&& sceneVector)
		{
			for (auto scene : sceneVector) {
				addScene(scene);
			}
		}

		void SceneManager::setScene(sptr<Scene> scene)
		{
			__ASSERT(scene, "Cannot change to a nullptr Scene");
			if (_currentScene) {
				_currentScene->privateOnExitScene();
			}
			else {
				LOG_DEBUG("Set first scene");
			}
			_currentScene = scene;
			_currentScene->privateOnEnterScene();
		}

		sptr<Scene> SceneManager::getSceneByName(const std::string &name) const
		{
			for (auto scene : _scenes) {
				if (scene->name() == name) {
					return scene;
				}
			}
			return nullptr;
		}

		void SceneManager::update()
		{
			if (!_currentScene) {
				if (_scenes.size() > 0) {
					setScene(_scenes[0]);
				}
			}
			else if (_nextScene) {
				setScene(_nextScene);
				_nextScene = nullptr;
			}
			else {
				if (_currentScene) {
					_currentScene->update();
				}
			}

			__ASSERT(m_renderStates.size() == 0, "Render states still on the stack");
			m_renderStates.emplace(RenderStates{service<core::Window>()->renderTarget().get()});
//			_currentScene->draw();
			visit(_currentScene);
			m_renderStates.pop();
			__ASSERT(m_renderStates.size() == 0, "Render states still on the stack");
		}

		void SceneManager::visit(const sptr<Node>& node)
		{
			service<Input>()->updateNode(node);
			const RenderStates &top{ m_renderStates.top() };
			RenderStates rStates{ top.blendMode, top.transform, top.texture, top.shader, top.currentTarget };

			if (auto transformableNode = as<Transformable>(node)) {
				rStates.transform *= transformableNode->transformation();
			}

			if (auto renderableNode = as<RenderNode>(node)) {
				rStates.texture = renderableNode->texture();
			}

			m_renderStates.push(rStates);

			if (auto drawableNode = as<RenderNode>(node)) {
				drawableNode->draw();
				if (drawableNode->vertexArray().getVertexCount() > 0) {
					rStates.currentTarget->draw(drawableNode->vertexArray(), rStates);
				}
			}

			if (auto renderGroupNode = as<RenderGroup>(node)) {
				for (auto node_ : renderGroupNode->renderNodes()) {
					visit(node_);
				}
			}

			if (auto transformableNode = as<Transformable>(node)) {
				m_renderStates.pop();
			}

		}

		void SceneManager::exitProgram()
		{
//			p_parentWindow->exitProgram();
		}

		const RenderStates &SceneManager::frameRenderStates() const
		{
			return m_renderStates.top();
		}
	}
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
#define __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__

#include <mutex>
#include <string>

#include <stout/nothing.hpp>
#include <stout/synchronized.hpp>
#include <stout/try.hpp>
#include <stout/windows.hpp>

#include <stout/os/mkdir.hpp>
#include <stout/os/realpath.hpp>

#include <stout/internal/windows/attributes.hpp>
#include <stout/internal/windows/longpath.hpp>

namespace os {
namespace stat {

// Specify whether symlink path arguments should be followed or
// not. APIs in the os::stat family that take a FollowSymlink
// argument all provide FollowSymlink::FOLLOW_SYMLINK as the default value,
// so they will follow symlinks unless otherwise specified.
enum class FollowSymlink
{
  DO_NOT_FOLLOW_SYMLINK,
  FOLLOW_SYMLINK
};

} // namespace stat {
} // namespace os {

// We pass this struct to `DeviceIoControl` to get information about a reparse
// point (including things like whether it's a symlink). It is normally part of
// the Device Driver Kit (DDK), specifically `nitfs.h`, but rather than taking
// a dependency on the DDK, we choose to simply copy the struct here. This is a
// well-worn path used by Boost FS[1], among others. See documentation
// here[2][3].
//
// [1] http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/src/operations.cpp
// [2] https://msdn.microsoft.com/en-us/library/cc232007.aspx
// [3] https://msdn.microsoft.com/en-us/library/cc232005.aspx
//
// We are declaring this structure (and the REPARSE_DATA_BUFFER_HEADER_SIZE
// macro right below it in the global namespace, to be consistent with the
// original Windows DDK declarations.
typedef struct _REPARSE_DATA_BUFFER
{
  // Describes, among other things, which type of reparse point this is (e.g.,
  // a symlink).
  ULONG  ReparseTag;
  // Size in bytes of common portion of the `REPARSE_DATA_BUFFER`.
  USHORT  ReparseDataLength;
  // Unused. Ignore.
  USHORT  Reserved;
  union
  {
    // Holds symlink data.
    struct
    {
      // Byte offset in `PathBuffer` where the substitute name begins.
      // Calculated as an offset from 0.
      USHORT SubstituteNameOffset;
      // Length in bytes of the substitute name.
      USHORT SubstituteNameLength;
      // Byte offset in `PathBuffer` where the print name begins. Calculated as
      // an offset from 0.
      USHORT PrintNameOffset;
      // Length in bytes of the print name.
      USHORT PrintNameLength;
      // Indicates whether symlink is absolute or relative. If flags containing
      // `SYMLINK_FLAG_RELATIVE`, then the substitute name is a relative
      // symlink.
      ULONG Flags;
      // The first byte of the path string -- according to the documentation[1],
      // this is followed in memory by the rest of the path string. The "path
      // string" itself is a unicode char array containing both substitute name
      // and print name. They can occur in any order. Use the offset and length
      // of each in this struct to calculate where each starts and ends.
      //
      // [1] https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
      WCHAR PathBuffer[1];
    } SymbolicLinkReparseBuffer;

    // Unused: holds mount point data.
    struct
    {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      WCHAR PathBuffer[1];
    } MountPointReparseBuffer;
    struct
    {
      UCHAR DataBuffer[1];
    } GenericReparseBuffer;
  };
} REPARSE_DATA_BUFFER;

#define REPARSE_DATA_BUFFER_HEADER_SIZE \
  FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)

namespace internal {
namespace windows {

// Convenience struct for holding symlink data, meant purely for internal use.
// We pass this around instead of the `REPARSE_DATA_BUFFER` struct, simply
// because this struct is easier to deal with and reason about.
struct SymbolicLink
{
  std::wstring substitute_name;
  std::wstring print_name;
  ULONG flags;
};

// Checks file/folder attributes for a path to see if the reparse point
// attribute is set; this indicates whether the path points at a reparse point,
// rather than a "normal" file or folder.
inline Try<bool> reparse_point_attribute_set(const std::wstring& absolute_path)
{
  const Try<DWORD> attributes = get_file_attributes(absolute_path.data());
  if (attributes.isError()) {
    return Error(attributes.error());
  }

  return (attributes.get() & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}

// Attempts to extract symlink data out of a `REPARSE_DATA_BUFFER` (which could
// hold other things, e.g., mount point data).
inline Try<SymbolicLink> build_symbolic_link(const REPARSE_DATA_BUFFER& data)
{
  const bool is_symLink = (data.ReparseTag & IO_REPARSE_TAG_SYMLINK) != 0;
  if (!is_symLink) {
    return Error("Data buffer is not a symlink");
  }

  // NOTE: This buffer is not null terminated.
  const WCHAR* substitute_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
  const size_t substitute_name_length =
    data.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);

  // NOTE: This buffer is not null terminated.
  const WCHAR* print_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR);
  const size_t print_name_length =
    data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR);

  return SymbolicLink{
      std::wstring(substitute_name, substitute_name_length),
      std::wstring(print_name, print_name_length),
      data.SymbolicLinkReparseBuffer.Flags};
}

// Attempts to get a file or folder handle for an absolute path, and does not
// follow symlinks. That is, if the path points at a symlink, the handle will
// refer to the symlink rather than the file or folder the symlink points at.
inline Try<SharedHandle> get_handle_no_follow(const std::string& absolute_path)
{
  const Try<DWORD> attributes = ::internal::windows::get_file_attributes(
      ::internal::windows::longpath(absolute_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  bool resolved_path_is_directory = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // NOTE: According to the `CreateFile` documentation[1], the `OPEN_EXISTING`
  // and `FILE_FLAG_OPEN_REPARSE_POINT` flags need to be used when getting a
  // handle for the symlink.
  //
  // Note also that `CreateFile` will appropriately generate a handle for
  // either a folder or a file, as long as the appropriate flag is being set:
  // `FILE_FLAG_BACKUP_SEMANTICS` or `FILE_FLAG_OPEN_REPARSE_POINT`.
  //
  // The `FILE_FLAG_BACKUP_SEMANTICS` flag is being set whenever the target is
  // a directory. According to MSDN[1]: "You must set this flag to obtain a
  // handle to a directory. A directory handle can be passed to some functions
  // instead of a file handle". More `FILE_FLAG_BACKUP_SEMANTICS` documentation
  // can be found in MSDN[2].
  //
  // The `GENERIC_READ` flag is being used because it's the most common way of
  // opening a file for reading only. The `FILE_SHARE_READ` allows other
  // processes to read the file at the same time. MSDN[1] provides a more
  // detailed explanation of these flags.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx
  const DWORD access_flags = resolved_path_is_directory
    ? (FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
    : FILE_FLAG_OPEN_REPARSE_POINT;

  const HANDLE handle = ::CreateFileW(
      longpath(absolute_path).data(),
      GENERIC_READ,     // Open the file for reading only.
      FILE_SHARE_READ,  // Just reading this file, allow others to do the same.
      nullptr,          // Ignored.
      OPEN_EXISTING,    // Open existing symlink.
      access_flags,     // Open symlink, not the file it points to.
      nullptr);         // Ignored.

  if (handle == INVALID_HANDLE_VALUE) {
    return WindowsError(
        "'internal::windows::get_handle_no_follow': 'CreateFile' call failed "
        "at path '" + absolute_path + "'");
  }

  return SharedHandle(handle, CloseHandle);
}

// Attempts to get the symlink data for a file or folder handle.
inline Try<SymbolicLink> get_symbolic_link_data(const HANDLE handle)
{
  // To get the symlink data, we call `DeviceIoControl`. This function is part
  // of the Device Driver Kit (DDK)[1] and, along with `FSCTL_GET_REPARSE_POINT`
  // is used to emit information about reparse points (and, thus, symlinks,
  // since symlinks are implemented with reparse points). This technique is
  // being used in Boost FS code as well[2].
  //
  // Summarized, the documentation tells us that we need to pass in
  // `FSCTL_GET_REPARSE_POINT` to get the function to populate a
  // `REPARSE_DATA_BUFFER` struct with data about a reparse point.
  // The `REPARSE_DATA_BUFFER` struct is defined in a DDK header file,
  // so to avoid bringing in a multitude of DDK headers we take a cue from
  // Boost FS, and copy the struct into this header (see above).
  //
  // Finally, for context, it may be worth looking at the MSDN
  // documentation[3] for `DeviceIoControl` itself.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
  // [2] https://svn.boost.org/trac/boost/ticket/4663
  // [3] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
  const size_t reparse_point_data_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
  BYTE buffer[reparse_point_data_size];
  REPARSE_DATA_BUFFER* reparse_point_data =
    reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer);

  DWORD ignored = 0;

  // The semantics of this function are: get the reparse data associated with
  // the `handle` of some open directory or file, and that data in
  // `reparse_point_data`.
  const BOOL reparse_data_obtained = ::DeviceIoControl(
      handle,                   // Handle to file or directory.
      FSCTL_GET_REPARSE_POINT,  // Gets reparse point data for file/folder.
      nullptr,                  // Ignored.
      0,                        // Ignored.
      reparse_point_data,
      reparse_point_data_size,
      &ignored,                 // Ignored.
      nullptr);                 // Ignored.

  if (!reparse_data_obtained) {
    return WindowsError(
        "'internal::windows::get_symbolic_link_data': 'DeviceIoControl' call "
        "failed");
  }

  return build_symbolic_link(*reparse_point_data);
}

// Creates a reparse point with the specified target. The target can be either
// a file (in which case a junction is created), or a folder (in which case a
// mount point is created).
//
// Calling this function results in a temporary elevation of the process token's
// privileges (if those privileges are not already held), to allow for junction
// or mount point creation. This operation is gated by a static mutex, which
// makes it thread-safe.
inline Try<Nothing> create_symbolic_link(
    const std::string& target,
    const std::string& reparse_point)
{
  // Normalize input paths.
  const Result<std::string> real_reparse_point_path =
    os::realpath(reparse_point);

  const Result<std::string> real_target_path = os::realpath(target);

  if (!real_reparse_point_path.isSome()) {
    return Error(
        "Failed to get realpath for '" +
        reparse_point + "': " + (real_reparse_point_path.isError() ?
        real_reparse_point_path.error() : "No such directory"));
  }

  if (!real_target_path.isSome()) {
    return Error(
        "Failed to get realpath for '" +
        target + "': " + (real_target_path.isError() ?
        real_target_path.error() : "No such directory"));
  }

  const std::string& absolute_target_path = real_target_path.get();

  // Determine if target is a folder or a file. This makes a difference
  // in the way we call `create_symbolic_link`.
  const Try<DWORD> attributes = ::internal::windows::get_file_attributes(
      ::internal::windows::longpath(absolute_target_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  const bool target_is_folder = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // Bail out if target is already a reparse point.
  Try<bool> attribute_set = reparse_point_attribute_set(
      longpath(absolute_target_path));

  if (!attribute_set.isSome()) {
    return Error(
        "Could not get reparse point attribute for '" + absolute_target_path +
        "'" + (attribute_set.isError() ? ": " + attribute_set.error() : ""));
  }

  if (attribute_set.get()) {
    return Error(
        "Path '" + absolute_target_path + "' is already a reparse point");
  }

  // `CreateSymbolicLink` adjusts the process token's privileges to allow for
  // symlink creation. MSDN[1] makes no guarantee when it comes to the thread
  // safety of this operation, so we are making use of a mutex to prevent
  // multiple concurrent calls.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363866(v=vs.85).aspx
  static std::mutex adjust_privileges_mutex;
  synchronized(adjust_privileges_mutex) {
    if (!::CreateSymbolicLinkW(
            // Path to link.
            longpath(real_reparse_point_path.get()).data(),
            // Path to target.
            longpath(real_target_path.get()).data(),
            target_is_folder ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) {
      return WindowsError(
          "'internal::windows::create_symbolic_link': 'CreateSymbolicLink' "
          "call failed");
    }
  }

  return Nothing();
}

} // namespace windows {
} // namespace internal {

#endif // __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
<commit_msg>Windows: Fixed `fs::symlink` to not need admin privileges.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
#define __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__

#include <string>

#include <stout/nothing.hpp>
#include <stout/try.hpp>
#include <stout/windows.hpp>

#include <stout/os/mkdir.hpp>
#include <stout/os/realpath.hpp>

#include <stout/internal/windows/attributes.hpp>
#include <stout/internal/windows/longpath.hpp>

namespace os {
namespace stat {

// Specify whether symlink path arguments should be followed or
// not. APIs in the os::stat family that take a FollowSymlink
// argument all provide FollowSymlink::FOLLOW_SYMLINK as the default value,
// so they will follow symlinks unless otherwise specified.
enum class FollowSymlink
{
  DO_NOT_FOLLOW_SYMLINK,
  FOLLOW_SYMLINK
};

} // namespace stat {
} // namespace os {

namespace internal {
namespace windows {

// We pass this struct to `DeviceIoControl` to get information about a reparse
// point (including things like whether it's a symlink). It is normally part of
// the Device Driver Kit (DDK), specifically `nitfs.h`, but rather than taking
// a dependency on the DDK, we choose to simply copy the struct here. This is a
// well-worn path used by Boost FS[1], among others. See documentation
// here[2][3].
//
// [1] http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/src/operations.cpp
// [2] https://msdn.microsoft.com/en-us/library/cc232007.aspx
// [3] https://msdn.microsoft.com/en-us/library/cc232005.aspx
typedef struct _REPARSE_DATA_BUFFER
{
  // Describes, among other things, which type of reparse point this is (e.g.,
  // a symlink).
  ULONG ReparseTag;
  // Size in bytes of common portion of the `REPARSE_DATA_BUFFER`.
  USHORT ReparseDataLength;
  // Unused. Ignore.
  USHORT Reserved;
  // Holds symlink data.
  struct
  {
    // Byte offset in `PathBuffer` where the substitute name begins.
    // Calculated as an offset from 0.
    USHORT SubstituteNameOffset;
    // Length in bytes of the substitute name.
    USHORT SubstituteNameLength;
    // Byte offset in `PathBuffer` where the print name begins. Calculated as
    // an offset from 0.
    USHORT PrintNameOffset;
    // Length in bytes of the print name.
    USHORT PrintNameLength;
    // Indicates whether symlink is absolute or relative. If flags containing
    // `SYMLINK_FLAG_RELATIVE`, then the substitute name is a relative
    // symlink.
    ULONG Flags;
    // The path string; the Windows declaration is the first byte, but we
    // declare a suitably sized array so we can use this struct more easily. The
    // "path string" itself is a Unicode `wchar` array containing both
    // substitute name and print name. They can occur in any order. Use the
    // offset and length of each in this struct to calculate where each starts
    // and ends.
    //
    // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
    WCHAR PathBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  } SymbolicLinkReparseBuffer;
} REPARSE_DATA_BUFFER;

// Convenience struct for holding symlink data, meant purely for internal use.
// We pass this around instead of the `REPARSE_DATA_BUFFER` struct, simply
// because this struct is easier to deal with and reason about.
struct SymbolicLink
{
  std::wstring substitute_name;
  std::wstring print_name;
  ULONG flags;
};

// Checks file/folder attributes for a path to see if the reparse point
// attribute is set; this indicates whether the path points at a reparse point,
// rather than a "normal" file or folder.
inline Try<bool> reparse_point_attribute_set(const std::wstring& absolute_path)
{
  const Try<DWORD> attributes = get_file_attributes(absolute_path.data());
  if (attributes.isError()) {
    return Error(attributes.error());
  }

  return (attributes.get() & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}

// Attempts to extract symlink data out of a `REPARSE_DATA_BUFFER` (which could
// hold other things, e.g., mount point data).
inline Try<SymbolicLink> build_symbolic_link(const REPARSE_DATA_BUFFER& data)
{
  const bool is_symLink = (data.ReparseTag & IO_REPARSE_TAG_SYMLINK) != 0;
  if (!is_symLink) {
    return Error("Data buffer is not a symlink");
  }

  // NOTE: This buffer is not null terminated.
  const WCHAR* substitute_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
  const size_t substitute_name_length =
    data.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);

  // NOTE: This buffer is not null terminated.
  const WCHAR* print_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR);
  const size_t print_name_length =
    data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR);

  return SymbolicLink{
      std::wstring(substitute_name, substitute_name_length),
      std::wstring(print_name, print_name_length),
      data.SymbolicLinkReparseBuffer.Flags};
}

// Attempts to get a file or folder handle for an absolute path, and does not
// follow symlinks. That is, if the path points at a symlink, the handle will
// refer to the symlink rather than the file or folder the symlink points at.
inline Try<SharedHandle> get_handle_no_follow(const std::string& absolute_path)
{
  const Try<DWORD> attributes = ::internal::windows::get_file_attributes(
      ::internal::windows::longpath(absolute_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  bool resolved_path_is_directory = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // NOTE: According to the `CreateFile` documentation[1], the `OPEN_EXISTING`
  // and `FILE_FLAG_OPEN_REPARSE_POINT` flags need to be used when getting a
  // handle for the symlink.
  //
  // Note also that `CreateFile` will appropriately generate a handle for
  // either a folder or a file, as long as the appropriate flag is being set:
  // `FILE_FLAG_BACKUP_SEMANTICS` or `FILE_FLAG_OPEN_REPARSE_POINT`.
  //
  // The `FILE_FLAG_BACKUP_SEMANTICS` flag is being set whenever the target is
  // a directory. According to MSDN[1]: "You must set this flag to obtain a
  // handle to a directory. A directory handle can be passed to some functions
  // instead of a file handle". More `FILE_FLAG_BACKUP_SEMANTICS` documentation
  // can be found in MSDN[2].
  //
  // The `GENERIC_READ` flag is being used because it's the most common way of
  // opening a file for reading only. The `FILE_SHARE_READ` allows other
  // processes to read the file at the same time. MSDN[1] provides a more
  // detailed explanation of these flags.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx
  const DWORD access_flags = resolved_path_is_directory
    ? (FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
    : FILE_FLAG_OPEN_REPARSE_POINT;

  const HANDLE handle = ::CreateFileW(
      longpath(absolute_path).data(),
      GENERIC_READ,     // Open the file for reading only.
      FILE_SHARE_READ,  // Just reading this file, allow others to do the same.
      nullptr,          // Ignored.
      OPEN_EXISTING,    // Open existing symlink.
      access_flags,     // Open symlink, not the file it points to.
      nullptr);         // Ignored.

  if (handle == INVALID_HANDLE_VALUE) {
    return WindowsError(
        "'internal::windows::get_handle_no_follow': 'CreateFile' call failed "
        "at path '" + absolute_path + "'");
  }

  return SharedHandle(handle, CloseHandle);
}

// Attempts to get the symlink data for a file or folder handle.
inline Try<SymbolicLink> get_symbolic_link_data(const HANDLE handle)
{
  // To get the symlink data, we call `DeviceIoControl`. This function is part
  // of the Device Driver Kit (DDK)[1] and, along with `FSCTL_GET_REPARSE_POINT`
  // is used to emit information about reparse points (and, thus, symlinks,
  // since symlinks are implemented with reparse points). This technique is
  // being used in Boost FS code as well[2].
  //
  // Summarized, the documentation tells us that we need to pass in
  // `FSCTL_GET_REPARSE_POINT` to get the function to populate a
  // `REPARSE_DATA_BUFFER` struct with data about a reparse point.
  // The `REPARSE_DATA_BUFFER` struct is defined in a DDK header file,
  // so to avoid bringing in a multitude of DDK headers we take a cue from
  // Boost FS, and copy the struct into this header (see above).
  //
  // Finally, for context, it may be worth looking at the MSDN
  // documentation[3] for `DeviceIoControl` itself.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
  // [2] https://svn.boost.org/trac/boost/ticket/4663
  // [3] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
  REPARSE_DATA_BUFFER buffer;
  DWORD ignored = 0;

  // The semantics of this function are: get the reparse data associated with
  // the `handle` of some open directory or file, and that data in
  // `reparse_point_data`.
  const BOOL reparse_data_obtained = ::DeviceIoControl(
      handle,                   // Handle to file or directory.
      FSCTL_GET_REPARSE_POINT,  // Gets reparse point data for file/folder.
      nullptr,                  // Ignored.
      0,                        // Ignored.
      &buffer,
      sizeof(buffer),
      &ignored,                 // Ignored.
      nullptr);                 // Ignored.

  if (!reparse_data_obtained) {
    return WindowsError(
        "'internal::windows::get_symbolic_link_data': 'DeviceIoControl' call "
        "failed");
  }

  return build_symbolic_link(buffer);
}

// Creates a reparse point with the specified target. The target can be either
// a file (in which case a junction is created), or a folder (in which case a
// mount point is created).
inline Try<Nothing> create_symbolic_link(
    const std::string& target,
    const std::string& reparse_point)
{
  // Normalize input paths.
  const Result<std::string> real_reparse_point_path =
    os::realpath(reparse_point);

  const Result<std::string> real_target_path = os::realpath(target);

  if (!real_reparse_point_path.isSome()) {
    return Error(
        "Failed to get realpath for '" +
        reparse_point + "': " + (real_reparse_point_path.isError() ?
        real_reparse_point_path.error() : "No such directory"));
  }

  if (!real_target_path.isSome()) {
    return Error(
        "Failed to get realpath for '" +
        target + "': " + (real_target_path.isError() ?
        real_target_path.error() : "No such directory"));
  }

  const std::string& absolute_target_path = real_target_path.get();

  // Determine if target is a folder or a file. This makes a difference
  // in the way we call `create_symbolic_link`.
  const Try<DWORD> attributes = get_file_attributes(
      longpath(absolute_target_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  const bool target_is_folder = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // Bail out if target is already a reparse point.
  Try<bool> attribute_set = reparse_point_attribute_set(
      longpath(absolute_target_path));

  if (!attribute_set.isSome()) {
    return Error(
        "Could not get reparse point attribute for '" + absolute_target_path +
        "'" + (attribute_set.isError() ? ": " + attribute_set.error() : ""));
  }

  if (attribute_set.get()) {
    return Error(
        "Path '" + absolute_target_path + "' is already a reparse point");
  }

  // Avoid requiring administrative privileges to create symbolic links.
  DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
  if (target_is_folder) {
    flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
  }

  // `CreateSymbolicLink` normally adjusts the process token's privileges to
  // allow for symlink creation; however, we explicitly avoid this with the
  // above flag.
  if (!::CreateSymbolicLinkW(
          // Path to link.
          longpath(real_reparse_point_path.get()).data(),
          // Path to target.
          longpath(real_target_path.get()).data(),
          flags)) {
    return WindowsError(
        "'internal::windows::create_symbolic_link': 'CreateSymbolicLink' "
        "call failed");
  }

  return Nothing();
}

} // namespace windows {
} // namespace internal {

#endif // __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
<|endoftext|>


<commit_before>/// HEADER
#include "flip.h"

/// PROJECT
#include <csapex/utility/register_apex_plugin.h>
#include <csapex/msg/input.h>
#include <csapex/msg/output.h>
#include <utils_param/parameter_factory.h>
#include <csapex_vision/cv_mat_message.h>
#include <csapex/model/node_modifier.h>

/// SYSTEM
#include <boost/assign.hpp>

using namespace csapex;
using namespace csapex::connection_types;
using namespace vision_plugins;

CSAPEX_REGISTER_CLASS(vision_plugins::Flip, csapex::Node)

Flip::Flip()
{
}

void Flip::process()
{
    CvMatMessage::Ptr in = input_->getMessage<connection_types::CvMatMessage>();
    CvMatMessage::Ptr out(new connection_types::CvMatMessage(in->getEncoding()));
    cv::flip(in->value, out->value, mode_);
    output_->publish(out);
}

void Flip::setup()
{
    input_ = modifier_->addInput<CvMatMessage>("original");
    output_ = modifier_->addOutput<CvMatMessage>("flipped");
}

void Flip::setupParameters()
{
    std::map<std::string, int> types = boost::assign::map_list_of
            ("v", 0)
            ("h", 1)
            ("v+h", -1);
    addParameter(param::ParameterFactory::declareParameterSet("type", types, -1),
                 boost::bind(&Flip::update, this));
}

void Flip::update()
{
    mode_ = readParameter<int>("type");
}
<commit_msg>flip can now rotate too<commit_after>/// HEADER
#include "flip.h"

/// PROJECT
#include <csapex/utility/register_apex_plugin.h>
#include <csapex/msg/input.h>
#include <csapex/msg/output.h>
#include <utils_param/parameter_factory.h>
#include <csapex_vision/cv_mat_message.h>
#include <csapex/model/node_modifier.h>

/// SYSTEM
#include <boost/assign.hpp>

using namespace csapex;
using namespace csapex::connection_types;
using namespace vision_plugins;

CSAPEX_REGISTER_CLASS(vision_plugins::Flip, csapex::Node)

Flip::Flip()
{
}

void Flip::process()
{
    CvMatMessage::Ptr in = input_->getMessage<connection_types::CvMatMessage>();
    CvMatMessage::Ptr out(new connection_types::CvMatMessage(in->getEncoding()));

    switch(mode_) {
    case -1:
    case 0:
    case 1:
        cv::flip(in->value, out->value, mode_);
        break;

    case 2: // +90
        cv::transpose(in->value, out->value);
        cv::flip(out->value, out->value, 0);
        break;

    case 3: // -90
        cv::transpose(in->value, out->value);
        cv::flip(out->value, out->value, 1);
        break;

    }
    output_->publish(out);
}

void Flip::setup()
{
    input_ = modifier_->addInput<CvMatMessage>("original");
    output_ = modifier_->addOutput<CvMatMessage>("flipped");
}

void Flip::setupParameters()
{
    std::map<std::string, int> types = boost::assign::map_list_of
            ("v", 0)
            ("h", 1)
            ("+90", 2)
            ("-90", 3)
            ("v+h", -1);
    addParameter(param::ParameterFactory::declareParameterSet("type", types, -1),
                 boost::bind(&Flip::update, this));
}

void Flip::update()
{
    mode_ = readParameter<int>("type");
}
<|endoftext|>


<commit_before>#include "MensajeHTTPRequest.h"
#include "gtest/gtest.h"
#include <string>
#include <iostream>

using namespace std;

class HttpMessageRequestTest : public testing::Test{

	protected:

		virtual void SetUp(){
			mg_str metodo, uri, header0, header1, headerVacio, valor0, valor1, valorVacio;
			this->iniciarMg_str(metodo, "METODO",6);
			this->iniciarMg_str(uri, "Uri1/Uri2/servicio",18);
			this->iniciarMg_str(header0, "Header0",7);
			this->iniciarMg_str(header1, "Header1",7);
			this->iniciarMg_str(headerVacio, "",0);
			this->iniciarMg_str(valor0, "Valor0",6);
			this->iniciarMg_str(valor1, "Valor1",6);
			this->iniciarMg_str(valorVacio, "",0);

			//Aca se copian los valores
			this->mensajeMG.method = metodo;
			this->mensajeMG.uri = uri;

			this->mensajeMG.header_names[0] = header0;
			this->mensajeMG.header_values[0] = valor0;

			this->mensajeMG.header_names[1] = header1;
			this->mensajeMG.header_values[1] = valor1;

			this->mensajeMG.header_names[2] = headerVacio;
			this->mensajeMG.header_values[2] = valorVacio;

			//El 0 dummy esta por ahora, porque queria probar algo en el codigo, TODO: sacarla
			mensaje = new MensajeHTTPRequest(&(this->mensajeMG));

		}

		virtual void TearDown(){
			delete mensaje;
		}

		string crearHeaderBodyLenght(string body){

			return ("Content-Length: " + to_string(body.length()) + "\r\n");
		}

		void iniciarMg_str(mg_str& mg_string, const char* contenido, size_t longitud){
			mg_string.p = contenido;
			mg_string.len = longitud;
		}

		http_message mensajeMG;
		MensajeHTTPRequest* mensaje;

};

TEST_F(HttpMessageRequestTest, testObjenerMetodoDelMensajeHTTPRequest){

	ASSERT_EQ("METODO", mensaje->getMetodo());

}

TEST_F(HttpMessageRequestTest, testObtenerUriDelMensajeHTTPRequest){
	ASSERT_EQ( "Uri1/Uri2/servicio", mensaje->getURI());

}

TEST_F(HttpMessageRequestTest, testObtenerHeadersDelMensajeHTTPRequest){
  ASSERT_EQ( "Valor0" , mensaje->getHeader("Header0"));
  ASSERT_EQ( "Valor1" , mensaje->getHeader("Header1"));

}

TEST_F(HttpMessageRequestTest,testCrearUnMensajeHTTPRequestDesdeCero){
  MensajeHTTPRequest mensajeDesdeCero;
  string metodo,uri,header1,valor1,header2,valor2, body;
  metodo = "METODO";
  uri = "Uri3/Uri4/servicio";
  header1 = "Header1";
  valor1 = "Valor1";
  header2= "Header2";
  valor2 = "Valor2";
  body = "body body body\n funciona con newlines\nsomething";
  mensajeDesdeCero.setMetodo(metodo);
  mensajeDesdeCero.setURI(uri);
  mensajeDesdeCero.agregarHeader(header1, valor1);
  mensajeDesdeCero.agregarHeader(header2, valor2);
  mensajeDesdeCero.setBody(body);

  string headerBodyLenght = crearHeaderBodyLenght(body);

  string mensajeEsperado = metodo + " " + uri + " HTTP/1.1\r\n" + headerBodyLenght + header1 + ": " + valor1 + "\r\n" + header2 + ": " + valor2 + "\r\n\r\n" + body;

  ASSERT_EQ( mensajeEsperado,mensajeDesdeCero.toString());
}

<commit_msg>Arreglo un bug de la Test de MensajeHTTPRequest, no se estaba inicializando el body<commit_after>#include "MensajeHTTPRequest.h"
#include "gtest/gtest.h"
#include <string>
#include <iostream>

using namespace std;

class HttpMessageRequestTest : public testing::Test{

	protected:

		virtual void SetUp(){
			mg_str metodo, uri, header0, header1, headerVacio, valor0, valor1, valorVacio, body;
			this->iniciarMg_str(metodo, "METODO",6);
			this->iniciarMg_str(uri, "Uri1/Uri2/servicio",18);
			this->iniciarMg_str(header0, "Header0",7);
			this->iniciarMg_str(header1, "Header1",7);
			this->iniciarMg_str(headerVacio, "",0);
			this->iniciarMg_str(valor0, "Valor0",6);
			this->iniciarMg_str(valor1, "Valor1",6);
			this->iniciarMg_str(valorVacio, "",0);
			this->iniciarMg_str(body, "",0);

			//Aca se copian los valores
			this->mensajeMG.method = metodo;
			this->mensajeMG.uri = uri;

			this->mensajeMG.header_names[0] = header0;
			this->mensajeMG.header_values[0] = valor0;

			this->mensajeMG.header_names[1] = header1;
			this->mensajeMG.header_values[1] = valor1;

			this->mensajeMG.header_names[2] = headerVacio;
			this->mensajeMG.header_values[2] = valorVacio;

			this->mensajeMG.body = body;

			mensaje = new MensajeHTTPRequest(&(this->mensajeMG));

		}

		virtual void TearDown(){
			delete mensaje;
		}

		string crearHeaderBodyLenght(string body){

			return ("Content-Length: " + to_string(body.length()) + "\r\n");
		}

		void iniciarMg_str(mg_str& mg_string, const char* contenido, size_t longitud){
			mg_string.p = contenido;
			mg_string.len = longitud;
		}

		http_message mensajeMG;
		MensajeHTTPRequest* mensaje;

};

TEST_F(HttpMessageRequestTest, testObjenerMetodoDelMensajeHTTPRequest){

	ASSERT_EQ("METODO", mensaje->getMetodo());

}

TEST_F(HttpMessageRequestTest, testObtenerUriDelMensajeHTTPRequest){
	ASSERT_EQ( "Uri1/Uri2/servicio", mensaje->getURI());

}

TEST_F(HttpMessageRequestTest, testObtenerHeadersDelMensajeHTTPRequest){
  ASSERT_EQ( "Valor0" , mensaje->getHeader("Header0"));
  ASSERT_EQ( "Valor1" , mensaje->getHeader("Header1"));

}

TEST_F(HttpMessageRequestTest,testCrearUnMensajeHTTPRequestDesdeCero){
  MensajeHTTPRequest mensajeDesdeCero;
  string metodo,uri,header1,valor1,header2,valor2, body;
  metodo = "METODO";
  uri = "Uri3/Uri4/servicio";
  header1 = "Header1";
  valor1 = "Valor1";
  header2= "Header2";
  valor2 = "Valor2";
  body = "body body body\n funciona con newlines\nsomething";
  mensajeDesdeCero.setMetodo(metodo);
  mensajeDesdeCero.setURI(uri);
  mensajeDesdeCero.agregarHeader(header1, valor1);
  mensajeDesdeCero.agregarHeader(header2, valor2);
  mensajeDesdeCero.setBody(body);

  string headerBodyLenght = crearHeaderBodyLenght(body);

  string mensajeEsperado = metodo + " " + uri + " HTTP/1.1\r\n" + headerBodyLenght + header1 + ": " + valor1 + "\r\n" + header2 + ": " + valor2 + "\r\n\r\n" + body;

  ASSERT_EQ( mensajeEsperado,mensajeDesdeCero.toString());
}

<|endoftext|>