<commit_before>/**
 * \file
 *
 * \brief Plugin which acts as proxy and calls other plugins written in python
 *
 * \copyright BSD License (see doc/COPYING or http://www.libelektra.org)
 *
 */

#ifndef SWIG_TYPE_TABLE
# error Build system error, SWIG_TYPE_TABLE is not defined
#endif

#include <Python.h>

#ifndef HAVE_KDBCONFIG
# include <kdbconfig.h>
#endif
#include <kdbhelper.h>
#include SWIG_RUNTIME
#include "python.hpp"

#include <key.hpp>
#include <keyset.hpp>
#include <libgen.h>
#include <pthread.h>
#include <iostream>

using namespace ckdb;
#include <kdberrors.h>

#define __stringify(x) __stringify2(x)
#define __stringify2(x) #x
#define PYTHON_PLUGIN_NAME_STR __stringify(PYTHON_PLUGIN_NAME)

static PyObject *Python_fromSWIG(ckdb::Key *key)
{
	swig_type_info *ti = SWIG_TypeQuery("kdb::Key *");
	if (key == NULL || ti == NULL)
		return Py_None;
	return SWIG_NewPointerObj(new kdb::Key(key), ti, 0);
}

static PyObject *Python_fromSWIG(ckdb::KeySet *keyset)
{
	swig_type_info *ti = SWIG_TypeQuery("kdb::KeySet *");
	if (keyset == NULL || ti == NULL)
		return Py_None;
	return SWIG_NewPointerObj(new kdb::KeySet(keyset), ti, 0);
}

extern "C"
{

typedef struct
{
	PyObject *instance;
	int printError;
	int shutdown;
} moduleData;

/* pythons repr() - a little debug helper - misses all Py_DECREF calls! */
#define Python_Repr(obj) \
	PyBytes_AS_STRING( \
		PyUnicode_AsEncodedString(PyObject_Repr(obj), "utf-8", "Error ~") \
	)

static PyObject *Python_ImportModule(const char *name)
{
	if (!name)
		return NULL;

	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *module = PyImport_ImportModule(name);
	PyGILState_Release(gstate);
	return module;
}

static PyObject *Python_CallFunction(PyObject *object, PyObject *args)
{
	PyGILState_STATE gstate = PyGILState_Ensure();

	if (!PyCallable_Check(object))
	{
		PyGILState_Release(gstate);
		return NULL;
	}

	PyObject *res = PyObject_CallObject(object, args ? args : PyTuple_New (0));
	PyGILState_Release(gstate);
	Py_XINCREF(res);
	return res;
}

static int Python_CallFunction_Int(moduleData *data, PyObject *object,
		PyObject *args, ckdb::Key *errorKey)
{
	int ret = -1;
	PyGILState_STATE gstate = PyGILState_Ensure();

	PyObject *res = Python_CallFunction(object, args);
	if (!res)
	{
		ELEKTRA_SET_ERROR(111, errorKey,
				"Error while calling python function");
		if (data->printError)
			PyErr_Print();
	}
	else
	{
#if PY_MAJOR_VERSION >= 3
		if (!PyLong_Check(res))
#else
		if (!PyInt_Check(res))
#endif
			ELEKTRA_SET_ERROR(111, errorKey,
					"Error: return value is no integer");
		else
			ret = PyLong_AsLong(res);
	}

	PyGILState_Release(gstate);
	Py_XDECREF(res);
	return ret;
}

static int Python_CallFunction_Helper1(moduleData *data, const char *funcName,
	ckdb::Key *errorKey)
{
	int ret = 0;
	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *func = PyObject_GetAttrString(data->instance, funcName);
	if (func)
	{
		PyObject *arg0 = Python_fromSWIG(errorKey);
		PyObject *args = Py_BuildValue("(O)", arg0);
		ret = Python_CallFunction_Int(data, func, args, errorKey);
		Py_DECREF(arg0);
		Py_DECREF(args);
		Py_DECREF(func);
	}
	PyGILState_Release(gstate);
	return ret;
}

static int Python_CallFunction_Helper2(moduleData *data, const char *funcName,
	ckdb::KeySet *returned, ckdb::Key *parentKey)
{
	int ret = 0;
	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *func = PyObject_GetAttrString(data->instance, funcName);
	if (func)
	{
		PyObject *arg0 = Python_fromSWIG(returned);
		PyObject *arg1 = Python_fromSWIG(parentKey);
		PyObject *args = Py_BuildValue("(OO)", arg0, arg1);
		ret = Python_CallFunction_Int(data, func, args, parentKey);
		Py_DECREF(arg0);
		Py_DECREF(arg1);
		Py_DECREF(args);
		Py_DECREF(func);
	}
	PyGILState_Release(gstate);
	return ret;
}

static int Python_AppendToSysPath(const char *path)
{
	if (path == NULL)
		return 0;

	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *sysPath = PySys_GetObject((char *)"path");
	PyObject *pyPath  = PyUnicode_FromString(path);
	PyList_Append(sysPath, pyPath);
	Py_DECREF(pyPath);
	PyGILState_Release(gstate);
	return 1;
}

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned open_cnt = 0;

static void Python_Shutdown(moduleData *data)
{
	/* destroy python if plugin isn't used anymore */
	//FIXME python reinitialization is known to be buggy
	pthread_mutex_lock(&mutex);
	if (Py_IsInitialized() && !--open_cnt && data->shutdown) // order matters!
		Py_Finalize();
	pthread_mutex_unlock(&mutex);
}

int PYTHON_PLUGIN_FUNCTION(Open)(ckdb::Plugin *handle, ckdb::Key *errorKey)
{
	KeySet *config = elektraPluginGetConfig(handle);
	Key *script = ksLookupByName(config, "/script", 0);
	if (script == NULL)
		return 0; // success if no script to execute

	if (keyString(script) == NULL)
	{
		ELEKTRA_SET_ERROR(111, errorKey, "No python script set");
		return -1;
	}

	/* create module data */
	moduleData *data = new moduleData;
	data->instance   = NULL;
	data->printError = (ksLookupByName(config, "/print", 0) != NULL);
	/* shutdown flag is integer by design. This way users can set the
	 * expected behaviour without worring about default values
	 */
	data->shutdown = (ksLookupByName(config, "/shutdown", 0) &&
			!!strcmp(keyString(ksLookupByName(config, "/shutdown", 0)), "0"));

	{
		/* initialize python interpreter - only once */
		pthread_mutex_lock(&mutex);
		if (!Py_IsInitialized())
		{
			Py_Initialize();
			PyEval_InitThreads();
			if (!Py_IsInitialized())
			{
				pthread_mutex_unlock(&mutex);
				goto error;
			}
		}
		open_cnt++;
		pthread_mutex_unlock(&mutex);

		/* import kdb */
		PyObject *kdbModule = Python_ImportModule("kdb");
		if (kdbModule == NULL)
		{
			ELEKTRA_SET_ERROR(111, errorKey, "Unable to import kdb module");
			goto error_print;
		}
		Py_XDECREF(kdbModule);

		/* extend sys path */
		char *tmpScript = elektraStrDup(keyString(script));
		const char *dname = dirname(tmpScript);
		if (!Python_AppendToSysPath(dname))
		{
			ELEKTRA_SET_ERROR(111, errorKey, "Unable to extend sys.path");
			elektraFree(tmpScript);
			goto error;
		}
		elektraFree(tmpScript);

		/* import module/script */
		tmpScript = elektraStrDup(keyString(script));
		char *bname = basename(tmpScript);
		size_t bname_len = strlen(bname);
		if (bname_len >= 4 && strcmp(bname + bname_len - 3, ".py") == 0)
			bname[bname_len - 3] = '\0';

		PyObject *pModule = Python_ImportModule(bname);
		if (pModule == NULL)
		{
			ELEKTRA_SET_ERRORF(111, errorKey,"Unable to import python script %s",
					keyString(script));
			elektraFree(tmpScript);
			goto error_print;
		}
		elektraFree(tmpScript);

		/* get class */
		PyGILState_STATE gstate = PyGILState_Ensure();
		PyObject *klass = PyObject_GetAttrString(pModule, "ElektraPlugin");
		Py_DECREF(pModule);
		PyGILState_Release(gstate);
		if (klass == NULL)
		{
			ELEKTRA_SET_ERROR(111, errorKey,
					"Module doesn't provide a ElektraPlugin class");
			goto error_print;
		}

		/* create instance of class */
		gstate = PyGILState_Ensure();
		PyObject *inst_args = Py_BuildValue("()");
		PyObject *inst = PyEval_CallObject(klass, inst_args);
		Py_DECREF(klass);
		Py_DECREF(inst_args);
		PyGILState_Release(gstate);
		if (inst == NULL)
		{
			ELEKTRA_SET_ERROR(111, errorKey,
					"Unable to create instance of ElektraPlugin");
			goto error_print;
		}
		data->instance = inst;
	}

	/* store module data after everything is set up */
	elektraPluginSetData(handle, data);

	/* call python function */
	return Python_CallFunction_Helper1(data, "open", errorKey);

error_print:
	if (data->printError)
		PyErr_Print();
error:
	/* destroy python */
	Python_Shutdown(data);
	delete data;
	return -1;
}

int PYTHON_PLUGIN_FUNCTION(Close)(ckdb::Plugin *handle, ckdb::Key *errorKey)
{
	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data == NULL)
		return 0;

	/* call python function */
	int ret = 0;
	if (data != NULL)
	{
		PyGILState_STATE gstate = PyGILState_Ensure();
		ret = Python_CallFunction_Helper1(data, "close", errorKey);

		/* clean up references */
		Py_DECREF(data->instance);
		data->instance = NULL;
		PyGILState_Release(gstate);
	}

	/* destroy python */
	Python_Shutdown(data);
	delete data;
	return ret;
}

int PYTHON_PLUGIN_FUNCTION(Get)(ckdb::Plugin *handle, ckdb::KeySet *returned,
	ckdb::Key *parentKey)
{
#define _MODULE_CONFIG_PATH "system/elektra/modules/" PYTHON_PLUGIN_NAME_STR
	if (!strcmp(keyName(parentKey), _MODULE_CONFIG_PATH))
	{
		KeySet *n;
		ksAppend(returned, n = ksNew(30,
			keyNew(_MODULE_CONFIG_PATH,
				KEY_VALUE, "python interpreter waits for your orders", KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports", KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/get",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Get),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/set",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Set),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/error",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Error),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/open",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Open),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/close",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Close),
				KEY_END),
#define __readme_header(x) __readme_header2(x)
#define __readme_header2(x) __stringify(readme_##x.c)
#include __readme_header(PYTHON_PLUGIN_NAME)
			keyNew(_MODULE_CONFIG_PATH "/infos/version",
				KEY_VALUE, PLUGINVERSION, KEY_END),
			KS_END));
		ksDel(n);
	}

	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data != NULL)
		return Python_CallFunction_Helper2(data, "get", returned,
				parentKey);
	return 0;
}

