<commit_before>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "thread.hh"

#include "config.h"              // for HAVE_PTHREADS, HAVE_PTHREAD_SETNAME_NP

#include <errno.h>               // for errno
#include <array>                 // for std::array
#include <atomic>                // for std::atomic_int
#include <mutex>                 // for std::mutex, std::lock_guard
#include <string>                // for std::string
#include <string_view>           // for std::string_view
#include <system_error>          // for std::system_error
#include <thread>                // for std::thread
#include <tuple>                 // for std::forward_as_tuple
#include <unordered_map>         // for std::unordered_map
#include <vector>                // for std::vector
#include <unistd.h>              // for getpid
#include <dlfcn.h>               // for dlsym
#ifdef HAVE_PTHREADS
#include <pthread.h>             // for pthread_self
#endif
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>          // for pthread_setname_np
#endif

#include "error.hh"              // for error::name, error::description
#include "log.h"                 // for L_WARNING_ONCE
#include "traceback.h"           // for backtrace, traceback

static std::mutex thread_names_mutex;
static std::unordered_map<std::thread::id, std::string> thread_names;
static std::array<pthread_t, 1000> pthreads;
static std::array<const char*, 1000> pthreads_names;
static std::array<void**, 1000> callstacks;
static std::array<void**, 1000> snapshots;
static std::atomic_size_t pthreads_num;
static std::atomic_size_t pthreads_busy;

#ifdef __APPLE__
#include <cpuid.h>
int
sched_getcpu()
{
	uint32_t info[4];
	__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
	if ( (info[3] & (1 << 9)) == 0) {
		return -1;  // no APIC on chip
	}
	// info[1] is EBX, bits 24-31 are APIC ID
	return (unsigned)info[1] >> 24;
}
#endif

void
run_thread(void *(*thread_routine)(void *), void *arg, ThreadPolicyType)
{
	std::thread(thread_routine, arg).detach();
}

void
setup_thread(const std::string& name, ThreadPolicyType)
{
	set_thread_name(name);
}

////////////////////////////////////////////////////////////////////////////////

void
set_thread_name(const std::string& name)
{
	auto pthread = pthread_self();
#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__linux__)
	pthread_setname_np(pthread, ("Xapiand:" + name).c_str());
	// pthread_setname_np(pthread, ("Xapiand:" + name).c_str(), nullptr);
#elif defined(HAVE_PTHREAD_SETNAME_NP)
	pthread_setname_np(("Xapiand:" + name).c_str());
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
	pthread_set_name_np(pthread, ("Xapiand:" + name).c_str());
#endif
	std::lock_guard<std::mutex> lk(thread_names_mutex);
	auto emplaced = thread_names.emplace(std::piecewise_construct,
		std::forward_as_tuple(std::this_thread::get_id()),
		std::forward_as_tuple(name));
	auto idx = pthreads_num.fetch_add(1);
	if (idx < pthreads.size()) {
		pthreads[idx] = pthread;
		pthreads_names[idx] = emplaced.first->second.c_str();
	}
}

const std::string&
get_thread_name(std::thread::id thread_id)
{
	std::lock_guard<std::mutex> lk(thread_names_mutex);
	auto thread = thread_names.find(thread_id);
	if (thread == thread_names.end()) {
		static std::string _ = "???";
		return _;
	}
	return thread->second;
}

const std::string&
get_thread_name()
{
	return get_thread_name(std::this_thread::get_id());
}

////////////////////////////////////////////////////////////////////////////////

void
collect_callstack_sig_handler(int /*signum*/)
{
	auto pthread = pthread_self();
	for (size_t idx = 0; idx < pthreads.size() && idx < pthreads_num.load(); ++idx) {
		if (pthreads[idx] == pthread) {
			auto& callstack = callstacks[idx];
			if (callstack != nullptr) {
				free(callstack);
			}
			callstack = backtrace();
			pthreads_busy.fetch_sub(1);
			return;
		}
	}
}

void
collect_callstacks()
{
	auto total = pthreads_num.load();
	if (total > pthreads.size()) {
		total = pthreads.size();
	}

	size_t zero;
	do {
		zero = 0;
		sched_yield();
	} while (!pthreads_busy.compare_exchange_weak(zero, total));

	for (size_t idx = 0; idx < total; ++idx) {
		if (pthread_kill(pthreads[idx], SIGUSR2) != 0) {
			pthreads_busy.fetch_sub(1);
		}
	}

	do {
		zero = 0;
		sched_yield();
	} while (!pthreads_busy.compare_exchange_weak(zero, 1));

	for (size_t idx = 1; idx < total; ++idx) {
		auto& callstack = callstacks[idx];
		auto& snapshot = snapshots[idx];
		size_t callstack_frames = static_cast<void**>(*callstack) - callstack;
		size_t snapshot_frames = snapshot ? static_cast<void**>(*snapshot) - snapshot : 0;
		if (snapshot_frames <= 3 || callstack_frames <= 3 || snapshot[3] != callstack[3]) {
			print(traceback(pthreads_names[idx], "", idx, callstack), 3);
		}
	}

	pthreads_busy.store(0);
}

void
callstacks_snapshot()
{
	bool retry;
	do {
		retry = false;
		auto total = pthreads_num.load();
		if (total > pthreads.size()) {
			total = pthreads.size();
		}

		size_t zero;
		do {
			zero = 0;
			sched_yield();
		} while (!pthreads_busy.compare_exchange_weak(zero, total));

		for (size_t idx = 0; idx < total; ++idx) {
			if (pthread_kill(pthreads[idx], SIGUSR2) != 0) {
				pthreads_busy.fetch_sub(1);
			}
		}

		do {
			zero = 0;
			sched_yield();
		} while (!pthreads_busy.compare_exchange_weak(zero, 1));

		for (size_t idx = 0; idx < total; ++idx) {
			auto& callstack = callstacks[idx];
			auto& snapshot = snapshots[idx];
			auto different = true;
			size_t callstack_frames = static_cast<void**>(*callstack) - callstack;
			if (snapshot) {
				size_t snapshot_frames = static_cast<void**>(*snapshot) - snapshot;
				if (snapshot_frames == callstack_frames) {
					different = false;
					for (size_t i = 0; i < callstack_frames; ++i) {
						if (callstack[i + 1] != snapshot[i + 1]) {
							different = true;
							break;
						}
					}
				}
			}
			if (different) {
				if (snapshot) {
					free(snapshot);
				}
				snapshot = (void**)malloc((callstack_frames + 1) * sizeof(void*));
				memcpy(snapshot + 1, callstack + 1, callstack_frames * sizeof(void*));
				snapshot[0] = &snapshot[callstack_frames];
				retry = true;
			}
		}

		pthreads_busy.store(0);
	} while (retry);
}
<commit_msg>Thread: Lock for pthreads<commit_after>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "thread.hh"

#include "config.h"              // for HAVE_PTHREADS, HAVE_PTHREAD_SETNAME_NP

#include <errno.h>               // for errno
#include <array>                 // for std::array
#include <atomic>                // for std::atomic_int
#include <mutex>                 // for std::mutex, std::lock_guard
#include <string>                // for std::string
#include <string_view>           // for std::string_view
#include <system_error>          // for std::system_error
#include <thread>                // for std::thread
#include <tuple>                 // for std::forward_as_tuple
#include <unordered_map>         // for std::unordered_map
#include <vector>                // for std::vector
#include <unistd.h>              // for getpid
#include <dlfcn.h>               // for dlsym
#ifdef HAVE_PTHREADS
#include <pthread.h>             // for pthread_self
#endif
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>          // for pthread_setname_np
#endif

#include "error.hh"              // for error::name, error::description
#include "log.h"                 // for L_WARNING_ONCE
#include "traceback.h"           // for backtrace, traceback

static std::mutex thread_names_mutex;
static std::unordered_map<std::thread::id, std::string> thread_names;
static std::array<pthread_t, 1000> pthreads;
static std::array<const char*, 1000> pthreads_names;
static std::array<void**, 1000> callstacks;
static std::array<void**, 1000> snapshots;
static std::atomic_size_t pthreads_num;
static std::atomic_size_t pthreads_busy;

#ifdef __APPLE__
#include <cpuid.h>
int
sched_getcpu()
{
	uint32_t info[4];
	__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
	if ( (info[3] & (1 << 9)) == 0) {
		return -1;  // no APIC on chip
	}
	// info[1] is EBX, bits 24-31 are APIC ID
	return (unsigned)info[1] >> 24;
}
#endif

void
run_thread(void *(*thread_routine)(void *), void *arg, ThreadPolicyType)
{
	std::thread(thread_routine, arg).detach();
}

void
setup_thread(const std::string& name, ThreadPolicyType)
{
	set_thread_name(name);
}

////////////////////////////////////////////////////////////////////////////////