int PYTHON_PLUGIN_FUNCTION(Set)(ckdb::Plugin *handle, ckdb::KeySet *returned,
	ckdb::Key *parentKey)
{
	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data != NULL)
		return Python_CallFunction_Helper2(data, "set", returned,
				parentKey);
	return 0;
}

int PYTHON_PLUGIN_FUNCTION(Error)(ckdb::Plugin *handle, ckdb::KeySet *returned,
	ckdb::Key *parentKey)
{
	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data != NULL)
		return Python_CallFunction_Helper2(data, "error", returned,
				parentKey);
	return 0;
}

ckdb::Plugin *PYTHON_PLUGIN_EXPORT(PYTHON_PLUGIN_NAME)
{
	return elektraPluginExport(PYTHON_PLUGIN_NAME_STR,
		ELEKTRA_PLUGIN_OPEN,  &PYTHON_PLUGIN_FUNCTION(Open),
		ELEKTRA_PLUGIN_CLOSE, &PYTHON_PLUGIN_FUNCTION(Close),
		ELEKTRA_PLUGIN_GET,   &PYTHON_PLUGIN_FUNCTION(Get),
		ELEKTRA_PLUGIN_SET,   &PYTHON_PLUGIN_FUNCTION(Set),
		ELEKTRA_PLUGIN_ERROR, &PYTHON_PLUGIN_FUNCTION(Error),
		ELEKTRA_PLUGIN_END);
}
}
<commit_msg>python: replace minor custom macros with elektra macros<commit_after>/**
 * \file
 *
 * \brief Plugin which acts as proxy and calls other plugins written in python
 *
 * \copyright BSD License (see doc/COPYING or http://www.libelektra.org)
 *
 */

#ifndef SWIG_TYPE_TABLE
# error Build system error, SWIG_TYPE_TABLE is not defined
#endif

#include <Python.h>

#ifndef HAVE_KDBCONFIG
# include <kdbconfig.h>
#endif
#include <kdbhelper.h>
#include SWIG_RUNTIME
#include "python.hpp"

#include <key.hpp>
#include <keyset.hpp>
#include <libgen.h>
#include <pthread.h>
#include <iostream>

using namespace ckdb;
#include <kdberrors.h>

#define PYTHON_PLUGIN_NAME_STR ELEKTRA_QUOTE(PYTHON_PLUGIN_NAME)

static PyObject *Python_fromSWIG(ckdb::Key *key)
{
	swig_type_info *ti = SWIG_TypeQuery("kdb::Key *");
	if (key == NULL || ti == NULL)
		return Py_None;
	return SWIG_NewPointerObj(new kdb::Key(key), ti, 0);
}

static PyObject *Python_fromSWIG(ckdb::KeySet *keyset)
{
	swig_type_info *ti = SWIG_TypeQuery("kdb::KeySet *");
	if (keyset == NULL || ti == NULL)
		return Py_None;
	return SWIG_NewPointerObj(new kdb::KeySet(keyset), ti, 0);
}

extern "C"
{

typedef struct
{
	PyObject *instance;
	int printError;
	int shutdown;
} moduleData;

/* pythons repr() - a little debug helper - misses all Py_DECREF calls! */
#define Python_Repr(obj) \
	PyBytes_AS_STRING( \
		PyUnicode_AsEncodedString(PyObject_Repr(obj), "utf-8", "Error ~") \
	)

static PyObject *Python_ImportModule(const char *name)
{
	if (!name)
		return NULL;

	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *module = PyImport_ImportModule(name);
	PyGILState_Release(gstate);
	return module;
}

static PyObject *Python_CallFunction(PyObject *object, PyObject *args)
{
	PyGILState_STATE gstate = PyGILState_Ensure();

	if (!PyCallable_Check(object))
	{
		PyGILState_Release(gstate);
		return NULL;
	}

	PyObject *res = PyObject_CallObject(object, args ? args : PyTuple_New (0));
	PyGILState_Release(gstate);
	Py_XINCREF(res);
	return res;
}

static int Python_CallFunction_Int(moduleData *data, PyObject *object,
		PyObject *args, ckdb::Key *errorKey)
{
	int ret = -1;
	PyGILState_STATE gstate = PyGILState_Ensure();

	PyObject *res = Python_CallFunction(object, args);
	if (!res)
	{
		ELEKTRA_SET_ERROR(111, errorKey,
				"Error while calling python function");
		if (data->printError)
			PyErr_Print();
	}
	else
	{
#if PY_MAJOR_VERSION >= 3
		if (!PyLong_Check(res))
#else
		if (!PyInt_Check(res))
#endif
			ELEKTRA_SET_ERROR(111, errorKey,
					"Error: return value is no integer");
		else
			ret = PyLong_AsLong(res);
	}

	PyGILState_Release(gstate);
	Py_XDECREF(res);
	return ret;
}

static int Python_CallFunction_Helper1(moduleData *data, const char *funcName,
	ckdb::Key *errorKey)
{
	int ret = 0;
	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *func = PyObject_GetAttrString(data->instance, funcName);
	if (func)
	{
		PyObject *arg0 = Python_fromSWIG(errorKey);
		PyObject *args = Py_BuildValue("(O)", arg0);
		ret = Python_CallFunction_Int(data, func, args, errorKey);
		Py_DECREF(arg0);
		Py_DECREF(args);
		Py_DECREF(func);
	}
	PyGILState_Release(gstate);
	return ret;
}

static int Python_CallFunction_Helper2(moduleData *data, const char *funcName,
	ckdb::KeySet *returned, ckdb::Key *parentKey)
{
	int ret = 0;
	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *func = PyObject_GetAttrString(data->instance, funcName);
	if (func)
	{
		PyObject *arg0 = Python_fromSWIG(returned);
		PyObject *arg1 = Python_fromSWIG(parentKey);
		PyObject *args = Py_BuildValue("(OO)", arg0, arg1);
		ret = Python_CallFunction_Int(data, func, args, parentKey);
		Py_DECREF(arg0);
		Py_DECREF(arg1);
		Py_DECREF(args);
		Py_DECREF(func);
	}
	PyGILState_Release(gstate);
	return ret;
}

static int Python_AppendToSysPath(const char *path)
{
	if (path == NULL)
		return 0;

	PyGILState_STATE gstate = PyGILState_Ensure();
	PyObject *sysPath = PySys_GetObject((char *)"path");
	PyObject *pyPath  = PyUnicode_FromString(path);
	PyList_Append(sysPath, pyPath);
	Py_DECREF(pyPath);
	PyGILState_Release(gstate);
	return 1;
}

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned open_cnt = 0;

static void Python_Shutdown(moduleData *data)
{
	/* destroy python if plugin isn't used anymore */
	//FIXME python reinitialization is known to be buggy
	pthread_mutex_lock(&mutex);
	if (Py_IsInitialized() && !--open_cnt && data->shutdown) // order matters!
		Py_Finalize();
	pthread_mutex_unlock(&mutex);
}

int PYTHON_PLUGIN_FUNCTION(Open)(ckdb::Plugin *handle, ckdb::Key *errorKey)
{
	KeySet *config = elektraPluginGetConfig(handle);
	Key *script = ksLookupByName(config, "/script", 0);
	if (script == NULL)
		return 0; // success if no script to execute

	if (keyString(script) == NULL)
	{
		ELEKTRA_SET_ERROR(111, errorKey, "No python script set");
		return -1;
	}

	/* create module data */
	moduleData *data = new moduleData;
	data->instance   = NULL;
	data->printError = (ksLookupByName(config, "/print", 0) != NULL);
	/* shutdown flag is integer by design. This way users can set the
	 * expected behaviour without worring about default values
	 */
	data->shutdown = (ksLookupByName(config, "/shutdown", 0) &&
			!!strcmp(keyString(ksLookupByName(config, "/shutdown", 0)), "0"));

	{
		/* initialize python interpreter - only once */
		pthread_mutex_lock(&mutex);
		if (!Py_IsInitialized())
		{
			Py_Initialize();
			PyEval_InitThreads();
			if (!Py_IsInitialized())
			{
				pthread_mutex_unlock(&mutex);
				goto error;
			}
		}
		open_cnt++;
		pthread_mutex_unlock(&mutex);

		/* import kdb */
		PyObject *kdbModule = Python_ImportModule("kdb");
		if (kdbModule == NULL)
		{
			ELEKTRA_SET_ERROR(111, errorKey, "Unable to import kdb module");
			goto error_print;
		}
		Py_XDECREF(kdbModule);

		/* extend sys path */
		char *tmpScript = elektraStrDup(keyString(script));
		const char *dname = dirname(tmpScript);
		if (!Python_AppendToSysPath(dname))
		{
			ELEKTRA_SET_ERROR(111, errorKey, "Unable to extend sys.path");
			elektraFree(tmpScript);
			goto error;
		}
		elektraFree(tmpScript);

		/* import module/script */
		tmpScript = elektraStrDup(keyString(script));
		char *bname = basename(tmpScript);
		size_t bname_len = strlen(bname);
		if (bname_len >= 4 && strcmp(bname + bname_len - 3, ".py") == 0)
			bname[bname_len - 3] = '\0';

		PyObject *pModule = Python_ImportModule(bname);
		if (pModule == NULL)
		{
			ELEKTRA_SET_ERRORF(111, errorKey,"Unable to import python script %s",
					keyString(script));
			elektraFree(tmpScript);
			goto error_print;
		}
		elektraFree(tmpScript);

		/* get class */
		PyGILState_STATE gstate = PyGILState_Ensure();
		PyObject *klass = PyObject_GetAttrString(pModule, "ElektraPlugin");
		Py_DECREF(pModule);
		PyGILState_Release(gstate);
		if (klass == NULL)
		{
			ELEKTRA_SET_ERROR(111, errorKey,
					"Module doesn't provide a ElektraPlugin class");
			goto error_print;
		}

		/* create instance of class */
		gstate = PyGILState_Ensure();
		PyObject *inst_args = Py_BuildValue("()");
		PyObject *inst = PyEval_CallObject(klass, inst_args);
		Py_DECREF(klass);
		Py_DECREF(inst_args);
		PyGILState_Release(gstate);
		if (inst == NULL)
		{
			ELEKTRA_SET_ERROR(111, errorKey,
					"Unable to create instance of ElektraPlugin");
			goto error_print;
		}
		data->instance = inst;
	}

	/* store module data after everything is set up */
	elektraPluginSetData(handle, data);

	/* call python function */
	return Python_CallFunction_Helper1(data, "open", errorKey);

error_print:
	if (data->printError)
		PyErr_Print();
error:
	/* destroy python */
	Python_Shutdown(data);
	delete data;
	return -1;
}

int PYTHON_PLUGIN_FUNCTION(Close)(ckdb::Plugin *handle, ckdb::Key *errorKey)
{
	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data == NULL)
		return 0;

	/* call python function */
	int ret = 0;
	if (data != NULL)
	{
		PyGILState_STATE gstate = PyGILState_Ensure();
		ret = Python_CallFunction_Helper1(data, "close", errorKey);

		/* clean up references */
		Py_DECREF(data->instance);
		data->instance = NULL;
		PyGILState_Release(gstate);
	}

	/* destroy python */
	Python_Shutdown(data);
	delete data;
	return ret;
}

int PYTHON_PLUGIN_FUNCTION(Get)(ckdb::Plugin *handle, ckdb::KeySet *returned,
	ckdb::Key *parentKey)
{
#define _MODULE_CONFIG_PATH "system/elektra/modules/" PYTHON_PLUGIN_NAME_STR
	if (!strcmp(keyName(parentKey), _MODULE_CONFIG_PATH))
	{
		KeySet *n;
		ksAppend(returned, n = ksNew(30,
			keyNew(_MODULE_CONFIG_PATH,
				KEY_VALUE, "python interpreter waits for your orders", KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports", KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/get",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Get),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/set",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Set),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/error",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Error),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/open",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Open),
				KEY_END),
			keyNew(_MODULE_CONFIG_PATH "/exports/close",
				KEY_FUNC, PYTHON_PLUGIN_FUNCTION(Close),
				KEY_END),
#include ELEKTRA_README(PYTHON_PLUGIN_NAME)
			keyNew(_MODULE_CONFIG_PATH "/infos/version",
				KEY_VALUE, PLUGINVERSION, KEY_END),
			KS_END));
		ksDel(n);
	}

	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data != NULL)
		return Python_CallFunction_Helper2(data, "get", returned,
				parentKey);
	return 0;
}

int PYTHON_PLUGIN_FUNCTION(Set)(ckdb::Plugin *handle, ckdb::KeySet *returned,
	ckdb::Key *parentKey)
{
	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data != NULL)
		return Python_CallFunction_Helper2(data, "set", returned,
				parentKey);
	return 0;
}

int PYTHON_PLUGIN_FUNCTION(Error)(ckdb::Plugin *handle, ckdb::KeySet *returned,
	ckdb::Key *parentKey)
{
	moduleData *data = static_cast<moduleData *>(elektraPluginGetData(handle));
	if (data != NULL)
		return Python_CallFunction_Helper2(data, "error", returned,
				parentKey);
	return 0;
}

ckdb::Plugin *PYTHON_PLUGIN_EXPORT(PYTHON_PLUGIN_NAME)
{
	return elektraPluginExport(PYTHON_PLUGIN_NAME_STR,
		ELEKTRA_PLUGIN_OPEN,  &PYTHON_PLUGIN_FUNCTION(Open),
		ELEKTRA_PLUGIN_CLOSE, &PYTHON_PLUGIN_FUNCTION(Close),
		ELEKTRA_PLUGIN_GET,   &PYTHON_PLUGIN_FUNCTION(Get),
		ELEKTRA_PLUGIN_SET,   &PYTHON_PLUGIN_FUNCTION(Set),
		ELEKTRA_PLUGIN_ERROR, &PYTHON_PLUGIN_FUNCTION(Error),
		ELEKTRA_PLUGIN_END);
}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Tim Ruffing <tim.ruffing@mmci.uni-saarland.de>
 *
 * 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 "prf.h"
#include "node.h"

#include <assert.h>

const unsigned char Prf::X = 'X';
const unsigned char Prf::R = 'R';

Prf::Prf(Prf::key_t key) : key(key) { }

Prf::Prf(ChameleonHash::sk_t dsk, bool extract) : key(key) {
    if (extract) {
        secp256k1_sha256_t hash;
        secp256k1_sha256_initialize(&hash);
        secp256k1_sha256_write(&hash, dsk.data(), dsk.size());
        assert(KEY_LEN == 256/8);
        secp256k1_sha256_finalize(&hash, this->key.data());
    }
}

void Prf::getX(Prf::out_t& x, Node& i)
{
    Prf::data_t ibytes;
    i.toBytes(ibytes);
    get_random_with_prefix(x, ibytes, X);
}

void Prf::getR(Prf::out_t& r, Node& i)
{
    Prf::data_t ibytes;
    i.toBytes(ibytes);
    get_random_with_prefix(r, ibytes, R);
}

void Prf::get_random_with_prefix(out_t& x, const data_t& data, const unsigned char& prefix)
{
    secp256k1_hmac_sha256_initialize(&hash, key.data(), key.size());
    secp256k1_hmac_sha256_write(&hash, &prefix, 1);
    secp256k1_hmac_sha256_write(&hash, data.data(), data.size());;
    secp256k1_hmac_sha256_finalize(&hash, x.data());
}
<commit_msg>Remove wrong member initializer<commit_after>/*
 * Copyright (c) 2015 Tim Ruffing <tim.ruffing@mmci.uni-saarland.de>
 *
 * 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 "prf.h"
#include "node.h"

#include <assert.h>

const unsigned char Prf::X = 'X';
const unsigned char Prf::R = 'R';

Prf::Prf(Prf::key_t key) : key(key) { }

Prf::Prf(ChameleonHash::sk_t dsk, bool extract) {
    if (extract) {
        secp256k1_sha256_t hash;
        secp256k1_sha256_initialize(&hash);
        secp256k1_sha256_write(&hash, dsk.data(), dsk.size());
        assert(KEY_LEN == 256/8);
        secp256k1_sha256_finalize(&hash, this->key.data());
    }
}

void Prf::getX(Prf::out_t& x, Node& i)
{
    Prf::data_t ibytes;
    i.toBytes(ibytes);
    get_random_with_prefix(x, ibytes, X);
}

void Prf::getR(Prf::out_t& r, Node& i)
{
    Prf::data_t ibytes;
    i.toBytes(ibytes);
    get_random_with_prefix(r, ibytes, R);
}

void Prf::get_random_with_prefix(out_t& x, const data_t& data, const unsigned char& prefix)
{
    secp256k1_hmac_sha256_initialize(&hash, key.data(), key.size());
    secp256k1_hmac_sha256_write(&hash, &prefix, 1);
    secp256k1_hmac_sha256_write(&hash, data.data(), data.size());;
    secp256k1_hmac_sha256_finalize(&hash, x.data());
}
<|endoftext|>


<commit_before>/**
 * @file
 *
 * @brief Write key sets using yaml-cpp
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#include "write.hpp"
#include "yaml-cpp/yaml.h"

#include <kdbease.h>
#include <kdblogger.h>
#include <kdbplugin.h>

#include <fstream>

using namespace std;
using namespace kdb;

namespace
{

/**
 * @brief This function returns a `NameIterator` starting at the first level that is not part of `parent`.
 *
 * @pre The parameter `key` must be a child of `parent`.
 *
 * @param key This is the key for which this function returns a relative iterator.
 * @param parent This key specifies the part of the name iterator that will not be part of the return value of this function.
 *
 * @returns A relative iterator that starts with the first part of the name of `key` not contained in `parent`.
 */
NameIterator relativeKeyIterator (Key const & key, Key const & parent)
{
	auto parentIterator = parent.begin ();
	auto keyIterator = key.begin ();
	while (parentIterator != parent.end () && keyIterator != key.end ())
	{
		parentIterator++;
		keyIterator++;
	}
	return keyIterator;
}

/**
 * @brief This function checks if a key name specifies an array key.
 *
 * If the key name contains a valid array index that is smaller than `unsigned long long`, then the function will also return this index.
 *
 * @param nameIterator This iterator specifies the name of the key.
 *
 * @retval (true, arrayIndex) if `name` specifies an array key, where `arrayIndex` specifies the index stored in the array key.
 * @retval (false, 0) otherwise
 */
std::pair<bool, unsigned long long> isArrayIndex (NameIterator const & nameIterator)
{
	string const name = *nameIterator;
	auto const offsetIndex = ckdb::elektraArrayValidateBaseNameString (name.c_str ());
	auto const isArrayElement = offsetIndex >= 1;
	return { isArrayElement, isArrayElement ? stoull (name.substr (offsetIndex)) : 0 };
}

/**
 * @brief This function creates a YAML node representing a key value.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 before you use YAML CPP.
 *
 * @returns A new YAML node containing the data specified in `key`
 */
YAML::Node createMetaDataNode (Key const & key)
{
	return key.hasMeta ("array") ?
		       YAML::Node (YAML::NodeType::Sequence) :
		       key.getBinarySize () == 0 ? YAML::Node (YAML::NodeType::Null) :
						   YAML::Node (key.isBinary () ? "Unsupported binary value!" : key.getString ());
}

/**
 * @brief This function creates a YAML Node containing a key value and optionally metadata.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 before you use YAML CPP.
 *
 * @returns A new YAML node containing the data and metadata specified in `key`
 */
YAML::Node createLeafNode (Key & key)
{

	YAML::Node metaNode{ YAML::Node (YAML::NodeType::Map) };
	YAML::Node dataNode = createMetaDataNode (key);

	key.rewindMeta ();
	while (Key meta = key.nextMeta ())
	{
		if (meta.getName () == "array" || meta.getName () == "binary") continue;
		if (meta.getName () == "type" && meta.getString () == "binary")
		{
			dataNode.SetTag ("tag:yaml.org,2002:binary");
			continue;
		}
		metaNode[meta.getName ()] = meta.getString ();
		ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", meta.getName ().c_str (), meta.getString ().c_str ());
	}

	if (metaNode.size () <= 0)
	{
		ELEKTRA_LOG_DEBUG ("Return leaf node with value “%s”",
				   dataNode.IsNull () ? "~" : dataNode.IsSequence () ? "[]" : dataNode.as<string> ().c_str ());
		return dataNode;
	}

	YAML::Node node{ YAML::Node (YAML::NodeType::Sequence) };
	node.SetTag ("!elektra/meta");
	node.push_back (dataNode);
	node.push_back (metaNode);

#ifdef HAVE_LOGGER
	ostringstream data;
	data << node;
	ELEKTRA_LOG_DEBUG ("Return meta leaf node with value “%s”", data.str ().c_str ());
#endif

	return node;
}

/**
 * @brief This function adds `null` elements to the given YAML collection.
 *
 * @param sequence This node stores the collection to which this function adds `numberOfElements` empty elements.
 * @param numberOfElements This parameter specifies the number of empty element this function adds to `sequence`.
 */
void addEmptyArrayElements (YAML::Node & sequence, unsigned long long const numberOfElements)
{
	ELEKTRA_LOG_DEBUG ("Add %lld empty array elements", numberOfElements);
	for (auto missingFields = numberOfElements; missingFields > 0; missingFields--)
	{
		sequence.push_back ({});
	}
}

/**
 * @brief This function adds a key to a YAML node.
 *
 * @param data This node stores the data specified via `keyIterator`.
 * @param keyIterator This iterator specifies the current part of the key name this function adds to `data`.
 * @param key This parameter specifies the key that should be added to `data`.
 */