void
set_thread_name(const std::string& name)
{
	auto pthread = pthread_self();
#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__linux__)
	pthread_setname_np(pthread, ("Xapiand:" + name).c_str());
	// pthread_setname_np(pthread, ("Xapiand:" + name).c_str(), nullptr);
#elif defined(HAVE_PTHREAD_SETNAME_NP)
	pthread_setname_np(("Xapiand:" + name).c_str());
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
	pthread_set_name_np(pthread, ("Xapiand:" + name).c_str());
#endif
	std::lock_guard<std::mutex> lk(thread_names_mutex);
	auto emplaced = thread_names.emplace(std::piecewise_construct,
		std::forward_as_tuple(std::this_thread::get_id()),
		std::forward_as_tuple(name));

	size_t zero;
	do {
		zero = 0;
		sched_yield();
	} while (!pthreads_busy.compare_exchange_weak(zero, 1));

	auto idx = pthreads_num.fetch_add(1);
	if (idx < pthreads.size()) {
		pthreads[idx] = pthread;
		pthreads_names[idx] = emplaced.first->second.c_str();
	}

	pthreads_busy.store(0);
}

const std::string&
get_thread_name(std::thread::id thread_id)
{
	std::lock_guard<std::mutex> lk(thread_names_mutex);
	auto thread = thread_names.find(thread_id);
	if (thread == thread_names.end()) {
		static std::string _ = "???";
		return _;
	}
	return thread->second;
}

const std::string&
get_thread_name()
{
	return get_thread_name(std::this_thread::get_id());
}

////////////////////////////////////////////////////////////////////////////////

void
collect_callstack_sig_handler(int /*signum*/)
{
	auto pthread = pthread_self();
	for (size_t idx = 0; idx < pthreads.size() && idx < pthreads_num.load(); ++idx) {
		if (pthreads[idx] == pthread) {
			auto& callstack = callstacks[idx];
			if (callstack != nullptr) {
				free(callstack);
			}
			callstack = backtrace();
			pthreads_busy.fetch_sub(1);
			return;
		}
	}
}

void
collect_callstacks()
{
	auto total = pthreads_num.load();
	if (total > pthreads.size()) {
		total = pthreads.size();
	}

	size_t zero;
	do {
		zero = 0;
		sched_yield();
	} while (!pthreads_busy.compare_exchange_weak(zero, total));

	for (size_t idx = 0; idx < total; ++idx) {
		if (pthread_kill(pthreads[idx], SIGUSR2) != 0) {
			pthreads_busy.fetch_sub(1);
		}
	}

	do {
		zero = 0;
		sched_yield();
	} while (!pthreads_busy.compare_exchange_weak(zero, 1));

	for (size_t idx = 1; idx < total; ++idx) {
		auto& callstack = callstacks[idx];
		auto& snapshot = snapshots[idx];
		size_t callstack_frames = static_cast<void**>(*callstack) - callstack;
		size_t snapshot_frames = snapshot ? static_cast<void**>(*snapshot) - snapshot : 0;
		if (snapshot_frames <= 3 || callstack_frames <= 3 || snapshot[3] != callstack[3]) {
			print(traceback(pthreads_names[idx], "", idx, callstack), 3);
		}
	}

	pthreads_busy.store(0);
}

void
callstacks_snapshot()
{
	bool retry;
	do {
		retry = false;
		auto total = pthreads_num.load();
		if (total > pthreads.size()) {
			total = pthreads.size();
		}

		size_t zero;
		do {
			zero = 0;
			sched_yield();
		} while (!pthreads_busy.compare_exchange_weak(zero, total));

		for (size_t idx = 0; idx < total; ++idx) {
			if (pthread_kill(pthreads[idx], SIGUSR2) != 0) {
				pthreads_busy.fetch_sub(1);
			}
		}

		do {
			zero = 0;
			sched_yield();
		} while (!pthreads_busy.compare_exchange_weak(zero, 1));

		for (size_t idx = 0; idx < total; ++idx) {
			auto& callstack = callstacks[idx];
			auto& snapshot = snapshots[idx];
			auto different = true;
			size_t callstack_frames = static_cast<void**>(*callstack) - callstack;
			if (snapshot) {
				size_t snapshot_frames = static_cast<void**>(*snapshot) - snapshot;
				if (snapshot_frames == callstack_frames) {
					different = false;
					for (size_t i = 0; i < callstack_frames; ++i) {
						if (callstack[i + 1] != snapshot[i + 1]) {
							different = true;
							break;
						}
					}
				}
			}
			if (different) {
				if (snapshot) {
					free(snapshot);
				}
				snapshot = (void**)malloc((callstack_frames + 1) * sizeof(void*));
				memcpy(snapshot + 1, callstack + 1, callstack_frames * sizeof(void*));
				snapshot[0] = &snapshot[callstack_frames];
				retry = true;
			}
		}

		pthreads_busy.store(0);
	} while (retry);
}
<|endoftext|>


<commit_before>/* The MIT License (MIT)

Copyright (c) 2015 Gabriel Corona

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <sys/types.h>

#include <cstdlib> // atoll, exit
#include <cinttypes>

#include <iostream>

#include <llvm-c/Target.h>
#include <llvm-c/Disassembler.h>

#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options.hpp>

#include "unjit.hpp"

struct Config {
  pid_t pid = -1;
  std::uint64_t start = 0, stop = 0;
};

static unsigned long long int parse_integer(char const* value)
{
  if (value[0] == '0' && value[1] == 'x') {
    return strtoull(value + 2, NULL, 16);
  } else {
    return strtoull(value, NULL, 10);
  }
}

static int parse_config(Config& config, int argc, const char** argv)
{
  using boost::program_options::options_description;
  using boost::program_options::value;
  using boost::program_options::variables_map;
  using boost::program_options::store;
  using boost::program_options::option;
  using boost::program_options::notify;
  using boost::program_options::parse_command_line;
  using boost::program_options::command_line_parser;
  using boost::program_options::positional_options_description;

  options_description desc("Commandline options");
  desc.add_options()
    ("help,h,?", "help")
    ("pid,p", value<pid_t>(), "PID of the target process")
    ("start-address", value<std::string>(), "Address")
    ("stop-address", value<std::string>(), "Address")
    ;
  variables_map vm;
  store(command_line_parser(argc, argv).options(desc).run(), vm);
  notify(vm);

  if (vm.count("help")) {
    std::cout << desc  << '\n';
    std::exit(0);
  }

  if (vm.count("pid"))
    config.pid = vm["pid"].as<pid_t>();
  if (vm.count("start-address"))
    config.start = parse_integer(vm["start-address"].as<std::string>().c_str());
  if (vm.count("stop-address"))
    config.stop = parse_integer(vm["stop-address"].as<std::string>().c_str());

  return 0;
}

int main(int argc, const char** argv)
{
  Config config;
  try {
    int res = parse_config(config, argc, argv);
    if (res)
      return res;
  }
  catch(boost::program_options::unknown_option) {
    std::cerr << "Unknown option\n";
    return 1;
  }
  if (config.pid < 0 ) {
    std::cerr << "Missing PID\n";
    return 1;
  }

  LLVMInitializeAllTargetInfos();
  LLVMInitializeAllTargetMCs();
  LLVMInitializeAllDisassemblers();
  LLVMInitializeNativeDisassembler();

  unjit::Process process(config.pid);
  process.load_vm_maps();
  process.load_modules();
  process.load_map_file();

  unjit::Disassembler disassembler(process);
  if (config.start == 0 && config.stop == 0)
    for (auto const& k : process.jit_symbols())
      disassembler.disassemble(std::cout, k.second.value, k.second.size);
  else
    disassembler.disassemble(std::cout, config.start, config.stop - config.start);

  return 0;
}
<commit_msg>Option to disassemble AOT code<commit_after>/* The MIT License (MIT)

Copyright (c) 2015 Gabriel Corona

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <sys/types.h>

#include <cstdlib> // atoll, exit
#include <cinttypes>

#include <iostream>

#include <llvm-c/Target.h>
#include <llvm-c/Disassembler.h>

#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options.hpp>

#include "unjit.hpp"

struct Config {
  pid_t pid = -1;
  std::uint64_t start = 0, stop = 0;
  bool all = false;
};

static unsigned long long int parse_integer(char const* value)
{
  if (value[0] == '0' && value[1] == 'x') {
    return strtoull(value + 2, NULL, 16);
  } else {
    return strtoull(value, NULL, 10);
  }
}

static int parse_config(Config& config, int argc, const char** argv)
{
  using boost::program_options::options_description;
  using boost::program_options::value;
  using boost::program_options::variables_map;
  using boost::program_options::store;
  using boost::program_options::option;
  using boost::program_options::notify;
  using boost::program_options::parse_command_line;
  using boost::program_options::command_line_parser;
  using boost::program_options::positional_options_description;

  options_description desc("Commandline options");
  desc.add_options()
    ("help,h,?", "help")
    ("pid,p", value<pid_t>(), "PID of the target process")
    ("start-address", value<std::string>(), "Address")
    ("stop-address", value<std::string>(), "Address")
    ("all", "Disassemble all symbols")
    ;
  variables_map vm;
  store(command_line_parser(argc, argv).options(desc).run(), vm);
  notify(vm);

  if (vm.count("help")) {
    std::cout << desc  << '\n';
    std::exit(0);
  }

  if (vm.count("pid"))
    config.pid = vm["pid"].as<pid_t>();
  if (vm.count("start-address"))
    config.start = parse_integer(vm["start-address"].as<std::string>().c_str());
  if (vm.count("stop-address"))
    config.stop = parse_integer(vm["stop-address"].as<std::string>().c_str());
  if (vm.count("all"))
    config.all = true;

  return 0;
}

int main(int argc, const char** argv)
{
  Config config;
  try {
    int res = parse_config(config, argc, argv);
    if (res)
      return res;
  }
  catch(boost::program_options::unknown_option) {
    std::cerr << "Unknown option\n";
    return 1;
  }
  if (config.pid < 0 ) {
    std::cerr << "Missing PID\n";
    return 1;
  }

  LLVMInitializeAllTargetInfos();
  LLVMInitializeAllTargetMCs();
  LLVMInitializeAllDisassemblers();
  LLVMInitializeNativeDisassembler();

  unjit::Process process(config.pid);
  process.load_vm_maps();
  process.load_modules();
  process.load_map_file();

  unjit::Disassembler disassembler(process);

  if (config.all)
    for (auto const& module : process.modules())
      for (auto const& p : module.symbols)
        if (p.second.flags & SYMBOL_FLAG_CODE)
          disassembler.disassemble(std::cout, p.second.value, p.second.size);

  if (config.start != 0)
    disassembler.disassemble(std::cout, config.start, config.stop - config.start);
  else for (auto const& k : process.jit_symbols())
    disassembler.disassemble(std::cout, k.second.value, k.second.size);

  return 0;
}
<|endoftext|>


<commit_before>// Copyright 2010-2014 RethinkDB, all rights reserved.
#ifndef UTILS_HPP_
#define UTILS_HPP_

#include <stdio.h>
#include <stdlib.h>

#include <functional>
#include <stdexcept>
#include <string>

#include "errors.hpp"
#include "config/args.hpp"

// Using `malloc()` is dangerous. Use `rmalloc()` instead to make sure
// that the return value of `malloc()` is checked correctly.
#define DANGEROUS_MALLOC malloc
#define DANGEROUS_CALLOC calloc
#define DANGEROUS_REALLOC realloc
#define DANGEROUS_POSIX_MEMALIGN posix_memalign
#pragma GCC poison malloc
#pragma GCC poison calloc
#pragma GCC poison realloc
#pragma GCC poison valloc
#pragma GCC poison memalign
#pragma GCC poison posix_memalign

class printf_buffer_t;

namespace ph = std::placeholders;

class startup_shutdown_t {
public:
    startup_shutdown_t();
    ~startup_shutdown_t();
};

struct const_charslice {
    const char *beg, *end;
    const_charslice(const char *_beg, const char *_end) : beg(_beg), end(_end) { }
    const_charslice() : beg(NULL), end(NULL) { }
};

/* Forbid the following function definition to be inlined
 * (note: some compilers might require `noinline` instead of `__attribute__ ((noinline))`)
 */