void addKey (YAML::Node & data, NameIterator & keyIterator, Key & key)
{
	auto const isArrayAndIndex = isArrayIndex (keyIterator);
	auto const isArray = isArrayAndIndex.first;
	auto const arrayIndex = isArrayAndIndex.second;

	if (data.IsScalar ()) data = YAML::Node (YAML::NodeType::Undefined);

#ifdef HAVE_LOGGER
	ostringstream output;
	output << data;
	ELEKTRA_LOG_DEBUG ("Add key part “%s” to node “%s”", (*keyIterator).c_str (), output.str ().c_str ());
#endif

	if (keyIterator == key.end ())
	{
		ELEKTRA_LOG_DEBUG ("Create leaf node for key “%s”", key.getName ().c_str ());
		data = createLeafNode (key);
		return;
	}
	if (keyIterator == --key.end ())
	{
		if (isArray)
		{
			addEmptyArrayElements (data, arrayIndex - data.size ());
			data.push_back (createLeafNode (key));
		}
		else
		{
			data[*keyIterator] = createLeafNode (key);
		}

		return;
	}

	YAML::Node node;

	if (isArray)
	{
		node = (data[arrayIndex] && !data[arrayIndex].IsScalar ()) ? data[arrayIndex] : YAML::Node ();
		data[arrayIndex] = node;
	}
	else
	{
		node = (data[*keyIterator] && !data[*keyIterator].IsScalar ()) ? data[*keyIterator] : YAML::Node ();
		data[*keyIterator] = node;
	}
	addKey (node, ++keyIterator, key);
}

/**
 * @brief This function adds a key set to a YAML node.
 *
 * @param data This node stores the data specified via `mappings`.
 * @param mappings This keyset specifies all keys and values this function adds to `data`.
 * @param parent This key is the root of all keys stored in `mappings`.
 */
void addKeys (YAML::Node & data, KeySet const & mappings, Key const & parent)
{
	for (auto key : mappings)
	{
		ELEKTRA_LOG_DEBUG ("Convert key “%s”: “%s”", key.getName ().c_str (),
				   key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
		NameIterator keyIterator = relativeKeyIterator (key, parent);
		addKey (data, keyIterator, key);

#ifdef HAVE_LOGGER
		ostringstream output;
		output << data;
		ELEKTRA_LOG_DEBUG ("Converted key data “%s”", output.str ().c_str ());
#endif
	}
}

} // end namespace

/**
 * @brief This function saves the key-value pairs stored in `mappings` as YAML data in the location specified via `parent`.
 *
 * @param mappings This key set stores the mappings that should be saved as YAML data.
 * @param parent This key specifies the path to the YAML data file that should be written.
 */
void yamlcpp::yamlWrite (KeySet const & mappings, Key const & parent)
{
	ofstream output (parent.getString ());
	auto data = YAML::Node ();
	addKeys (data, mappings, parent);

#ifdef HAVE_LOGGER
	ostringstream outputString;
	outputString << data;

	ELEKTRA_LOG_DEBUG ("Write data “%s”", outputString.str ().c_str ());
#endif

	output << data;
}
<commit_msg>YAML CPP: Improve logging output<commit_after>/**
 * @file
 *
 * @brief Write key sets using yaml-cpp
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#include "write.hpp"
#include "yaml-cpp/yaml.h"

#include <kdbease.h>
#include <kdblogger.h>
#include <kdbplugin.h>

#include <fstream>

using namespace std;
using namespace kdb;

namespace
{

/**
 * @brief This function returns a `NameIterator` starting at the first level that is not part of `parent`.
 *
 * @pre The parameter `key` must be a child of `parent`.
 *
 * @param key This is the key for which this function returns a relative iterator.
 * @param parent This key specifies the part of the name iterator that will not be part of the return value of this function.
 *
 * @returns A relative iterator that starts with the first part of the name of `key` not contained in `parent`.
 */
NameIterator relativeKeyIterator (Key const & key, Key const & parent)
{
	auto parentIterator = parent.begin ();
	auto keyIterator = key.begin ();
	while (parentIterator != parent.end () && keyIterator != key.end ())
	{
		parentIterator++;
		keyIterator++;
	}
	return keyIterator;
}

/**
 * @brief This function checks if a key name specifies an array key.
 *
 * If the key name contains a valid array index that is smaller than `unsigned long long`, then the function will also return this index.
 *
 * @param nameIterator This iterator specifies the name of the key.
 *
 * @retval (true, arrayIndex) if `name` specifies an array key, where `arrayIndex` specifies the index stored in the array key.
 * @retval (false, 0) otherwise
 */
std::pair<bool, unsigned long long> isArrayIndex (NameIterator const & nameIterator)
{
	string const name = *nameIterator;
	auto const offsetIndex = ckdb::elektraArrayValidateBaseNameString (name.c_str ());
	auto const isArrayElement = offsetIndex >= 1;
	return { isArrayElement, isArrayElement ? stoull (name.substr (offsetIndex)) : 0 };
}

/**
 * @brief This function creates a YAML node representing a key value.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 before you use YAML CPP.
 *
 * @returns A new YAML node containing the data specified in `key`
 */
YAML::Node createMetaDataNode (Key const & key)
{
	return key.hasMeta ("array") ?
		       YAML::Node (YAML::NodeType::Sequence) :
		       key.getBinarySize () == 0 ? YAML::Node (YAML::NodeType::Null) :
						   YAML::Node (key.isBinary () ? "Unsupported binary value!" : key.getString ());
}

/**
 * @brief This function creates a YAML Node containing a key value and optionally metadata.
 *
 * @param key This key specifies the data that should be saved in the YAML node returned by this function.
 *
 * @note Since YAML does not support non-empty binary data directly this function replaces data stored in binary keys with the string
 *       `Unsupported binary value!`. If you need support for binary data, please load the Base64 before you use YAML CPP.
 *
 * @returns A new YAML node containing the data and metadata specified in `key`
 */
YAML::Node createLeafNode (Key & key)
{

	YAML::Node metaNode{ YAML::Node (YAML::NodeType::Map) };
	YAML::Node dataNode = createMetaDataNode (key);

	key.rewindMeta ();
	while (Key meta = key.nextMeta ())
	{
		if (meta.getName () == "array" || meta.getName () == "binary") continue;
		if (meta.getName () == "type" && meta.getString () == "binary")
		{
			dataNode.SetTag ("tag:yaml.org,2002:binary");
			continue;
		}
		metaNode[meta.getName ()] = meta.getString ();
		ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", meta.getName ().c_str (), meta.getString ().c_str ());
	}

	if (metaNode.size () <= 0)
	{
		ELEKTRA_LOG_DEBUG ("Return leaf node with value “%s”",
				   dataNode.IsNull () ? "~" : dataNode.IsSequence () ? "[]" : dataNode.as<string> ().c_str ());
		return dataNode;
	}

	YAML::Node node{ YAML::Node (YAML::NodeType::Sequence) };
	node.SetTag ("!elektra/meta");
	node.push_back (dataNode);
	node.push_back (metaNode);

#ifdef HAVE_LOGGER
	ostringstream data;
	data << node;
	ELEKTRA_LOG_DEBUG ("Return meta leaf node with value “%s”", data.str ().c_str ());
#endif

	return node;
}

/**
 * @brief This function adds `null` elements to the given YAML collection.
 *
 * @param sequence This node stores the collection to which this function adds `numberOfElements` empty elements.
 * @param numberOfElements This parameter specifies the number of empty element this function adds to `sequence`.
 */
void addEmptyArrayElements (YAML::Node & sequence, unsigned long long const numberOfElements)
{
	ELEKTRA_LOG_DEBUG ("Add %lld empty array elements", numberOfElements);
	for (auto missingFields = numberOfElements; missingFields > 0; missingFields--)
	{
		sequence.push_back ({});
	}
}

/**
 * @brief This function adds a key to a YAML node.
 *
 * @param data This node stores the data specified via `keyIterator`.
 * @param keyIterator This iterator specifies the current part of the key name this function adds to `data`.
 * @param key This parameter specifies the key that should be added to `data`.
 */
void addKey (YAML::Node & data, NameIterator & keyIterator, Key & key)
{
	auto const isArrayAndIndex = isArrayIndex (keyIterator);
	auto const isArray = isArrayAndIndex.first;
	auto const arrayIndex = isArrayAndIndex.second;

	if (data.IsScalar ()) data = YAML::Node (YAML::NodeType::Undefined);

#ifdef HAVE_LOGGER
	ostringstream output;
	output << data;
	ELEKTRA_LOG_DEBUG ("Add key part “%s” to node “%s”", (*keyIterator).c_str (), output.str ().c_str ());
#endif

	if (keyIterator == key.end ())
	{
		ELEKTRA_LOG_DEBUG ("Create leaf node for key “%s”", key.getName ().c_str ());
		data = createLeafNode (key);
		return;
	}
	if (keyIterator == --key.end ())
	{
		if (isArray)
		{
			addEmptyArrayElements (data, arrayIndex - data.size ());
			data.push_back (createLeafNode (key));
		}
		else
		{
			data[*keyIterator] = createLeafNode (key);
		}

		return;
	}

	YAML::Node node;

	if (isArray)
	{
		node = (data[arrayIndex] && !data[arrayIndex].IsScalar ()) ? data[arrayIndex] : YAML::Node ();
		data[arrayIndex] = node;
	}
	else
	{
		node = (data[*keyIterator] && !data[*keyIterator].IsScalar ()) ? data[*keyIterator] : YAML::Node ();
		data[*keyIterator] = node;
	}
	addKey (node, ++keyIterator, key);
}

/**
 * @brief This function adds a key set to a YAML node.
 *
 * @param data This node stores the data specified via `mappings`.
 * @param mappings This keyset specifies all keys and values this function adds to `data`.
 * @param parent This key is the root of all keys stored in `mappings`.
 */
void addKeys (YAML::Node & data, KeySet const & mappings, Key const & parent)
{
	for (auto key : mappings)
	{
		ELEKTRA_LOG_DEBUG ("Convert key “%s”: “%s”", key.getName ().c_str (),
				   key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
		NameIterator keyIterator = relativeKeyIterator (key, parent);
		addKey (data, keyIterator, key);

#ifdef HAVE_LOGGER
		ostringstream output;
		output << data;

		ELEKTRA_LOG_DEBUG ("Converted Data:");
		ELEKTRA_LOG_DEBUG ("__________");

		istringstream stream (output.str ());
		for (string line; std::getline (stream, line);)
		{
			ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
		}

		ELEKTRA_LOG_DEBUG ("__________");
#endif
	}
}

} // end namespace

/**
 * @brief This function saves the key-value pairs stored in `mappings` as YAML data in the location specified via `parent`.
 *
 * @param mappings This key set stores the mappings that should be saved as YAML data.
 * @param parent This key specifies the path to the YAML data file that should be written.
 */
void yamlcpp::yamlWrite (KeySet const & mappings, Key const & parent)
{
	ofstream output (parent.getString ());
	auto data = YAML::Node ();
	addKeys (data, mappings, parent);

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Write Data:");
	ELEKTRA_LOG_DEBUG ("__________");

	ostringstream outputString;
	outputString << data;
	istringstream stream (outputString.str ());
	for (string line; std::getline (stream, line);)
	{
		ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
	}

	ELEKTRA_LOG_DEBUG ("__________");
#endif

	output << data;
}
<|endoftext|>


<commit_before>#include "heap_snapshot.h"
#include "heap_output_stream.h"
#include "heap_graph_node.h"