#define NOINLINE __attribute__ ((noinline))

void *malloc_aligned(size_t size, size_t alignment);

/* Calls `malloc()` and checks its return value to crash if the allocation fails. */
void *rmalloc(size_t size);
/* Calls `realloc()` and checks its return value to crash if the allocation fails. */
void *rrealloc(void *ptr, size_t size);

class rng_t {
public:
// Returns a random number in [0, n).  Is not perfectly uniform; the
// bias tends to get worse when RAND_MAX is far from a multiple of n.
    int randint(int n);
    double randdouble();
    explicit rng_t(int seed = -1);
private:
    unsigned short xsubi[3];  // NOLINT(runtime/int)
    DISABLE_COPYING(rng_t);
};

// Reads from /dev/urandom.  Use this sparingly, please.
void get_dev_urandom(void *out, int64_t nbytes);

int randint(int n);
size_t randsize(size_t n);
double randdouble();
std::string rand_string(int len);

bool begins_with_minus(const char *string);
// strtoul() and strtoull() will for some reason not fail if the input begins
// with a minus sign. strtou64_strict() does.  Also we fix the constness of the
// end parameter.
int64_t strtoi64_strict(const char *string, const char **end, int base);
uint64_t strtou64_strict(const char *string, const char **end, int base);

// These functions return false and set the result to 0 if the conversion fails or
// does not consume the whole string.
MUST_USE bool strtoi64_strict(const std::string &str, int base, int64_t *out_result);
MUST_USE bool strtou64_strict(const std::string &str, int base, uint64_t *out_result);

std::string strprintf(const char *format, ...) __attribute__((format (printf, 1, 2)));
std::string vstrprintf(const char *format, va_list ap) __attribute__((format (printf, 1, 0)));

// formatted time:
// yyyy-mm-ddThh:mm:ss.nnnnnnnnn   (29 characters)
const size_t formatted_time_length = 29;    // not including null

void format_time(struct timespec time, printf_buffer_t *buf);
std::string format_time(struct timespec time);

struct timespec parse_time(const std::string &str) THROWS_ONLY(std::runtime_error);

/* Printing binary data to stderr in a nice format */
void print_hd(const void *buf, size_t offset, size_t length);

/* `with_priority_t` changes the priority of the current coroutine to the
 value given in its constructor. When it is destructed, it restores the
 original priority of the coroutine. */

class with_priority_t {
public:
    explicit with_priority_t(int priority);
    ~with_priority_t();
private:
    int previous_priority;
};

template <class InputIterator, class UnaryPredicate>
bool all_match_predicate(InputIterator begin, InputIterator end, UnaryPredicate f) {
    bool res = true;
    for (; begin != end; begin++) {
        res &= f(*begin);
    }
    return res;
}

template <class T, class UnaryPredicate>
bool all_in_container_match_predicate (const T &container, UnaryPredicate f) {
    return all_match_predicate(container.begin(), container.end(), f);
}

bool notf(bool x);

/* Translates to and from `0123456789ABCDEF`. */
bool hex_to_int(char c, int *out);
char int_to_hex(int i);

std::string blocking_read_file(const char *path);
bool blocking_read_file(const char *path, std::string *contents_out);

template <class T>
class assignment_sentry_t {
public:
    assignment_sentry_t(T *v, const T &value) :
        var(v), old_value(*var) {
        *var = value;
    }
    ~assignment_sentry_t() {
        *var = old_value;
    }
private:
    T *var;
    T old_value;
};

std::string sanitize_for_logger(const std::string &s);
static inline std::string time2str(const time_t &t) {
    char timebuf[26]; // I apologize for the magic constant.
    //           ^^ See man 3 ctime_r
    return ctime_r(&t, timebuf);
}

std::string errno_string(int errsv);

// Contains the name of the directory in which all data is stored.
class base_path_t {
public:
    explicit base_path_t(const std::string& path);
    const std::string& path() const;

    // Make this base_path_t into an absolute path (useful for daemonizing)
    // This can only be done if the path already exists, which is why we don't do it at construction
    void make_absolute();
private:
    std::string path_;
};

static const char *TEMPORARY_DIRECTORY_NAME = "tmp";

class serializer_filepath_t;

namespace unittest {
serializer_filepath_t manual_serializer_filepath(const std::string& permanent_path,
                                                 const std::string& temporary_path);
}  // namespace unittest

// Contains the name of a serializer file.
class serializer_filepath_t {
public:
    serializer_filepath_t(const base_path_t& directory, const std::string& relative_path)
        : permanent_path_(directory.path() + "/" + relative_path),
          temporary_path_(directory.path() + "/" + TEMPORARY_DIRECTORY_NAME + "/" + relative_path + ".create") {
        guarantee(!relative_path.empty());
    }

    // A serializer_file_opener_t will first open the file in a temporary location, then move it to
    // the permanent location when it's finished being created.  These give the names of those
    // locations.
    std::string permanent_path() const { return permanent_path_; }
    std::string temporary_path() const { return temporary_path_; }

private:
    friend serializer_filepath_t unittest::manual_serializer_filepath(const std::string& permanent_path,
                                                                      const std::string& temporary_path);
    serializer_filepath_t(const std::string& permanent_path, const std::string& temporary_path)
        : permanent_path_(permanent_path), temporary_path_(temporary_path) { }

    const std::string permanent_path_;
    const std::string temporary_path_;
};

void recreate_temporary_directory(const base_path_t& base_path);

// This will be thrown by remove_directory_recursive if a file cannot be removed
class remove_directory_exc_t : public std::exception {
public:
    remove_directory_exc_t(const std::string &path, int errsv) {
        char buf[512];
        info = strprintf("Fatal error: failed to delete file '%s': %s.",
                         path.c_str(),
                         errno_string_maybe_using_buffer(errsv, buf, sizeof(buf)));
    }
    ~remove_directory_exc_t() throw () { }
    const char *what() const throw () {
        return info.c_str();
    }
private:
    std::string info;
};

void remove_directory_recursive(const char *path) THROWS_ONLY(remove_directory_exc_t);

bool ptr_in_byte_range(const void *p, const void *range_start, size_t size_in_bytes);
bool range_inside_of_byte_range(const void *p, size_t n_bytes, const void *range_start, size_t size_in_bytes);

#define MSTR(x) stringify(x) // Stringify a macro
#if defined __clang__
#define COMPILER "CLANG " __clang_version__
#elif defined __GNUC__
#define COMPILER "GCC " MSTR(__GNUC__) "." MSTR(__GNUC_MINOR__) "." MSTR(__GNUC_PATCHLEVEL__)
#else
#define COMPILER "UNKNOWN COMPILER"
#endif

#ifndef NDEBUG
#define RETHINKDB_VERSION_STR "rethinkdb " RETHINKDB_VERSION " (debug)" " (" COMPILER ")"
#else
#define RETHINKDB_VERSION_STR "rethinkdb " RETHINKDB_VERSION " (" COMPILER ")"
#endif