namespace nodex {
  using v8::Array;
  using v8::Handle;
  using v8::HeapSnapshot;
  using v8::HeapGraphNode;
  using v8::HeapGraphEdge;
  using v8::Integer;
  using v8::Local;
  using v8::Object;
  using v8::ObjectTemplate;
  using v8::FunctionTemplate;
  using v8::SnapshotObjectId;
  using v8::String;
  using v8::Function;
  using v8::Value;

  Nan::Persistent<ObjectTemplate> Snapshot::snapshot_template_;
  Nan::Persistent<Object> Snapshot::snapshots;

  NAN_METHOD(Snapshot_EmptyMethod) {
  }

  void Snapshot::Initialize () {
    Nan::HandleScope scope;

    Local<FunctionTemplate> f = Nan::New<FunctionTemplate>(Snapshot_EmptyMethod);
    Local<ObjectTemplate> o = f->InstanceTemplate();
    o->SetInternalFieldCount(1);
    Nan::SetAccessor(o, Nan::New<String>("root").ToLocalChecked(), Snapshot::GetRoot);
    Nan::SetMethod(o, "getNode", Snapshot::GetNode);
    Nan::SetMethod(o, "getNodeById", Snapshot::GetNodeById);
    Nan::SetMethod(o, "delete", Snapshot::Delete);
    Nan::SetMethod(o, "serialize", Snapshot::Serialize);
    snapshot_template_.Reset(o);
  }

  NAN_GETTER(Snapshot::GetRoot) {
    Local<Object> _root;
    Local<String> __root = Nan::New<String>("_root").ToLocalChecked();
    if (info.This()->Has(__root)) {
      Local<Value> root;
      Nan::GetPrivate(info.This(), __root).ToLocal(&root);
      info.GetReturnValue().Set(root);
    } else {
      void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
      Local<Value> _root = GraphNode::New(static_cast<HeapSnapshot*>(ptr)->GetRoot());
      Nan::SetPrivate(info.This(), __root, _root);
      info.GetReturnValue().Set(_root);
    }
  }

  NAN_METHOD(Snapshot::GetNode) {
    if (!info.Length()) {
      return Nan::ThrowError("No index specified");
    } else if (!info[0]->IsInt32()) {
      return Nan::ThrowTypeError("Argument must be an integer");
    }

    int32_t index = info[0]->Int32Value();
    void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
    info.GetReturnValue().Set(GraphNode::New(static_cast<HeapSnapshot*>(ptr)->GetNode(index)));
  }

  NAN_METHOD(Snapshot::GetNodeById) {
    if (!info.Length()) {
      return Nan::ThrowError("No id specified");
    } else if (!info[0]->IsInt32()) {
      return Nan::ThrowTypeError("Argument must be an integer");
    }

    SnapshotObjectId id = info[0]->Int32Value();
    void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
    info.GetReturnValue().Set(GraphNode::New(static_cast<HeapSnapshot*>(ptr)->GetNodeById(id)));
  }

  NAN_METHOD(Snapshot::Serialize) {
    void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
    if (info.Length() < 2) {
      return Nan::ThrowError("Invalid number of arguments");
    } else if (!info[0]->IsFunction() || !info[1]->IsFunction()) {
      return Nan::ThrowTypeError("Arguments must be a functions");
    }

    Local<Function> iterator = Local<Function>::Cast(info[0]);
    Local<Function> callback = Local<Function>::Cast(info[1]);

    OutputStreamAdapter *stream = new OutputStreamAdapter(iterator, callback);
    static_cast<HeapSnapshot*>(ptr)->Serialize(stream, HeapSnapshot::kJSON);
  }

  NAN_METHOD(Snapshot::Delete) {
    void* ptr = Nan::GetInternalFieldPointer(info.Holder(), 0);

    Local<Object> snapshots = Nan::New<Object>(Snapshot::snapshots);

    Local<String> __uid = Nan::New<String>("uid").ToLocalChecked();
    Local<Integer> _uid = Nan::To<Integer>(Nan::Get(info.Holder(), __uid).ToLocalChecked()).ToLocalChecked();
    Nan::Delete(snapshots, _uid->Value());

    static_cast<HeapSnapshot*>(ptr)->Delete();
    info.GetReturnValue().Set(snapshots);
  }

  Local<Value> Snapshot::New(const HeapSnapshot* node) {
    Nan::EscapableHandleScope scope;

    if (snapshot_template_.IsEmpty()) {
      Snapshot::Initialize();
    }

    Local<Object> snapshot = Nan::New(snapshot_template_)->NewInstance();
    Nan::SetInternalFieldPointer(snapshot, 0, const_cast<HeapSnapshot*>(node));

    Local<Value> HEAP = Nan::New<String>("HEAP").ToLocalChecked();

    // starting with iojs 3 GetUid() and GetTitle() APIs were removed
    uint32_t _uid = node->GetMaxSnapshotJSObjectId();

    char _title[32];
    sprintf(_title, "Snapshot %i", _uid);
    Local<String> title = Nan::New<String>(_title).ToLocalChecked();

    Local<Value> uid = Nan::New<Integer>(_uid);
    Local<Integer> nodesCount = Nan::New<Integer>(node->GetNodesCount());
    Local<Integer> objectId = Nan::New<Integer>(node->GetMaxSnapshotJSObjectId());

    snapshot->Set(Nan::New<String>("typeId").ToLocalChecked(), HEAP);
    snapshot->Set(Nan::New<String>("title").ToLocalChecked(), title);
    snapshot->Set(Nan::New<String>("uid").ToLocalChecked(), uid);
    snapshot->Set(Nan::New<String>("nodesCount").ToLocalChecked(), nodesCount);
    snapshot->Set(Nan::New<String>("maxSnapshotJSObjectId").ToLocalChecked(), objectId);

    Local<Object> snapshots = Nan::New<Object>(Snapshot::snapshots);
    Nan::Set(snapshots, _uid, snapshot);

    return scope.Escape(snapshot);
  }
}
<commit_msg>fix: warning (#107)<commit_after>#include "heap_snapshot.h"
#include "heap_output_stream.h"
#include "heap_graph_node.h"

namespace nodex {
  using v8::Array;
  using v8::Handle;
  using v8::HeapSnapshot;
  using v8::HeapGraphNode;
  using v8::HeapGraphEdge;
  using v8::Integer;
  using v8::Local;
  using v8::Object;
  using v8::ObjectTemplate;
  using v8::FunctionTemplate;
  using v8::SnapshotObjectId;
  using v8::String;
  using v8::Function;
  using v8::Value;

  Nan::Persistent<ObjectTemplate> Snapshot::snapshot_template_;
  Nan::Persistent<Object> Snapshot::snapshots;

  NAN_METHOD(Snapshot_EmptyMethod) {
  }

  void Snapshot::Initialize () {
    Nan::HandleScope scope;

    Local<FunctionTemplate> f = Nan::New<FunctionTemplate>(Snapshot_EmptyMethod);
    Local<ObjectTemplate> o = f->InstanceTemplate();
    o->SetInternalFieldCount(1);
    Nan::SetAccessor(o, Nan::New<String>("root").ToLocalChecked(), Snapshot::GetRoot);
    Nan::SetMethod(o, "getNode", Snapshot::GetNode);
    Nan::SetMethod(o, "getNodeById", Snapshot::GetNodeById);
    Nan::SetMethod(o, "delete", Snapshot::Delete);
    Nan::SetMethod(o, "serialize", Snapshot::Serialize);
    snapshot_template_.Reset(o);
  }

  NAN_GETTER(Snapshot::GetRoot) {
    Local<Object> _root;
    Local<String> __root = Nan::New<String>("_root").ToLocalChecked();
    if (info.This()->Has(__root)) {
      Local<Value> root = Nan::GetPrivate(info.This(), __root).ToLocalChecked();
      info.GetReturnValue().Set(root);
    } else {
      void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
      Local<Value> _root = GraphNode::New(static_cast<HeapSnapshot*>(ptr)->GetRoot());
      Nan::SetPrivate(info.This(), __root, _root);
      info.GetReturnValue().Set(_root);
    }
  }

  NAN_METHOD(Snapshot::GetNode) {
    if (!info.Length()) {
      return Nan::ThrowError("No index specified");
    } else if (!info[0]->IsInt32()) {
      return Nan::ThrowTypeError("Argument must be an integer");
    }

    int32_t index = info[0]->Int32Value();
    void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
    info.GetReturnValue().Set(GraphNode::New(static_cast<HeapSnapshot*>(ptr)->GetNode(index)));
  }

  NAN_METHOD(Snapshot::GetNodeById) {
    if (!info.Length()) {
      return Nan::ThrowError("No id specified");
    } else if (!info[0]->IsInt32()) {
      return Nan::ThrowTypeError("Argument must be an integer");
    }

    SnapshotObjectId id = info[0]->Int32Value();
    void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
    info.GetReturnValue().Set(GraphNode::New(static_cast<HeapSnapshot*>(ptr)->GetNodeById(id)));
  }

  NAN_METHOD(Snapshot::Serialize) {
    void* ptr = Nan::GetInternalFieldPointer(info.This(), 0);
    if (info.Length() < 2) {
      return Nan::ThrowError("Invalid number of arguments");
    } else if (!info[0]->IsFunction() || !info[1]->IsFunction()) {
      return Nan::ThrowTypeError("Arguments must be a functions");
    }

    Local<Function> iterator = Local<Function>::Cast(info[0]);
    Local<Function> callback = Local<Function>::Cast(info[1]);

    OutputStreamAdapter *stream = new OutputStreamAdapter(iterator, callback);
    static_cast<HeapSnapshot*>(ptr)->Serialize(stream, HeapSnapshot::kJSON);
  }

  NAN_METHOD(Snapshot::Delete) {
    void* ptr = Nan::GetInternalFieldPointer(info.Holder(), 0);

    Local<Object> snapshots = Nan::New<Object>(Snapshot::snapshots);

    Local<String> __uid = Nan::New<String>("uid").ToLocalChecked();
    Local<Integer> _uid = Nan::To<Integer>(Nan::Get(info.Holder(), __uid).ToLocalChecked()).ToLocalChecked();
    Nan::Delete(snapshots, _uid->Value());

    static_cast<HeapSnapshot*>(ptr)->Delete();
    info.GetReturnValue().Set(snapshots);
  }

  Local<Value> Snapshot::New(const HeapSnapshot* node) {
    Nan::EscapableHandleScope scope;

    if (snapshot_template_.IsEmpty()) {
      Snapshot::Initialize();
    }

    Local<Object> snapshot = Nan::New(snapshot_template_)->NewInstance();
    Nan::SetInternalFieldPointer(snapshot, 0, const_cast<HeapSnapshot*>(node));

    Local<Value> HEAP = Nan::New<String>("HEAP").ToLocalChecked();

    // starting with iojs 3 GetUid() and GetTitle() APIs were removed
    uint32_t _uid = node->GetMaxSnapshotJSObjectId();

    char _title[32];
    sprintf(_title, "Snapshot %i", _uid);
    Local<String> title = Nan::New<String>(_title).ToLocalChecked();

    Local<Value> uid = Nan::New<Integer>(_uid);
    Local<Integer> nodesCount = Nan::New<Integer>(node->GetNodesCount());
    Local<Integer> objectId = Nan::New<Integer>(node->GetMaxSnapshotJSObjectId());

    snapshot->Set(Nan::New<String>("typeId").ToLocalChecked(), HEAP);
    snapshot->Set(Nan::New<String>("title").ToLocalChecked(), title);
    snapshot->Set(Nan::New<String>("uid").ToLocalChecked(), uid);
    snapshot->Set(Nan::New<String>("nodesCount").ToLocalChecked(), nodesCount);
    snapshot->Set(Nan::New<String>("maxSnapshotJSObjectId").ToLocalChecked(), objectId);

    Local<Object> snapshots = Nan::New<Object>(Snapshot::snapshots);
    Nan::Set(snapshots, _uid, snapshot);

    return scope.Escape(snapshot);
  }
}
<|endoftext|>


<commit_before>//===--- Mangle.cpp - Swift Name Mangling --------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements declaration name mangling in Swift.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Attr.h"
#include "swift/AST/Types.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"

#include "IRGenModule.h"
#include "Linking.h"
#include "IRGen.h"

using namespace swift;
using namespace irgen;

/// Translate the given operator character into its mangled form.
///
/// Current operator characters:    /=-+*%<>!&|^~ and the special operator '..'
static char mangleOperatorChar(char op) {
  switch (op) {
  case '&': return 'a'; // 'and'
  case '/': return 'd'; // 'divide'
  case '=': return 'e'; // 'equal'
  case '>': return 'g'; // 'greater'
  case '<': return 'l'; // 'less'
  case '*': return 'm'; // 'multiply'
  case '!': return 'n'; // 'negate'
  case '|': return 'o'; // 'or'
  case '+': return 'p'; // 'plus'
  case '%': return 'r'; // 'remainder'
  case '-': return 's'; // 'subtract'
  case '^': return 'x'; // 'xor'
  case '~': return 't'; // 'tilde'
  case '.': return 'z'; // 'period'
  default: llvm_unreachable("bad identifier character");
  }
}

namespace {
  /// A class for mangling declarations.
  class Mangler {
    raw_ostream &Buffer;
    llvm::DenseMap<void*, unsigned> Substitutions;

  public:
    Mangler(raw_ostream &buffer) : Buffer(buffer) {}
    void mangleDeclName(ValueDecl *decl);
    void mangleType(Type type, ExplosionKind kind, unsigned uncurryingLevel);

  private:
    void mangleDeclContext(DeclContext *ctx);
    void mangleIdentifier(Identifier ident);
    bool tryMangleSubstitution(void *ptr);
    void addSubstitution(void *ptr);
  };
}

/// Mangle an identifier into the buffer.
void Mangler::mangleIdentifier(Identifier ident) {
  StringRef str = ident.str();
  assert(!str.empty() && "mangling an empty identifier!");

  // Mangle normal identifiers as
  //   count identifier-char+
  // where the count is the number of characters in the identifier,
  // and where individual identifier characters represent themselves.
  if (!ident.isOperator()) {
    Buffer << str.size() << str;
    return;
  }

  // Mangle operator identifiers as
  //   'op' count operator-char+
  // where the count is the number of characters in the operator,
  // and where the individual operator characters are translated.
  Buffer << "op";

  Buffer << str.size();
  for (unsigned i = 0, e = str.size(); i != e; ++i) {
    Buffer << mangleOperatorChar(str[i]);
  }
}

bool Mangler::tryMangleSubstitution(void *ptr) {
  auto ir = Substitutions.find(ptr);
  if (ir == Substitutions.end()) return false;

  // substitution ::= 'S' integer? '_'

  unsigned index = ir->second;
  Buffer << 'S';
  if (index) Buffer << (index - 1);
  Buffer << '_';
  return true;
}

void Mangler::addSubstitution(void *ptr) {
  Substitutions.insert(std::make_pair(ptr, Substitutions.size()));
}

void Mangler::mangleDeclContext(DeclContext *ctx) {
  switch (ctx->getContextKind()) {
  case DeclContextKind::BuiltinModule:
    llvm_unreachable("mangling member of builtin module!");

  case DeclContextKind::TranslationUnit: {
    Module *module = cast<Module>(ctx);

    // Try the special 'swift' substitution.
    // context ::= Ss
    if (!module->getParent() && module->Name.str() == "swift") {
      Buffer << "Ss";
      return;
    }

    // context ::= substitution identifier*
    // context ::= identifier+

    if (tryMangleSubstitution(module)) return;

    if (DeclContext *parent = module->getParent())
      mangleDeclContext(parent);

    // This should work, because the language should be restricting
    // the name of a module to be a valid language identifier.
    mangleIdentifier(module->Name);
    addSubstitution(module);
    return;
  }

  case DeclContextKind::DistinctTypeDecl: {
    if (isa<OneOfDecl>(ctx) || isa<StructDecl>(ctx) || isa<ClassDecl>(ctx)) {
      // FIXME: The mangling rule here is kind of weird.
      mangleType(cast<DistinctTypeDecl>(ctx)->getDeclaredType(),
                 ExplosionKind::Minimal, 0);
      return;
    }
    // It's not clear what would be mangled for protocols.
    llvm_unreachable("Unexpected context");
  }

  case DeclContextKind::ExtensionDecl:
    // Mandle the extension as the original type.
    mangleType(cast<ExtensionDecl>(ctx)->getExtendedType(),
               ExplosionKind::Minimal, 0);
    return;

  case DeclContextKind::CapturingExpr:
  case DeclContextKind::TopLevelCodeDecl:
    // FIXME: we don't need to agree about these across components, but
    // that's no excuse for not mangling *something* in here.
    break;
  }

  llvm_unreachable("bad decl context");
}

void Mangler::mangleDeclName(ValueDecl *decl) {
  // decl ::= context identifier
  mangleDeclContext(decl->getDeclContext());

  // Special case: mangle getters and setters.
  // FIXME: Come up with a more sane mangling.
  if (FuncDecl *Func = dyn_cast<FuncDecl>(decl)) {
    if (Decl *D = Func->getGetterDecl()) {
      if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
        Buffer << "__get";
        mangleIdentifier(Var->getName());
        return;
      }

      assert(isa<SubscriptDecl>(D) && "Unknown getter kind?");
      Buffer << "__subset";
      return;
    }

    if (Decl *D = Func->getSetterDecl()) {
      if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
        Buffer << "__set";
        mangleIdentifier(Var->getName());
        return;
      }

      assert(isa<SubscriptDecl>(D) && "Unknown setter kind?");
      Buffer << "__subget";
      return;
    }
  }

  mangleIdentifier(decl->getName());
}