#define NULLPTR (static_cast<void *>(0))

#define DBLPRI "%.20g"

#define ANY_PORT 0

#endif // UTILS_HPP_
<commit_msg>Removed allocation poison pragmas.<commit_after>// Copyright 2010-2014 RethinkDB, all rights reserved.
#ifndef UTILS_HPP_
#define UTILS_HPP_

#include <stdio.h>
#include <stdlib.h>

#include <functional>
#include <stdexcept>
#include <string>

#include "errors.hpp"
#include "config/args.hpp"

// Using `malloc()` is dangerous. Use `rmalloc()` instead to make sure
// that the return value of `malloc()` is checked correctly.
#define DANGEROUS_MALLOC malloc
#define DANGEROUS_CALLOC calloc
#define DANGEROUS_REALLOC realloc
#define DANGEROUS_POSIX_MEMALIGN posix_memalign

class printf_buffer_t;

namespace ph = std::placeholders;

class startup_shutdown_t {
public:
    startup_shutdown_t();
    ~startup_shutdown_t();
};

struct const_charslice {
    const char *beg, *end;
    const_charslice(const char *_beg, const char *_end) : beg(_beg), end(_end) { }
    const_charslice() : beg(NULL), end(NULL) { }
};

/* Forbid the following function definition to be inlined
 * (note: some compilers might require `noinline` instead of `__attribute__ ((noinline))`)
 */
#define NOINLINE __attribute__ ((noinline))

void *malloc_aligned(size_t size, size_t alignment);

/* Calls `malloc()` and checks its return value to crash if the allocation fails. */
void *rmalloc(size_t size);
/* Calls `realloc()` and checks its return value to crash if the allocation fails. */
void *rrealloc(void *ptr, size_t size);

class rng_t {
public:
// Returns a random number in [0, n).  Is not perfectly uniform; the
// bias tends to get worse when RAND_MAX is far from a multiple of n.
    int randint(int n);
    double randdouble();
    explicit rng_t(int seed = -1);
private:
    unsigned short xsubi[3];  // NOLINT(runtime/int)
    DISABLE_COPYING(rng_t);
};

// Reads from /dev/urandom.  Use this sparingly, please.
void get_dev_urandom(void *out, int64_t nbytes);

int randint(int n);
size_t randsize(size_t n);
double randdouble();
std::string rand_string(int len);

bool begins_with_minus(const char *string);
// strtoul() and strtoull() will for some reason not fail if the input begins
// with a minus sign. strtou64_strict() does.  Also we fix the constness of the
// end parameter.
int64_t strtoi64_strict(const char *string, const char **end, int base);
uint64_t strtou64_strict(const char *string, const char **end, int base);

// These functions return false and set the result to 0 if the conversion fails or
// does not consume the whole string.
MUST_USE bool strtoi64_strict(const std::string &str, int base, int64_t *out_result);
MUST_USE bool strtou64_strict(const std::string &str, int base, uint64_t *out_result);

std::string strprintf(const char *format, ...) __attribute__((format (printf, 1, 2)));
std::string vstrprintf(const char *format, va_list ap) __attribute__((format (printf, 1, 0)));

// formatted time:
// yyyy-mm-ddThh:mm:ss.nnnnnnnnn   (29 characters)
const size_t formatted_time_length = 29;    // not including null

void format_time(struct timespec time, printf_buffer_t *buf);
std::string format_time(struct timespec time);

struct timespec parse_time(const std::string &str) THROWS_ONLY(std::runtime_error);

/* Printing binary data to stderr in a nice format */
void print_hd(const void *buf, size_t offset, size_t length);

/* `with_priority_t` changes the priority of the current coroutine to the
 value given in its constructor. When it is destructed, it restores the
 original priority of the coroutine. */

class with_priority_t {
public:
    explicit with_priority_t(int priority);
    ~with_priority_t();
private:
    int previous_priority;
};

template <class InputIterator, class UnaryPredicate>
bool all_match_predicate(InputIterator begin, InputIterator end, UnaryPredicate f) {
    bool res = true;
    for (; begin != end; begin++) {
        res &= f(*begin);
    }
    return res;
}

template <class T, class UnaryPredicate>
bool all_in_container_match_predicate (const T &container, UnaryPredicate f) {
    return all_match_predicate(container.begin(), container.end(), f);
}

bool notf(bool x);

/* Translates to and from `0123456789ABCDEF`. */
bool hex_to_int(char c, int *out);
char int_to_hex(int i);

std::string blocking_read_file(const char *path);
bool blocking_read_file(const char *path, std::string *contents_out);

template <class T>
class assignment_sentry_t {
public:
    assignment_sentry_t(T *v, const T &value) :
        var(v), old_value(*var) {
        *var = value;
    }
    ~assignment_sentry_t() {
        *var = old_value;
    }
private:
    T *var;
    T old_value;
};

std::string sanitize_for_logger(const std::string &s);
static inline std::string time2str(const time_t &t) {
    char timebuf[26]; // I apologize for the magic constant.
    //           ^^ See man 3 ctime_r
    return ctime_r(&t, timebuf);
}

std::string errno_string(int errsv);

// Contains the name of the directory in which all data is stored.
class base_path_t {
public:
    explicit base_path_t(const std::string& path);
    const std::string& path() const;

    // Make this base_path_t into an absolute path (useful for daemonizing)
    // This can only be done if the path already exists, which is why we don't do it at construction
    void make_absolute();
private:
    std::string path_;
};

static const char *TEMPORARY_DIRECTORY_NAME = "tmp";

class serializer_filepath_t;

namespace unittest {
serializer_filepath_t manual_serializer_filepath(const std::string& permanent_path,
                                                 const std::string& temporary_path);
}  // namespace unittest

// Contains the name of a serializer file.
class serializer_filepath_t {
public:
    serializer_filepath_t(const base_path_t& directory, const std::string& relative_path)
        : permanent_path_(directory.path() + "/" + relative_path),
          temporary_path_(directory.path() + "/" + TEMPORARY_DIRECTORY_NAME + "/" + relative_path + ".create") {
        guarantee(!relative_path.empty());
    }

    // A serializer_file_opener_t will first open the file in a temporary location, then move it to
    // the permanent location when it's finished being created.  These give the names of those
    // locations.
    std::string permanent_path() const { return permanent_path_; }
    std::string temporary_path() const { return temporary_path_; }

private:
    friend serializer_filepath_t unittest::manual_serializer_filepath(const std::string& permanent_path,
                                                                      const std::string& temporary_path);
    serializer_filepath_t(const std::string& permanent_path, const std::string& temporary_path)
        : permanent_path_(permanent_path), temporary_path_(temporary_path) { }

    const std::string permanent_path_;
    const std::string temporary_path_;
};

void recreate_temporary_directory(const base_path_t& base_path);

// This will be thrown by remove_directory_recursive if a file cannot be removed
class remove_directory_exc_t : public std::exception {
public:
    remove_directory_exc_t(const std::string &path, int errsv) {
        char buf[512];
        info = strprintf("Fatal error: failed to delete file '%s': %s.",
                         path.c_str(),
                         errno_string_maybe_using_buffer(errsv, buf, sizeof(buf)));
    }
    ~remove_directory_exc_t() throw () { }
    const char *what() const throw () {
        return info.c_str();
    }
private:
    std::string info;
};

void remove_directory_recursive(const char *path) THROWS_ONLY(remove_directory_exc_t);

bool ptr_in_byte_range(const void *p, const void *range_start, size_t size_in_bytes);
bool range_inside_of_byte_range(const void *p, size_t n_bytes, const void *range_start, size_t size_in_bytes);

#define MSTR(x) stringify(x) // Stringify a macro
#if defined __clang__
#define COMPILER "CLANG " __clang_version__
#elif defined __GNUC__
#define COMPILER "GCC " MSTR(__GNUC__) "." MSTR(__GNUC_MINOR__) "." MSTR(__GNUC_PATCHLEVEL__)
#else
#define COMPILER "UNKNOWN COMPILER"
#endif

#ifndef NDEBUG
#define RETHINKDB_VERSION_STR "rethinkdb " RETHINKDB_VERSION " (debug)" " (" COMPILER ")"
#else
#define RETHINKDB_VERSION_STR "rethinkdb " RETHINKDB_VERSION " (" COMPILER ")"
#endif

#define NULLPTR (static_cast<void *>(0))

#define DBLPRI "%.20g"

#define ANY_PORT 0

#endif // UTILS_HPP_
<|endoftext|>


<commit_before>//utils.hpp
#ifndef _UTILS_H
#define _UTILS_H

#include <vector>

namespace utils {

    //Enums utilizados
    enum TYPE{
        VAR,
        CTE,
        FUN1,
        FUN2,
        SIZETYPE //Tamanho do enum TYPE
    };

    enum FUNC1{
        LN,
        EXP,
        SQRT,
        SIN,
        COS,
        TAN,
        SIZEFUNC1 //Tamanho do enum FUNC1
    };

    enum FUNC2{
        ADD,
        SUB,
        MULT,
        DIV,
        POW,
        SIZEFUNC2 //Tamanho do enum FUNC2
    };