/// Mangle a type into the buffer.
void Mangler::mangleType(Type type, ExplosionKind explosion,
                         unsigned uncurryLevel) {
  TypeBase *base = type.getPointer();

  switch (base->getKind()) {
  case TypeKind::Error:
    llvm_unreachable("mangling error type");
  case TypeKind::UnstructuredDependent:
    llvm_unreachable("mangling dependent type");

  case TypeKind::MetaType:
    llvm_unreachable("Cannot mangle metatype yet");
  case TypeKind::Module:
    llvm_unreachable("Cannot mangle module type yet");

  // We don't care about these types being a bit verbose because we
  // don't expect them to come up that often in API names.
  case TypeKind::BuiltinFloat:
    switch (cast<BuiltinFloatType>(type)->getFPKind()) {
    case BuiltinFloatType::IEEE16: Buffer << "f16"; return;
    case BuiltinFloatType::IEEE32: Buffer << "f32"; return;
    case BuiltinFloatType::IEEE64: Buffer << "f64"; return;
    case BuiltinFloatType::IEEE80: Buffer << "f80"; return;
    case BuiltinFloatType::IEEE128: Buffer << "f128"; return;
    case BuiltinFloatType::PPC128: llvm_unreachable("ppc128 not supported");
    }
    llvm_unreachable("bad floating-point kind");
  case TypeKind::BuiltinInteger:
    Buffer << "i" << cast<BuiltinIntegerType>(type)->getBitWidth();
    return;
  case TypeKind::BuiltinRawPointer:
    Buffer << "p";
    return;
  case TypeKind::BuiltinObjectPointer:
    Buffer << "o";
    return;

#define SUGARED_TYPE(id, parent) \
  case TypeKind::id: \
    return mangleType(cast<id##Type>(base)->getDesugaredType(), \
                      explosion, uncurryLevel);
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"

  case TypeKind::LValue:
    Buffer << 'R';
    return mangleType(cast<LValueType>(base)->getObjectType(),
                      ExplosionKind::Minimal, 0);

  case TypeKind::Tuple: {
    TupleType *tuple = cast<TupleType>(base);
    // type ::= 'T' tuple-field+ '_'
    // tuple-field ::= identifier? type
    Buffer << 'T';
    for (auto &field : tuple->getFields()) {
      if (field.hasName())
        mangleIdentifier(field.getName());
      mangleType(field.getType(), explosion, 0);
    }
    Buffer << '_';
    return;
  }

  case TypeKind::OneOf: {
    // Try to mangle the entire name as a substitution.
    // type ::= substitution
    if (tryMangleSubstitution(base))
      return;

    // type ::= 'N' decl
    Buffer << 'N';
    mangleDeclName(cast<OneOfType>(base)->getDecl());

    addSubstitution(base);
    return;
  }

  case TypeKind::Struct: {
    // Try to mangle the entire name as a substitution.
    // type ::= substitution
    if (tryMangleSubstitution(base))
      return;

    // FIXME: Leaving this the same as oneof for the moment, to avoid changing
    // the mangling, but this should probably change eventually.
    // type ::= 'N' decl
    Buffer << 'N';
    mangleDeclName(cast<StructType>(base)->getDecl());

    addSubstitution(base);
    return;
  }

  case TypeKind::Class: {
    // Try to mangle the entire name as a substitution.
    // type ::= substitution
    if (tryMangleSubstitution(base))
      return;

    // FIXME: Leaving this the same as oneof for the moment, to avoid changing
    // the mangling, but this should probably change eventually.
    // type ::= 'N' decl
    Buffer << 'N';
    mangleDeclName(cast<ClassType>(base)->getDecl());

    addSubstitution(base);
    return;
  }

  case TypeKind::Function: {
    // type ::= 'F' type type (curried)
    // type ::= 'f' type type (uncurried)
    FunctionType *fn = cast<FunctionType>(base);
    Buffer << (uncurryLevel > 0 ? 'f' : 'F');
    mangleType(fn->getInput(), explosion, 0);
    mangleType(fn->getResult(), explosion,
               (uncurryLevel > 0 ? uncurryLevel - 1 : 0));
    return;
  }

  case TypeKind::Array: {
    // type ::= 'A' integer type
    ArrayType *array = cast<ArrayType>(base);
    Buffer << 'A';
    Buffer << array->getSize();
    mangleType(array->getBaseType(), ExplosionKind::Minimal, 0);
    return;
  };

  case TypeKind::Protocol: {
    // FIXME: mangle protocol elements?
    // type ::= 'P'
    Buffer << 'P';
    return;
  }

  }
  llvm_unreachable("bad type kind");
}

void LinkEntity::mangle(raw_ostream &buffer) const {
  // mangled-name ::= '_T' identifier+ type?

  if (!TheDecl->getAttrs().AsmName.empty()) {
    buffer << TheDecl->getAttrs().AsmName;
    return;
  }

  // Add the prefix.
  buffer << "_T"; // T is for Tigger

  Mangler mangler(buffer);

  mangler.mangleDeclName(TheDecl);

  // Mangle in a type as well.  Note that we have to mangle the type
  // on all kinds of declarations, even variables, because at the
  // moment they can *all* be overloaded.
  if (ValueDecl *valueDecl = dyn_cast<ValueDecl>(TheDecl))
    if (!isa<TypeDecl>(TheDecl))
      mangler.mangleType(valueDecl->getType(),
                         getExplosionKind(),
                         getUncurryLevel());

  // Add a getter/setter suffix if applicable.
  switch (getKind()) {
  case Kind::Function: break;
  case Kind::Other: break;
  case Kind::Getter: buffer << "g"; break;
  case Kind::Setter: buffer << "s"; break;
  }

  // TODO: mangle generics information here.
}
<commit_msg>Minor fix for mangling; I wasn't trying to change the mangling of oneofs with my last commit.<commit_after>//===--- Mangle.cpp - Swift Name Mangling --------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements declaration name mangling in Swift.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Attr.h"
#include "swift/AST/Types.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"

#include "IRGenModule.h"
#include "Linking.h"
#include "IRGen.h"

using namespace swift;
using namespace irgen;

/// Translate the given operator character into its mangled form.
///
/// Current operator characters:    /=-+*%<>!&|^~ and the special operator '..'
static char mangleOperatorChar(char op) {
  switch (op) {
  case '&': return 'a'; // 'and'
  case '/': return 'd'; // 'divide'
  case '=': return 'e'; // 'equal'
  case '>': return 'g'; // 'greater'
  case '<': return 'l'; // 'less'
  case '*': return 'm'; // 'multiply'
  case '!': return 'n'; // 'negate'
  case '|': return 'o'; // 'or'
  case '+': return 'p'; // 'plus'
  case '%': return 'r'; // 'remainder'
  case '-': return 's'; // 'subtract'
  case '^': return 'x'; // 'xor'
  case '~': return 't'; // 'tilde'
  case '.': return 'z'; // 'period'
  default: llvm_unreachable("bad identifier character");
  }
}

namespace {
  /// A class for mangling declarations.
  class Mangler {
    raw_ostream &Buffer;
    llvm::DenseMap<void*, unsigned> Substitutions;

  public:
    Mangler(raw_ostream &buffer) : Buffer(buffer) {}
    void mangleDeclName(ValueDecl *decl);
    void mangleType(Type type, ExplosionKind kind, unsigned uncurryingLevel);

  private:
    void mangleDeclContext(DeclContext *ctx);
    void mangleIdentifier(Identifier ident);
    bool tryMangleSubstitution(void *ptr);
    void addSubstitution(void *ptr);
  };
}

/// Mangle an identifier into the buffer.
void Mangler::mangleIdentifier(Identifier ident) {
  StringRef str = ident.str();
  assert(!str.empty() && "mangling an empty identifier!");

  // Mangle normal identifiers as
  //   count identifier-char+
  // where the count is the number of characters in the identifier,
  // and where individual identifier characters represent themselves.
  if (!ident.isOperator()) {
    Buffer << str.size() << str;
    return;
  }

  // Mangle operator identifiers as
  //   'op' count operator-char+
  // where the count is the number of characters in the operator,
  // and where the individual operator characters are translated.
  Buffer << "op";

  Buffer << str.size();
  for (unsigned i = 0, e = str.size(); i != e; ++i) {
    Buffer << mangleOperatorChar(str[i]);
  }
}

bool Mangler::tryMangleSubstitution(void *ptr) {
  auto ir = Substitutions.find(ptr);
  if (ir == Substitutions.end()) return false;

  // substitution ::= 'S' integer? '_'

  unsigned index = ir->second;
  Buffer << 'S';
  if (index) Buffer << (index - 1);
  Buffer << '_';
  return true;
}

void Mangler::addSubstitution(void *ptr) {
  Substitutions.insert(std::make_pair(ptr, Substitutions.size()));
}

void Mangler::mangleDeclContext(DeclContext *ctx) {
  switch (ctx->getContextKind()) {
  case DeclContextKind::BuiltinModule:
    llvm_unreachable("mangling member of builtin module!");

  case DeclContextKind::TranslationUnit: {
    Module *module = cast<Module>(ctx);

    // Try the special 'swift' substitution.
    // context ::= Ss
    if (!module->getParent() && module->Name.str() == "swift") {
      Buffer << "Ss";
      return;
    }

    // context ::= substitution identifier*
    // context ::= identifier+

    if (tryMangleSubstitution(module)) return;

    if (DeclContext *parent = module->getParent())
      mangleDeclContext(parent);

    // This should work, because the language should be restricting
    // the name of a module to be a valid language identifier.
    mangleIdentifier(module->Name);
    addSubstitution(module);
    return;
  }

  case DeclContextKind::DistinctTypeDecl: {
    if (isa<OneOfDecl>(ctx)) {
      OneOfDecl *oneof = cast<OneOfDecl>(ctx);

      // FIXME: The mangling rule here is kind of weird.
      if (tryMangleSubstitution(oneof->getDeclaredType())) return;
      mangleDeclContext(ctx->getParent());
      mangleIdentifier(oneof->getName());
      addSubstitution(oneof->getDeclaredType());
      return;
    }
    if (isa<StructDecl>(ctx) || isa<ClassDecl>(ctx)) {
      // FIXME: The mangling rule here is kind of weird.
      mangleType(cast<DistinctTypeDecl>(ctx)->getDeclaredType(),
                 ExplosionKind::Minimal, 0);
      return;
    }
    // It's not clear what would be mangled for protocols.
    llvm_unreachable("Unexpected context");
  }

  case DeclContextKind::ExtensionDecl:
    // Mandle the extension as the original type.
    mangleType(cast<ExtensionDecl>(ctx)->getExtendedType(),
               ExplosionKind::Minimal, 0);
    return;

  case DeclContextKind::CapturingExpr:
  case DeclContextKind::TopLevelCodeDecl:
    // FIXME: we don't need to agree about these across components, but
    // that's no excuse for not mangling *something* in here.
    break;
  }

  llvm_unreachable("bad decl context");
}

void Mangler::mangleDeclName(ValueDecl *decl) {
  // decl ::= context identifier
  mangleDeclContext(decl->getDeclContext());

  // Special case: mangle getters and setters.
  // FIXME: Come up with a more sane mangling.
  if (FuncDecl *Func = dyn_cast<FuncDecl>(decl)) {
    if (Decl *D = Func->getGetterDecl()) {
      if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
        Buffer << "__get";
        mangleIdentifier(Var->getName());
        return;
      }

      assert(isa<SubscriptDecl>(D) && "Unknown getter kind?");
      Buffer << "__subset";
      return;
    }

    if (Decl *D = Func->getSetterDecl()) {
      if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
        Buffer << "__set";
        mangleIdentifier(Var->getName());
        return;
      }

      assert(isa<SubscriptDecl>(D) && "Unknown setter kind?");
      Buffer << "__subget";
      return;
    }
  }

  mangleIdentifier(decl->getName());
}

/// Mangle a type into the buffer.
void Mangler::mangleType(Type type, ExplosionKind explosion,
                         unsigned uncurryLevel) {
  TypeBase *base = type.getPointer();

  switch (base->getKind()) {
  case TypeKind::Error:
    llvm_unreachable("mangling error type");
  case TypeKind::UnstructuredDependent:
    llvm_unreachable("mangling dependent type");

  case TypeKind::MetaType:
    llvm_unreachable("Cannot mangle metatype yet");
  case TypeKind::Module:
    llvm_unreachable("Cannot mangle module type yet");

  // We don't care about these types being a bit verbose because we
  // don't expect them to come up that often in API names.
  case TypeKind::BuiltinFloat:
    switch (cast<BuiltinFloatType>(type)->getFPKind()) {
    case BuiltinFloatType::IEEE16: Buffer << "f16"; return;
    case BuiltinFloatType::IEEE32: Buffer << "f32"; return;
    case BuiltinFloatType::IEEE64: Buffer << "f64"; return;
    case BuiltinFloatType::IEEE80: Buffer << "f80"; return;
    case BuiltinFloatType::IEEE128: Buffer << "f128"; return;
    case BuiltinFloatType::PPC128: llvm_unreachable("ppc128 not supported");
    }
    llvm_unreachable("bad floating-point kind");
  case TypeKind::BuiltinInteger:
    Buffer << "i" << cast<BuiltinIntegerType>(type)->getBitWidth();
    return;
  case TypeKind::BuiltinRawPointer:
    Buffer << "p";
    return;
  case TypeKind::BuiltinObjectPointer:
    Buffer << "o";
    return;

#define SUGARED_TYPE(id, parent) \
  case TypeKind::id: \
    return mangleType(cast<id##Type>(base)->getDesugaredType(), \
                      explosion, uncurryLevel);
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"

  case TypeKind::LValue:
    Buffer << 'R';
    return mangleType(cast<LValueType>(base)->getObjectType(),
                      ExplosionKind::Minimal, 0);

  case TypeKind::Tuple: {
    TupleType *tuple = cast<TupleType>(base);
    // type ::= 'T' tuple-field+ '_'
    // tuple-field ::= identifier? type
    Buffer << 'T';
    for (auto &field : tuple->getFields()) {
      if (field.hasName())
        mangleIdentifier(field.getName());
      mangleType(field.getType(), explosion, 0);
    }
    Buffer << '_';
    return;
  }

  case TypeKind::OneOf: {
    // Try to mangle the entire name as a substitution.
    // type ::= substitution
    if (tryMangleSubstitution(base))
      return;

    // type ::= 'N' decl
    Buffer << 'N';
    mangleDeclName(cast<OneOfType>(base)->getDecl());

    addSubstitution(base);
    return;
  }

  case TypeKind::Struct: {
    // Try to mangle the entire name as a substitution.
    // type ::= substitution
    if (tryMangleSubstitution(base))
      return;

    // FIXME: Leaving this the same as oneof for the moment, to avoid changing
    // the mangling, but this should probably change eventually.
    // type ::= 'N' decl
    Buffer << 'N';
    mangleDeclName(cast<StructType>(base)->getDecl());

    addSubstitution(base);
    return;
  }

  case TypeKind::Class: {
    // Try to mangle the entire name as a substitution.
    // type ::= substitution
    if (tryMangleSubstitution(base))
      return;

    // FIXME: Leaving this the same as oneof for the moment, to avoid changing
    // the mangling, but this should probably change eventually.
    // type ::= 'N' decl
    Buffer << 'N';
    mangleDeclName(cast<ClassType>(base)->getDecl());

    addSubstitution(base);
    return;
  }

  case TypeKind::Function: {
    // type ::= 'F' type type (curried)
    // type ::= 'f' type type (uncurried)
    FunctionType *fn = cast<FunctionType>(base);
    Buffer << (uncurryLevel > 0 ? 'f' : 'F');
    mangleType(fn->getInput(), explosion, 0);
    mangleType(fn->getResult(), explosion,
               (uncurryLevel > 0 ? uncurryLevel - 1 : 0));
    return;
  }

  case TypeKind::Array: {
    // type ::= 'A' integer type
    ArrayType *array = cast<ArrayType>(base);
    Buffer << 'A';
    Buffer << array->getSize();
    mangleType(array->getBaseType(), ExplosionKind::Minimal, 0);
    return;
  };

  case TypeKind::Protocol: {
    // FIXME: mangle protocol elements?
    // type ::= 'P'
    Buffer << 'P';
    return;
  }

  }
  llvm_unreachable("bad type kind");
}