    typedef struct DataPoint{
        std::vector<double> x = std::vector<double> (2, 0.0);
        double y;

        DataPoint(double x1, double x2, double y);
    }DataPoint;

    //funções solucionadoras
    double func1_solver(int F_id, double v);
    double func2_solver(int F_id, double v1, double v2);

    //função de conversão
    double rad_to_degrees(double v);

    //funções de 2 parâmetros
    double  uDiv(double v1, double v2);
    double  uAdd(double, double);
    double  uSub(double v1, double v2);
    double uMult(double v1, double v2);
    double  uPow(double v1, double v2);

    //funções de 1 parâmetro
    double   uSin(double v);
    double   uCos(double v);
    double   uTan(double v);
    double    uLn(double v);
    double   uExp(double v);
    double  uSqrt(double v);
}

#endif
<commit_msg>1.2.1 DataPoint.x não é mais de tamanho fixo<commit_after>//utils.hpp
#ifndef _UTILS_H
#define _UTILS_H

#include <vector>

namespace utils {

    //Enums utilizados
    enum TYPE{
        VAR,
        CTE,
        FUN1,
        FUN2,
        SIZETYPE //Tamanho do enum TYPE
    };

    enum FUNC1{
        LN,
        EXP,
        SQRT,
        SIN,
        COS,
        TAN,
        SIZEFUNC1 //Tamanho do enum FUNC1
    };

    enum FUNC2{
        ADD,
        SUB,
        MULT,
        DIV,
        POW,
        SIZEFUNC2 //Tamanho do enum FUNC2
    };

    typedef struct DataPoint{
        std::vector<double> x;
        double y;

        DataPoint(std::vector<double> x, double y);
    }DataPoint;

    //funções solucionadoras
    double func1_solver(int F_id, double v);
    double func2_solver(int F_id, double v1, double v2);

    //função de conversão
    double rad_to_degrees(double v);

    //funções de 2 parâmetros
    double  uDiv(double v1, double v2);
    double  uAdd(double, double);
    double  uSub(double v1, double v2);
    double uMult(double v1, double v2);
    double  uPow(double v1, double v2);

    //funções de 1 parâmetro
    double   uSin(double v);
    double   uCos(double v);
    double   uTan(double v);
    double    uLn(double v);
    double   uExp(double v);
    double  uSqrt(double v);
}

#endif
<|endoftext|>


<commit_before>#include <fstream>
#include <string>
#include <variant>
#include <boost/hana/functional/overload.hpp>
#include <tmxpp.hpp>
#include <tmxpp/exceptions.hpp>
#include <tmxpp/impl/Xml.hpp>
#include <tmxpp/impl/tmx_info.hpp>
#include <tmxpp/impl/to_string_color.hpp>
#include <tmxpp/impl/write_utility.hpp>

namespace tmxpp {

namespace impl {
namespace {

using namespace tmx_info;

void write(iSize sz, Xml::Element elem)
{
    add(elem, size_width, sz.w);
    add(elem, size_height, sz.h);
}

void write_tile(pxSize sz, Xml::Element elem)
{
    add(elem, tile_size_width, sz.w);
    add(elem, tile_size_height, sz.h);
}

// Properties ------------------------------------------------------------------

void write(const Property::Value& value, Xml::Element prop)
{
    auto add = [=](Xml::Attribute::Value alternative, auto value) {
        prop.add(property_alternative, alternative);
        impl::add(prop, property_value, value);
    };

    std::visit(
        boost::hana::overload(
            [=](int i) { add(property_alternative_int, i); },
            [=](double d) { add(property_alternative_double, d); },
            [=](bool b) {
                add(property_alternative_bool,
                    get(b ? property_value_true : property_value_false));
            },
            [=](Color c) { add(property_alternative_color, c); },
            [=](File f) { add(property_alternative_file, f.string()); },
            [=](const std::string& s) {
                prop.add(property_alternative, property_alternative_string);

                if (bool is_multiline{s.find('\n') != std::string::npos})
                    prop.value(Xml::Element::Value{s});
                else
                    impl::add(prop, property_value, s);
            }),
        value);
}

void write(const Property& p, Xml::Element elem)
{
    add(elem, property_name, p.name);
    write(p.value, elem);
}

void write(const Properties& ps, Xml::Element parent)
{
    if (ps.empty())
        return;

    auto elem{parent.add(properties)};

    for (const auto& p : ps)
        write(p, elem.add(property));
}

// Map -------------------------------------------------------------------------

void write(Map::Render_order ro, Xml::Element map)
{
    map.add(map_render_order, [ro] {
        switch (ro) {
        case Map::Render_order::right_down: return map_render_order_right_down;
        case Map::Render_order::right_up: return map_render_order_right_up;
        case Map::Render_order::left_down: return map_render_order_left_down;
        case Map::Render_order::left_up: return map_render_order_left_up;
        default: throw Exception{""};
        }
    }());
}

void write(Map::Staggered::Axis a, Xml::Element map)
{
    map.add(map_staggered_axis, [a] {
        switch (a) {
        case Map::Staggered::Axis::x: return map_staggered_axis_x;
        case Map::Staggered::Axis::y: return map_staggered_axis_y;
        default: throw Exception{""};
        }
    }());
}

void write(Map::Staggered::Index i, Xml::Element map)
{
    map.add(map_staggered_index, [i] {
        switch (i) {
        case Map::Staggered::Index::even: return map_staggered_index_even;
        case Map::Staggered::Index::odd: return map_staggered_index_odd;
        default: throw Exception{""};
        }
    }());
}

void write(Map::Staggered s, Xml::Element map)
{
    write(s.axis, map);
    write(s.index, map);
}

void write(Map::Hexagonal h, Xml::Element map)
{
    add(map, map_hexagonal_side_legth, h.side_length);
    write(static_cast<Map::Staggered>(h), map);
}

void write(
    Map::Orientation orient, Map::Render_order render_order, Xml::Element map)
{
    auto add = [=](Xml::Attribute::Value orient) {
        map.add(map_orientation, orient);

        write(render_order, map);
    };

    std::visit(
        boost::hana::overload(
            [=](Map::Orthogonal) { add(map_orthogonal); },
            [=](Map::Isometric) { add(map_isometric); },
            [=](Map::Staggered s) {
                add(map_staggered);
                write(s, map);
            },
            [=](Map::Hexagonal h) {
                add(map_hexagonal);
                write(h, map);
            }),
        orient);
}

void write(const Map& map, Xml::Element elem)
{
    add(elem, map_version, map.version);
    write(map.orientation, map.render_order, elem);
    write(map.size, elem);
    write_tile(map.general_tile_size, elem);
    if (auto bg{map.background})
        add(elem, map_background, *bg);
    add(elem, map_next_unique_id, map.next_unique_id);
    write(map.properties, elem);
}

} // namespace
} // namespace impl

void write(const Map& map, gsl::not_null<gsl::czstring<>> path)
{
    impl::Xml tmx{impl::tmx_info::map};

    impl::write(map, tmx.root());

    std::ofstream{path} << tmx;
}

void write(const Map::Tile_set&);
void write(const Tile_set&);
void write(const Image_collection&);

} // namespace tmxpp
<commit_msg>Write Map::Tile_set attributes and Offset<commit_after>#include <fstream>
#include <string>
#include <type_traits>
#include <variant>
#include <boost/hana/functional/overload.hpp>
#include <tmxpp.hpp>
#include <tmxpp/exceptions.hpp>
#include <tmxpp/impl/Xml.hpp>
#include <tmxpp/impl/tmx_info.hpp>
#include <tmxpp/impl/to_string_color.hpp>
#include <tmxpp/impl/write_utility.hpp>

namespace tmxpp {

namespace impl {
namespace {

using namespace tmx_info;

void write(iSize sz, Xml::Element elem)
{
    add(elem, size_width, sz.w);
    add(elem, size_height, sz.h);
}

void write_tile(pxSize sz, Xml::Element elem)
{
    add(elem, tile_size_width, sz.w);
    add(elem, tile_size_height, sz.h);
}

// Properties ------------------------------------------------------------------

void write(const Property::Value& value, Xml::Element prop)
{
    auto add = [=](Xml::Attribute::Value alternative, auto value) {
        prop.add(property_alternative, alternative);
        impl::add(prop, property_value, value);
    };

    std::visit(
        boost::hana::overload(
            [=](int i) { add(property_alternative_int, i); },
            [=](double d) { add(property_alternative_double, d); },
            [=](bool b) {
                add(property_alternative_bool,
                    get(b ? property_value_true : property_value_false));
            },
            [=](Color c) { add(property_alternative_color, c); },
            [=](File f) { add(property_alternative_file, f.string()); },
            [=](const std::string& s) {
                prop.add(property_alternative, property_alternative_string);

                if (bool is_multiline{s.find('\n') != std::string::npos})
                    prop.value(Xml::Element::Value{s});
                else
                    impl::add(prop, property_value, s);
            }),
        value);
}

void write(const Property& p, Xml::Element elem)
{
    add(elem, property_name, p.name);
    write(p.value, elem);
}

void write(const Properties& ps, Xml::Element parent)
{
    if (ps.empty())
        return;

    auto elem{parent.add(properties)};

    for (const auto& p : ps)
        write(p, elem.add(property));
}

// Map::Tile_set ---------------------------------------------------------------

void write_tile(Offset o, Xml::Element parent)
{
    if (o == Offset{})
        return;

    auto elem{parent.add(tile_offset)};

    add(elem, tile_offset_x, o.x);
    add(elem, tile_offset_y, o.y);
}

void write(const Map::Tile_set& ts, Xml::Element elem)
{
    std::visit(
        [elem](const auto& ts) {
            add(elem, tile_set_first_global_id, ts.first_global_id);
            if (!ts.tsx.empty())
                add(elem, tile_set_tsx, ts.tsx.string());
            add(elem, tile_set_name, ts.name);
            if
                constexpr(std::is_same_v<std::decay_t<decltype(ts)>, Tile_set>)
                {
                    write_tile(ts.tile_size, elem);
                    if (ts.spacing != Pixel{0})
                        add(elem, tile_set_spacing, ts.spacing);
                    if (ts.margin != Pixel{0})
                        add(elem, tile_set_margin, ts.margin);
                    add(elem, tile_set_tile_count, ts.size.w * ts.size.h);
                    add(elem, tile_set_columns, ts.size.w);
                }
            else {
                write_tile(ts.max_tile_size, elem);
                add(elem, tile_set_tile_count, ts.tile_count);
                add(elem, tile_set_columns, ts.columns);
            };
            write_tile(ts.tile_offset, elem);
            write(ts.properties, elem);
        },
        ts);
}

// Map -------------------------------------------------------------------------

void write(Map::Render_order ro, Xml::Element map)
{
    map.add(map_render_order, [ro] {
        switch (ro) {
        case Map::Render_order::right_down: return map_render_order_right_down;
        case Map::Render_order::right_up: return map_render_order_right_up;
        case Map::Render_order::left_down: return map_render_order_left_down;
        case Map::Render_order::left_up: return map_render_order_left_up;
        default: throw Exception{""};
        }
    }());
}

void write(Map::Staggered::Axis a, Xml::Element map)
{
    map.add(map_staggered_axis, [a] {
        switch (a) {
        case Map::Staggered::Axis::x: return map_staggered_axis_x;
        case Map::Staggered::Axis::y: return map_staggered_axis_y;
        default: throw Exception{""};
        }
    }());
}

void write(Map::Staggered::Index i, Xml::Element map)
{
    map.add(map_staggered_index, [i] {
        switch (i) {
        case Map::Staggered::Index::even: return map_staggered_index_even;
        case Map::Staggered::Index::odd: return map_staggered_index_odd;
        default: throw Exception{""};
        }
    }());
}

void write(Map::Staggered s, Xml::Element map)
{
    write(s.axis, map);
    write(s.index, map);
}

void write(Map::Hexagonal h, Xml::Element map)
{
    add(map, map_hexagonal_side_legth, h.side_length);
    write(static_cast<Map::Staggered>(h), map);
}

void write(
    Map::Orientation orient, Map::Render_order render_order, Xml::Element map)
{
    auto add = [=](Xml::Attribute::Value orient) {
        map.add(map_orientation, orient);

        write(render_order, map);
    };

    std::visit(
        boost::hana::overload(
            [=](Map::Orthogonal) { add(map_orthogonal); },
            [=](Map::Isometric) { add(map_isometric); },
            [=](Map::Staggered s) {
                add(map_staggered);
                write(s, map);
            },
            [=](Map::Hexagonal h) {
                add(map_hexagonal);
                write(h, map);
            }),
        orient);
}

void write(const Map::Tile_sets& tses, Xml::Element map)
{
    for (const auto& ts : tses)
        write(ts, map.add(tile_set));
}

void write(const Map& map, Xml::Element elem)
{
    add(elem, map_version, map.version);
    write(map.orientation, map.render_order, elem);
    write(map.size, elem);
    write_tile(map.general_tile_size, elem);
    if (auto bg{map.background})
        add(elem, map_background, *bg);
    add(elem, map_next_unique_id, map.next_unique_id);
    write(map.properties, elem);
    write(map.tile_sets, elem);
}

} // namespace
} // namespace impl

void write(const Map& map, gsl::not_null<gsl::czstring<>> path)
{
    impl::Xml tmx{impl::tmx_info::map};

    impl::write(map, tmx.root());

    std::ofstream{path} << tmx;
}

void write(const Map::Tile_set&);
void write(const Tile_set&);
void write(const Image_collection&);

} // namespace tmxpp
<|endoftext|>


<commit_before>#include <algorithm>
#include <experimental/string_view>
#include <unordered_map>
#include <vector>

#include <fcntl.h>
#include <netdb.h>
#include <netinet/tcp.h>

#include <sys/stat.h>
#include <unistd.h>

#include "core/yta_event_loop.h"
#include "http/yta_http.hpp"

#include "picohttpparser/picohttpparser.h"

const std::size_t MAX_URL_SIXE = 512;
const std::size_t MAX_HEADERS = 20;
const std::size_t MAX_BUFFER_SIZE = 1024;

typedef unsigned char byte;

struct parser_data {
    const char* method = nullptr;
    std::size_t method_len = 0;
    const char* path = nullptr;
    std::size_t path_len = 0;
    int pret = 0;
    int minor_version = 0;
    phr_header headers[MAX_HEADERS];
    std::size_t num_headers = MAX_HEADERS;
};

struct user_data {
    std::size_t counter;

    char buf[MAX_BUFFER_SIZE];
    char response_buf[MAX_BUFFER_SIZE];

    bool finalized;

    std::size_t response_size;
    bool content;
    std::experimental::string_view extension;

    int file_fd;
    std::size_t file_size;
    std::size_t offset = 0;

    parser_data parser;

    struct stat file_stat;
};

void return_400(user_data* udata) {
    auto ret = yta::http::serve_400(udata->response_buf);
    udata->response_size = ret - udata->response_buf;
    udata->content = false;
    udata->finalized = true;
}

void return_404(user_data* udata) {
    auto ret = yta::http::serve_404(udata->response_buf);
    udata->response_size = ret - udata->response_buf;
    udata->content = false;
    udata->finalized = true;
}

std::unordered_map<std::experimental::string_view, std::experimental::string_view> mime_types {
    { ".html", "text/html" }, { ".css", "text/css" }, { ".png", "image/png"}, { ".jpg", "image/jpeg" },
    { ".jpeg", "image/jpeg"}, { ".txt", "text/plain" }, {".js", "application/x-javascript"},
    { ".json", "application/json"}, { ".pdf", "application/pdf" }, { ".zip", "application/zip" },
    { ".woff", "application/font-woff" }, { ".woff2", "application/font-woff2" },
    { ".opus", "audio/opus"}, { ".mp4", "video/mp4"}, { ".mpeg", "video/mpeg" }, { ".mpg", "video/mpeg" },
    { ".mp3", "audio/mpeg"}
};

int parse_url(yta_ctx* ctx, const char* path, size_t length) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    // parse query string
    auto pos = std::find(path, path + length, '?');
    if (pos != path + length) {
        length = pos - path;
    }

    // clean path buffer is MAX_URL_SICE
    // leave space for appended index.html
    if ((length > (MAX_URL_SIXE - 20)) || (length == 0)) {
        return_404(udata);
        return 0;
    }

    char normalized_path[MAX_URL_SIXE] = { 0 };
    // prepend . for current directory
    normalized_path[0] = '.';
    std::size_t new_length = 1;

    // clean path
    new_length += yta::http::clean_path(path, length, normalized_path + 1);

	// clean_path removes trailing slash except for root;
    // put the trailing slash back if necessary.
    if (path[length - 1] == '/' && new_length != 2) {
        normalized_path[new_length] = '/';
        ++new_length;
    }

    // append index.html to urls with trailing slashes
    if (normalized_path[new_length - 1] == '/') {
        const char index_html[] = "index.html";
        std::copy(index_html, index_html + 10, normalized_path + new_length);
        new_length += 10;
    }

    int ffd = open(normalized_path, O_RDONLY);

    if (ffd == -1) {
        return_404(udata);
        return 0;
    }

    struct stat file_stat;
    int status = fstat(ffd, &file_stat);

    if (status != 0 || !S_ISREG(file_stat.st_mode)) {
        return_404(udata);
        close(ffd);
        return 0;
    }

    std::experimental::string_view path_view(normalized_path, new_length);
    auto filetype_begin = path_view.rfind('.');
    auto file_begin = path_view.rfind('/');

    // we always find . at the beginning because of ./FILENAME
    if (file_begin < filetype_begin) {
        auto it = mime_types.find(path_view.substr(filetype_begin));
        if (it != mime_types.end()) {
            udata->extension = it->second;
        } else {
            udata->extension = { "text/plain" };
        }
    } else {
        udata->extension = { "text/plain" };
    }

    udata->file_fd = ffd;
    udata->file_size = file_stat.st_size;
    udata->file_stat = file_stat;

    return 0;
}

typedef bool (*header_handler)(yta_ctx*, std::experimental::string_view);

bool handle_if_modified_since(yta_ctx* ctx, std::experimental::string_view value) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    tm stamp;
    if (strptime(value.data(), yta::http::http_time_format(), &stamp) == NULL) {
        auto end = yta::http::serve_400(udata->response_buf);
        udata->response_size = end - udata->response_buf;
        udata->content = false;
        udata->finalized = true;
        return true;
    }
    time_t time = timegm(&stamp);

    // not modified
    if (time >= udata->file_stat.st_mtime) {
        auto end = yta::http::serve_304(udata->response_buf);
        udata->response_size = end - udata->response_buf;
        udata->content = false;
        udata->finalized = true;
        return true;
    }

    return false;
}