void LinkEntity::mangle(raw_ostream &buffer) const {
  // mangled-name ::= '_T' identifier+ type?

  if (!TheDecl->getAttrs().AsmName.empty()) {
    buffer << TheDecl->getAttrs().AsmName;
    return;
  }

  // Add the prefix.
  buffer << "_T"; // T is for Tigger

  Mangler mangler(buffer);

  mangler.mangleDeclName(TheDecl);

  // Mangle in a type as well.  Note that we have to mangle the type
  // on all kinds of declarations, even variables, because at the
  // moment they can *all* be overloaded.
  if (ValueDecl *valueDecl = dyn_cast<ValueDecl>(TheDecl))
    if (!isa<TypeDecl>(TheDecl))
      mangler.mangleType(valueDecl->getType(),
                         getExplosionKind(),
                         getUncurryLevel());

  // Add a getter/setter suffix if applicable.
  switch (getKind()) {
  case Kind::Function: break;
  case Kind::Other: break;
  case Kind::Getter: buffer << "g"; break;
  case Kind::Setter: buffer << "s"; break;
  }

  // TODO: mangle generics information here.
}
<|endoftext|>


<commit_before>/*******************************************************************************
 The MIT License (MIT)

 Copyright (c) 2014 Alexander Zolotarev <me@alex.bio> from Minsk, Belarus

 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 "../http_client.h"

#include <stdio.h>  // popen
#include <fstream>
#include <iostream>  // std::cerr
#include <stdexcept> // std::runtime_error
#include <array>

#ifdef _MSC_VER
#define popen _popen
#define pclose _pclose
#else
#include <unistd.h> // write, close
#endif

// Used as a test stub for basic HTTP client implementation.

namespace alohalytics {

struct ScopedTmpFileDeleter {
  std::string file;
  ~ScopedTmpFileDeleter() {
    if (!file.empty()) {
      ::remove(file.c_str());
    }
  }
};

std::string RunCurl(const std::string& cmd) {
  FILE* pipe = ::popen(cmd.c_str(), "r");
  assert(pipe);
  std::array<char, 8 * 1024> s;
  std::string result;
  while (nullptr != ::fgets(s.data(), s.size(), pipe)) {
    result += s.data();
  }
  const int err = ::pclose(pipe);
  if (err) {
    throw std::runtime_error("Error " + std::to_string(err) + " while calling " + cmd);
  }
  return result;
}

// TODO(AlexZ): Not a production-ready implementation.
bool HTTPClientPlatformWrapper::RunHTTPRequest() {
  // Last 3 chars in server's response will be http status code
  std::string cmd = "curl -s -w '%{http_code}' ";
  if (!content_type_.empty()) {
    cmd += "-H 'Content-Type: application/json' ";
  }

  ScopedTmpFileDeleter deleter;
  if (!post_body_.empty()) {
    // POST body through tmp file to avoid breaking command line.
#ifdef _MSC_VER
    char tmp_file[L_tmpnam];
    ::tmpnam_s(tmp_file, L_tmpnam);
    std::ofstream(tmp_file) << post_body_;
#else
    char tmp_file[] = "curltmp-XXXXXX";
    int fd = ::mkstemp(tmp_file);
    if (fd < 0) {
      return false;
    }
    ::write(fd, post_body_.data(), post_body_.size());
    ::close(fd);
#endif
    post_file_ = tmp_file;
    deleter.file = post_file_;
  }
  if (!post_file_.empty()) {
    cmd += "--data-binary @" + post_file_ + " ";
  }

  cmd += url_requested_;
  try {
    server_response_ = RunCurl(cmd);
    error_code_ = -1;
    std::string & s = server_response_;
    if (s.size() < 3) {
      return false;
    }
    // Extract http status code from the last response line.
    error_code_ = std::stoi(s.substr(s.size() - 3));
    s.resize(s.size() - 3);

    if (!received_file_.empty()) {
      std::ofstream file(received_file_);
      file.exceptions(std::ios::failbit | std::ios::badbit);
      file << server_response_;
    }
  } catch (std::exception const & ex) {
    std::cerr << "Exception " << ex.what() << std::endl;
    return false;
  }
  return true;
}

}  // namespace aloha
<commit_msg>More reliable http curl impl for Posix<commit_after>/*******************************************************************************
 The MIT License (MIT)

 Copyright (c) 2014 Alexander Zolotarev <me@alex.bio> from Minsk, Belarus

 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 "../http_client.h"

#include <stdio.h>  // popen
#include <fstream>
#include <iostream>  // std::cerr
#include <stdexcept> // std::runtime_error
#include <array>

#ifdef _MSC_VER
#define popen _popen
#define pclose _pclose
#else
#include <unistd.h> // write, close
#endif

// Used as a test stub for basic HTTP client implementation.

namespace alohalytics {

struct ScopedTmpFileDeleter {
  std::string file;
  ~ScopedTmpFileDeleter() {
    if (!file.empty()) {
      ::remove(file.c_str());
    }
  }
};

std::string RunCurl(const std::string& cmd) {
  FILE* pipe = ::popen(cmd.c_str(), "r");
  assert(pipe);
  std::array<char, 8 * 1024> s;
  std::string result;
  while (nullptr != ::fgets(s.data(), s.size(), pipe)) {
    result += s.data();
  }
  const int err = ::pclose(pipe);
  if (err) {
    throw std::runtime_error("Error " + std::to_string(err) + " while calling " + cmd);
  }
  return result;
}

// TODO(AlexZ): Not a production-ready implementation.
bool HTTPClientPlatformWrapper::RunHTTPRequest() {
  // Last 3 chars in server's response will be http status code
  std::string cmd = "curl -s -w '%{http_code}' ";
  if (!content_type_.empty()) {
    cmd += "-H 'Content-Type: application/json' ";
  }

  ScopedTmpFileDeleter deleter;
  if (!post_body_.empty()) {
    // POST body through tmp file to avoid breaking command line.
#ifdef _MSC_VER
    char tmp_file[L_tmpnam];
    ::tmpnam_s(tmp_file, L_tmpnam);
    std::ofstream(tmp_file) << post_body_;
#else
    char tmp_file[] = "curltmp-XXXXXX";
    int fd = ::mkstemp(tmp_file);
    if (fd < 0) {
      return false;
    }
    ssize_t const written = ::write(fd, post_body_.data(), post_body_.size());
    ::close(fd);
    if (written != static_cast<ssize_t>(post_body_.size())) {
      return false;
    }
#endif
    post_file_ = tmp_file;
    deleter.file = post_file_;
  }
  if (!post_file_.empty()) {
    cmd += "--data-binary @" + post_file_ + " ";
  }

  cmd += url_requested_;
  try {
    server_response_ = RunCurl(cmd);
    error_code_ = -1;
    std::string & s = server_response_;
    if (s.size() < 3) {
      return false;
    }
    // Extract http status code from the last response line.
    error_code_ = std::stoi(s.substr(s.size() - 3));
    s.resize(s.size() - 3);

    if (!received_file_.empty()) {
      std::ofstream file(received_file_);
      file.exceptions(std::ios::failbit | std::ios::badbit);
      file << server_response_;
    }
  } catch (std::exception const & ex) {
    std::cerr << "Exception " << ex.what() << std::endl;
    return false;
  }
  return true;
}

}  // namespace aloha
<|endoftext|>


<commit_before>#include <pddlparse/detail/parsing/PrimitiveType.h>

#include <pddlparse/Exception.h>
#include <pddlparse/detail/Requirements.h>

namespace pddl
{
namespace detail
{

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PrimitiveType
//
////////////////////////////////////////////////////////////////////////////////////////////////////

ast::PrimitiveTypePointer parsePrimitiveType(Context &context, ast::Domain &domain)
{
	auto &tokenizer = context.tokenizer;
	auto &types = domain.types;

	auto typeName = tokenizer.getIdentifier();

	if (typeName.empty())
		throw ParserException(tokenizer, "could not parse primitive type, expected identifier");

	auto matchingType = std::find_if(types.begin(), types.end(),
		[&](auto &primitiveTypeDeclaration)
		{
			return primitiveTypeDeclaration->name == typeName;
		});

	// If the type has not been declared yet, add it but issue a warning
	if (matchingType == types.end())
	{
		if (context.mode != Mode::Compatibility)
			throw ParserException(tokenizer, "primitive type “" + typeName + "” used without or before declaration");

		context.warningCallback(tokenizer, "primitive type “" + typeName + "” used without or before declaration, silently adding declaration");

		types.emplace_back(std::make_unique<ast::PrimitiveTypeDeclaration>(std::move(typeName)));

		return std::make_unique<ast::PrimitiveType>(types.back().get());
	}

	return std::make_unique<ast::PrimitiveType>(matchingType->get());
}

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

}
}
<commit_msg>Allowing not declaring implicit “object” type without warning or error.<commit_after>#include <pddlparse/detail/parsing/PrimitiveType.h>

#include <pddlparse/Exception.h>
#include <pddlparse/detail/Requirements.h>

namespace pddl
{
namespace detail
{

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PrimitiveType
//
////////////////////////////////////////////////////////////////////////////////////////////////////

ast::PrimitiveTypePointer parsePrimitiveType(Context &context, ast::Domain &domain)
{
	auto &tokenizer = context.tokenizer;
	auto &types = domain.types;

	auto typeName = tokenizer.getIdentifier();

	if (typeName.empty())
		throw ParserException(tokenizer, "could not parse primitive type, expected identifier");

	auto matchingType = std::find_if(types.begin(), types.end(),
		[&](auto &primitiveTypeDeclaration)
		{
			return primitiveTypeDeclaration->name == typeName;
		});

	// If the type has not been declared yet, add it but issue a warning
	if (matchingType == types.end())
	{
		// “object” type is always allowed without warning
		if (typeName != "object")
		{
			if (context.mode != Mode::Compatibility)
				throw ParserException(tokenizer, "primitive type “" + typeName + "” used without or before declaration");

			context.warningCallback(tokenizer, "primitive type “" + typeName + "” used without or before declaration, silently adding declaration");
		}

		types.emplace_back(std::make_unique<ast::PrimitiveTypeDeclaration>(std::move(typeName)));

		return std::make_unique<ast::PrimitiveType>(types.back().get());
	}

	return std::make_unique<ast::PrimitiveType>(matchingType->get());
}

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

}
}
<|endoftext|>