bool handle_range(yta_ctx* ctx, std::experimental::string_view value) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    // 7 bytes=X-
    if (value.size() < 8) {
        return_400(udata);
        return true;
    }

    auto iter = value.begin();

    if (!std::equal(iter, iter + 6, "bytes=")) {
        return_400(udata);
        return true;
    }

    std::advance(iter, 6);

    auto sep = std::find(iter, value.end(), '-');

    if (sep == value.end()) {
        return_400(udata);
        return true;
    }

    // Range: bytes=-123
    if (sep == iter) {
        // this should be supported by rfc
        return_400(udata);
        return true;
    }

    // TODO: think of something smarter to not create temp std::string
    // boost::lexical_cast would work
    auto val = std::string(iter, sep);
    int start_value = std::stoi(val);
    int end_value = 0;

    if (std::next(sep, 1) == value.end()) { // Range: bytes=123-
        end_value = udata->file_stat.st_size - 1;
    } else { // Range: bytes=123-200
        auto val = std::string(sep + 1, value.end());
        end_value = std::stoi(val);
    }

    // sanity checks
    if (end_value <= start_value) {
        end_value = udata->file_stat.st_size - 1;
    }

    if (start_value < 0) {
        start_value = 0;
    }

    if (end_value >= udata->file_stat.st_size) {
        end_value = udata->file_stat.st_size - 1;
    }

    udata->file_size = end_value - start_value + 1;
    udata->offset = start_value;

    auto end = yta::http::serve_206(udata->response_buf, udata->file_size,
                                    &udata->file_stat.st_mtime, start_value, end_value,
                                    udata->file_stat.st_size);
    udata->response_size = end - udata->response_buf;
    udata->content = true;
    udata->finalized = true;

    return true;
}

std::unordered_map<std::experimental::string_view, header_handler> header_callbacks{
    { "If-Modified-Since", handle_if_modified_since }, { "Range", handle_range }
};

int parse_headers(yta_ctx* ctx) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    for (std::size_t i = 0; (i < udata->parser.num_headers) && !udata->finalized; ++i) {
        std::experimental::string_view header(udata->parser.headers[i].name,
                                              udata->parser.headers[i].name_len);
        auto it = header_callbacks.find(header);
        if (it != header_callbacks.end()) {
            std::experimental::string_view value(udata->parser.headers[i].value,
                                                  udata->parser.headers[i].value_len);
            auto done = it->second(ctx, value);
            if (done) {
                return 0;
            }
        }
    }

    if (!udata->finalized) {
        auto end = yta::http::serve_200(udata->response_buf, udata->file_size,
                                        &udata->file_stat.st_mtime, udata->extension);
        udata->response_size = end - udata->response_buf;
        udata->content = true;
        udata->finalized = true;
    }
    return 0;
}

void accept_logic(yta_ctx* ctx, user_data* udata);

yta_callback_status http_finish_callback(yta_ctx* ctx, void*, size_t) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    if (udata->file_fd != 0) {
        close(udata->file_fd);
        udata->file_fd = 0;
    }

    // uncork
    int enable = 0;
    if (setsockopt(ctx->fd, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable)) <
        0) {
        fprintf(stderr, "error setting TCP_CORK");
        exit(1);
    }

    accept_logic(ctx, udata);
    return YTA_OK;
}

yta_callback_status write_header_callback(yta_ctx* ctx, void*, size_t) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);
    if (udata->content) {
        yta_async_sendfile(ctx, http_finish_callback, udata->file_fd, udata->file_size,
                           udata->offset);
        return YTA_OK;
    } else {
        return http_finish_callback(ctx, NULL, 0);
    }
}

yta_callback_status read_callback_http(yta_ctx* ctx, void* buf, size_t read) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    std::size_t prev_count = udata->counter;
    udata->counter += read;

    int parsed = phr_parse_request(
        udata->buf, udata->counter, &udata->parser.method, &udata->parser.method_len,
        &udata->parser.path, &udata->parser.path_len, &udata->parser.minor_version,
        udata->parser.headers, &udata->parser.num_headers, prev_count);

    if (parsed == -1) {
        return_400(udata);
    } else if (parsed > 0) {
        parse_url(ctx, udata->parser.path, udata->parser.path_len);
        parse_headers(ctx);
    }

    if (udata->finalized) {
        // cork
        int enable = 1;
        if (setsockopt(ctx->fd, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable)) <
            0) {
            fprintf(stderr, "error setting TCP_CORK");
            exit(1);
        }

        yta_async_write(ctx, write_header_callback, udata->response_buf,
                        udata->response_size);
        return YTA_OK;
    }

    if (udata->counter == MAX_BUFFER_SIZE) {
        return http_finish_callback(ctx, NULL, 0);
    }

    yta_async_read(ctx, read_callback_http, (byte*)buf + read, MAX_BUFFER_SIZE - udata->counter);
    return YTA_OK;
}

yta_callback_status http_cleanup(yta_ctx* ctx) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    if (udata->file_fd != 0) {
        close(udata->file_fd);
        udata->file_fd = 0;
    }

    delete udata;

    return YTA_EXIT;
}

yta_callback_status timer_callback(yta_ctx* /* ctx */) {
    printf("Force closing http connection\n");
    return YTA_EXIT;
}

void accept_logic(yta_ctx* ctx, user_data* udata) {
    udata->parser = parser_data{};
    udata->counter = 0;
    udata->finalized = false;
    udata->file_fd = 0;
    yta_async_read(ctx, read_callback_http, udata->buf, MAX_BUFFER_SIZE);
}

yta_callback_status accept_callback_http(yta_ctx* ctx) {
    user_data* udata = new user_data;
    if (udata == nullptr) {
        return YTA_EXIT;
    }
    ctx->user_data = udata;
    yta_set_close_callback(ctx, http_cleanup);
    yta_async_timer(ctx, timer_callback, 300, 0);
    accept_logic(ctx, udata);
    return YTA_OK;
}

int main(int argc, char** argv) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s addr port\n", argv[0]);
        exit(1);
    }

    char default_pidfile[] =  { "/tmp/yotta.pid" };
    char* pidfile_path = default_pidfile;
    int daemonize = 0;

    if (argc == 4) {
        pidfile_path = argv[3];
    }

    if (argc == 5) {
        pidfile_path = argv[3];
        daemonize = atoi(argv[4]);
    }

    yta_run(argv, argv[1], argv[2], pidfile_path, daemonize, accept_callback_http);

    return 0;
}
<commit_msg>more cleanup<commit_after>#include <algorithm>
#include <experimental/string_view>
#include <unordered_map>
#include <vector>

#include <fcntl.h>
#include <netdb.h>
#include <netinet/tcp.h>

#include <sys/stat.h>
#include <unistd.h>

#include "core/yta_event_loop.h"
#include "http/yta_http.hpp"

#include "picohttpparser/picohttpparser.h"

const std::size_t MAX_URL_SIXE = 512;
const std::size_t MAX_HEADERS = 20;
const std::size_t MAX_BUFFER_SIZE = 1024;

typedef unsigned char byte;

struct parser_data {
    const char* method = nullptr;
    std::size_t method_len = 0;
    const char* path = nullptr;
    std::size_t path_len = 0;
    int pret = 0;
    int minor_version = 0;
    std::array<phr_header, MAX_HEADERS> headers;
    std::size_t num_headers = MAX_HEADERS;
};

struct user_data {
    std::size_t counter;

    std::array<char, MAX_BUFFER_SIZE> buf;
    std::array<char, MAX_BUFFER_SIZE> response_buf;

    bool finalized;

    std::size_t response_size;
    bool content;
    std::experimental::string_view extension;

    int file_fd;
    std::size_t file_size;
    std::size_t offset = 0;

    parser_data parser;

    struct stat file_stat;
};

void return_400(user_data* udata) {
    auto ret = yta::http::serve_400(udata->response_buf.data());
    udata->response_size = ret - udata->response_buf.data();
    udata->content = false;
    udata->finalized = true;
}

void return_404(user_data* udata) {
    auto ret = yta::http::serve_404(udata->response_buf.data());
    udata->response_size = ret - udata->response_buf.data();
    udata->content = false;
    udata->finalized = true;
}

std::unordered_map<std::experimental::string_view, std::experimental::string_view> mime_types {
    { ".html", "text/html" }, { ".css", "text/css" }, { ".png", "image/png"}, { ".jpg", "image/jpeg" },
    { ".jpeg", "image/jpeg"}, { ".txt", "text/plain" }, {".js", "application/x-javascript"},
    { ".json", "application/json"}, { ".pdf", "application/pdf" }, { ".zip", "application/zip" },
    { ".woff", "application/font-woff" }, { ".woff2", "application/font-woff2" },
    { ".opus", "audio/opus"}, { ".mp4", "video/mp4"}, { ".mpeg", "video/mpeg" }, { ".mpg", "video/mpeg" },
    { ".mp3", "audio/mpeg"}
};

int parse_url(yta_ctx* ctx, const char* path, size_t length) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    // parse query string
    auto pos = std::find(path, path + length, '?');
    if (pos != path + length) {
        length = pos - path;
    }

    // clean path buffer is MAX_URL_SICE
    // leave space for appended index.html
    if ((length > (MAX_URL_SIXE - 20)) || (length == 0)) {
        return_404(udata);
        return 0;
    }

    char normalized_path[MAX_URL_SIXE] = { 0 };
    // prepend . for current directory
    normalized_path[0] = '.';
    std::size_t new_length = 1;

    // clean path
    new_length += yta::http::clean_path(path, length, normalized_path + 1);

	// clean_path removes trailing slash except for root;
    // put the trailing slash back if necessary.
    if (path[length - 1] == '/' && new_length != 2) {
        normalized_path[new_length] = '/';
        ++new_length;
    }

    // append index.html to urls with trailing slashes
    if (normalized_path[new_length - 1] == '/') {
        const char index_html[] = "index.html";
        std::copy(std::begin(index_html), std::end(index_html), normalized_path + new_length);
        new_length += 10;
    }

    int ffd = open(normalized_path, O_RDONLY);

    if (ffd == -1) {
        return_404(udata);
        return 0;
    }

    struct stat file_stat;
    int status = fstat(ffd, &file_stat);

    if (status != 0 || !S_ISREG(file_stat.st_mode)) {
        return_404(udata);
        close(ffd);
        return 0;
    }

    std::experimental::string_view path_view(normalized_path, new_length);
    auto filetype_begin = path_view.rfind('.');
    auto file_begin = path_view.rfind('/');

    // we always find . at the beginning because of ./FILENAME
    if (file_begin < filetype_begin) {
        auto it = mime_types.find(path_view.substr(filetype_begin));
        if (it != mime_types.end()) {
            udata->extension = it->second;
        } else {
            udata->extension = { "text/plain" };
        }
    } else {
        udata->extension = { "text/plain" };
    }

    udata->file_fd = ffd;
    udata->file_size = file_stat.st_size;
    udata->file_stat = file_stat;

    return 0;
}

typedef bool (*header_handler)(yta_ctx*, std::experimental::string_view);

bool handle_if_modified_since(yta_ctx* ctx, std::experimental::string_view value) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    tm stamp;
    if (strptime(value.data(), yta::http::http_time_format(), &stamp) == NULL) {
        auto end = yta::http::serve_400(udata->response_buf.data());
        udata->response_size = end - udata->response_buf.data();
        udata->content = false;
        udata->finalized = true;
        return true;
    }
    time_t time = timegm(&stamp);

    // not modified
    if (time >= udata->file_stat.st_mtime) {
        auto end = yta::http::serve_304(udata->response_buf.data());
        udata->response_size = end - udata->response_buf.data();
        udata->content = false;
        udata->finalized = true;
        return true;
    }

    return false;
}

bool handle_range(yta_ctx* ctx, std::experimental::string_view value) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    // 7 bytes=X-
    if (value.size() < 8) {
        return_400(udata);
        return true;
    }

    auto iter = value.begin();

    if (!std::equal(iter, iter + 6, "bytes=")) {
        return_400(udata);
        return true;
    }

    std::advance(iter, 6);

    auto sep = std::find(iter, value.end(), '-');

    if (sep == value.end()) {
        return_400(udata);
        return true;
    }

    // Range: bytes=-123
    if (sep == iter) {
        // this should be supported by rfc
        return_400(udata);
        return true;
    }

    // TODO: think of something smarter to not create temp std::string
    // boost::lexical_cast would work
    auto val = std::string(iter, sep);
    int start_value = std::stoi(val);
    int end_value = 0;

    if (std::next(sep, 1) == value.end()) { // Range: bytes=123-
        end_value = udata->file_stat.st_size - 1;
    } else { // Range: bytes=123-200
        auto val = std::string(sep + 1, value.end());
        end_value = std::stoi(val);
    }

    // sanity checks
    if (end_value <= start_value) {
        end_value = udata->file_stat.st_size - 1;
    }

    if (start_value < 0) {
        start_value = 0;
    }

    if (end_value >= udata->file_stat.st_size) {
        end_value = udata->file_stat.st_size - 1;
    }

    udata->file_size = end_value - start_value + 1;
    udata->offset = start_value;

    auto end = yta::http::serve_206(udata->response_buf.data(), udata->file_size,
                                    &udata->file_stat.st_mtime, start_value, end_value,
                                    udata->file_stat.st_size);
    udata->response_size = end - udata->response_buf.data();
    udata->content = true;
    udata->finalized = true;

    return true;
}

std::unordered_map<std::experimental::string_view, header_handler> header_callbacks{
    { "If-Modified-Since", handle_if_modified_since }, { "Range", handle_range }
};

int parse_headers(yta_ctx* ctx) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    for (std::size_t i = 0; (i < udata->parser.num_headers) && !udata->finalized; ++i) {
        std::experimental::string_view header(udata->parser.headers[i].name,
                                              udata->parser.headers[i].name_len);
        auto it = header_callbacks.find(header);
        if (it != header_callbacks.end()) {
            std::experimental::string_view value(udata->parser.headers[i].value,
                                                  udata->parser.headers[i].value_len);
            auto done = it->second(ctx, value);
            if (done) {
                return 0;
            }
        }
    }

    if (!udata->finalized) {
        auto end = yta::http::serve_200(udata->response_buf.data(), udata->file_size,
                                        &udata->file_stat.st_mtime, udata->extension);
        udata->response_size = end - udata->response_buf.data();
        udata->content = true;
        udata->finalized = true;
    }
    return 0;
}

void accept_logic(yta_ctx* ctx, user_data* udata);

yta_callback_status http_finish_callback(yta_ctx* ctx, void*, size_t) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    if (udata->file_fd != 0) {
        close(udata->file_fd);
        udata->file_fd = 0;
    }

    // uncork
    int enable = 0;
    if (setsockopt(ctx->fd, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable)) <
        0) {
        fprintf(stderr, "error setting TCP_CORK");
        exit(1);
    }

    accept_logic(ctx, udata);
    return YTA_OK;
}

yta_callback_status write_header_callback(yta_ctx* ctx, void*, size_t) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);
    if (udata->content) {
        yta_async_sendfile(ctx, http_finish_callback, udata->file_fd, udata->file_size,
                           udata->offset);
        return YTA_OK;
    } else {
        return http_finish_callback(ctx, NULL, 0);
    }
}

yta_callback_status read_callback_http(yta_ctx* ctx, void* buf, size_t read) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    std::size_t prev_count = udata->counter;
    udata->counter += read;

    int parsed = phr_parse_request(
        udata->buf.data(), udata->counter, &udata->parser.method, &udata->parser.method_len,
        &udata->parser.path, &udata->parser.path_len, &udata->parser.minor_version,
        udata->parser.headers.data(), &udata->parser.num_headers, prev_count);

    if (parsed == -1) {
        return_400(udata);
    } else if (parsed > 0) {
        parse_url(ctx, udata->parser.path, udata->parser.path_len);
        parse_headers(ctx);
    }

    if (udata->finalized) {
        // cork
        int enable = 1;
        if (setsockopt(ctx->fd, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable)) <
            0) {
            fprintf(stderr, "error setting TCP_CORK");
            exit(1);
        }

        yta_async_write(ctx, write_header_callback, udata->response_buf.data(),
                        udata->response_size);
        return YTA_OK;
    }

    if (udata->counter == MAX_BUFFER_SIZE) {
        return http_finish_callback(ctx, NULL, 0);
    }

    yta_async_read(ctx, read_callback_http, (char*)buf + read, MAX_BUFFER_SIZE - udata->counter);
    return YTA_OK;
}

yta_callback_status http_cleanup(yta_ctx* ctx) {
    user_data* udata = static_cast<user_data*>(ctx->user_data);

    if (udata->file_fd != 0) {
        close(udata->file_fd);
        udata->file_fd = 0;
    }

    delete udata;

    return YTA_EXIT;
}

yta_callback_status timer_callback(yta_ctx* /* ctx */) {
    printf("Force closing http connection\n");
    return YTA_EXIT;
}

void accept_logic(yta_ctx* ctx, user_data* udata) {
    udata->parser = parser_data{};
    udata->counter = 0;
    udata->finalized = false;
    udata->file_fd = 0;
    yta_async_read(ctx, read_callback_http, udata->buf.data(), MAX_BUFFER_SIZE);
}

yta_callback_status accept_callback_http(yta_ctx* ctx) {
    user_data* udata = new user_data;
    if (udata == nullptr) {
        return YTA_EXIT;
    }
    ctx->user_data = udata;
    yta_set_close_callback(ctx, http_cleanup);
    yta_async_timer(ctx, timer_callback, 300, 0);
    accept_logic(ctx, udata);
    return YTA_OK;
}

int main(int argc, char** argv) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s addr port\n", argv[0]);
        exit(1);
    }

    char default_pidfile[] =  { "/tmp/yotta.pid" };
    char* pidfile_path = default_pidfile;
    int daemonize = 0;

    if (argc == 4) {
        pidfile_path = argv[3];
    }

    if (argc == 5) {
        pidfile_path = argv[3];
        daemonize = atoi(argv[4]);
    }

    yta_run(argv, argv[1], argv[2], pidfile_path, daemonize, accept_callback_http);

    return 0;
}
<|endoftext|>